cli_config/
fs.rs

1/*!
2  This module contains file utilities
3  The following traits should be used to ease
4  read/write operation in the fs.
5*/
6
7/// Generic trait
8pub trait File {
9    fn load(path: &Path) -> crate::Result<Self>
10    where
11        Self: Sized;
12
13    fn write(&self, path: &Path) -> crate::Result<()>;
14}
15
16#[cfg(any(feature = "json", feature = "toml", feature = "yaml"))]
17use serde::de::DeserializeOwned;
18
19#[cfg(any(feature = "json", feature = "toml", feature = "yaml"))]
20use std::fs;
21use std::path::Path;
22
23#[cfg(feature = "toml")]
24use std::io::Write;
25
26#[cfg(any(feature = "json", feature = "toml", feature = "yaml"))]
27use crate::error::Error;
28
29#[cfg(feature = "json")]
30pub trait JSONFile
31where
32    Self: DeserializeOwned + serde::Serialize,
33{
34    /// Load file content into `Self`
35    fn load(path: &Path) -> crate::Result<Self> {
36        let file = fs::File::open(path)?;
37
38        serde_json::from_reader(file).map_err(Error::JSON)
39    }
40
41    /// Write `Self` into specified file
42    fn write(&self, path: &Path) -> crate::Result<()> {
43        let file = fs::File::create(path)?;
44
45        serde_json::to_writer_pretty(file, self).map_err(Error::JSON)
46    }
47}
48
49#[cfg(feature = "yaml")]
50pub trait YAMLFile
51where
52    Self: DeserializeOwned + serde::Serialize,
53{
54    /// Load file content into `Self`
55    fn load(path: &Path) -> crate::Result<Self> {
56        let file = fs::File::open(path)?;
57
58        serde_yaml::from_reader(file).map_err(Error::YAML)
59    }
60
61    /// Write `Self` into specified file
62    fn write(&self, path: &Path) -> crate::Result<()> {
63        let file = fs::File::create(path)?;
64
65        serde_yaml::to_writer(file, self).map_err(Error::YAML)
66    }
67}
68
69#[cfg(feature = "toml")]
70pub trait TOMLFile
71where
72    Self: DeserializeOwned + serde::Serialize,
73{
74    /// Load file content into `Self`
75    fn load(path: &Path) -> crate::Result<Self> {
76        let file = fs::read_to_string(path)?;
77
78        toml::from_str(&file).map_err(Error::TOML)
79    }
80
81    /// Write `Self` into specified file
82    fn write(&self, path: &Path) -> crate::Result<()> {
83        let mut file = fs::File::create(path)?;
84        let str = toml::ser::to_string(&self)?;
85
86        file.write_all(&str.as_bytes())?;
87
88        Ok(())
89    }
90}
91
92mod test_utils {
93    use serde::{Deserialize, Serialize};
94
95    #[derive(Debug, PartialEq, Serialize, Deserialize)]
96    pub struct TestConfig {
97        pub foo: String,
98        pub bar: bool,
99        pub baz: u32,
100    }
101
102    impl Default for TestConfig {
103        fn default() -> Self {
104            Self {
105                foo: "foo".to_string(),
106                bar: true,
107                baz: 42,
108            }
109        }
110    }
111}
112
113#[cfg(feature = "toml")]
114#[cfg(test)]
115mod toml_tests {
116    use super::*;
117    use tempdir::TempDir;
118    use test_utils::TestConfig;
119
120    impl TOMLFile for TestConfig {}
121
122    #[test]
123    fn test_file_trait() {
124        let dir = TempDir::new("test_config").unwrap();
125        let config_file = dir.path().join("test-config.json");
126        let config = TestConfig::default();
127
128        // test write and load
129        config.write(&config_file).unwrap();
130        let loaded_config = TestConfig::load(&config_file).unwrap();
131        assert_eq!(config, loaded_config);
132    }
133}
134
135#[cfg(feature = "yaml")]
136#[cfg(test)]
137mod yaml_tests {
138    use super::*;
139    use tempdir::TempDir;
140    use test_utils::TestConfig;
141
142    impl YAMLFile for TestConfig {}
143
144    #[test]
145    fn test_file_trait() {
146        let dir = TempDir::new("test_config").unwrap();
147        let config_file = dir.path().join("test-config.json");
148        let config = TestConfig::default();
149
150        // test write and load
151        config.write(&config_file).unwrap();
152        let loaded_config = TestConfig::load(&config_file).unwrap();
153        assert_eq!(config, loaded_config);
154    }
155}
156
157#[cfg(feature = "json")]
158#[cfg(test)]
159mod json_tests {
160    use super::*;
161    use tempdir::TempDir;
162    use test_utils::TestConfig;
163
164    impl JSONFile for TestConfig {}
165
166    #[test]
167    fn test_file_trait() {
168        let dir = TempDir::new("test_config").unwrap();
169        let config_file = dir.path().join("test-config.json");
170        let config = TestConfig::default();
171
172        // test write and load
173        config.write(&config_file).unwrap();
174        let loaded_config = TestConfig::load(&config_file).unwrap();
175        assert_eq!(config, loaded_config);
176    }
177}