From 4b0cdd4f8917ac6358f27e3f643897c4bc4914ba Mon Sep 17 00:00:00 2001 From: Rusty Pickle Date: Sun, 16 Nov 2025 21:05:35 +0600 Subject: [PATCH 1/4] Fix rounding when converting --- shared/src/models.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/models.rs b/shared/src/models.rs index ca1320b..8d6b8f1 100644 --- a/shared/src/models.rs +++ b/shared/src/models.rs @@ -230,6 +230,6 @@ impl Dollar { #[must_use] pub fn cent(&self) -> Cent { - Cent::new((self.0 * 100.0) as i64) + Cent::new((self.0 * 100.0).round() as i64) } } From dcbb9438d786e58b4b8499c510cd85e26726664e Mon Sep 17 00:00:00 2001 From: Rusty Pickle Date: Sun, 16 Nov 2025 21:34:11 +0600 Subject: [PATCH 2/4] Remove unsafe operations --- app/src/utils.rs | 28 ++++------ app/src/views/summary_models.rs | 14 ++--- shared/src/models.rs | 96 +++++---------------------------- tui/src/pages/chart_ui.rs | 2 +- 4 files changed, 30 insertions(+), 110 deletions(-) diff --git a/app/src/utils.rs b/app/src/utils.rs index cfa0532..04b6f2a 100644 --- a/app/src/utils.rs +++ b/app/src/utils.rs @@ -58,28 +58,20 @@ pub fn parse_amount_nature_cent(amount: &str) -> Result> { } pub fn compare_change(current: Dollar, previous: Dollar) -> String { - if previous == 0.0 { - "∞".to_string() - } else { - let diff = ((current - previous) / previous) * 100.0; - if diff < 0.0 { - format!("↓{:.2}", diff.abs()) - } else { - format!("↑{diff:.2}") - } + match current.cent().percent_change(previous.cent()) { + None => "∞".to_string(), + Some(diff) if diff < 0.0 => format!("↓{:.2}", diff.abs()), + Some(diff) => format!("↑{diff:.2}"), } } pub fn compare_change_opt(current: Dollar, previous: Option) -> String { match previous { - Some(prev) if prev != 0.0 => { - let diff = ((current - prev) / prev) * 100.0; - if diff < 0.0 { - format!("↓{:.2}", diff.abs()) - } else { - format!("↑{diff:.2}") - } - } - _ => "∞".to_string(), + None => "∞".to_string(), + Some(prev) => match current.cent().percent_change(prev.cent()) { + None => "∞".to_string(), + Some(diff) if diff < 0.0 => format!("↓{:.2}", diff.abs()), + Some(diff) => format!("↑{diff:.2}"), + }, } } diff --git a/app/src/views/summary_models.rs b/app/src/views/summary_models.rs index deffb44..5f1c3cd 100644 --- a/app/src/views/summary_models.rs +++ b/app/src/views/summary_models.rs @@ -24,7 +24,7 @@ impl PeakMonthlyMovement { } } -#[derive(PartialEq, Debug)] +#[derive(Debug)] pub(crate) struct SummaryNet { pub(crate) total_income: Dollar, pub(crate) total_expense: Dollar, @@ -96,13 +96,13 @@ impl SummaryNet { } } -#[derive(PartialEq, Debug)] +#[derive(Debug)] pub(crate) enum LargestType { Earning, Expense, } -#[derive(PartialEq, Debug)] +#[derive(Debug)] pub(crate) enum PeakType { Earning, Expense, @@ -126,7 +126,7 @@ impl fmt::Display for PeakType { } } -#[derive(PartialEq, Debug)] +#[derive(Debug)] pub(crate) struct SummaryLargest { largest_type: LargestType, method: String, @@ -171,7 +171,7 @@ impl SummaryLargest { } } -#[derive(PartialEq, Debug)] +#[derive(Debug)] pub(crate) struct SummaryPeak { peak_type: PeakType, amount: Dollar, @@ -207,7 +207,7 @@ impl SummaryPeak { } } -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub(crate) struct SummaryMethods { method: String, pub(crate) total_earning: Dollar, @@ -281,7 +281,7 @@ impl SummaryMethods { } } -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub(crate) struct SummaryLendBorrows { pub(crate) borrows: Dollar, pub(crate) lends: Dollar, diff --git a/shared/src/models.rs b/shared/src/models.rs index 8d6b8f1..4375aa1 100644 --- a/shared/src/models.rs +++ b/shared/src/models.rs @@ -6,6 +6,18 @@ use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}; pub const LAST_POSSIBLE_TIME: NaiveTime = NaiveTime::from_hms_nano_opt(23, 59, 59, 999_999_999).unwrap(); +impl Cent { + pub fn percent_change(self, previous: Cent) -> Option { + if previous.0 == 0 { + None + } else { + let cur = self.0 as f64; + let prev = previous.0 as f64; + Some(((cur - prev) / prev) * 100.0) + } + } +} + impl Add for Cent { type Output = Cent; @@ -14,14 +26,6 @@ impl Add for Cent { } } -impl Add for Dollar { - type Output = Dollar; - - fn add(self, rhs: f64) -> Self::Output { - Dollar(self.0 + rhs) - } -} - impl Sub for Cent { type Output = Cent; @@ -30,30 +34,6 @@ impl Sub for Cent { } } -impl Sub for Dollar { - type Output = Dollar; - - fn sub(self, rhs: f64) -> Self::Output { - Dollar(self.0 - rhs) - } -} - -impl Sub for Dollar { - type Output = Dollar; - - fn sub(self, rhs: Dollar) -> Self::Output { - Dollar(self.0 - rhs.0) - } -} - -impl Div for Dollar { - type Output = f64; - - fn div(self, rhs: Dollar) -> Self::Output { - self.0 / rhs.0 - } -} - impl Mul for Cent { type Output = Cent; @@ -62,20 +42,6 @@ impl Mul for Cent { } } -impl Mul for Dollar { - type Output = Dollar; - - fn mul(self, rhs: f64) -> Self::Output { - Dollar(self.0 * rhs) - } -} - -impl AddAssign for Dollar { - fn add_assign(&mut self, rhs: f64) { - self.0 += rhs; - } -} - impl AddAssign for Cent { fn add_assign(&mut self, rhs: i64) { self.0 += rhs; @@ -100,20 +66,6 @@ impl AddAssign for Cent { } } -impl AddAssign for f64 { - fn add_assign(&mut self, other: Dollar) { - *self += other.0; - } -} - -impl Div for Cent { - type Output = Cent; - - fn div(self, rhs: i64) -> Self::Output { - Cent(self.0 / rhs) - } -} - impl Div for Dollar { type Output = Dollar; @@ -146,48 +98,24 @@ impl PartialEq for Cent { } } -impl PartialEq for Dollar { - fn eq(&self, other: &f64) -> bool { - self.0 == *other - } -} - impl PartialEq for Cent { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } -impl PartialEq for Dollar { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - impl PartialOrd for Cent { fn partial_cmp(&self, other: &Self) -> Option { self.0.partial_cmp(&other.0) } } -impl PartialOrd for Dollar { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - impl PartialOrd for Cent { fn partial_cmp(&self, other: &i64) -> Option { self.0.partial_cmp(other) } } -impl PartialOrd for Dollar { - fn partial_cmp(&self, other: &f64) -> Option { - self.0.partial_cmp(other) - } -} - impl fmt::Display for Dollar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:.2}", self.0) diff --git a/tui/src/pages/chart_ui.rs b/tui/src/pages/chart_ui.rs index ce408b3..21c9215 100644 --- a/tui/src/pages/chart_ui.rs +++ b/tui/src/pages/chart_ui.rs @@ -169,7 +169,7 @@ pub fn chart_ui( let method_at_index = tx_methods[method_index]; let balance = current_balances.get(&method_at_index.id).unwrap().dollar(); - cumulative_balance += balance; + cumulative_balance += balance.value(); balance.value() }; From 036846125378ed2d648504c55f0f3b174d89ad45 Mon Sep 17 00:00:00 2001 From: Rusty Pickle Date: Sun, 16 Nov 2025 21:35:45 +0600 Subject: [PATCH 3/4] Format --- shared/src/models.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/src/models.rs b/shared/src/models.rs index 4375aa1..d4b2a4a 100644 --- a/shared/src/models.rs +++ b/shared/src/models.rs @@ -7,6 +7,7 @@ pub const LAST_POSSIBLE_TIME: NaiveTime = NaiveTime::from_hms_nano_opt(23, 59, 59, 999_999_999).unwrap(); impl Cent { + #[must_use] pub fn percent_change(self, previous: Cent) -> Option { if previous.0 == 0 { None From 7cd754bc5c95136b57d4dde31d4372b9fe0fc065 Mon Sep 17 00:00:00 2001 From: Rusty Pickle Date: Mon, 17 Nov 2025 18:39:34 +0600 Subject: [PATCH 4/4] Format --- shared/src/models.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/models.rs b/shared/src/models.rs index d4b2a4a..80b8313 100644 --- a/shared/src/models.rs +++ b/shared/src/models.rs @@ -7,7 +7,7 @@ pub const LAST_POSSIBLE_TIME: NaiveTime = NaiveTime::from_hms_nano_opt(23, 59, 59, 999_999_999).unwrap(); impl Cent { - #[must_use] + #[must_use] pub fn percent_change(self, previous: Cent) -> Option { if previous.0 == 0 { None