started using ratatui
This commit is contained in:
parent
24ac0a0fbe
commit
55dfd16cd2
@ -5,4 +5,5 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.42"
|
chrono = "0.4.42"
|
||||||
|
ratatui = "0.29.0"
|
||||||
sqlite = "0.37.0"
|
sqlite = "0.37.0"
|
||||||
|
|||||||
29
src/app.rs
Normal file
29
src/app.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use sqlite::Connection;
|
||||||
|
|
||||||
|
pub enum CurrentScreen {
|
||||||
|
Main,
|
||||||
|
Exiting,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct App {
|
||||||
|
pub current_screen: CurrentScreen,
|
||||||
|
connection: Connection,
|
||||||
|
exit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn new() -> App {
|
||||||
|
let connection = match Connection::open("ft_rs.db") {
|
||||||
|
Ok(con) => con,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error opening database: {}", e);
|
||||||
|
panic!("stopping");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return App {
|
||||||
|
current_screen: CurrentScreen::Main,
|
||||||
|
connection: connection,
|
||||||
|
exit: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
332
src/main.rs
332
src/main.rs
@ -1,150 +1,214 @@
|
|||||||
|
mod app;
|
||||||
mod data_layer;
|
mod data_layer;
|
||||||
mod entities;
|
mod entities;
|
||||||
use chrono::{Local, TimeZone};
|
mod ui;
|
||||||
use sqlite::Connection;
|
use crate::{
|
||||||
use std::io;
|
app::{App, CurrentScreen},
|
||||||
|
ui::ui,
|
||||||
|
};
|
||||||
|
use ratatui::{
|
||||||
|
Terminal,
|
||||||
|
crossterm::{
|
||||||
|
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
||||||
|
execute,
|
||||||
|
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
||||||
|
},
|
||||||
|
prelude::Backend,
|
||||||
|
};
|
||||||
|
use std::{error::Error, io};
|
||||||
|
|
||||||
use crate::entities::{Account, Transaction, TransactionType};
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
enable_raw_mode()?;
|
||||||
|
let mut stderr = io::stderr();
|
||||||
|
execute!(stderr, EnterAlternateScreen, EnableMouseCapture)?;
|
||||||
|
|
||||||
fn main() {
|
let mut terminal = ratatui::init();
|
||||||
let connection = match Connection::open("ft_rs.db") {
|
let mut app = App::new();
|
||||||
Ok(con) => con,
|
let res = run_app(&mut terminal, &mut app);
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error opening database: {}", e);
|
disable_raw_mode()?;
|
||||||
return;
|
execute!(
|
||||||
|
terminal.backend_mut(),
|
||||||
|
LeaveAlternateScreen,
|
||||||
|
DisableMouseCapture
|
||||||
|
)?;
|
||||||
|
terminal.show_cursor()?;
|
||||||
|
|
||||||
|
if let Err(err) = res {
|
||||||
|
println!("{err:?}");
|
||||||
}
|
}
|
||||||
};
|
return Ok(());
|
||||||
|
}
|
||||||
data_layer::setup(&connection);
|
|
||||||
|
|
||||||
|
fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<bool> {
|
||||||
loop {
|
loop {
|
||||||
println!("Please choose an option:");
|
terminal.draw(|f| ui(f, app))?;
|
||||||
println!("1. List accounts");
|
|
||||||
println!("2. Add an account");
|
|
||||||
println!("3. List transaction types");
|
|
||||||
println!("4. Add a transaction type");
|
|
||||||
println!("5. List transactions");
|
|
||||||
println!("6. Add a transaction");
|
|
||||||
println!("0. Exit");
|
|
||||||
|
|
||||||
let mut choice = String::new();
|
// S'occupe des keyevents
|
||||||
io::stdin()
|
if let Event::Key(key) = event::read()? {
|
||||||
.read_line(&mut choice)
|
if key.kind == event::KeyEventKind::Release {
|
||||||
.expect("Failed to read line");
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let choice = choice.trim();
|
match app.current_screen {
|
||||||
|
CurrentScreen::Main => match key.code {
|
||||||
match choice {
|
KeyCode::Char('q') => {
|
||||||
"1" => {
|
app.current_screen = CurrentScreen::Exiting;
|
||||||
let accounts = data_layer::get_accounts(&connection);
|
|
||||||
let mut total = 0.0;
|
|
||||||
for ac in accounts.iter() {
|
|
||||||
let ac_total = ac.get_total(&connection);
|
|
||||||
println!(
|
|
||||||
"Id: {}, Name: {}, Total: {}",
|
|
||||||
ac.get_id(),
|
|
||||||
ac.get_name(),
|
|
||||||
ac_total
|
|
||||||
);
|
|
||||||
total += ac_total;
|
|
||||||
}
|
}
|
||||||
println!("Total: {}", total);
|
_ => {}
|
||||||
}
|
},
|
||||||
"2" => {
|
CurrentScreen::Exiting => match key.code {
|
||||||
println!("Please enter the account name:");
|
KeyCode::Char('y') => {
|
||||||
let mut ac_name = String::new();
|
return Ok(true);
|
||||||
io::stdin()
|
|
||||||
.read_line(&mut ac_name)
|
|
||||||
.expect("Failed to read line");
|
|
||||||
let ac_name = ac_name.trim();
|
|
||||||
let new_ac = Account::new(0, ac_name.to_string(), entities::AccountType::Cash);
|
|
||||||
data_layer::upsert_account(&connection, new_ac);
|
|
||||||
}
|
|
||||||
"3" => {
|
|
||||||
let types = data_layer::get_transaction_types(&connection);
|
|
||||||
for t in types.iter() {
|
|
||||||
println!("Name: {}", t.get_description());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"4" => {
|
|
||||||
println!("Please enter the transaction type:");
|
|
||||||
let mut desc = String::new();
|
|
||||||
io::stdin()
|
|
||||||
.read_line(&mut desc)
|
|
||||||
.expect("Failed to read line");
|
|
||||||
let desc = desc.trim();
|
|
||||||
let tr_type = TransactionType::new(0, desc.to_string());
|
|
||||||
data_layer::upsert_transaction_type(&connection, tr_type);
|
|
||||||
}
|
|
||||||
"5" => {
|
|
||||||
println!("Please enter the account id:");
|
|
||||||
let mut ac_id_str = String::new();
|
|
||||||
io::stdin()
|
|
||||||
.read_line(&mut ac_id_str)
|
|
||||||
.expect("Failed to read line");
|
|
||||||
ac_id_str = ac_id_str.trim().to_string();
|
|
||||||
let ac_id = ac_id_str.parse::<i64>().unwrap();
|
|
||||||
let trx: Vec<entities::Transaction> =
|
|
||||||
data_layer::get_account_transactions(&connection, ac_id);
|
|
||||||
// .sort_by(|a, b| b.get_date().cmp(&a.get_date()))
|
|
||||||
// .try_into()
|
|
||||||
// .unwrap();
|
|
||||||
for t in trx.iter() {
|
|
||||||
println!(
|
|
||||||
"Date: {}, Type: {}, Description: {}, Amount: {}$",
|
|
||||||
Local.from_utc_datetime(&t.get_date().naive_local()),
|
|
||||||
t.get_type().get_description(),
|
|
||||||
t.get_desc(),
|
|
||||||
t.get_amount()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"6" => {
|
|
||||||
println!("Please enter the account id:");
|
|
||||||
let mut ac_id_str = String::new();
|
|
||||||
io::stdin()
|
|
||||||
.read_line(&mut ac_id_str)
|
|
||||||
.expect("Failed to read line");
|
|
||||||
ac_id_str = ac_id_str.trim().to_string();
|
|
||||||
let ac_id = ac_id_str.parse::<i64>().unwrap();
|
|
||||||
println!("Please enter the transaction type id:");
|
|
||||||
let mut tr_type_id_str = String::new();
|
|
||||||
io::stdin()
|
|
||||||
.read_line(&mut tr_type_id_str)
|
|
||||||
.expect("Failed to read line");
|
|
||||||
tr_type_id_str = tr_type_id_str.trim().to_string();
|
|
||||||
let type_id = tr_type_id_str.parse::<i64>().unwrap();
|
|
||||||
println!("Please enter the amount:");
|
|
||||||
let mut amnt_str = String::new();
|
|
||||||
io::stdin()
|
|
||||||
.read_line(&mut amnt_str)
|
|
||||||
.expect("Failed to read line");
|
|
||||||
amnt_str = amnt_str.trim().to_string();
|
|
||||||
let amount = amnt_str.parse::<f64>().unwrap();
|
|
||||||
println!("Please enter the description:");
|
|
||||||
let mut desc = String::new();
|
|
||||||
io::stdin()
|
|
||||||
.read_line(&mut desc)
|
|
||||||
.expect("Failed to read line");
|
|
||||||
desc = desc.trim().to_string();
|
|
||||||
let tr = Transaction::new(
|
|
||||||
0,
|
|
||||||
ac_id,
|
|
||||||
amount,
|
|
||||||
chrono::offset::Utc::now(),
|
|
||||||
desc,
|
|
||||||
type_id,
|
|
||||||
&connection,
|
|
||||||
);
|
|
||||||
data_layer::upsert_transaction(&connection, tr);
|
|
||||||
}
|
|
||||||
"0" => {
|
|
||||||
println!("Exiting...");
|
|
||||||
break; // Exit the loop
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Invalid choice.");
|
app.current_screen = CurrentScreen::Main;
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fn main() {
|
||||||
|
// let connection = match Connection::open("ft_rs.db") {
|
||||||
|
// Ok(con) => con,
|
||||||
|
// Err(e) => {
|
||||||
|
// eprintln!("Error opening database: {}", e);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// data_layer::setup(&connection);
|
||||||
|
|
||||||
|
// loop {
|
||||||
|
// println!("Please choose an option:");
|
||||||
|
// println!("1. List accounts");
|
||||||
|
// println!("2. Add an account");
|
||||||
|
// println!("3. List transaction types");
|
||||||
|
// println!("4. Add a transaction type");
|
||||||
|
// println!("5. List transactions");
|
||||||
|
// println!("6. Add a transaction");
|
||||||
|
// println!("0. Exit");
|
||||||
|
|
||||||
|
// let mut choice = String::new();
|
||||||
|
// io::stdin()
|
||||||
|
// .read_line(&mut choice)
|
||||||
|
// .expect("Failed to read line");
|
||||||
|
|
||||||
|
// let choice = choice.trim();
|
||||||
|
|
||||||
|
// match choice {
|
||||||
|
// "1" => {
|
||||||
|
// let accounts = data_layer::get_accounts(&connection);
|
||||||
|
// let mut total = 0.0;
|
||||||
|
// for ac in accounts.iter() {
|
||||||
|
// let ac_total = ac.get_total(&connection);
|
||||||
|
// println!(
|
||||||
|
// "Id: {}, Name: {}, Total: {}",
|
||||||
|
// ac.get_id(),
|
||||||
|
// ac.get_name(),
|
||||||
|
// ac_total
|
||||||
|
// );
|
||||||
|
// total += ac_total;
|
||||||
|
// }
|
||||||
|
// println!("Total: {}", total);
|
||||||
|
// }
|
||||||
|
// "2" => {
|
||||||
|
// println!("Please enter the account name:");
|
||||||
|
// let mut ac_name = String::new();
|
||||||
|
// io::stdin()
|
||||||
|
// .read_line(&mut ac_name)
|
||||||
|
// .expect("Failed to read line");
|
||||||
|
// let ac_name = ac_name.trim();
|
||||||
|
// let new_ac = Account::new(0, ac_name.to_string(), entities::AccountType::Cash);
|
||||||
|
// data_layer::upsert_account(&connection, new_ac);
|
||||||
|
// }
|
||||||
|
// "3" => {
|
||||||
|
// let types = data_layer::get_transaction_types(&connection);
|
||||||
|
// for t in types.iter() {
|
||||||
|
// println!("Name: {}", t.get_description());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// "4" => {
|
||||||
|
// println!("Please enter the transaction type:");
|
||||||
|
// let mut desc = String::new();
|
||||||
|
// io::stdin()
|
||||||
|
// .read_line(&mut desc)
|
||||||
|
// .expect("Failed to read line");
|
||||||
|
// let desc = desc.trim();
|
||||||
|
// let tr_type = TransactionType::new(0, desc.to_string());
|
||||||
|
// data_layer::upsert_transaction_type(&connection, tr_type);
|
||||||
|
// }
|
||||||
|
// "5" => {
|
||||||
|
// println!("Please enter the account id:");
|
||||||
|
// let mut ac_id_str = String::new();
|
||||||
|
// io::stdin()
|
||||||
|
// .read_line(&mut ac_id_str)
|
||||||
|
// .expect("Failed to read line");
|
||||||
|
// ac_id_str = ac_id_str.trim().to_string();
|
||||||
|
// let ac_id = ac_id_str.parse::<i64>().unwrap();
|
||||||
|
// let trx: Vec<entities::Transaction> =
|
||||||
|
// data_layer::get_account_transactions(&connection, ac_id);
|
||||||
|
// // .sort_by(|a, b| b.get_date().cmp(&a.get_date()))
|
||||||
|
// // .try_into()
|
||||||
|
// // .unwrap();
|
||||||
|
// for t in trx.iter() {
|
||||||
|
// println!(
|
||||||
|
// "Date: {}, Type: {}, Description: {}, Amount: {}$",
|
||||||
|
// Local.from_utc_datetime(&t.get_date().naive_local()),
|
||||||
|
// t.get_type().get_description(),
|
||||||
|
// t.get_desc(),
|
||||||
|
// t.get_amount()
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// "6" => {
|
||||||
|
// println!("Please enter the account id:");
|
||||||
|
// let mut ac_id_str = String::new();
|
||||||
|
// io::stdin()
|
||||||
|
// .read_line(&mut ac_id_str)
|
||||||
|
// .expect("Failed to read line");
|
||||||
|
// ac_id_str = ac_id_str.trim().to_string();
|
||||||
|
// let ac_id = ac_id_str.parse::<i64>().unwrap();
|
||||||
|
// println!("Please enter the transaction type id:");
|
||||||
|
// let mut tr_type_id_str = String::new();
|
||||||
|
// io::stdin()
|
||||||
|
// .read_line(&mut tr_type_id_str)
|
||||||
|
// .expect("Failed to read line");
|
||||||
|
// tr_type_id_str = tr_type_id_str.trim().to_string();
|
||||||
|
// let type_id = tr_type_id_str.parse::<i64>().unwrap();
|
||||||
|
// println!("Please enter the amount:");
|
||||||
|
// let mut amnt_str = String::new();
|
||||||
|
// io::stdin()
|
||||||
|
// .read_line(&mut amnt_str)
|
||||||
|
// .expect("Failed to read line");
|
||||||
|
// amnt_str = amnt_str.trim().to_string();
|
||||||
|
// let amount = amnt_str.parse::<f64>().unwrap();
|
||||||
|
// println!("Please enter the description:");
|
||||||
|
// let mut desc = String::new();
|
||||||
|
// io::stdin()
|
||||||
|
// .read_line(&mut desc)
|
||||||
|
// .expect("Failed to read line");
|
||||||
|
// desc = desc.trim().to_string();
|
||||||
|
// let tr = Transaction::new(
|
||||||
|
// 0,
|
||||||
|
// ac_id,
|
||||||
|
// amount,
|
||||||
|
// chrono::offset::Utc::now(),
|
||||||
|
// desc,
|
||||||
|
// type_id,
|
||||||
|
// &connection,
|
||||||
|
// );
|
||||||
|
// data_layer::upsert_transaction(&connection, tr);
|
||||||
|
// }
|
||||||
|
// "0" => {
|
||||||
|
// println!("Exiting...");
|
||||||
|
// break; // Exit the loop
|
||||||
|
// }
|
||||||
|
// _ => {
|
||||||
|
// println!("Invalid choice.");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
51
src/ui.rs
Normal file
51
src/ui.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use crate::app::{App, CurrentScreen};
|
||||||
|
use ratatui::{
|
||||||
|
Frame,
|
||||||
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
|
style::{Color, Style},
|
||||||
|
text::Text,
|
||||||
|
widgets::{Block, Borders, Clear, Paragraph, Wrap},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn ui(frame: &mut Frame, app: &App) {
|
||||||
|
if let CurrentScreen::Exiting = app.current_screen {
|
||||||
|
frame.render_widget(Clear, frame.area());
|
||||||
|
|
||||||
|
let popup = Block::default()
|
||||||
|
.title("Exiting program")
|
||||||
|
.borders(Borders::all())
|
||||||
|
.style(Style::default());
|
||||||
|
|
||||||
|
let exit_text = Text::styled(
|
||||||
|
"Are you sure you want to close the program? (y/n)",
|
||||||
|
Style::default().fg(Color::Red),
|
||||||
|
);
|
||||||
|
|
||||||
|
let exit_paragraph = Paragraph::new(exit_text)
|
||||||
|
.block(popup)
|
||||||
|
.wrap(Wrap { trim: false });
|
||||||
|
|
||||||
|
let area = centered_rect(60, 20, frame.area());
|
||||||
|
frame.render_widget(exit_paragraph, area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
|
||||||
|
let popup_layout = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage((100 - percent_y) / 2),
|
||||||
|
Constraint::Percentage(percent_y),
|
||||||
|
Constraint::Percentage((100 - percent_y) / 2),
|
||||||
|
])
|
||||||
|
.split(r);
|
||||||
|
|
||||||
|
return Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage((100 - percent_x) / 2),
|
||||||
|
Constraint::Percentage(percent_x),
|
||||||
|
Constraint::Percentage((100 - percent_x) / 2),
|
||||||
|
])
|
||||||
|
.split(popup_layout[1])[1];
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user