koibumi_common/
config.rs

1//! Functions to load and save configs.
2
3use std::{
4    fmt,
5    fs::{self, File},
6    io::{self, BufRead, BufReader, BufWriter, Write},
7    path::{Path, PathBuf},
8};
9
10use koibumi_node::Config;
11
12use crate::{
13    constant::{BOOTSTRAPS, LOCAL_SERVER, ONION_SEED, TOR_SOCKS},
14    param::Params,
15};
16
17/// An error which can be returned when operating on config files.
18#[derive(Debug)]
19pub enum Error {
20    /// A standard I/O error was caught during operating on a config file.
21    /// The actual error caught is returned as a payload of this variant.
22    IoError(io::Error),
23    /// An error was caught during deserializing a config file.
24    /// The actual error caught is returned as a payload of this variant.
25    DeError(toml::de::Error),
26    /// An error was caught during serializing a config object.
27    /// The actual error caught is returned as a payload of this variant.
28    SerError(toml::ser::Error),
29}
30
31impl fmt::Display for Error {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            Self::IoError(err) => err.fmt(f),
35            Self::DeError(err) => err.fmt(f),
36            Self::SerError(err) => err.fmt(f),
37        }
38    }
39}
40
41impl std::error::Error for Error {}
42
43impl From<io::Error> for Error {
44    fn from(err: io::Error) -> Self {
45        Self::IoError(err)
46    }
47}
48
49impl From<toml::de::Error> for Error {
50    fn from(err: toml::de::Error) -> Self {
51        Self::DeError(err)
52    }
53}
54
55impl From<toml::ser::Error> for Error {
56    fn from(err: toml::ser::Error) -> Self {
57        Self::SerError(err)
58    }
59}
60
61fn load_config_string(path: &Path) -> Result<String, Error> {
62    let f = File::open(path)?;
63    let reader = BufReader::new(f);
64    let lines = reader.lines().collect::<io::Result<Vec<String>>>()?;
65    Ok(lines.join("\n"))
66}
67
68fn default_config() -> Config {
69    Config::builder()
70        .server(Some(LOCAL_SERVER.parse().unwrap()))
71        .socks(Some(TOR_SOCKS.parse().unwrap()))
72        .connect_to_onion(true)
73        .connect_to_ip(true)
74        .seeds(vec![ONION_SEED.parse().unwrap()])
75        .bootstraps(vec![
76            BOOTSTRAPS[0].parse().unwrap(),
77            BOOTSTRAPS[1].parse().unwrap(),
78        ])
79        .build()
80}
81
82/// Loads a config object from the config file.
83pub fn load(params: &Params) -> Result<Config, Error> {
84    let mut path = params.data_dir().to_path_buf();
85    path.push("node.toml");
86    if !path.exists() {
87        return Ok(default_config());
88    }
89    let string = load_config_string(&path)?;
90    Ok(toml::from_str(&string)?)
91}
92
93pub(crate) fn create_data_dir(params: &Params) -> Result<PathBuf, io::Error> {
94    let path = params.data_dir();
95    if !path.exists() {
96        fs::create_dir_all(&path)?;
97    }
98    Ok(path.to_path_buf())
99}
100
101/// Saves a config object to the config file.
102pub fn save(params: &Params, config: &Config) -> Result<(), Error> {
103    let string = toml::to_string(&config)?;
104
105    let path = params.data_dir();
106    if !path.exists() {
107        fs::create_dir_all(&path)?;
108    }
109
110    let mut new_path = path.to_path_buf();
111    new_path.push("node-new.toml");
112    let f = File::create(new_path.clone())?;
113    let mut writer = BufWriter::new(f);
114    writer.write_all(string.as_bytes())?;
115    writer.flush()?;
116
117    let mut to_path = path.to_path_buf();
118    to_path.push("node.toml");
119    fs::rename(new_path, to_path)?;
120    Ok(())
121}