From 242c535169fb7ed56689417136f0f79ac75b467f Mon Sep 17 00:00:00 2001 From: Cayle Sharrock Date: Tue, 21 Nov 2023 21:27:24 +0000 Subject: [PATCH] feat: improve balance formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a little newtype, TariFormat that formats the balance values in a human-friendly way. De- and Serialization is auto-unwrapped back to a u64 (muT) Balances now looks like: Available 974,845.997 T Incoming 0 μT Outgoing 0 μT Timelocked 970,590.372 T rather than: Available 974845997000000 Incoming 0 Outgoing 0 Timelocked 970590372000 --- cli/src/component/normal/wallet/balance.rs | 3 +- libs/protocol/src/lib.rs | 2 + libs/protocol/src/tari_format.rs | 80 ++++++++++++++++++++++ libs/protocol/src/wallet.rs | 10 +-- libs/sdm-launchpad/src/wallet_grpc.rs | 8 +-- libs/sim-launchpad/src/simulator.rs | 10 +-- 6 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 libs/protocol/src/tari_format.rs diff --git a/cli/src/component/normal/wallet/balance.rs b/cli/src/component/normal/wallet/balance.rs index 2d71b8e7..2027e78e 100644 --- a/cli/src/component/normal/wallet/balance.rs +++ b/cli/src/component/normal/wallet/balance.rs @@ -28,6 +28,7 @@ use ratatui::{ layout::{Constraint, Direction, Layout, Rect}, widgets::{Padding, Row, Table}, }; +use tari_launchpad_protocol::tari_format::TariFormat; use crate::{ component::{elements::block_with_title, Component, ComponentEvent, Frame, Input, Pass}, @@ -112,7 +113,7 @@ impl Component for BalanceWidget { } } -fn rows<'a>(items: impl IntoIterator)>) -> Vec> { +fn rows<'a>(items: impl IntoIterator)>) -> Vec> { let mut rows = Vec::new(); for (title, value) in items { let value = value diff --git a/libs/protocol/src/lib.rs b/libs/protocol/src/lib.rs index f83f2eae..3405909e 100644 --- a/libs/protocol/src/lib.rs +++ b/libs/protocol/src/lib.rs @@ -31,5 +31,7 @@ pub mod session; pub mod settings; pub mod wallet; +pub mod tari_format; + pub const ACTIONS: &str = "tari://actions"; pub const REACTIONS: &str = "tari://reactions"; diff --git a/libs/protocol/src/tari_format.rs b/libs/protocol/src/tari_format.rs new file mode 100644 index 00000000..c45eb9a5 --- /dev/null +++ b/libs/protocol/src/tari_format.rs @@ -0,0 +1,80 @@ +use std::{fmt, fmt::Display}; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] +#[serde(transparent)] +pub struct TariFormat { + value: u64, +} + +impl TariFormat { + pub fn as_u64(&self) -> u64 { + self.value + } +} + +impl From for TariFormat { + fn from(value: u64) -> Self { + Self { value } + } +} + +impl Display for TariFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut value = self.value; + let unit = if value < 1_000_000 { "μT" } else { "T" }; + let mut decimals = None; + if value >= 1_000_000 { + decimals = Some((value % 1_000_000).to_string()); + value /= 1_000_000; + } + let val_str = value + .to_string() + .as_bytes() + .rchunks(3) + .rev() + .map(std::str::from_utf8) + .collect::, _>>() + .unwrap() + .join(","); + + let dec_str = match decimals { + Some(dec) => format!(".{:0<03.3}", dec), + None => String::new(), + }; + write!(f, "{val_str}{dec_str} {unit}") + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn format_whole_number() { + let value = 1_234_567_891_000_000; + let tari = TariFormat::from(value); + assert_eq!(tari.to_string(), "1,234,567,891.000 T"); + } + + #[test] + fn format_small_number() { + let value = 123_456; + let tari = TariFormat::from(value); + assert_eq!(tari.to_string(), "123,456 μT"); + } + #[test] + fn format_big_number_w_frac() { + let value = 1_234_567_890_222_333; + let tari = TariFormat::from(value); + assert_eq!(tari.to_string(), "1,234,567,890.222 T"); + } + + #[test] + fn format_zero() { + let value = 0; + let tari = TariFormat::from(value); + assert_eq!(tari.to_string(), "0 μT"); + } +} diff --git a/libs/protocol/src/wallet.rs b/libs/protocol/src/wallet.rs index e57a6f8f..c622bf58 100644 --- a/libs/protocol/src/wallet.rs +++ b/libs/protocol/src/wallet.rs @@ -25,6 +25,8 @@ use std::collections::VecDeque; use serde::{Deserialize, Serialize}; +use crate::tari_format::TariFormat; + const HISTORY_LIMIT: usize = 30; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -55,10 +57,10 @@ impl Default for WalletState { #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct WalletBalance { - pub available: u64, - pub pending_incoming: u64, - pub pending_outgoing: u64, - pub timelocked: u64, + pub available: TariFormat, + pub pending_incoming: TariFormat, + pub pending_outgoing: TariFormat, + pub timelocked: TariFormat, } impl WalletState { diff --git a/libs/sdm-launchpad/src/wallet_grpc.rs b/libs/sdm-launchpad/src/wallet_grpc.rs index fd799c4a..6abc4650 100644 --- a/libs/sdm-launchpad/src/wallet_grpc.rs +++ b/libs/sdm-launchpad/src/wallet_grpc.rs @@ -146,10 +146,10 @@ impl WalletGrpcWorker { fn process_balance(&mut self, response: GetBalanceResponse) -> Result<(), Error> { let balance = WalletBalance { - available: response.available_balance, - pending_incoming: response.pending_incoming_balance, - pending_outgoing: response.pending_outgoing_balance, - timelocked: response.timelocked_balance, + available: response.available_balance.into(), + pending_incoming: response.pending_incoming_balance.into(), + pending_outgoing: response.pending_outgoing_balance.into(), + timelocked: response.timelocked_balance.into(), }; let delta = WalletDelta::UpdateBalance(balance); self.send_update(delta) diff --git a/libs/sim-launchpad/src/simulator.rs b/libs/sim-launchpad/src/simulator.rs index 08a39c18..224c5f8e 100644 --- a/libs/sim-launchpad/src/simulator.rs +++ b/libs/sim-launchpad/src/simulator.rs @@ -253,10 +253,10 @@ impl Simulator { self.apply_wallet_delta(delta)?; if new_state { let balance = WalletBalance { - available: 0, - pending_incoming: 0, - pending_outgoing: 0, - timelocked: 0, + available: 0.into(), + pending_incoming: 0.into(), + pending_outgoing: 0.into(), + timelocked: 0.into(), }; let delta = WalletDelta::UpdateBalance(balance); self.apply_wallet_delta(delta)?; @@ -269,7 +269,7 @@ impl Simulator { let session = self.lp_state.config.session.clone(); if session.is_miner_active() && self.mined_at.elapsed() >= Duration::from_secs(10) { let mut balance = self.lp_state.wallet.balance.clone().unwrap_or_default(); - balance.available += 1_000; + balance.available = (balance.available.as_u64() + 1_000).into(); let delta = WalletDelta::UpdateBalance(balance); self.apply_wallet_delta(delta)?; // TODO: Add a transaction