1use 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 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 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 },
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 #[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() { 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() { buf = current_dir().map(|cwd| cwd.join(&file_name)).ok();
84 }
85 while let Some(b) = buf.as_ref() { 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) { 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}