altrios_core/traits/
serde_api.rs1use super::*;
2use include_dir::{include_dir, Dir};
3
4pub trait Init {
5 fn init(&mut self) -> Result<(), Error> {
8 Ok(())
9 }
10}
11
12pub trait SerdeAPI: Serialize + for<'a> Deserialize<'a> + Init {
13 const ACCEPTED_BYTE_FORMATS: &'static [&'static str] = &[
14 #[cfg(feature = "yaml")]
15 "yaml",
16 #[cfg(feature = "json")]
17 "json",
18 #[cfg(feature = "msgpack")]
19 "msgpack",
20 #[cfg(feature = "toml")]
21 "toml",
22 ];
23 const ACCEPTED_STR_FORMATS: &'static [&'static str] = &[
24 #[cfg(feature = "yaml")]
25 "yaml",
26 #[cfg(feature = "json")]
27 "json",
28 #[cfg(feature = "toml")]
29 "toml",
30 ];
31 #[cfg(feature = "resources")]
32 const RESOURCES_SUBDIR: &'static str = "";
33 #[cfg(feature = "resources")]
34 const RESOURCES_DIR: &'static Dir<'_> = &include_dir!("$CARGO_MANIFEST_DIR/resources");
35
36 #[cfg(feature = "resources")]
42 fn from_resource<P: AsRef<Path>>(filepath: P, skip_init: bool) -> Result<Self, Error> {
43 let filepath = Path::new(Self::RESOURCES_SUBDIR).join(filepath);
44 let extension = filepath
45 .extension()
46 .and_then(OsStr::to_str)
47 .ok_or_else(|| {
48 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
49 })?;
50 let file = Self::RESOURCES_DIR.get_file(&filepath).ok_or_else(|| {
51 Error::SerdeError(format!("File not found in resources: {filepath:?}"))
52 })?;
53 Self::from_reader(&mut file.contents(), extension, skip_init)
54 }
55
56 fn list_resources() -> Result<Vec<PathBuf>, Error> {
60 fn collect_paths(dir: &Dir, paths: &mut Vec<PathBuf>) {
62 for entry in dir.entries() {
63 match entry {
64 include_dir::DirEntry::Dir(subdir) => {
65 collect_paths(subdir, paths);
67 }
68 include_dir::DirEntry::File(file) => {
69 paths.push(file.path().to_path_buf());
71 }
72 }
73 }
74 }
75
76 let mut paths = Vec::new();
77 if let Some(resources_subdir) = Self::RESOURCES_DIR.get_dir(Self::RESOURCES_SUBDIR) {
78 collect_paths(resources_subdir, &mut paths);
79 for p in paths.iter_mut() {
80 *p = p
81 .strip_prefix(Self::RESOURCES_SUBDIR)
82 .map_err(|err| Error::SerdeError(format!("{err}")))?
83 .to_path_buf();
84 }
85 paths.sort();
86 }
87 Ok(paths)
88 }
89
90 fn to_file<P: AsRef<Path>>(&self, filepath: P) -> anyhow::Result<()> {
99 let filepath = filepath.as_ref();
100 let extension = filepath
101 .extension()
102 .and_then(OsStr::to_str)
103 .with_context(|| format!("File extension could not be parsed: {filepath:?}"))?;
104 self.to_writer(File::create(filepath)?, extension)
105 }
106
107 fn from_file<P: AsRef<Path>>(filepath: P, skip_init: bool) -> Result<Self, Error> {
115 let filepath = filepath.as_ref();
116 let extension = filepath
117 .extension()
118 .and_then(OsStr::to_str)
119 .ok_or_else(|| {
120 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
121 })?;
122 let mut file = File::open(filepath).map_err(|err| {
123 Error::SerdeError(format!(
124 "{err}\n{}",
125 if !filepath.exists() {
126 format!("File not found: {filepath:?}")
127 } else {
128 format!("Could not open file: {filepath:?}")
129 }
130 ))
131 })?;
132 Self::from_reader(&mut file, extension, skip_init)
133 }
134
135 fn to_writer<W: std::io::Write>(&self, mut wtr: W, format: &str) -> anyhow::Result<()> {
143 match format.trim_start_matches('.').to_lowercase().as_str() {
144 #[cfg(feature = "yaml")]
145 "yaml" | "yml" => serde_yaml::to_writer(wtr, self)?,
146 #[cfg(feature = "json")]
147 "json" => serde_json::to_writer(wtr, self)?,
148 #[cfg(feature = "msgpack")]
149 "msgpack" => rmp_serde::encode::write(&mut wtr, self)
150 .map_err(|err| Error::SerdeError(format!("{err}")))?,
151 #[cfg(feature = "toml")]
152 "toml" => {
153 let toml_string = self.to_toml()?;
154 wtr.write_all(toml_string.as_bytes())?;
155 }
156 _ => bail!(
157 "Unsupported format {format:?}, must be one of {:?}",
158 Self::ACCEPTED_BYTE_FORMATS
159 ),
160 }
161 Ok(())
162 }
163
164 fn to_str(&self, format: &str) -> anyhow::Result<String> {
171 match format.trim_start_matches('.').to_lowercase().as_str() {
172 #[cfg(feature = "yaml")]
173 "yaml" | "yml" => self.to_yaml(),
174 #[cfg(feature = "json")]
175 "json" => self.to_json(),
176 #[cfg(feature = "toml")]
177 "toml" => self.to_toml(),
178 _ => bail!(
179 "Unsupported format {format:?}, must be one of {:?}",
180 Self::ACCEPTED_STR_FORMATS
181 ),
182 }
183 }
184
185 fn from_str<S: AsRef<str>>(contents: S, format: &str, skip_init: bool) -> anyhow::Result<Self> {
193 Ok(
194 match format.trim_start_matches('.').to_lowercase().as_str() {
195 #[cfg(feature = "yaml")]
196 "yaml" | "yml" => Self::from_yaml(contents, skip_init)?,
197 #[cfg(feature = "json")]
198 "json" => Self::from_json(contents, skip_init)?,
199 #[cfg(feature = "toml")]
200 "toml" => Self::from_toml(contents, skip_init)?,
201 _ => bail!(
202 "Unsupported format {format:?}, must be one of {:?}",
203 Self::ACCEPTED_STR_FORMATS
204 ),
205 },
206 )
207 }
208
209 fn from_reader<R: std::io::Read>(
217 rdr: &mut R,
218 format: &str,
219 skip_init: bool,
220 ) -> Result<Self, Error> {
221 let mut deserialized: Self = match format.trim_start_matches('.').to_lowercase().as_str() {
222 "yaml" | "yml" => serde_yaml::from_reader(rdr)
223 .map_err(|err| Error::SerdeError(format!("{err} while reading `yaml`")))?,
224 "json" => serde_json::from_reader(rdr)
225 .map_err(|err| Error::SerdeError(format!("{err} while reading `json`")))?,
226 #[cfg(feature = "msgpack")]
227 "msgpack" => rmp_serde::decode::from_read(rdr)
228 .map_err(|err| Error::SerdeError(format!("{err} while reading `msgpack`")))?,
229 _ => {
230 return Err(Error::SerdeError(format!(
231 "Unsupported format {format:?}, must be one of {:?}",
232 Self::ACCEPTED_BYTE_FORMATS
233 )))
234 }
235 };
236 if !skip_init {
237 deserialized.init()?;
238 }
239 Ok(deserialized)
240 }
241
242 #[cfg(feature = "json")]
244 fn to_json(&self) -> anyhow::Result<String> {
245 Ok(serde_json::to_string(&self)?)
246 }
247
248 #[cfg(feature = "json")]
255 fn from_json<S: AsRef<str>>(json_str: S, skip_init: bool) -> anyhow::Result<Self> {
256 let mut json_de: Self = serde_json::from_str(json_str.as_ref())?;
257 if !skip_init {
258 json_de.init()?;
259 }
260 Ok(json_de)
261 }
262
263 #[cfg(feature = "msgpack")]
265 fn to_msg_pack(&self) -> anyhow::Result<Vec<u8>> {
266 Ok(rmp_serde::encode::to_vec_named(&self)?)
267 }
268
269 #[cfg(feature = "msgpack")]
276 fn from_msg_pack(msg_pack: &[u8], skip_init: bool) -> anyhow::Result<Self> {
277 let mut msg_pack_de: Self = rmp_serde::decode::from_slice(msg_pack)?;
278 if !skip_init {
279 msg_pack_de.init()?;
280 }
281 Ok(msg_pack_de)
282 }
283
284 #[cfg(feature = "toml")]
286 fn to_toml(&self) -> anyhow::Result<String> {
287 Ok(toml::to_string(&self)?)
288 }
289
290 #[cfg(feature = "toml")]
297 fn from_toml<S: AsRef<str>>(toml_str: S, skip_init: bool) -> anyhow::Result<Self> {
298 let mut toml_de: Self = toml::from_str(toml_str.as_ref())?;
299 if !skip_init {
300 toml_de.init()?;
301 }
302 Ok(toml_de)
303 }
304
305 #[cfg(feature = "yaml")]
307 fn to_yaml(&self) -> anyhow::Result<String> {
308 Ok(serde_yaml::to_string(&self)?)
309 }
310
311 #[cfg(feature = "yaml")]
318 fn from_yaml<S: AsRef<str>>(yaml_str: S, skip_init: bool) -> anyhow::Result<Self> {
319 let mut yaml_de: Self = serde_yaml::from_str(yaml_str.as_ref())?;
320 if !skip_init {
321 yaml_de.init()?;
322 }
323 Ok(yaml_de)
324 }
325}
326
327impl<T: Init> Init for Vec<T> {
328 fn init(&mut self) -> Result<(), Error> {
329 for val in self {
330 val.init()?
331 }
332 Ok(())
333 }
334}
335
336impl<T: SerdeAPI> SerdeAPI for Vec<T> {}