cargo fmt

This commit is contained in:
Nickiel12 2023-08-12 11:54:09 -07:00
parent f639d350b9
commit 871d6317ff
12 changed files with 362 additions and 179 deletions

View file

@ -1,15 +1,15 @@
use std::sync::Arc;
use crossterm::event::{Event, self, KeyCode};
use crossterm::event::{self, Event, KeyCode};
use tokio;
use log::warn;
use crate::db::DB;
use crate::db::data_cache::DataCache;
use crate::db::DB;
use crate::uis::history::HistoryState;
use crate::uis::new_transaction::NewTransactionTabState;
use crate::uis::navigation_frame::NavigationState;
use crate::uis::new_transaction::NewTransactionTabState;
pub type AppResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
@ -80,21 +80,19 @@ impl<'a> App<'a> {
}
}
}
return Ok(())
return Ok(());
}
pub fn refresh(&mut self) {
let fut = Arc::clone(&self.db);
tokio::spawn(async move {
let res = fut.lock().await.get_all_records().await;
match res {
Ok(_) => {},
Err(e) => warn!("{}", e)
Ok(_) => {}
Err(e) => warn!("{}", e),
}
});
}
}

View file

@ -1,8 +1,7 @@
use futures::TryStreamExt;
use sqlx::postgres::PgPoolOptions;
use sqlx::Error;
use sqlx::PgPool;
use sqlx::postgres::PgPoolOptions;
use super::data_cache::DataCache;
use super::tables::Transaction;
@ -13,12 +12,12 @@ pub struct DB {
data_cache: DataCache,
}
impl DB {
pub async fn new(data_cache: DataCache) -> Result<DB, sqlx::Error> {
let connection_pool = PgPoolOptions::new()
.max_connections(3)
.connect("postgres://rcntuser:Devel@pmentPa$$w0rd@10.0.0.183/Borealis").await?;
.connect("postgres://rcntuser:Devel@pmentPa$$w0rd@10.0.0.183/Borealis")
.await?;
Ok(DB {
conn_pool: connection_pool,
data_cache,
@ -35,7 +34,6 @@ impl DB {
temp_transactions.push(row);
}
{
let mut transactions = self.data_cache.transactions.lock().unwrap();
transactions.clear();
@ -43,7 +41,5 @@ impl DB {
}
return Ok(());
}
}

View file

@ -1,11 +1,9 @@
use std::sync::{Arc, Mutex};
use super::tables;
use super::tables::{self, Account};
#[derive(Clone)]
pub struct DataCache {
pub accounts: Arc<Mutex<Vec<tables::Account>>>,
pub buckets: Arc<Mutex<Vec<tables::Buckets>>>,
@ -15,10 +13,8 @@ pub struct DataCache {
pub transaction_breakdowns: Arc<Mutex<Vec<tables::TransactionBreakdown>>>,
pub transactions: Arc<Mutex<Vec<tables::Transaction>>>,
}
impl DataCache {
pub fn new() -> DataCache {
DataCache {
@ -29,4 +25,16 @@ impl DataCache {
transactions: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn find_account_by_id(&self, id: i32) -> Option<Account> {
{
let accounts = self.accounts.lock().unwrap();
for i in accounts.iter() {
if i.acnt_id == id {
return Some(i.clone());
}
}
}
return None;
}
}

View file

@ -1,6 +1,5 @@
pub mod connection;
pub use self::connection::DB;
pub mod tables;
pub mod data_cache;
pub mod tables;

View file

@ -1,9 +1,8 @@
use std::borrow::Cow;
use sqlx::{FromRow, postgres::types::PgMoney};
use sqlx::{postgres::types::PgMoney, FromRow};
use time::Date;
#[derive(Clone, FromRow)]
pub struct Account {
pub acnt_id: i32,
@ -11,7 +10,6 @@ pub struct Account {
pub acnt_description: String,
}
#[derive(Clone, FromRow)]
pub struct Buckets {
pub bkt_id: i32,
@ -20,7 +18,6 @@ pub struct Buckets {
pub bkt_description: String,
}
#[derive(Clone, FromRow)]
pub struct TransactionCatagories {
pub trns_ctgry_id: i32,
@ -29,7 +26,6 @@ pub struct TransactionCatagories {
pub trns_ctgry_description: String,
}
#[derive(Clone, FromRow)]
pub struct TransactionBreakdown {
pub trns_brkdwn_id: i32,
@ -39,6 +35,26 @@ pub struct TransactionBreakdown {
pub trns_brkdwn_bucket: i32,
}
#[derive(Clone)]
pub struct PartialTransactionBreakdown {
pub trns_brkdwn_id: Option<i32>,
pub trns_brkdwn_amount: Option<PgMoney>,
pub trns_brkdwn_parent_transaction: Option<i32>,
pub trns_brkdwn_catagory: Option<i32>,
pub trns_brkdwn_bucket: Option<i32>,
}
impl PartialTransactionBreakdown {
pub fn new_empty() -> PartialTransactionBreakdown {
PartialTransactionBreakdown {
trns_brkdwn_id: None,
trns_brkdwn_amount: None,
trns_brkdwn_parent_transaction: None,
trns_brkdwn_catagory: None,
trns_brkdwn_bucket: None,
}
}
}
#[derive(Clone, FromRow)]
pub struct Transaction {
@ -46,20 +62,33 @@ pub struct Transaction{
pub trns_amount: PgMoney,
pub trns_description: String,
pub trns_bucket: i32,
pub trns_date: Date
pub trns_date: Date,
}
#[derive(Clone)]
pub struct PartialTransaction {
pub trns_id: Option<i32>,
pub trns_amount: Option<PgMoney>,
pub trns_description: Option<String>,
pub trns_bucket: Option<i32>,
pub trns_date: Option<Date>,
}
impl Transaction {
pub fn get_header() -> String {
return format!(" {:<7} | {:<9} | {:<10}", "Id", "Amount", "Date")
return format!(" {:<7} | {:<9} | {:<10}", "Id", "Amount", "Date");
}
pub fn to_string(&self) -> String {
return format!(" T{:0>6} | {:>9} | {:>10}", self.trns_id, self.trns_amount.to_decimal(2).to_string(), self.trns_date.to_string())
return format!(
" T{:0>6} | {:>9} | {:>10}",
self.trns_id,
self.trns_amount.to_decimal(2).to_string(),
self.trns_date.to_string()
);
}
pub fn as_str(&self) -> Cow<str> {
return self.to_string().into()
return self.to_string().into();
}
}

View file

@ -1,9 +1,8 @@
pub mod app;
pub mod tui;
pub mod ui;
pub mod uis;
pub mod db;
pub mod uis;

View file

@ -1,22 +1,18 @@
use std::io;
use std::fs::File;
use std::io;
use std::time::Duration;
use simplelog::*;
use ratatui::{
backend::CrosstermBackend,
Terminal,
};
use ratatui::{backend::CrosstermBackend, Terminal};
use recount::db::DB;
use recount::db::data_cache::DataCache;
use recount::app::{App, AppResult};
use recount::db::data_cache::DataCache;
use recount::db::DB;
use recount::tui::Tui;
#[tokio::main]
async fn main() -> AppResult<()> {
let log_file = "testing_log.txt".to_string();
init_logger(log_file);
@ -49,7 +45,6 @@ async fn main() -> AppResult<()> {
tui.exit()
}
pub fn init_logger(output_file: String) {
// TODO: configure the log levels to something appropriate
CombinedLogger::init(vec![
@ -60,7 +55,7 @@ pub fn init_logger(output_file: String) {
// ColorChoice::Auto,
// ),
WriteLogger::new(
LevelFilter::Info,
LevelFilter::Debug,
Config::default(),
File::create(output_file).unwrap(),
),

View file

@ -1,10 +1,10 @@
use crate::ui;
use crate::app::{App, AppResult};
use crate::ui;
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen};
use std::io;
use ratatui::backend::Backend;
use ratatui::Terminal;
use std::io;
/// Representation of a terminal user interface.
///

View file

@ -1,26 +1,33 @@
use crate::{app::{App, ActiveFrame}, uis::render_history_tab};
use crate::{
app::{ActiveFrame, App},
uis::render_history_tab,
};
use log;
use ratatui::{
backend::Backend,
layout::{Constraint, Direction, Layout},
style::Style,
widgets::{Block, Borders},
Frame, style::Style,
Frame,
};
use log;
use crate::uis::{render_navigation_frame, render_new_transaction_tab};
pub fn render<B: Backend>(f: &mut Frame<B>, app: &mut App) {
let size = f.size();
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(3), Constraint::Min(0), Constraint::Length(3)].as_ref())
.constraints(
[
Constraint::Length(3),
Constraint::Min(0),
Constraint::Length(3),
]
.as_ref(),
)
.split(size);
let bottom_block = Block::default()
.borders(Borders::ALL)
.border_style({
let bottom_block = Block::default().borders(Borders::ALL).border_style({
if let ActiveFrame::Navigation = app.states.active_frame {
Style::default()
} else {

View file

@ -1,14 +1,19 @@
use crossterm::event::{KeyEvent, KeyEventKind, KeyCode};
use ratatui::{backend::Backend, Frame, layout::{Rect, Layout, Direction, Constraint}, widgets::{Block, Borders, List, ListItem, Paragraph}, text::{Text, Line, Span}, style::{Style, Color}};
use crate::{app::App, db::tables::Transaction};
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Style},
text::{Line, Span, Text},
widgets::{Block, Borders, List, ListItem, Paragraph},
Frame,
};
pub struct HistoryState {
pub selected_index: Option<usize>,
}
impl HistoryState {
pub fn new() -> HistoryState {
HistoryState {
selected_index: None,
@ -16,7 +21,6 @@ impl HistoryState {
}
pub fn handle_event(event: KeyEvent, app: &mut App) {
if app.states.history.selected_index.is_none() {
app.states.history.selected_index = Some(0);
}
@ -33,7 +37,6 @@ impl HistoryState {
}
pub fn next(&mut self, max_index: usize) {
if self.selected_index.is_some() {
let cur_index = self.selected_index.unwrap();
if cur_index + 1 < max_index {
@ -43,19 +46,16 @@ impl HistoryState {
}
pub fn previous(&mut self) {
if self.selected_index.is_some() {
let cur_index = self.selected_index.unwrap();
if cur_index > 0 {
self.selected_index = Some(cur_index - 1);
}
}
}
}
pub fn render_history_tab<B: Backend>(f: &mut Frame<B>, body_rect: Rect, app: &mut App) {
let split_body = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
@ -65,45 +65,51 @@ pub fn render_history_tab<B: Backend> (f: &mut Frame<B>, body_rect: Rect, app: &
{
let transacts_list = app.data_cache.transactions.lock().unwrap();
if app.states.history.selected_index.is_some() {
let selected_item: &Transaction = &transacts_list[app.states.history.selected_index.unwrap()];
let selected_item: &Transaction =
&transacts_list[app.states.history.selected_index.unwrap()];
let ident_style: Style = Style::default().fg(Color::Yellow);
let value_style: Style = Style::default().fg(Color::LightBlue);
lines.push(
Line::from(vec![
lines.push(Line::from(vec![
Span::styled("Transaction Id: ", ident_style),
Span::styled(format!("T{:0>6}", selected_item.trns_id), value_style),
])
);
lines.push(
Line::from(vec![
]));
lines.push(Line::from(vec![
Span::styled("Amount: ", ident_style),
Span::styled(format!("{}", selected_item.trns_amount.to_decimal(2).to_string()), value_style)
])
);
lines.push(
Line::from(vec![
Span::styled(
format!("{}", selected_item.trns_amount.to_decimal(2).to_string()),
value_style,
),
]));
lines.push(Line::from(vec![
Span::styled("Transaction Date: ", ident_style),
Span::styled(format!("{}", selected_item.trns_date.to_string()), value_style)
])
);
Span::styled(
format!("{}", selected_item.trns_date.to_string()),
value_style,
),
]));
}
let paragraph = Paragraph::new(lines.clone())
.block(Block::default().borders(Borders::ALL).title("Details").title_alignment(ratatui::layout::Alignment::Left));
let paragraph = Paragraph::new(lines.clone()).block(
Block::default()
.borders(Borders::ALL)
.title("Details")
.title_alignment(ratatui::layout::Alignment::Left),
);
f.render_widget(paragraph, split_body[1]);
let list_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(1), Constraint::Min(3)])
.split(split_body[0]);
let list_header = Paragraph::new(
Text::styled(Transaction::get_header(), Style::default().fg(Color::Black).bg(Color::Cyan))
).block(Block::default().borders(Borders::NONE));
let list_header = Paragraph::new(Text::styled(
Transaction::get_header(),
Style::default().fg(Color::Black).bg(Color::Cyan),
))
.block(Block::default().borders(Borders::NONE));
f.render_widget(list_header, list_chunks[0]);
@ -117,18 +123,16 @@ pub fn render_history_tab<B: Backend> (f: &mut Frame<B>, body_rect: Rect, app: &
} else {
Style::default().fg(Color::Black).bg(Color::White)
}
},
}
None => Style::default().fg(Color::Black).bg(Color::White),
};
items.push(ListItem::new(
Text::from(transacts_list.get(i).unwrap().as_str())
).style(style));
items.push(
ListItem::new(Text::from(transacts_list.get(i).unwrap().as_str())).style(style),
);
}
let history_items = List::new(items)
.block(Block::default().borders(Borders::NONE));
let history_items = List::new(items).block(Block::default().borders(Borders::NONE));
f.render_widget(history_items, list_chunks[1]);
}

View file

@ -1,7 +1,14 @@
use crossterm::event::{KeyEvent, KeyEventKind, KeyCode};
use ratatui::{widgets::{Borders, Paragraph, Block, Tabs}, backend::Backend, Frame, style::{Style, Color, Modifier}, text::{Text, Line}, layout::{Rect, Layout, Direction, Constraint}};
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Text},
widgets::{Block, Borders, Paragraph, Tabs},
Frame,
};
use crate::app::{App, ActiveFrame};
use crate::app::{ActiveFrame, App};
pub struct NavigationState<'a> {
pub tabs: Vec<&'a str>,
@ -37,7 +44,7 @@ impl<'a> NavigationState<'a> {
match self.tab_index {
0 => Some(ActiveFrame::History),
1 => Some(ActiveFrame::NewTransaction),
_ => todo!()
_ => todo!(),
}
}
@ -45,34 +52,39 @@ impl<'a> NavigationState<'a> {
if event.kind == KeyEventKind::Press {
match event.code {
KeyCode::Tab => app.states.nav_state.next_tab(),
KeyCode::Enter => app.states.active_frame = app.states.nav_state.get_active_tab_frametype().unwrap(),
KeyCode::Enter => {
app.states.active_frame =
app.states.nav_state.get_active_tab_frametype().unwrap()
}
_ => {}
}
}
}
}
pub fn render_navigation_frame<B: Backend> (f: &mut Frame<B>, status_rect: Rect, navbar_rect: Rect, app: &App) {
pub fn render_navigation_frame<B: Backend>(
f: &mut Frame<B>,
status_rect: Rect,
navbar_rect: Rect,
app: &App,
) {
let status_bar_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(75), Constraint::Min(10)])
.split(status_rect);
let left_block = Block::default()
.borders(Borders::ALL)
.border_style({
let left_block = Block::default().borders(Borders::ALL).border_style({
if let ActiveFrame::Navigation = app.states.active_frame {
Style::default().fg(ratatui::style::Color::Green)
} else {
Style::default()
}
});
let right_block = Block::default()
.borders(Borders::ALL);
let right_block = Block::default().borders(Borders::ALL);
let titles = app.states.nav_state
let titles = app
.states
.nav_state
.tabs
.iter()
.cloned()
@ -93,11 +105,9 @@ pub fn render_navigation_frame<B: Backend> (f: &mut Frame<B>, status_rect: Rect,
f.render_widget(tabs, status_bar_chunks[0]);
let connection_paragraph = Paragraph::new(
Text::styled("Aurora",
Style::default().fg(Color::Green)
)
).block(right_block);
let connection_paragraph =
Paragraph::new(Text::styled("Aurora", Style::default().fg(Color::Green)))
.block(right_block);
f.render_widget(connection_paragraph, status_bar_chunks[1]);
@ -112,9 +122,7 @@ pub fn render_navigation_frame<B: Backend> (f: &mut Frame<B>, status_rect: Rect,
app.states.nav_state.message.clone().unwrap()
}
};
let bottom_navbar = Paragraph::new(
Text::styled(navbar, Style::default().fg(Color::White))
)
let bottom_navbar = Paragraph::new(Text::styled(navbar, Style::default().fg(Color::White)))
.block(Block::default().borders(Borders::ALL));
f.render_widget(bottom_navbar, navbar_rect);
}

View file

@ -1,17 +1,72 @@
use crate::app::App;
use crossterm::event::{KeyEvent, KeyEventKind, KeyCode};
use ratatui::{backend::Backend, Frame, layout::{Rect, Layout, Direction, Constraint}, widgets::{Paragraph, Borders, Block}, text::Text, style::{Style, Color}};
use crate::{
app::App,
db::tables::{Account, Buckets, PartialTransactionBreakdown},
};
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Style},
text::Text,
widgets::{Block, Borders, Paragraph},
Frame,
};
use rust_decimal::Decimal;
use time::Date;
use log::debug;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ManualDataFocus {
Account,
Amount,
Date,
Bucket,
Description,
Breakdowns,
}
struct ManualData {
pub focus: Option<ManualDataFocus>,
pub account: Option<Account>,
pub amount: Option<Decimal>,
pub date: Option<Date>,
pub bucket: Option<Buckets>,
pub description: Option<String>,
pub editing_breakdown: PartialTransactionBreakdown,
pub breakdowns: Vec<PartialTransactionBreakdown>,
}
impl ManualData {
pub fn new() -> ManualData {
ManualData {
focus: None,
account: None,
amount: None,
date: None,
bucket: None,
description: None,
editing_breakdown: PartialTransactionBreakdown::new_empty(),
breakdowns: Vec::new(),
}
}
}
pub struct NewTransactionTabState<'a> {
pub cur_tab_index: usize,
pub tabs: Vec<&'a str>,
manual_data: ManualData,
}
impl<'a> NewTransactionTabState<'a> {
pub fn new() -> NewTransactionTabState<'a> {
NewTransactionTabState {
cur_tab_index: 0,
tabs: vec!["Quick Entry", "Manual Entry"]
tabs: vec!["Quick Entry", "Manual Entry"],
manual_data: ManualData::new(),
}
}
@ -30,7 +85,6 @@ impl<'a> NewTransactionTabState<'a> {
}
pub fn render_new_transaction_tab<B: Backend>(f: &mut Frame<B>, body_rect: Rect, app: &App) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(3), Constraint::Min(0)])
@ -49,40 +103,126 @@ pub fn render_new_transaction_tab<B: Backend> (f: &mut Frame<B>, body_rect: Rect
.split(chunks[0]);
for i in 0..app.states.transactions.tabs.len() {
let tab = Paragraph::new(
Text::styled(app.states.transactions.tabs[i],
Style::default().fg(Color::White)
)
)
let tab = Paragraph::new(Text::styled(
app.states.transactions.tabs[i],
Style::default().fg(Color::White),
))
.alignment(ratatui::layout::Alignment::Center)
.block(
Block::default()
.borders(Borders::ALL)
.style({
.block(Block::default().borders(Borders::ALL).style({
if app.states.transactions.cur_tab_index == i {
Style::default().bg(Color::Blue)
} else {
Style::default()
}
})
);
}));
f.render_widget(tab, tab_chunks[i])
}
match app.states.transactions.tabs[app.states.transactions.cur_tab_index] {
"Quick Entry" => render_quick_entry(f, chunks[1], app),
"Manual Entry" => render_manual_entry(f, chunks[1], app),
_ => return
_ => return,
};
}
pub fn render_manual_entry<B: Backend>(f: &mut Frame<B>, area: Rect, app: &App) {
let num_can_show = (area.height / 3) as usize;
let mut constraints: Vec<Constraint> = Vec::new();
for _ in 0..num_can_show {
constraints.push(Constraint::Length(1));
}
debug!("Can show: {}", num_can_show);
debug!("saved {} constraints", constraints.len());
pub fn render_quick_entry<B: Backend> (f: &mut Frame<B>, area: Rect, app: &App) {
let split_body = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(area);
{
let mut first_found = false;
let mut num_rendered = 0;
let manual_state = &app.states.transactions.manual_data;
if manual_state.focus.is_none()
|| matches!(manual_state.focus.unwrap(), ManualDataFocus::Account)
{
let right_text = match manual_state.account.clone() {
Some(val) => val.acnt_dsply_name,
None => "".to_string(),
};
render_manual_row(
f,
split_body[num_rendered * 2],
"Account: ",
right_text,
manual_state.focus,
ManualDataFocus::Account
);
first_found = true;
num_rendered = 1;
}
if (first_found && num_rendered < num_can_show)
|| matches!(manual_state.focus.unwrap(), ManualDataFocus::Amount)
{
let right_text = match manual_state.amount {
Some(val) => val.to_string(),
None => "".to_string(),
};
render_manual_row(
f,
split_body[num_rendered * 2],
"Amount: ",
right_text,
manual_state.focus,
ManualDataFocus::Amount
);
first_found = true;
num_rendered += 1;
}
}
}
pub fn render_manual_row<B: Backend>(
f: &mut Frame<B>,
row_area: Rect,
left_text: &str,
right_text: String,
focus: Option<ManualDataFocus>,
matching: ManualDataFocus,
) {
let is_active = focus.map(|focus| focus == matching).unwrap_or(false);
let horizontal_pieces = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(row_area);
f.render_widget(
Paragraph::new(Text::styled(left_text, Style::default().fg(Color::Yellow))),
horizontal_pieces[0],
);
let right_bg_color = if is_active {
Color::Yellow
} else {
Color::White
};
f.render_widget(
Paragraph::new(Text::styled(right_text, Style::default().fg(Color::Black))).block(
Block::default()
.borders(Borders::NONE)
.style(Style::default().bg(right_bg_color)),
),
horizontal_pieces[1],
);
}
pub fn render_quick_entry<B: Backend>(f: &mut Frame<B>, area: Rect, app: &App) {}