commit 4e0704b3732a7a139f9ec7eef8a5adfdfa391aaf
parent 25696769c34802040aa0af5a1022adfe0fd93f54
Author: Antoine A <>
Date: Thu, 11 Jun 2026 14:57:46 +0200
common: clean code and improve logic
Diffstat:
9 files changed, 139 insertions(+), 102 deletions(-)
diff --git a/common/http-client/src/builder.rs b/common/http-client/src/builder.rs
@@ -32,15 +32,12 @@ use url::Url;
use crate::{Client, ClientErr, Ctx, headers::HeaderParser, sse::SseClient};
-struct Builder {
- headers: HeaderMap,
- body: Full<Bytes>,
-}
-
pub struct Req {
client: Client,
url: Url,
- builder: Result<Builder, ClientErr>,
+ body: Bytes,
+ headers: HeaderMap,
+ err: Option<ClientErr>,
ctx: Ctx,
}
@@ -56,10 +53,9 @@ impl Req {
Self {
client: client.clone(),
url,
- builder: Ok(Builder {
- headers: HeaderMap::new(),
- body: Full::default(),
- }),
+ headers: HeaderMap::new(),
+ body: Bytes::default(),
+ err: None,
ctx: Ctx {
path,
method,
@@ -76,6 +72,14 @@ impl Req {
&self.url
}
+ pub fn headers(&self) -> &HeaderMap {
+ &self.headers
+ }
+
+ pub fn body(&self) -> &Bytes {
+ &self.body
+ }
+
pub fn header<K, V>(mut self, key: K, value: V) -> Self
where
K: TryInto<HeaderName>,
@@ -83,12 +87,17 @@ impl Req {
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::Error: Into<http::Error>,
{
- self.builder = self.builder.and_then(move |mut builder| {
+ let res = (|| {
let name = key.try_into().map_err(Into::into)?;
let value = value.try_into().map_err(Into::into)?;
- builder.headers.insert(name, value);
- Ok(builder)
- });
+ self.headers.insert(name, value);
+ Ok(())
+ })();
+ if let Err(e) = res
+ && self.err.is_none()
+ {
+ self.err = Some(e);
+ }
self
}
@@ -99,27 +108,30 @@ impl Req {
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::Error: Into<http::Error>,
{
- self.builder = self.builder.and_then(move |mut builder| {
+ let res = (|| {
let name = key.try_into().map_err(Into::into)?;
let mut value = value.try_into().map_err(Into::into)?;
value.set_sensitive(true);
- builder.headers.insert(name, value);
- Ok(builder)
- });
+ self.headers.insert(name, value);
+ Ok(())
+ })();
+ if let Err(e) = res
+ && self.err.is_none()
+ {
+ self.err = Some(e);
+ }
self
}
pub fn query<T: Serialize>(mut self, name: &str, value: T) -> Self {
- if self.builder.is_ok() {
- let mut pairs = self.url.query_pairs_mut();
- let serializer = serde_urlencoded::Serializer::new(&mut pairs);
- if let Err(e) = [(name, value)].serialize(serializer) {
- drop(pairs);
- self.builder = Err(e.into());
- return self;
- }
+ let mut pairs = self.url.query_pairs_mut();
+ let serializer = serde_urlencoded::Serializer::new(&mut pairs);
+ if let Err(e) = [(name, value)].serialize(serializer)
+ && self.err.is_none()
+ {
+ self.err = Some(e.into());
}
-
+ drop(pairs);
self
}
@@ -128,17 +140,17 @@ impl Req {
let serializer: &mut serde_json::Serializer<&mut Vec<u8>> =
&mut serde_json::Serializer::new(&mut buf);
if let Err(e) = serde_path_to_error::serialize(json, serializer).map_err(ClientErr::ReqJson)
+ && self.err.is_none()
{
- self.builder = Err(e);
+ self.err = Some(e);
return self;
};
- if let Ok(builder) = &mut self.builder {
- builder.headers.insert(
- header::CONTENT_TYPE,
- HeaderValue::from_static("application/json"),
- );
- builder.body = Full::new(buf.into())
- }
+ self.content(buf.into(), HeaderValue::from_static("application/json"))
+ }
+
+ pub fn content(mut self, body: Bytes, ty: HeaderValue) -> Self {
+ self.headers.insert(header::CONTENT_TYPE, ty);
+ self.body = body;
self
}
@@ -179,18 +191,22 @@ impl Req {
let Self {
client,
ctx,
- builder,
url,
+ err,
+ headers,
+ body,
} = self;
+ if let Some(e) = err {
+ return Err((ctx, e));
+ }
let req = match async {
- let Builder { headers, body } = builder?;
let mut builder = http::request::Request::builder()
.uri(url.as_str())
.method(ctx.method.clone());
if let Some(headers_mut) = builder.headers_mut() {
*headers_mut = headers;
}
- let req = builder.body(body)?;
+ let req = builder.body(Full::new(body))?;
Ok(req)
}
.await
@@ -218,6 +234,10 @@ impl Res {
self.head.status
}
+ pub fn headers(&self) -> &HeaderMap {
+ &self.head.headers
+ }
+
pub fn str_header(&self, name: &'static str) -> Result<String, ClientErr> {
self.head
.headers
diff --git a/common/taler-api/src/config.rs b/common/taler-api/src/config.rs
@@ -26,16 +26,17 @@ use taler_common::{
use crate::{Serve, auth::AuthMethod};
/// Basic database config
+#[derive(Debug)]
pub struct DbCfg {
pub cfg: PgConnectOptions,
pub sql_dir: String,
}
impl DbCfg {
- pub fn parse(sect: Section) -> Result<Self, ValueErr> {
+ pub fn parse(s: Section) -> Result<Self, ValueErr> {
Ok(Self {
- cfg: sect.postgres("CONFIG").require()?,
- sql_dir: sect.path("SQL_DIR").require()?,
+ cfg: s.postgres("CONFIG").require()?,
+ sql_dir: s.path("SQL_DIR").require()?,
})
}
}
@@ -47,6 +48,26 @@ pub enum AuthCfg {
}
impl AuthCfg {
+ pub fn parse(s: &Section) -> Result<Self, ValueErr> {
+ map_config!(s, "auth_method", "AUTH_METHOD",
+ "none" => { AuthCfg::None },
+ "basic" => {
+ AuthCfg::Basic {
+ username: s.str("USERNAME").require()?,
+ password: s.str("PASSWORD").require()?
+ }
+ },
+ "bearer" => {
+ if let Some(token) = s.str("AUTH_TOKEN").opt()? {
+ AuthCfg::Bearer(token)
+ } else {
+ AuthCfg::Bearer(s.str("TOKEN").require()?)
+ }
+ }
+ )
+ .require()
+ }
+
pub fn method(&self) -> AuthMethod {
match self {
AuthCfg::None => AuthMethod::None,
@@ -64,27 +85,11 @@ pub struct ApiCfg {
}
impl ApiCfg {
- pub fn parse(sect: Section) -> Result<Option<Self>, ValueErr> {
- Ok(if sect.boolean("ENABLED").require()? {
- let auth = map_config!(sect, "auth_method", "AUTH_METHOD",
- "none" => { AuthCfg::None },
- "basic" => {
- AuthCfg::Basic {
- username: sect.str("USERNAME").require()?,
- password: sect.str("PASSWORD").require()?
- }
- },
- "bearer" => {
- let token = sect.str("AUTH_TOKEN").opt()?;
- if let Some(token) = token {
- AuthCfg::Bearer(token)
- } else {
- AuthCfg::Bearer(sect.str("TOKEN").require()?)
- }
- }
- )
- .require()?;
- Some(Self { auth })
+ pub fn parse(s: Section) -> Result<Option<Self>, ValueErr> {
+ Ok(if s.boolean("ENABLED").require()? {
+ Some(Self {
+ auth: AuthCfg::parse(&s)?,
+ })
} else {
None
})
@@ -92,16 +97,16 @@ impl ApiCfg {
}
impl Serve {
- pub fn parse(sect: Section) -> Result<Self, ValueErr> {
- map_config!(sect, "serve", "SERVE",
+ pub fn parse(s: &Section) -> Result<Self, ValueErr> {
+ map_config!(s, "serve", "SERVE",
"tcp" => {
- let port = sect.number("PORT").require()?;
- let ip: IpAddr = sect.parse("IP addr", "BIND_TO").require()?;
+ let port = s.number("PORT").require()?;
+ let ip: IpAddr = s.parse("IP addr", "BIND_TO").require()?;
Serve::Tcp(SocketAddr::new(ip, port))
},
"unix" => {
- let path = sect.path("UNIXPATH").require()?;
- let permission = sect.unix_mode("UNIXPATH_MODE").require()?;
+ let path = s.path("UNIXPATH").require()?;
+ let permission = s.unix_mode("UNIXPATH_MODE").require()?;
Serve::Unix { path, permission }
},
"systemd" => { Serve::Systemd }
diff --git a/common/taler-common/src/cli.rs b/common/taler-common/src/cli.rs
@@ -50,11 +50,11 @@ impl ConfigCmd {
option,
filename,
} => {
- let sect = cfg.section(§ion);
+ let s = cfg.section(§ion);
let value = if filename {
- sect.path(&option).require()?
+ s.path(&option).require()?
} else {
- sect.str(&option).require()?
+ s.str(&option).require()?
};
writeln!(&mut out, "{value}")?;
}
diff --git a/common/taler-common/src/db.rs b/common/taler-common/src/db.rs
@@ -128,7 +128,19 @@ pub async fn dbinit(
}
}
- exec_sql_file(&mut *tx, &format!("{prefix}-procedures.sql"), "procedures").await?;
+ if let Err(e) = exec_sql_file(&mut *tx, &format!("{prefix}-procedures.sql"), "procedures").await
+ {
+ if let MigrationErr::Io(_, e) = &e
+ && e.kind() == ErrorKind::NotFound
+ {
+ debug!(
+ target: "dbinit",
+ "no procedures.sql for the SQL collection: '{prefix}'"
+ );
+ } else {
+ return Err(e);
+ }
+ }
tx.commit().await?;
diff --git a/common/taler-common/src/log.rs b/common/taler-common/src/log.rs
@@ -128,7 +128,6 @@ pub fn taler_logger(max_level: Option<Level>, verbose: bool) -> impl SubscriberI
*metadata.level() <= max_level
&& (verbose
|| !(target.starts_with("sqlx")
- || target.starts_with("log")
|| target.starts_with("axum")
|| target.contains("hyper_util")
|| target.starts_with("h2")
diff --git a/taler-apns-relay/src/config.rs b/taler-apns-relay/src/config.rs
@@ -31,9 +31,9 @@ pub struct ServeCfg {
impl ServeCfg {
pub fn parse(cfg: &Config) -> Result<Self, ValueErr> {
- let sect = cfg.section("apns-relay-httpd");
+ let s = cfg.section("apns-relay-httpd");
- let serve = Serve::parse(sect)?;
+ let serve = Serve::parse(&s)?;
Ok(Self { serve })
}
diff --git a/taler-cyclos/src/config.rs b/taler-cyclos/src/config.rs
@@ -111,9 +111,9 @@ impl ServeCfg {
let main = MainCfg::parse(cfg)?;
let payto = parse_account_payto(cfg, &main)?;
- let sect = cfg.section("cyclos-httpd");
+ let s = cfg.section("cyclos-httpd");
- let serve = Serve::parse(sect)?;
+ let serve = Serve::parse(&s)?;
let wire_gateway = ApiCfg::parse(cfg.section("cyclos-httpd-wire-gateway-api"))?;
let revenue = ApiCfg::parse(cfg.section("cyclos-httpd-revenue-api"))?;
@@ -175,18 +175,18 @@ pub struct WorkerCfg {
impl WorkerCfg {
pub fn parse(cfg: &Config) -> Result<Self, ValueErr> {
let main = MainCfg::parse(cfg)?;
- let sect = cfg.section("cyclos-worker");
+ let s = cfg.section("cyclos-worker");
Ok(Self {
- frequency: sect.duration("FREQUENCY").require()?,
- account_type: map_config!(sect, "account type", "ACCOUNT_TYPE",
+ frequency: s.duration("FREQUENCY").require()?,
+ account_type: map_config!(s, "account type", "ACCOUNT_TYPE",
"exchange" => { AccountType::Exchange },
"normal" => { AccountType::Normal }
)
.require()?,
- account_type_id: sect
+ account_type_id: s
.parse("cyclos account type id", "ACCOUNT_TYPE_ID")
.require()?,
- payment_type_id: sect
+ payment_type_id: s
.parse("cyclos payment type id", "PAYMENT_TYPE_ID")
.require()?,
host: HostCfg::parse(cfg, &main)?,
@@ -207,12 +207,12 @@ impl HarnessCfg {
pub fn parse(cfg: &Config) -> Result<Self, ValueErr> {
let worker = WorkerCfg::parse(cfg)?;
- let sect = cfg.section("cyclos-harness");
+ let s = cfg.section("cyclos-harness");
Ok(Self {
worker,
- username: sect.str("USERNAME").require()?,
- password: sect.str("PASSWORD").require()?,
+ username: s.str("USERNAME").require()?,
+ password: s.str("PASSWORD").require()?,
})
}
}
diff --git a/taler-cyclos/src/cyclos_api/api.rs b/taler-cyclos/src/cyclos_api/api.rs
@@ -58,6 +58,7 @@ pub enum CyclosErr {
}
pub type ApiResult<R> = std::result::Result<R, ApiErr<CyclosErr>>;
+
pub struct CyclosRequest<'a> {
req: Req,
auth: &'a CyclosAuth,
diff --git a/taler-magnet-bank/src/config.rs b/taler-magnet-bank/src/config.rs
@@ -34,9 +34,9 @@ pub fn parse_db_cfg(cfg: &Config) -> Result<DbCfg, ValueErr> {
}
pub fn parse_account_payto(cfg: &Config) -> Result<FullHuPayto, ValueErr> {
- let sect = cfg.section("magnet-bank");
- let iban: HuIban = sect.parse("iban", "IBAN").require()?;
- let name = sect.str("NAME").require()?;
+ let s = cfg.section("magnet-bank");
+ let iban: HuIban = s.parse("iban", "IBAN").require()?;
+ let name = s.str("NAME").require()?;
Ok(FullHuPayto::new(iban, &name))
}
@@ -53,9 +53,9 @@ impl ServeCfg {
pub fn parse(cfg: &Config) -> Result<Self, ValueErr> {
let payto = parse_account_payto(cfg)?;
- let sect = cfg.section("magnet-bank-httpd");
+ let s = cfg.section("magnet-bank-httpd");
- let serve = Serve::parse(sect)?;
+ let serve = Serve::parse(&s)?;
let wire_gateway = ApiCfg::parse(cfg.section("magnet-bank-httpd-wire-gateway-api"))?;
let revenue = ApiCfg::parse(cfg.section("magnet-bank-httpd-revenue-api"))?;
@@ -90,23 +90,23 @@ pub struct WorkerCfg {
impl WorkerCfg {
pub fn parse(cfg: &Config) -> Result<Self, ValueErr> {
let payto = parse_account_payto(cfg)?;
- let sect = cfg.section("magnet-bank-worker");
+ let s = cfg.section("magnet-bank-worker");
Ok(Self {
payto,
- frequency: sect.duration("FREQUENCY").require()?,
- account_type: map_config!(sect, "account type", "ACCOUNT_TYPE",
+ frequency: s.duration("FREQUENCY").require()?,
+ account_type: map_config!(s, "account type", "ACCOUNT_TYPE",
"exchange" => { AccountType::Exchange },
"normal" => { AccountType::Normal }
)
.require()?,
- api_url: sect.base_url("API_URL").require()?,
+ api_url: s.base_url("API_URL").require()?,
consumer: Token {
- key: sect.str("CONSUMER_KEY").require()?,
- secret: sect.str("CONSUMER_SECRET").require()?,
+ key: s.str("CONSUMER_KEY").require()?,
+ secret: s.str("CONSUMER_SECRET").require()?,
},
- keys_path: sect.path("KEYS_FILE").require()?,
- ignore_tx_before: sect.date("IGNORE_TRANSACTIONS_BEFORE").opt()?,
- ignore_bounces_before: sect.date("IGNORE_BOUNCES_BEFORE").opt()?,
+ keys_path: s.path("KEYS_FILE").require()?,
+ ignore_tx_before: s.date("IGNORE_TRANSACTIONS_BEFORE").opt()?,
+ ignore_bounces_before: s.date("IGNORE_BOUNCES_BEFORE").opt()?,
})
}
}
@@ -121,9 +121,9 @@ impl HarnessCfg {
pub fn parse(cfg: &Config) -> Result<Self, ValueErr> {
let worker = WorkerCfg::parse(cfg)?;
- let sect = cfg.section("magnet-bank-harness");
- let iban: HuIban = sect.parse("iban", "IBAN").require()?;
- let name = sect.str("NAME").require()?;
+ let s = cfg.section("magnet-bank-harness");
+ let iban: HuIban = s.parse("iban", "IBAN").require()?;
+ let name = s.str("NAME").require()?;
Ok(Self {
worker,