config.rs (6195B)
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::{Config, ValueErr}, 26 map_config, 27 types::{amount::Currency, payto::PaytoURI}, 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, ValueErr> { 40 DbCfg::parse(cfg.section("cyclosdb-postgres")) 41 } 42 43 pub fn parse_account_payto(cfg: &Config, main: &MainCfg) -> Result<FullCyclosPayto, ValueErr> { 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, ValueErr> { 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, ValueErr> { 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: PaytoURI, 102 pub currency: Currency, 103 pub root: CompactString, 104 pub serve: Serve, 105 pub wire_gateway: Option<ApiCfg>, 106 pub revenue: Option<ApiCfg>, 107 } 108 109 impl ServeCfg { 110 pub fn parse(cfg: &Config) -> Result<Self, ValueErr> { 111 let main = MainCfg::parse(cfg)?; 112 let payto = parse_account_payto(cfg, &main)?; 113 114 let sect = cfg.section("cyclos-httpd"); 115 116 let serve = Serve::parse(sect)?; 117 118 let wire_gateway = ApiCfg::parse(cfg.section("cyclos-httpd-wire-gateway-api"))?; 119 let revenue = ApiCfg::parse(cfg.section("cyclos-httpd-revenue-api"))?; 120 121 Ok(Self { 122 payto: payto.as_payto(), 123 currency: main.currency, 124 root: main.root, 125 serve, 126 wire_gateway, 127 revenue, 128 }) 129 } 130 } 131 132 /// taler-cyclos setup config 133 pub struct SetupCfg { 134 pub currency: Currency, 135 pub root: Url, 136 pub host: HostCfg, 137 pub id: Option<CyclosId>, 138 pub name: Option<String>, 139 pub account_type_id: Option<CyclosId>, 140 pub payment_type_id: Option<CyclosId>, 141 } 142 143 impl SetupCfg { 144 pub fn parse(cfg: &Config) -> Result<Self, ValueErr> { 145 let main = MainCfg::parse(cfg)?; 146 let main_s = cfg.section("cyclos"); 147 let worker_s = cfg.section("cyclos-worker"); 148 Ok(Self { 149 id: main_s.parse("cyclos account id", "ACCOUNT_ID").opt()?, 150 account_type_id: worker_s 151 .parse("cyclos account type id", "ACCOUNT_TYPE_ID") 152 .opt()?, 153 payment_type_id: worker_s 154 .parse("cyclos payment type id", "PAYMENT_TYPE_ID") 155 .opt()?, 156 name: main_s.str("NAME").opt()?, 157 host: HostCfg::parse(cfg, &main)?, 158 currency: main.currency, 159 root: main.url, 160 }) 161 } 162 } 163 164 /// taler-cyclos worker config 165 pub struct WorkerCfg { 166 pub currency: Currency, 167 pub root: CompactString, 168 pub frequency: Duration, 169 pub host: HostCfg, 170 pub account_type: AccountType, 171 pub account_type_id: CyclosId, 172 pub payment_type_id: CyclosId, 173 } 174 175 impl WorkerCfg { 176 pub fn parse(cfg: &Config) -> Result<Self, ValueErr> { 177 let main = MainCfg::parse(cfg)?; 178 let sect = cfg.section("cyclos-worker"); 179 Ok(Self { 180 frequency: sect.duration("FREQUENCY").require()?, 181 account_type: map_config!(sect, "account type", "ACCOUNT_TYPE", 182 "exchange" => { Ok(AccountType::Exchange) }, 183 "normal" => { Ok(AccountType::Normal) } 184 ) 185 .require()?, 186 account_type_id: sect 187 .parse("cyclos account type id", "ACCOUNT_TYPE_ID") 188 .require()?, 189 payment_type_id: sect 190 .parse("cyclos payment type id", "PAYMENT_TYPE_ID") 191 .require()?, 192 host: HostCfg::parse(cfg, &main)?, 193 currency: main.currency, 194 root: main.root, 195 }) 196 } 197 } 198 199 /// cyclos-harness config 200 pub struct HarnessCfg { 201 pub worker: WorkerCfg, 202 pub username: String, 203 pub password: String, 204 } 205 206 impl HarnessCfg { 207 pub fn parse(cfg: &Config) -> Result<Self, ValueErr> { 208 let worker = WorkerCfg::parse(cfg)?; 209 210 let sect = cfg.section("cyclos-harness"); 211 212 Ok(Self { 213 worker, 214 username: sect.str("USERNAME").require()?, 215 password: sect.str("PASSWORD").require()?, 216 }) 217 } 218 }