taler-rust

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

lib.rs (3500B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2024-2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 use std::{path::PathBuf, time::Duration};
     18 
     19 use config::{Config, parser::ConfigSource};
     20 use tracing::error;
     21 use tracing_subscriber::util::SubscriberInitExt;
     22 
     23 use crate::log::taler_logger;
     24 
     25 pub mod api;
     26 pub mod bench;
     27 pub mod cli;
     28 pub mod config;
     29 pub mod db;
     30 pub mod encoding;
     31 pub mod error;
     32 pub mod error_code;
     33 pub mod json_file;
     34 pub mod log;
     35 pub mod signature;
     36 pub mod types;
     37 
     38 #[derive(clap::Parser, Debug, Clone)]
     39 pub struct CommonArgs {
     40     /// Specifies the configuration file
     41     #[arg(short, long, global = true)]
     42     config: Option<PathBuf>,
     43 
     44     /// Configure logging to use LOGLEVEL
     45     #[arg(short('L'), long, global = true)]
     46     log: Option<tracing::Level>,
     47 
     48     /// Show logs from all sources
     49     #[arg(short, long, global = true, hide = true)]
     50     verbose: bool,
     51 }
     52 
     53 /// DD102: will fail with exit code 9 and will not be restarted by systemd
     54 #[derive(Debug, thiserror::Error)]
     55 #[error("{0}")]
     56 pub struct PermanentErr(String);
     57 
     58 pub fn taler_main(
     59     src: ConfigSource,
     60     args: CommonArgs,
     61     app: impl AsyncFnOnce(&Config) -> Result<(), anyhow::Error>,
     62 ) {
     63     taler_logger(args.log, args.verbose).init();
     64     let cfg = match Config::load(src, args.config) {
     65         Ok(cfg) => cfg,
     66         Err(err) => {
     67             error!(target: "config", "{}", err);
     68             std::process::exit(6);
     69         }
     70     };
     71 
     72     // Setup async runtime
     73     let runtime = tokio::runtime::Builder::new_multi_thread()
     74         .enable_all()
     75         .build()
     76         .unwrap();
     77 
     78     // Run app
     79     let result = runtime.block_on(app(&cfg));
     80     if let Err(err) = result {
     81         error!(target: "cli", "{}", err);
     82         // DD102: only diagnosed configuration errors suppress service recovery
     83         std::process::exit(if err.chain().any(|cause| cause.is::<config::CfgErr>()) {
     84             6
     85         } else if err.chain().any(|cause| cause.is::<PermanentErr>()) {
     86             9
     87         } else {
     88             1
     89         })
     90     }
     91 }
     92 
     93 /// Infinite exponential backoff with decorrelated jitter
     94 pub struct ExpoBackoffDecorr {
     95     base: u32,
     96     max: u32,
     97     factor: f32,
     98     sleep: u32,
     99 }
    100 
    101 impl ExpoBackoffDecorr {
    102     pub fn new(base: Duration, max: Duration, factor: f32) -> Self {
    103         Self {
    104             base: base.as_millis() as u32,
    105             max: max.as_millis() as u32,
    106             factor,
    107             sleep: base.as_millis() as u32,
    108         }
    109     }
    110 
    111     pub fn backoff(&mut self) -> Duration {
    112         self.sleep =
    113             rand::random_range(self.base..(self.sleep as f32 * self.factor) as u32).min(self.max);
    114         Duration::from_millis(self.sleep as u64)
    115     }
    116 
    117     pub fn reset(&mut self) {
    118         self.sleep = self.base
    119     }
    120 }
    121 
    122 impl Default for ExpoBackoffDecorr {
    123     fn default() -> Self {
    124         Self::new(Duration::from_millis(400), Duration::from_secs(30), 2.5)
    125     }
    126 }