1pub 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 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 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 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 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 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 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 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 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 config.write(&config_file).unwrap();
174 let loaded_config = TestConfig::load(&config_file).unwrap();
175 assert_eq!(config, loaded_config);
176 }
177}