taler-rust

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

setup.rs (4978B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 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 taler_common::config::Config;
     18 use tracing::{debug, error, info, warn};
     19 
     20 use crate::{
     21     config::{SetupCfg, WiseBalance},
     22     wise_api::client::Client,
     23 };
     24 
     25 pub async fn setup(cfg: &Config, client: &http_client::Client) -> anyhow::Result<()> {
     26     let cfg = SetupCfg::parse(cfg)?;
     27     let client = Client::new(client, &cfg.token);
     28 
     29     info!(target: "setup", "Check API access and configuration");
     30     let mut ready = true;
     31 
     32     // Check profile
     33     let profiles = client.profiles().await?;
     34     let profiles_fmt = std::fmt::from_fn(|w| {
     35         for p in &profiles {
     36             write!(w, "\n{} {:?} '{}'", p.id, p.ty, p.full_name)?;
     37         }
     38         Ok(())
     39     });
     40     if let Some(id) = cfg.profile_id {
     41         if let Some(profile) = profiles.iter().find(|it| it.id == id) {
     42             debug!(target: "setup", "PROFILE_ID {id} in config is one of:{profiles_fmt}");
     43             match cfg.name {
     44                 Some(name) => {
     45                     if name != profile.full_name {
     46                         warn!(
     47                             target: "setup",
     48                             "Expected NAME '{name}' from config got '{}' from server",
     49                             profile.full_name
     50                         )
     51                     } else {
     52                         debug!(target: "setup", "NAME {name} from config match server")
     53                     }
     54                 }
     55                 None => {
     56                     error!(target: "setup", "Missing NAME got '{}' from server", profile.full_name);
     57                     ready = false;
     58                 }
     59             }
     60         } else {
     61             error!(target: "setup", "Unknown PROFILE_ID {id} in config, must be one of:{profiles_fmt}");
     62             ready = false;
     63         }
     64     } else {
     65         error!(target: "setup", "Missing PROFILE_ID in config, must be one of:{profiles_fmt}");
     66         ready = false;
     67     }
     68 
     69     if let Some(profile_id) = cfg.profile_id {
     70         // Check accounts
     71         let accounts = client.borderless_accounts(profile_id).await?;
     72         let account = accounts
     73             .iter()
     74             .find(|it| it.profile_id == profile_id)
     75             .unwrap();
     76 
     77         let mapped: Vec<_> = account
     78             .balances
     79             .iter()
     80             .map(|it| (it.id, it.currency, it.bank_details.account()))
     81             .collect();
     82 
     83         let balances_fmt = std::fmt::from_fn(|w| {
     84             for (id, currency, account) in &mapped {
     85                 if let Some(account) = account {
     86                     write!(w, "\n{id} {currency} ({account})")?;
     87                 }
     88             }
     89             Ok(())
     90         });
     91         info!(target: "setup", "Supported balances are: {balances_fmt}");
     92 
     93         if cfg.balances.is_empty() {
     94             error!(target: "setup", "No balances are configured");
     95             ready = false
     96         } else {
     97             for WiseBalance {
     98                 id,
     99                 currency,
    100                 payto,
    101             } in &cfg.balances
    102             {
    103                 match mapped.iter().find(|it| it.0 == *id as u64) {
    104                     Some((_, curr, acc)) => {
    105                         if curr != currency {
    106                             error!(target: "setup", "Balance {id} in config use currency {currency} expected {curr}");
    107                             ready = false;
    108                         }
    109                         if let Some(acc) = acc {
    110                             if acc != payto {
    111                                 error!(target: "setup", "Balance {id} in config use payto ({payto}) expected ({acc})");
    112                                 ready = false
    113                             }
    114                         } else {
    115                             error!(target: "setup", "Balance {id} in config use payto ({payto}) but it's not supported");
    116                             ready = false
    117                         }
    118                     }
    119                     None => {
    120                         error!(target: "setup", "Unknown balance {id} {currency}");
    121                         ready = false
    122                     }
    123                 }
    124             }
    125         }
    126     }
    127 
    128     if ready {
    129         info!(target: "setup", "Setup finished");
    130         Ok(())
    131     } else {
    132         error!(target: "setup", "Setup failed, config need editing");
    133         std::process::exit(0);
    134     }
    135 }