taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

commit d74a04e7b9e16049e5f3fcc1ffdaf0aa19d24d59
parent 80e493da20074792bb682f529cdbfad65892883c
Author: Antoine A <>
Date:   Sun, 28 Dec 2025 13:44:29 +0100

common: optimize payto and ApiError

Diffstat:
Mcommon/taler-api/src/error.rs | 15+++++++++------
Mcommon/taler-common/Cargo.toml | 1+
Mcommon/taler-common/src/types/payto.rs | 34+++++++++++++++++++---------------
Mtaler-cyclos/src/bin/cyclos-harness.rs | 2+-
Mtaler-cyclos/src/config.rs | 2+-
Mtaler-cyclos/src/cyclos_api/types.rs | 2+-
Mtaler-cyclos/src/worker.rs | 4++--
Mtaler-magnet-bank/src/bin/magnet-bank-harness.rs | 27++++++++-------------------
Mtaler-magnet-bank/src/config.rs | 4++--
Mtaler-magnet-bank/src/dev.rs | 1+
Mtaler-magnet-bank/src/worker.rs | 2+-
11 files changed, 46 insertions(+), 48 deletions(-)

diff --git a/common/taler-api/src/error.rs b/common/taler-api/src/error.rs @@ -31,7 +31,7 @@ pub struct ApiError { log: Option<Box<str>>, status: Option<StatusCode>, path: Option<Box<str>>, - headers: HeaderMap, + headers: Option<Box<HeaderMap>>, } impl ApiError { @@ -42,7 +42,7 @@ impl ApiError { log: None, status: None, path: None, - headers: HeaderMap::new(), + headers: None, } } @@ -75,7 +75,8 @@ impl ApiError { } pub fn with_header(mut self, key: impl IntoHeaderName, value: HeaderValue) -> Self { - self.headers.append(key, value); + let headers = self.headers.get_or_insert_default(); + headers.append(key, value); self } } @@ -101,7 +102,7 @@ impl From<sqlx::Error> for ApiError { status: Some(status), log: Some(format!("db: {value}").into_boxed_str()), path: None, - headers: HeaderMap::new(), + headers: None, } } } @@ -155,8 +156,10 @@ impl IntoResponse for ApiError { }), ) .into_response(); - for (k, v) in self.headers { - resp.headers_mut().append(k.unwrap(), v); + if let Some(headers) = self.headers { + for (k, v) in *headers { + resp.headers_mut().append(k.unwrap(), v); + } } if let Some(log) = log { resp.extensions_mut().insert(log); diff --git a/common/taler-common/Cargo.toml b/common/taler-common/Cargo.toml @@ -28,6 +28,7 @@ ed25519-dalek.workspace = true rand_core.workspace = true tokio = { workspace = true, features = ["rt-multi-thread"] } sqlx = { workspace = true, features = ["macros"] } +compact_str = { version = "0.9.0", features = ["serde", "sqlx-postgres"] } [dev-dependencies] criterion.workspace = true diff --git a/common/taler-common/src/types/payto.rs b/common/taler-common/src/types/payto.rs @@ -14,6 +14,7 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ +use compact_str::CompactString; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde_with::{DeserializeFromStr, SerializeDisplay}; use std::{ @@ -205,16 +206,16 @@ impl PaytoImpl for IBAN { #[derive(Debug, Clone, Deserialize)] struct FullQuery { #[serde(rename = "receiver-name")] - receiver_name: String, + receiver_name: CompactString, } /// Transfer payto query #[derive(Debug, Clone, Deserialize)] struct TransferQuery { #[serde(rename = "receiver-name")] - receiver_name: String, + receiver_name: CompactString, amount: Option<Amount>, - message: Option<String>, + message: Option<CompactString>, } #[derive(Debug, Clone, PartialEq, Eq, DeserializeFromStr, SerializeDisplay)] @@ -280,12 +281,15 @@ impl<P: PaytoImpl> Deref for Payto<P> { #[derive(Debug, Clone, PartialEq, Eq, DeserializeFromStr, SerializeDisplay)] pub struct FullPayto<P> { inner: P, - pub name: String, + pub name: CompactString, } impl<P: PaytoImpl> FullPayto<P> { - pub fn new(inner: P, name: String) -> Self { - Self { inner, name } + pub fn new(inner: P, name: &str) -> Self { + Self { + inner, + name: CompactString::new(name), + } } pub fn as_payto(&self) -> PaytoURI { @@ -345,18 +349,18 @@ impl<P: PaytoImpl> Deref for FullPayto<P> { #[derive(Debug, Clone, PartialEq, Eq, DeserializeFromStr, SerializeDisplay)] pub struct TransferPayto<P> { inner: P, - pub name: String, + pub name: CompactString, pub amount: Option<Amount>, - pub subject: Option<String>, + pub subject: Option<CompactString>, } impl<P: PaytoImpl> TransferPayto<P> { - pub fn new(inner: P, name: String, amount: Option<Amount>, subject: Option<String>) -> Self { + pub fn new(inner: P, name: &str, amount: Option<Amount>, subject: Option<&str>) -> Self { Self { inner, - name, + name: CompactString::new(name), amount, - subject, + subject: subject.map(CompactString::new), } } @@ -438,7 +442,7 @@ mod test { ); // Full payto - let full_payto = FullPayto::new(iban.clone(), "John Smith".to_owned()); + let full_payto = FullPayto::new(iban.clone(), "John Smith"); assert_eq!( full_payto, FullPayto::from_str(&format!("payto://iban/{iban}?receiver-name=John+Smith")).unwrap() @@ -454,7 +458,7 @@ mod test { assert_eq!(simple_payto, full_payto.clone().into()); // Transfer simple payto - let transfer_payto = TransferPayto::new(iban.clone(), "John Smith".to_owned(), None, None); + let transfer_payto = TransferPayto::new(iban.clone(), "John Smith", None, None); assert_eq!( transfer_payto, TransferPayto::from_str(&format!("payto://iban/{iban}?receiver-name=John+Smith")) @@ -473,9 +477,9 @@ mod test { // Transfer full payto let transfer_payto = TransferPayto::new( iban.clone(), - "John Smith".to_owned(), + "John Smith", Some(amount("EUR:12")), - Some("Wire transfer subject".to_owned()), + Some("Wire transfer subject"), ); assert_eq!( transfer_payto, diff --git a/taler-cyclos/src/bin/cyclos-harness.rs b/taler-cyclos/src/bin/cyclos-harness.rs @@ -393,7 +393,7 @@ async fn logic_harness(cfg: &Config, reset: bool) -> anyhow::Result<()> { let transfer_id = harness .custom_transfer( decimal("10.1"), - &FullCyclosPayto::new(CyclosId(42), "Unknown".to_string()), + &FullCyclosPayto::new(CyclosId(42), "Unknown"), ) .await; // Should failed diff --git a/taler-cyclos/src/config.rs b/taler-cyclos/src/config.rs @@ -42,7 +42,7 @@ pub fn parse_account_payto(cfg: &Config) -> Result<FullCyclosPayto, ValueErr> { let id = sect.parse("cyclos account id", "ACCOUNT_ID").require()?; let name = sect.str("NAME").require()?; - Ok(FullCyclosPayto::new(id, name)) + Ok(FullCyclosPayto::new(id, &name)) } /// taler-cyclos httpd config diff --git a/taler-cyclos/src/cyclos_api/types.rs b/taler-cyclos/src/cyclos_api/types.rs @@ -68,7 +68,7 @@ pub struct User { impl User { pub fn payto(&self) -> FullCyclosPayto { - FullPayto::new(self.id, self.name.clone()) + FullPayto::new(self.id, &self.name) } } diff --git a/taler-cyclos/src/worker.rs b/taler-cyclos/src/worker.rs @@ -394,9 +394,9 @@ pub fn extract_tx_info(tx: HistoryItem) -> Tx { let amount = amount::decimal(tx.amount.trim_start_matches('-')); let payto = match tx.related_account.kind { AccountKind::System => { - FullCyclosPayto::new(tx.related_account.ty.id, tx.related_account.ty.name) + FullCyclosPayto::new(tx.related_account.ty.id, &tx.related_account.ty.name) } - AccountKind::User { user } => FullCyclosPayto::new(user.id, user.display), + AccountKind::User { user } => FullCyclosPayto::new(user.id, &user.display), }; if tx.amount.starts_with("-") { Tx::Out(TxOut { diff --git a/taler-magnet-bank/src/bin/magnet-bank-harness.rs b/taler-magnet-bank/src/bin/magnet-bank-harness.rs @@ -158,11 +158,8 @@ impl<'a> Harness<'a> { } async fn transfer(&self, forint: u32) -> u64 { - self.custom_transfer( - forint, - &FullHuPayto::new(self.client.iban.clone(), "Name".to_owned()), - ) - .await + self.custom_transfer(forint, &FullHuPayto::new(self.client.iban.clone(), "Name")) + .await } async fn expect_transfer_status(&self, id: u64, status: TransferState, msg: Option<&str>) { @@ -346,10 +343,8 @@ async fn logic_harness(cfg: &Config, reset: bool) -> anyhow::Result<()> { tokio::time::sleep(Duration::from_secs(5)).await; harness.worker().await?; - let unknown_account = FullHuPayto::new( - HuIban::from_bban("1620000310991642").unwrap(), - "Unknown".to_string(), - ); + let unknown_account = + FullHuPayto::new(HuIban::from_bban("1620000310991642").unwrap(), "Unknown"); let now = Timestamp::now(); let balance = &mut Balances::new(&harness).await; @@ -383,7 +378,7 @@ async fn logic_harness(cfg: &Config, reset: bool) -> anyhow::Result<()> { let transfer_id = harness .custom_transfer( 102, - &FullHuPayto::new(harness.client.iban.clone(), "Client".to_string()), + &FullHuPayto::new(harness.client.iban.clone(), "Client"), ) .await; // Should send @@ -406,7 +401,7 @@ async fn logic_harness(cfg: &Config, reset: bool) -> anyhow::Result<()> { let transfer_id = harness .custom_transfer( 101, - &FullHuPayto::new(harness.exchange.iban.clone(), "Self".to_string()), + &FullHuPayto::new(harness.exchange.iban.clone(), "Self"), ) .await; // Should failed @@ -561,16 +556,10 @@ async fn online_harness(config: &Config, reset: bool) -> anyhow::Result<()> { step("Test outgoing transactions"); let transfer_self = harness - .custom_transfer( - 1, - &FullHuPayto::new(harness.exchange.iban.clone(), "Self".to_string()), - ) + .custom_transfer(1, &FullHuPayto::new(harness.exchange.iban.clone(), "Self")) .await; let transfer_id = harness - .custom_transfer( - 2, - &FullHuPayto::new(harness.client.iban.clone(), "Client".to_string()), - ) + .custom_transfer(2, &FullHuPayto::new(harness.client.iban.clone(), "Client")) .await; balance.expect(-2).await; harness diff --git a/taler-magnet-bank/src/config.rs b/taler-magnet-bank/src/config.rs @@ -37,7 +37,7 @@ pub fn parse_account_payto(cfg: &Config) -> Result<FullHuPayto, ValueErr> { let iban: HuIban = sect.parse("iban", "IBAN").require()?; let name = sect.str("NAME").require()?; - Ok(FullHuPayto::new(iban, name)) + Ok(FullHuPayto::new(iban, &name)) } /// taler-magnet-bank httpd config @@ -124,7 +124,7 @@ impl HarnessCfg { Ok(Self { worker, - client_payto: FullHuPayto::new(iban, name), + client_payto: FullHuPayto::new(iban, &name), }) } } diff --git a/taler-magnet-bank/src/dev.rs b/taler-magnet-bank/src/dev.rs @@ -126,6 +126,7 @@ pub async fn dev(cfg: &Config, cmd: DevCmd) -> anyhow::Result<()> { let subject = creditor .subject .clone() + .map(|it| it.to_string()) .or(subject) .ok_or_else(|| anyhow!("Missing subject"))?; diff --git a/taler-magnet-bank/src/worker.rs b/taler-magnet-bank/src/worker.rs @@ -493,7 +493,7 @@ pub fn extract_tx_info(tx: TxDto) -> Tx { } else { HuIban::from_bban(&tx.counter_account).unwrap() }; - let counter_account = FullHuPayto::new(iban, tx.counter_name); + let counter_account = FullHuPayto::new(iban, &tx.counter_name); if tx.amount.is_sign_positive() { Tx::In(TxIn { code: tx.code,