Create non-owned variant of the table.

It yields ~12% faster time for cached results in historical day resolution
This commit is contained in:
Love 2025-01-08 15:01:57 +01:00
parent 27fb228e87
commit 81ec482918
8 changed files with 264 additions and 93 deletions

View File

@ -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<Vec<ExchangeRateResult>> {
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)
})

View File

@ -1,91 +0,0 @@
use colored::*;
use std::fmt::Display;
use crate::models::ExchangeRateResult;
pub struct Table {
header: Option<String>,
column_left: String,
column_right: String,
rows: Vec<(String, String)>,
pub color: bool,
pub width: usize,
}
impl Table {
fn new(header: Option<String>, 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<ExchangeRateResult> 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
}
}

11
src/table/mod.rs Normal file
View File

@ -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;

View File

@ -0,0 +1,46 @@
use colored::Colorize;
use super::TableGet;
pub fn helper_table_print<T: TableGet>(
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(())
}

10
src/table/table_getter.rs Normal file
View File

@ -0,0 +1,10 @@
pub trait TableGet {
type RowLeftRef: AsRef<str>;
type RowRightRef: AsRef<str>;
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;
}

91
src/table/table_owned.rs Normal file
View File

@ -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<String>,
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<Self::Header>,
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<ExchangeRateResult> 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)
}
}

92
src/table/table_ref.rs Normal file
View File

@ -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<Self::Header>,
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)
}
}

12
src/table/table_trait.rs Normal file
View File

@ -0,0 +1,12 @@
pub trait TableTrait<'a> {
type Header;
type ColumnLeft;
type ColumnRight;
type RowLeft;
fn new(header: Option<Self::Header>, 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);
}