From 0f930271c73722883c017b1a3d49a2fe14374dae Mon Sep 17 00:00:00 2001 From: Love Billenius Date: Wed, 17 Dec 2025 22:10:46 +0100 Subject: [PATCH] Use smolstr since we only expect small strings --- Cargo.lock | 30 ++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/cli/cli_t.rs | 5 +++-- src/main.rs | 5 +++-- src/models.rs | 5 +++-- src/parsing.rs | 21 ++++++++++++++------- src/table/table_owned.rs | 26 ++++++++++++++------------ src/utils_calc.rs | 8 +++++--- 8 files changed, 71 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c0781a..74bcadc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,6 +91,15 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "cfg_aliases", +] + [[package]] name = "bumpalo" version = "3.19.1" @@ -119,6 +128,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.42" @@ -227,6 +242,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "smol_str", "tokio", ] @@ -961,9 +977,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "zeroize", ] @@ -1096,6 +1112,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smol_str" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3498b0a27f93ef1402f20eefacfaa1691272ac4eca1cdc8c596cb0a245d6cbf5" +dependencies = [ + "borsh", + "serde_core", +] + [[package]] name = "socket2" version = "0.6.1" diff --git a/Cargo.toml b/Cargo.toml index 2840721..fabf354 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,5 @@ quick-xml = { version = "0.38", features = ["async-tokio", "tokio"] } reqwest = "0.12" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +smol_str = { version = "0.3", features = ["serde"] } tokio = { version = "1.48", features = ["macros"] } diff --git a/src/cli/cli_t.rs b/src/cli/cli_t.rs index 0ab555c..a52e9b0 100644 --- a/src/cli/cli_t.rs +++ b/src/cli/cli_t.rs @@ -1,4 +1,5 @@ use clap::{arg, Parser, ValueEnum}; +use smol_str::SmolStr; use super::{ShowDays, SortBy}; @@ -8,7 +9,7 @@ use super::{ShowDays, SortBy}; pub struct Cli { /// Which currencies do you want to fetch rates for? #[arg(long = "currencies", short = 'c')] - pub currencies: Vec, + pub currencies: Vec, #[arg(value_enum, default_value_t = FormatOption::Plain)] pub command: FormatOption, @@ -35,7 +36,7 @@ pub struct Cli { /// Recalculate to the perspective from an included currency #[arg(long = "perspective", short = 'p')] - pub perspective: Option, + pub perspective: Option, /// Invert the rate #[arg(long = "invert", short = 'i')] diff --git a/src/main.rs b/src/main.rs index 573c9af..e352130 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use clap::Parser as _; use ecb_rates::cache::{Cache, CacheLine}; use ecb_rates::HeaderDescription; use reqwest::{Client, IntoUrl}; +use smol_str::StrExt; use std::process::ExitCode; use ecb_rates::cli::{Cli, FormatOption}; @@ -78,7 +79,7 @@ async fn main() -> ExitCode { parsed }; - cli.perspective = cli.perspective.map(|s| s.to_uppercase()); + cli.perspective = cli.perspective.map(|s| s.to_uppercase_smolstr()); if let Some(currency) = cli.perspective.as_ref() { header_description.replace_eur(¤cy); let error_occured = change_perspective(&mut parsed, ¤cy).is_none(); @@ -99,7 +100,7 @@ async fn main() -> ExitCode { let currencies = cli .currencies .iter() - .map(|x| x.to_uppercase()) + .map(|x| x.to_uppercase_smolstr()) .collect::>(); filter_currencies(&mut parsed, ¤cies); diff --git a/src/models.rs b/src/models.rs index 2b0dedc..fc8ab85 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,8 +1,9 @@ use serde::{Deserialize, Serialize}; +use smol_str::SmolStr; use std::collections::HashMap; #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct ExchangeRateResult { - pub time: String, - pub rates: HashMap, + pub time: SmolStr, + pub rates: HashMap, } diff --git a/src/parsing.rs b/src/parsing.rs index daa6cac..4545c76 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -2,37 +2,44 @@ use std::collections::HashMap; use quick_xml::events::Event; use quick_xml::Reader; +use smol_str::SmolStr; use crate::models::ExchangeRateResult; +fn smol_from_utf8(bytes: &[u8]) -> SmolStr { + str::from_utf8(bytes) + .map(SmolStr::new) + .unwrap_or_else(|_| SmolStr::new(String::from_utf8_lossy(bytes))) +} + pub fn parse(xml: &str) -> anyhow::Result> { let mut reader = Reader::from_str(xml); reader.config_mut().trim_text(true); let mut results = Vec::new(); - let mut current_time: Option = None; + let mut current_time: Option = None; let mut inside_cube_time = false; let mut current_rates = HashMap::new(); fn handle_cube_element( e: &quick_xml::events::BytesStart, - current_time: &mut Option, + current_time: &mut Option, inside_cube_time: &mut bool, - current_rates: &mut HashMap, + current_rates: &mut HashMap, results: &mut Vec, ) -> anyhow::Result<()> { if e.name().local_name().as_ref() != b"Cube" { return Ok(()); } - let mut time_attr: Option = None; - let mut currency_attr: Option = None; - let mut rate_attr: Option = None; + let mut time_attr: Option = None; + let mut currency_attr: Option = None; + let mut rate_attr: Option = None; for attr_result in e.attributes() { let attr = attr_result?; let key = attr.key.as_ref(); - let val = String::from_utf8_lossy(attr.value.as_ref()).to_string(); + let val = smol_from_utf8(attr.value.as_ref()); match key { b"time" => { diff --git a/src/table/table_owned.rs b/src/table/table_owned.rs index f4f143a..0f0abd7 100644 --- a/src/table/table_owned.rs +++ b/src/table/table_owned.rs @@ -1,5 +1,7 @@ use std::fmt::Display; +use smol_str::SmolStr; + use crate::cli::SortBy; use crate::models::ExchangeRateResult; use crate::DEFAULT_WIDTH; @@ -8,20 +10,20 @@ use super::table_display::helper_table_print; use super::{TableGet, TableTrait}; pub struct Table { - pub(super) header: Option, - pub(super) column_left: String, - pub(super) column_right: String, - pub(super) rows: Vec<(String, f64)>, + pub(super) header: Option, + pub(super) column_left: SmolStr, + pub(super) column_right: SmolStr, + pub(super) rows: Vec<(SmolStr, f64)>, pub color: bool, pub width: usize, pub left_offset: usize, } impl<'a> TableTrait<'a> for Table { - type Header = String; - type ColumnLeft = String; - type ColumnRight = String; - type RowLeft = String; + type Header = SmolStr; + type ColumnLeft = SmolStr; + type ColumnRight = SmolStr; + type RowLeft = SmolStr; fn new( header: Option, @@ -59,8 +61,8 @@ impl<'a> TableTrait<'a> for Table { } impl TableGet for Table { - type RowLeftRef = String; - type RowRightRef = String; + type RowLeftRef = SmolStr; + type RowRightRef = SmolStr; fn get_header(&self) -> Option<&str> { self.header.as_deref() @@ -77,7 +79,7 @@ impl TableGet for Table { fn get_width(&self) -> usize { self.width } - + fn get_left_offset(&self) -> usize { self.left_offset } @@ -85,7 +87,7 @@ impl TableGet for Table { impl From for Table { fn from(value: ExchangeRateResult) -> Self { - let mut table = Table::new(Some(value.time), "Currency".to_string(), "Rate".to_string()); + let mut table = Table::new(Some(value.time), "Currency".into(), "Rate".into()); for (key, val) in value.rates.into_iter() { table.add_row(key, val); } diff --git a/src/utils_calc.rs b/src/utils_calc.rs index 81b9e3e..029936b 100644 --- a/src/utils_calc.rs +++ b/src/utils_calc.rs @@ -1,10 +1,12 @@ use std::{borrow::BorrowMut, collections::HashMap, ops::Deref}; +use smol_str::SmolStr; + use crate::models::ExchangeRateResult; -pub fn filter_currencies(exchange_rate_results: &mut [ExchangeRateResult], currencies: &[String]) { +pub fn filter_currencies(exchange_rate_results: &mut [ExchangeRateResult], currencies: &[SmolStr]) { for exchange_rate in exchange_rate_results { - let rates_ptr: *mut HashMap = &mut exchange_rate.rates; + let rates_ptr: *mut HashMap<_, _> = &mut exchange_rate.rates; exchange_rate .rates .keys() @@ -32,7 +34,7 @@ pub fn change_perspective( *iter_rate = eur_rate * iter_rate.deref(); } - rate_res.rates.insert("EUR".to_string(), eur_rate); + rate_res.rates.insert("EUR".into(), eur_rate); } Some(()) }