ckb_dev/
config.rs

1use std::{net::IpAddr, path::PathBuf, str::FromStr as _};
2
3use ini::Ini;
4use url::Url;
5
6use crate::{
7    ckb_config::AppConfig as CkbConfig,
8    error::{Error, Result},
9};
10
11const NORMAL_CONFIG_FILE: &str = "/etc/ckbdev.conf";
12const SECRET_CONFIG_FILE: &str = "/etc/ckbdev.secret.conf";
13
14pub struct Config {
15    pub(crate) normal: NormalConfig,
16    pub(crate) secret: SecretConfig,
17}
18
19pub(crate) struct NormalConfig {
20    pub(crate) host: HostSection,
21    pub(crate) ckb: CkbSection,
22}
23
24pub(crate) struct SecretConfig {
25    pub(crate) qiniu: QiniuSection,
26}
27
28pub(crate) struct HostSection {
29    #[allow(dead_code)]
30    pub(crate) ip: IpAddr,
31    pub(crate) name: String,
32}
33
34pub(crate) struct CkbSection {
35    pub(crate) service_name: String,
36    pub(crate) bin_path: PathBuf,
37    pub(crate) root_dir: PathBuf,
38    pub(crate) data_dir: PathBuf,
39    pub(crate) rpc_url: Url,
40}
41
42pub(crate) struct QiniuSection {
43    pub(crate) access_key: String,
44    pub(crate) secret_key: String,
45    pub(crate) bucket: String,
46    pub(crate) domain: Url,
47    pub(crate) path_prefix: String,
48}
49
50impl Config {
51    pub fn load_from_files() -> Result<Self> {
52        let normal = NormalConfig::load_from_file(NORMAL_CONFIG_FILE)?;
53        let secret = SecretConfig::load_from_file(SECRET_CONFIG_FILE)?;
54        Ok(Self { normal, secret })
55    }
56}
57
58impl NormalConfig {
59    pub(crate) fn load_from_file(path: &str) -> Result<Self> {
60        let ini = Ini::load_from_file(path)
61            .map_err(|err| Error::Cfg(format!("failed to load \"{}\" since {}", path, err)))?;
62        let host = {
63            let prop = ini
64                .section(Some("host"))
65                .ok_or_else(|| Error::config_not_found("host"))?;
66            let ip = prop
67                .get("ip")
68                .ok_or_else(|| Error::config_not_found("host.ip"))
69                .and_then(|s| {
70                    IpAddr::from_str(s).map_err(|err| {
71                        Error::Cfg(format!("failed to parse [host.ip] since {}", err))
72                    })
73                })?;
74            let name = prop
75                .get("name")
76                .ok_or_else(|| Error::config_not_found("host.name"))?
77                .to_owned();
78            HostSection { ip, name }
79        };
80        let ckb = {
81            let prop = ini
82                .section(Some("ckb"))
83                .ok_or_else(|| Error::config_not_found("ckb"))?;
84            let service_name = prop
85                .get("service_name")
86                .ok_or_else(|| Error::config_not_found("ckb.service_name"))?
87                .to_owned();
88            let bin_path = prop
89                .get("bin_path")
90                .ok_or_else(|| Error::config_not_found("ckb.bin_path"))
91                .map(PathBuf::from)?;
92            let root_dir = prop
93                .get("root_dir")
94                .ok_or_else(|| Error::config_not_found("ckb.root_dir"))
95                .map(PathBuf::from)?;
96            let ckb_cfg = CkbConfig::load_from_workdir(&root_dir)?;
97            let data_dir = root_dir.join(&ckb_cfg.data_dir);
98            let rpc_url = {
99                let url = format!("http://{}", ckb_cfg.rpc.listen_address);
100                Url::parse(&url).map_err(|err| {
101                    Error::Cfg(format!(
102                        "failed to parse CKB RPC URL [{}] since {}",
103                        url, err
104                    ))
105                })
106            }?;
107            CkbSection {
108                service_name,
109                bin_path,
110                root_dir,
111                data_dir,
112                rpc_url,
113            }
114        };
115        Ok(Self { host, ckb })
116    }
117}
118
119impl SecretConfig {
120    pub(crate) fn load_from_file(path: &str) -> Result<Self> {
121        let ini = Ini::load_from_file(path)
122            .map_err(|err| Error::Cfg(format!("failed to load \"{}\" since {}", path, err)))?;
123        let qiniu = {
124            let prop = ini
125                .section(Some("qiniu"))
126                .ok_or_else(|| Error::config_not_found("qiniu"))?;
127            let access_key = prop
128                .get("access_key")
129                .ok_or_else(|| Error::config_not_found("qiniu.access_key"))?
130                .to_owned();
131            let secret_key = prop
132                .get("secret_key")
133                .ok_or_else(|| Error::config_not_found("qiniu.secret_key"))?
134                .to_owned();
135            let bucket = prop
136                .get("bucket")
137                .ok_or_else(|| Error::config_not_found("qiniu.bucket"))?
138                .to_owned();
139            let domain = prop
140                .get("domain")
141                .ok_or_else(|| Error::config_not_found("qiniu.domain"))
142                .and_then(|s| {
143                    let u = Url::parse(s).map_err(|err| {
144                        Error::Cfg(format!("failed to parse [qiniu.domain] since {}", err))
145                    })?;
146                    if u.scheme() != "http" && u.scheme() != "https" {
147                        let msg = "invalid [qiniu.domain], scheme should be \"http\" or \"https\"";
148                        let err = Error::Cfg(msg.to_owned());
149                        Err(err)
150                    } else {
151                        Ok(u)
152                    }
153                })?;
154            let path_prefix = prop
155                .get("path_prefix")
156                .ok_or_else(|| Error::config_not_found("qiniu.path_prefix"))?
157                .to_owned();
158            QiniuSection {
159                access_key,
160                secret_key,
161                bucket,
162                domain,
163                path_prefix,
164            }
165        };
166        Ok(Self { qiniu })
167    }
168}