started using ratatui
This commit is contained in:
parent
24ac0a0fbe
commit
55dfd16cd2
@ -5,4 +5,5 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.42"
|
||||
ratatui = "0.29.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,
|
||||
};
|
||||
}
|
||||
}
|
||||
338
src/main.rs
338
src/main.rs
@ -1,150 +1,214 @@
|
||||
mod app;
|
||||
mod data_layer;
|
||||
mod entities;
|
||||
use chrono::{Local, TimeZone};
|
||||
use sqlite::Connection;
|
||||
use std::io;
|
||||
mod ui;
|
||||
use crate::{
|
||||
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 connection = match Connection::open("ft_rs.db") {
|
||||
Ok(con) => con,
|
||||
Err(e) => {
|
||||
eprintln!("Error opening database: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut terminal = ratatui::init();
|
||||
let mut app = App::new();
|
||||
let res = run_app(&mut terminal, &mut app);
|
||||
|
||||
data_layer::setup(&connection);
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
DisableMouseCapture
|
||||
)?;
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{err:?}");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<bool> {
|
||||
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");
|
||||
terminal.draw(|f| ui(f, app))?;
|
||||
|
||||
let mut choice = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut choice)
|
||||
.expect("Failed to read line");
|
||||
// S'occupe des keyevents
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if key.kind == event::KeyEventKind::Release {
|
||||
continue;
|
||||
}
|
||||
|
||||
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.");
|
||||
match app.current_screen {
|
||||
CurrentScreen::Main => match key.code {
|
||||
KeyCode::Char('q') => {
|
||||
app.current_screen = CurrentScreen::Exiting;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
CurrentScreen::Exiting => match key.code {
|
||||
KeyCode::Char('y') => {
|
||||
return Ok(true);
|
||||
}
|
||||
_ => {
|
||||
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