koibumi-common-sync 0.0.0

Common code among GUI and daemon versions for Koibumi (sync version), an experimental Bitmessage client
Documentation
//! Functions to load and save configs.

use std::{
    fmt,
    fs::{self, File},
    io::{self, BufRead, BufReader, BufWriter, Write},
    path::{Path, PathBuf},
};

use koibumi_node_sync::Config;

use crate::{
    constant::{BOOTSTRAPS, LOCAL_SERVER, ONION_SEED, TOR_SOCKS},
    param::Params,
};

/// An error which can be returned when operating on config files.
#[derive(Debug)]
pub enum Error {
    /// A standard I/O error was caught during operating on a config file.
    /// The actual error caught is returned as a payload of this variant.
    IoError(io::Error),
    /// An error was caught during deserializing a config file.
    /// The actual error caught is returned as a payload of this variant.
    DeError(toml::de::Error),
    /// An error was caught during serializing a config object.
    /// The actual error caught is returned as a payload of this variant.
    SerError(toml::ser::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::IoError(err) => err.fmt(f),
            Self::DeError(err) => err.fmt(f),
            Self::SerError(err) => err.fmt(f),
        }
    }
}

impl std::error::Error for Error {}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Self::IoError(err)
    }
}

impl From<toml::de::Error> for Error {
    fn from(err: toml::de::Error) -> Self {
        Self::DeError(err)
    }
}

impl From<toml::ser::Error> for Error {
    fn from(err: toml::ser::Error) -> Self {
        Self::SerError(err)
    }
}

fn load_config_string(path: &Path) -> Result<String, Error> {
    let f = File::open(path)?;
    let reader = BufReader::new(f);
    let lines = reader.lines().collect::<io::Result<Vec<String>>>()?;
    Ok(lines.join("\n"))
}

fn default_config() -> Config {
    Config::builder()
        .server(Some(LOCAL_SERVER.parse().unwrap()))
        .socks(Some(TOR_SOCKS.parse().unwrap()))
        .connect_to_onion(true)
        .connect_to_ip(true)
        .seeds(vec![ONION_SEED.parse().unwrap()])
        .bootstraps(vec![
            BOOTSTRAPS[0].parse().unwrap(),
            BOOTSTRAPS[1].parse().unwrap(),
        ])
        .build()
}

/// Loads a config object from the config file.
pub fn load(params: &Params) -> Result<Config, Error> {
    let mut path = params.data_dir().to_path_buf();
    path.push("node.toml");
    if !path.exists() {
        return Ok(default_config());
    }
    let string = load_config_string(&path)?;
    Ok(toml::from_str(&string)?)
}

pub(crate) fn create_data_dir(params: &Params) -> Result<PathBuf, io::Error> {
    let path = params.data_dir();
    if !path.exists() {
        fs::create_dir_all(&path)?;
    }
    Ok(path.to_path_buf())
}

/// Saves a config object to the config file.
pub fn save(params: &Params, config: &Config) -> Result<(), Error> {
    let string = toml::to_string(&config)?;

    let path = params.data_dir();
    if !path.exists() {
        fs::create_dir_all(&path)?;
    }

    let mut new_path = path.to_path_buf();
    new_path.push("node-new.toml");
    let f = File::create(new_path.clone())?;
    let mut writer = BufWriter::new(f);
    writer.write_all(string.as_bytes())?;
    writer.flush()?;

    let mut to_path = path.to_path_buf();
    to_path.push("node.toml");
    fs::rename(new_path, to_path)?;
    Ok(())
}