config.rs (6266B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025-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::time::Duration; 18 19 use compact_str::{CompactString, format_compact}; 20 use taler_api::{ 21 Serve, 22 config::{ApiCfg, DbCfg}, 23 }; 24 use taler_common::{ 25 config::{CfgErr, Config}, 26 map_config, 27 types::amount::Currency, 28 }; 29 use url::Url; 30 31 use crate::payto::{CyclosAccount, CyclosId, FullCyclosPayto}; 32 33 #[derive(Debug, Clone, Copy)] 34 pub enum AccountType { 35 Exchange, 36 Normal, 37 } 38 39 pub fn parse_db_cfg(cfg: &Config) -> Result<DbCfg, CfgErr> { 40 DbCfg::parse(cfg.section("cyclosdb-postgres")) 41 } 42 43 pub fn parse_account_payto(cfg: &Config, main: &MainCfg) -> Result<FullCyclosPayto, CfgErr> { 44 let sect = cfg.section("cyclos"); 45 let id = sect.parse("cyclos account id", "ACCOUNT_ID").require()?; 46 let name = sect.str("NAME").require()?; 47 48 Ok(FullCyclosPayto::new( 49 CyclosAccount { 50 id, 51 root: main.root.clone(), 52 }, 53 &name, 54 )) 55 } 56 57 /// Cyclos main config 58 pub struct MainCfg { 59 pub currency: Currency, 60 pub url: Url, 61 pub root: CompactString, 62 } 63 64 impl MainCfg { 65 pub fn parse(cfg: &Config) -> Result<Self, CfgErr> { 66 let sect = cfg.section("cyclos"); 67 let url = sect.base_url("CYCLOS_URL").require()?; 68 let root = format_compact!( 69 "{}{}", 70 url.host_str().unwrap_or_default(), 71 url.path().trim_end_matches('/') 72 ); 73 Ok(Self { 74 currency: sect.parse("currency", "CURRENCY").require()?, 75 url, 76 root, 77 }) 78 } 79 } 80 81 /// Cyclos API config 82 pub struct HostCfg { 83 pub api_url: Url, 84 pub username: String, 85 pub password: String, 86 } 87 88 impl HostCfg { 89 pub fn parse(cfg: &Config, main: &MainCfg) -> Result<Self, CfgErr> { 90 let sect = cfg.section("cyclos-worker"); 91 Ok(Self { 92 username: sect.str("USERNAME").require()?, 93 api_url: main.url.join("api/").unwrap(), 94 password: sect.str("PASSWORD").require()?, 95 }) 96 } 97 } 98 99 /// taler-cyclos httpd config 100 pub struct ServeCfg { 101 pub payto: FullCyclosPayto, 102 pub currency: Currency, 103 pub root: CompactString, 104 pub serve: Serve, 105 pub wire_gateway: Option<ApiCfg>, 106 pub revenue: Option<ApiCfg>, 107 pub observability: Option<ApiCfg>, 108 } 109 110 impl ServeCfg { 111 pub fn parse(cfg: &Config) -> Result<Self, CfgErr> { 112 let main = MainCfg::parse(cfg)?; 113 let payto = parse_account_payto(cfg, &main)?; 114 115 let s = cfg.section("cyclos-httpd"); 116 117 let serve = Serve::parse(&s)?; 118 119 let wire_gateway = ApiCfg::parse(cfg.section("cyclos-httpd-wire-gateway-api"))?; 120 let revenue = ApiCfg::parse(cfg.section("cyclos-httpd-revenue-api"))?; 121 let observability = ApiCfg::parse(cfg.section("cyclos-httpd-observability-api"))?; 122 123 Ok(Self { 124 payto, 125 currency: main.currency, 126 root: main.root, 127 serve, 128 wire_gateway, 129 revenue, 130 observability, 131 }) 132 } 133 } 134 135 /// taler-cyclos setup config 136 pub struct SetupCfg { 137 pub currency: Currency, 138 pub root: Url, 139 pub host: HostCfg, 140 pub id: Option<CyclosId>, 141 pub name: Option<String>, 142 pub account_type_id: Option<CyclosId>, 143 pub payment_type_id: Option<CyclosId>, 144 } 145 146 impl SetupCfg { 147 pub fn parse(cfg: &Config) -> Result<Self, CfgErr> { 148 let main = MainCfg::parse(cfg)?; 149 let main_s = cfg.section("cyclos"); 150 let worker_s = cfg.section("cyclos-worker"); 151 Ok(Self { 152 id: main_s.parse("cyclos account id", "ACCOUNT_ID").opt()?, 153 account_type_id: worker_s 154 .parse("cyclos account type id", "ACCOUNT_TYPE_ID") 155 .opt()?, 156 payment_type_id: worker_s 157 .parse("cyclos payment type id", "PAYMENT_TYPE_ID") 158 .opt()?, 159 name: main_s.str("NAME").opt()?, 160 host: HostCfg::parse(cfg, &main)?, 161 currency: main.currency, 162 root: main.url, 163 }) 164 } 165 } 166 167 /// taler-cyclos worker config 168 pub struct WorkerCfg { 169 pub currency: Currency, 170 pub root: CompactString, 171 pub frequency: Duration, 172 pub host: HostCfg, 173 pub account_type: AccountType, 174 pub account_type_id: CyclosId, 175 pub payment_type_id: CyclosId, 176 } 177 178 impl WorkerCfg { 179 pub fn parse(cfg: &Config) -> Result<Self, CfgErr> { 180 let main = MainCfg::parse(cfg)?; 181 let s = cfg.section("cyclos-worker"); 182 Ok(Self { 183 frequency: s.duration("FREQUENCY").require()?, 184 account_type: map_config!(s, "account type", "ACCOUNT_TYPE", 185 "exchange" => { AccountType::Exchange }, 186 "normal" => { AccountType::Normal } 187 ) 188 .require()?, 189 account_type_id: s 190 .parse("cyclos account type id", "ACCOUNT_TYPE_ID") 191 .require()?, 192 payment_type_id: s 193 .parse("cyclos payment type id", "PAYMENT_TYPE_ID") 194 .require()?, 195 host: HostCfg::parse(cfg, &main)?, 196 currency: main.currency, 197 root: main.root, 198 }) 199 } 200 } 201 202 /// cyclos-harness config 203 pub struct HarnessCfg { 204 pub worker: WorkerCfg, 205 pub username: String, 206 pub password: String, 207 } 208 209 impl HarnessCfg { 210 pub fn parse(cfg: &Config) -> Result<Self, CfgErr> { 211 let worker = WorkerCfg::parse(cfg)?; 212 213 let s = cfg.section("cyclos-harness"); 214 215 Ok(Self { 216 worker, 217 username: s.str("USERNAME").require()?, 218 password: s.str("PASSWORD").require()?, 219 }) 220 } 221 }