From 81ec4829184e1d6cf41dd68c9fe246e3e171027a Mon Sep 17 00:00:00 2001 From: Love Billenius Date: Wed, 8 Jan 2025 15:01:57 +0100 Subject: [PATCH] Create non-owned variant of the table. It yields ~12% faster time for cached results in historical day resolution --- src/main.rs | 4 +- src/table.rs | 91 ------------------------------------- src/table/mod.rs | 11 +++++ src/table/table_display.rs | 46 +++++++++++++++++++ src/table/table_getter.rs | 10 +++++ src/table/table_owned.rs | 91 +++++++++++++++++++++++++++++++++++++ src/table/table_ref.rs | 92 ++++++++++++++++++++++++++++++++++++++ src/table/table_trait.rs | 12 +++++ 8 files changed, 264 insertions(+), 93 deletions(-) delete mode 100644 src/table.rs create mode 100644 src/table/mod.rs create mode 100644 src/table/table_display.rs create mode 100644 src/table/table_getter.rs create mode 100644 src/table/table_owned.rs create mode 100644 src/table/table_ref.rs create mode 100644 src/table/table_trait.rs diff --git a/src/main.rs b/src/main.rs index 100c2d9..8f4b220 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use std::{borrow::BorrowMut, collections::HashMap, process::ExitCode}; use ecb_rates::cli::{Cli, FormatOption}; use ecb_rates::models::ExchangeRateResult; use ecb_rates::parsing::parse; -use ecb_rates::table::Table; +use ecb_rates::table::{TableRef, TableTrait}; async fn get_and_parse(url: impl IntoUrl) -> anyhow::Result> { let client = Client::new(); @@ -96,7 +96,7 @@ async fn main() -> ExitCode { FormatOption::Plain => parsed .iter() .map(|x| { - let mut t: Table = x.clone().into(); + let mut t: TableRef = x.into(); t.sort(); format!("{}", t) }) diff --git a/src/table.rs b/src/table.rs deleted file mode 100644 index f2eb819..0000000 --- a/src/table.rs +++ /dev/null @@ -1,91 +0,0 @@ -use colored::*; -use std::fmt::Display; - -use crate::models::ExchangeRateResult; - -pub struct Table { - header: Option, - column_left: String, - column_right: String, - rows: Vec<(String, String)>, - pub color: bool, - pub width: usize, -} - -impl Table { - fn new(header: Option, column_left: String, column_right: String) -> Self { - Self { - header, - column_left, - column_right, - rows: Vec::new(), - color: false, - width: 21, - } - } - - #[allow(dead_code)] - fn disable_header(&mut self) { - self.header = None - } - - #[allow(dead_code)] - fn set_header(&mut self, header: String) { - self.header = Some(header); - } - - fn add_row(&mut self, row_left: String, row_right: String) { - self.rows.push((row_left, row_right)); - } - - pub fn sort(&mut self) { - self.rows.sort_by(|a, b| a.1.cmp(&b.1)) - } -} - -impl Display for Table { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(header) = self.header.as_ref() { - let middle_padding_amount = (self.width - header.len()) / 2; - assert!(middle_padding_amount > 0); - let middle_padding = " ".repeat(middle_padding_amount); - writeln!( - f, - "{}{}{}", - middle_padding, - header.bold().cyan(), - middle_padding - )?; - } - - let right_padding_amount = self.width - self.column_left.len() - self.column_right.len(); - let right_padding = " ".repeat(right_padding_amount); - writeln!( - f, - "{}{}{}", - self.column_left.bold().yellow(), - right_padding, - self.column_right.bold().yellow() - )?; - writeln!(f, "{}", "-".repeat(self.width))?; - - for (left, right) in self.rows.iter() { - let padding_amount = self.width - left.len() - right.len(); - let padding = " ".repeat(padding_amount); - writeln!(f, "{}{}{}", left.bold().green(), padding, right)?; - } - - Ok(()) - } -} - -impl From for Table { - fn from(value: ExchangeRateResult) -> Self { - let mut table = Table::new(Some(value.time), "Currency".to_string(), "Rate".to_string()); - for (key, val) in value.rates.into_iter() { - table.add_row(key, val.to_string()); - } - - table - } -} diff --git a/src/table/mod.rs b/src/table/mod.rs new file mode 100644 index 0000000..9167d4f --- /dev/null +++ b/src/table/mod.rs @@ -0,0 +1,11 @@ +mod table_getter; +mod table_owned; +mod table_ref; +mod table_trait; +mod table_display; + +pub use table_getter::TableGet; +pub use table_owned::Table; +pub use table_ref::TableRef; +pub use table_trait::TableTrait; + diff --git a/src/table/table_display.rs b/src/table/table_display.rs new file mode 100644 index 0000000..25821f6 --- /dev/null +++ b/src/table/table_display.rs @@ -0,0 +1,46 @@ +use colored::Colorize; + +use super::TableGet; + +pub fn helper_table_print( + f: &mut std::fmt::Formatter<'_>, + table: &T, +) -> std::fmt::Result { + let width = table.get_width(); + + if let Some(header) = table.get_header() { + let middle_padding_amount = (width - header.len()) / 2; + assert!(middle_padding_amount > 0); + let middle_padding = " ".repeat(middle_padding_amount); + writeln!( + f, + "{}{}{}", + middle_padding, + header.bold().cyan(), + middle_padding + )?; + } + + let column_left = table.get_column_left(); + let column_right = table.get_column_right(); + let right_padding_amount = width - column_left.len() - column_right.len(); + let right_padding = " ".repeat(right_padding_amount); + writeln!( + f, + "{}{}{}", + column_left.bold().yellow(), + right_padding, + column_right.bold().yellow() + )?; + writeln!(f, "{}", "-".repeat(width))?; + + for (left, right) in table.get_rows().iter() { + let left_str = left.as_ref(); + let right_str = right.to_string(); + let padding_amount = width.saturating_sub(left_str.len() + right_str.len()); + let padding = " ".repeat(padding_amount); + writeln!(f, "{}{}{}", left_str.bold().green(), padding, right_str)?; + } + + Ok(()) +} diff --git a/src/table/table_getter.rs b/src/table/table_getter.rs new file mode 100644 index 0000000..4751932 --- /dev/null +++ b/src/table/table_getter.rs @@ -0,0 +1,10 @@ +pub trait TableGet { + type RowLeftRef: AsRef; + type RowRightRef: AsRef; + + fn get_header(&self) -> Option<&str>; + fn get_column_left(&self) -> &str; + fn get_column_right(&self) -> &str; + fn get_rows(&self) -> &Vec<(Self::RowLeftRef, f64)>; + fn get_width(&self) -> usize; +} diff --git a/src/table/table_owned.rs b/src/table/table_owned.rs new file mode 100644 index 0000000..11a92ad --- /dev/null +++ b/src/table/table_owned.rs @@ -0,0 +1,91 @@ +use std::fmt::Display; + +use crate::models::ExchangeRateResult; + +use super::table_display::helper_table_print; +use super::{TableGet, TableTrait}; + +pub struct Table { + header: Option, + column_left: String, + column_right: String, + rows: Vec<(String, f64)>, + pub color: bool, + pub width: usize, +} + +impl<'a> TableTrait<'a> for Table { + type Header = String; + type ColumnLeft = String; + type ColumnRight = String; + type RowLeft = String; + + fn new( + header: Option, + column_left: Self::ColumnLeft, + column_right: Self::ColumnRight, + ) -> Self { + Self { + header, + column_left, + column_right, + rows: Vec::new(), + color: false, + width: 21, + } + } + + fn disable_header(&mut self) { + self.header = None; + } + + fn set_header(&mut self, header: Self::Header) { + self.header = Some(header); + } + + fn add_row(&mut self, row_left: Self::RowLeft, row_right: f64) { + self.rows.push((row_left, row_right)); + } + + fn sort(&mut self) { + self.rows.sort_by(|a, b| a.1.total_cmp(&b.1)); + } +} + +impl TableGet for Table { + type RowLeftRef = String; + type RowRightRef = String; + + fn get_header(&self) -> Option<&str> { + self.header.as_deref() + } + fn get_column_left(&self) -> &str { + &self.column_left + } + fn get_column_right(&self) -> &str { + &self.column_right + } + fn get_rows(&self) -> &Vec<(Self::RowLeftRef, f64)> { + &self.rows + } + fn get_width(&self) -> usize { + self.width + } +} + +impl From for Table { + fn from(value: ExchangeRateResult) -> Self { + let mut table = Table::new(Some(value.time), "Currency".to_string(), "Rate".to_string()); + for (key, val) in value.rates.into_iter() { + table.add_row(key, val); + } + + table + } +} + +impl Display for Table { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + helper_table_print(f, self) + } +} diff --git a/src/table/table_ref.rs b/src/table/table_ref.rs new file mode 100644 index 0000000..4e27044 --- /dev/null +++ b/src/table/table_ref.rs @@ -0,0 +1,92 @@ +use std::fmt::Display; + +use crate::models::ExchangeRateResult; + +use super::table_display::helper_table_print; +use super::table_getter::TableGet; +use super::table_trait::TableTrait; + +pub struct TableRef<'a> { + header: Option<&'a str>, + column_left: &'a str, + column_right: &'a str, + rows: Vec<(&'a str, f64)>, + pub color: bool, + pub width: usize, +} + +impl<'a> TableTrait<'a> for TableRef<'a> { + type Header = &'a str; + type ColumnLeft = &'a str; + type ColumnRight = &'a str; + type RowLeft = &'a str; + + fn new( + header: Option, + column_left: Self::ColumnLeft, + column_right: Self::ColumnRight, + ) -> Self { + Self { + header, + column_left, + column_right, + rows: Vec::new(), + color: false, + width: 21, + } + } + + fn disable_header(&mut self) { + self.header = None; + } + + fn set_header(&mut self, header: Self::Header) { + self.header = Some(header); + } + + fn add_row(&mut self, row_left: Self::RowLeft, row_right: f64) { + self.rows.push((row_left, row_right)); + } + + fn sort(&mut self) { + self.rows.sort_by(|a, b| a.1.total_cmp(&b.1)); + } +} + +impl<'a> TableGet for TableRef<'a> { + type RowLeftRef = &'a str; + type RowRightRef = &'a str; + + fn get_header(&self) -> Option<&str> { + self.header + } + fn get_column_left(&self) -> &str { + self.column_left + } + fn get_column_right(&self) -> &str { + self.column_right + } + fn get_rows(&self) -> &Vec<(Self::RowLeftRef, f64)> { + &self.rows + } + fn get_width(&self) -> usize { + self.width + } +} + +impl<'a> From<&'a ExchangeRateResult> for TableRef<'a> { + fn from(value: &'a ExchangeRateResult) -> Self { + let mut table = TableRef::new(Some(&value.time), "Currency", "Rate"); + for (key, val) in value.rates.iter() { + table.add_row(key, *val); + } + + table + } +} + +impl<'a> Display for TableRef<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + helper_table_print(f, self) + } +} diff --git a/src/table/table_trait.rs b/src/table/table_trait.rs new file mode 100644 index 0000000..c68a3ad --- /dev/null +++ b/src/table/table_trait.rs @@ -0,0 +1,12 @@ +pub trait TableTrait<'a> { + type Header; + type ColumnLeft; + type ColumnRight; + type RowLeft; + + fn new(header: Option, column_left: Self::ColumnLeft, column_right: Self::ColumnRight) -> Self; + fn disable_header(&mut self); + fn set_header(&mut self, header: Self::Header); + fn add_row(&mut self, row_left: Self::RowLeft, row_right: f64); + fn sort(&mut self); +}