commit d74a04e7b9e16049e5f3fcc1ffdaf0aa19d24d59
parent 80e493da20074792bb682f529cdbfad65892883c
Author: Antoine A <>
Date: Sun, 28 Dec 2025 13:44:29 +0100
common: optimize payto and ApiError
Diffstat:
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,