ipc_chan/
config.rs

1//! Toml config module
2
3use crate::error::{Error, Result};
4use serde_derive::{Deserialize, Serialize};
5use std::env::current_dir;
6use std::fs::File;
7use std::io::{Read, Write};
8use std::path::{Path, PathBuf};
9
10#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[derive(Deserialize, Serialize)]
12pub struct Config {
13    pub host: String,
14    pub port: usize,
15}
16
17impl Default for Config {
18    fn default() -> Self {
19        Self {
20            host: "127.0.0.1".to_string(),
21            port: 10001,
22        }
23    }
24}
25
26impl Config {
27    /// Parse a TOML config file.
28    /// If the file can't be found, default settings are assumed and returned.
29    pub fn parse_toml<P: AsRef<Path>>(toml_path: P) -> Result<Self> {
30        let toml_path = toml_path.as_ref();
31        let toml_path = Self::find_in_ancestor_or_home_dir(&toml_path)
32            .ok_or_else(|| Error::IoError(std::io::Error::new(
33                std::io::ErrorKind::NotFound,
34                format!("TOML file: {}", toml_path.display())
35            )))?;
36
37        if let Ok(mut file) = File::open(toml_path) {
38            let mut contents = String::new();
39            file.read_to_string(&mut contents)?;
40            return Ok(toml::from_str(&contents)?);
41        } else {
42            Ok(Self::default())
43        }
44    }
45
46    /// Write `self` to a TOML config file @ `toml_path`.
47    /// If `overwrite_policy` is `OverwritePolicy::Overwrite`, the file will
48    /// be written regardless of whether or not it previously existed.
49    /// However, if `overwrite_policy` is `OverwritePolicy::DontOverwrite`, the
50    /// file will only be written iff. it did not previously exist.
51    pub fn write_toml<P: AsRef<Path>>(
52        &self,
53        toml_path: P,
54        overwrite_policy: OverwritePolicy
55    ) -> Result<()> {
56        let toml_path: &Path = toml_path.as_ref();
57        let mut file = File::create(toml_path)?;
58        match overwrite_policy {
59            OverwritePolicy::DontOverwrite if toml_path.exists() => {
60                // NOP  // NOTE don't remove this branch
61            },
62            OverwritePolicy::DontOverwrite | OverwritePolicy::Overwrite => {
63                let contents: String = toml::to_string_pretty(&self)?;
64                file.write_all(contents.as_bytes())?;
65            },
66        }
67        Ok(())
68    }
69
70    /// Search for a file in ancestor directories, then in $HOME.
71    /// First in the parent dir, then in the parent's parent dir, etc.
72    /// Return `None` if the file could not be found anywhere.
73    #[inline(always)]
74    fn find_in_ancestor_or_home_dir<P: AsRef<Path>>(path: P) -> Option<PathBuf> {
75        let path: PathBuf = path.as_ref().to_path_buf();
76        if path.exists() { // File found in current directory
77            return Some(path);
78        }
79        let exists = |p: &Option<PathBuf>| p.as_ref().map(|b| b.exists());
80        let file_name: String = path.file_name()?.to_str()?.to_string();
81        let mut buf: Option<PathBuf> = Some(path.clone());
82        if Some(Path::new("")) == path.parent() { // No parent directory
83            buf = current_dir().map(|cwd| cwd.join(&file_name)).ok();
84        }
85        while let Some(b) = buf.as_ref() { // NOTE Search ancestor directories
86            let parent: PathBuf = b.parent().map(|p| p.to_path_buf())?;
87            buf = match parent.parent() {
88                Some(gp) if gp.exists() => Some(gp.join(&file_name)),
89                _ => None,
90            };
91            if exists(&buf) == Some(true) { return buf; }
92        }
93        if buf.is_none() || Some(false) == exists(&buf) { // NOTE search $HOME
94            if let Some(home_dir_path) = dirs::home_dir() {
95                buf = Some(home_dir_path.join(&file_name));
96            }
97        }
98        match buf {
99            Some(b) if b.exists() => Some(b),
100            _ => None,
101        }
102    }
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
106#[derive(Deserialize, Serialize)]
107pub enum OverwritePolicy {
108    DontOverwrite,
109    Overwrite,
110}