fastsim_core/traits/
serde_api.rs1use super::*;
2#[cfg(feature = "resources")]
3use include_dir::{include_dir, Dir};
4
5pub trait SerdeAPI: Serialize + for<'a> Deserialize<'a> + Init {
6 const ACCEPTED_BYTE_FORMATS: &'static [&'static str] = &[
7 #[cfg(feature = "yaml")]
8 "yaml",
9 #[cfg(feature = "json")]
10 "json",
11 #[cfg(feature = "msgpack")]
12 "msgpack",
13 #[cfg(feature = "toml")]
14 "toml",
15 ];
16 const ACCEPTED_STR_FORMATS: &'static [&'static str] = &[
17 #[cfg(feature = "yaml")]
18 "yaml",
19 #[cfg(feature = "json")]
20 "json",
21 #[cfg(feature = "toml")]
22 "toml",
23 ];
24 #[cfg(feature = "resources")]
25 const RESOURCES_DIR: &'static Dir<'_> = &include_dir!("$CARGO_MANIFEST_DIR/resources");
26 #[cfg(feature = "resources")]
27 const RESOURCES_SUBDIR: &'static str = "";
28
29 #[cfg(feature = "resources")]
35 fn from_resource<P: AsRef<Path>>(filepath: P, skip_init: bool) -> Result<Self, Error> {
36 let filepath = Path::new(Self::RESOURCES_SUBDIR).join(filepath);
37 let extension = filepath
38 .extension()
39 .and_then(OsStr::to_str)
40 .ok_or_else(|| {
41 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
42 })?;
43 let file = Self::RESOURCES_DIR.get_file(&filepath).ok_or_else(|| {
44 Error::SerdeError(format!("File not found in resources: {filepath:?}"))
45 })?;
46 Self::from_reader(&mut file.contents(), extension, skip_init)
47 }
48
49 #[cfg(feature = "resources")]
53 fn list_resources() -> Result<Vec<PathBuf>, Error> {
54 fn collect_paths(dir: &Dir, paths: &mut Vec<PathBuf>) {
56 for entry in dir.entries() {
57 match entry {
58 include_dir::DirEntry::Dir(subdir) => {
59 collect_paths(subdir, paths);
61 }
62 include_dir::DirEntry::File(file) => {
63 paths.push(file.path().to_path_buf());
65 }
66 }
67 }
68 }
69
70 let mut paths = Vec::new();
71 if let Some(resources_subdir) = Self::RESOURCES_DIR.get_dir(Self::RESOURCES_SUBDIR) {
72 collect_paths(resources_subdir, &mut paths);
73 for p in paths.iter_mut() {
74 *p = p
75 .strip_prefix(Self::RESOURCES_SUBDIR)
76 .map_err(|err| Error::SerdeError(format!("{err}")))?
77 .to_path_buf();
78 }
79 paths.sort();
80 }
81 Ok(paths)
82 }
83
84 #[cfg(feature = "web")]
91 fn from_url<S: AsRef<str>>(url: S, skip_init: bool) -> Result<Self, Error> {
92 let url =
93 url::Url::parse(url.as_ref()).map_err(|err| Error::SerdeError(format!("{err}")))?;
94 let format = url
95 .path_segments()
96 .and_then(|segments| segments.last())
97 .and_then(|filename| Path::new(filename).extension())
98 .and_then(OsStr::to_str)
99 .with_context(|| "Could not parse file format from URL: {url:?}")
100 .map_err(|err| Error::SerdeError(format!("{err}")))?;
101 let mut response = ureq::get(url.as_ref())
102 .call()
103 .map_err(|err| Error::SerdeError(format!("{err}")))?
104 .into_reader();
105 Self::from_reader(&mut response, format, skip_init)
106 }
107
108 fn to_file<P: AsRef<Path>>(&self, filepath: P) -> Result<(), Error> {
117 let filepath = filepath.as_ref();
118 let extension = filepath
119 .extension()
120 .and_then(OsStr::to_str)
121 .ok_or_else(|| {
122 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
123 })?;
124 self.to_writer(
125 File::create(filepath).map_err(|err| Error::SerdeError(format!("{err}")))?,
126 extension,
127 )
128 }
129
130 fn from_file<P: AsRef<Path>>(filepath: P, skip_init: bool) -> Result<Self, Error> {
138 let filepath = filepath.as_ref();
139 let filepath = match filepath.to_str() {
141 Some("~") => home::home_dir().unwrap_or_else(|| filepath.to_path_buf()),
142 Some(path_str) if path_str.starts_with("~/") || path_str.starts_with("~\\") => {
143 match home::home_dir() {
144 Some(home_path) => home_path.join(&path_str[2..]),
145 None => filepath.to_path_buf(),
146 }
147 }
148 _ => filepath.to_path_buf(),
149 };
150 let extension = filepath
151 .extension()
152 .and_then(OsStr::to_str)
153 .ok_or_else(|| {
154 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
155 })?;
156 let mut file = File::open(&filepath)
157 .with_context(|| {
158 if !filepath.exists() {
159 format!("File not found: {filepath:?}")
160 } else {
161 format!("Could not open file: {filepath:?}")
162 }
163 })
164 .map_err(|err| Error::SerdeError(format!("{err}")))?;
165 Self::from_reader(&mut file, extension, skip_init)
166 }
167
168 fn to_writer<W: std::io::Write>(&self, mut wtr: W, format: &str) -> Result<(), Error> {
176 match format.trim_start_matches('.').to_lowercase().as_str() {
177 #[cfg(feature = "yaml")]
178 "yaml" | "yml" => serde_yaml::to_writer(wtr, self)
179 .map_err(|err| Error::SerdeError(format!("{err}")))?,
180 #[cfg(feature = "json")]
181 "json" => serde_json::to_writer(wtr, self)
182 .map_err(|err| Error::SerdeError(format!("{err}")))?,
183 #[cfg(feature = "msgpack")]
184 "msgpack" => rmp_serde::encode::write(&mut wtr, self)
185 .map_err(|err| Error::SerdeError(format!("{err}")))?,
186 #[cfg(feature = "toml")]
187 "toml" => {
188 let toml_string = self
189 .to_toml()
190 .map_err(|err| Error::SerdeError(format!("{err}")))?;
191 wtr.write_all(toml_string.as_bytes())
192 .map_err(|err| Error::SerdeError(format!("{err}")))?;
193 }
194 _ => Err(Error::SerdeError(format!(
195 "Unsupported format {format:?}, must be one of {:?}",
196 Self::ACCEPTED_BYTE_FORMATS
197 )))?,
198 }
199 Ok(())
200 }
201
202 fn from_reader<R: std::io::Read>(
210 rdr: &mut R,
211 format: &str,
212 skip_init: bool,
213 ) -> Result<Self, Error> {
214 let mut deserialized: Self =
215 match format.trim_start_matches('.').to_lowercase().as_str() {
216 #[cfg(feature = "yaml")]
217 "yaml" | "yml" => serde_yaml::from_reader(rdr)
218 .map_err(|err| Error::SerdeError(format!("{err}")))?,
219 #[cfg(feature = "json")]
220 "json" => serde_json::from_reader(rdr)
221 .map_err(|err| Error::SerdeError(format!("{err}")))?,
222 #[cfg(feature = "msgpack")]
223 "msgpack" => rmp_serde::decode::from_read(rdr)
224 .map_err(|err| Error::SerdeError(format!("{err}")))?,
225 #[cfg(feature = "toml")]
226 "toml" => {
227 let mut buf = String::new();
228 rdr.read_to_string(&mut buf)
229 .map_err(|err| Error::SerdeError(format!("{err}")))?;
230 Self::from_toml(buf, true).map_err(|err| Error::SerdeError(format!("{err}")))?
231 }
232 _ => Err(Error::SerdeError(format!(
233 "Unsupported format {format:?}, must be one of {:?}",
234 Self::ACCEPTED_BYTE_FORMATS,
235 )))?,
236 };
237 if !skip_init {
238 deserialized.init()?;
239 }
240 Ok(deserialized)
241 }
242
243 fn to_str(&self, format: &str) -> anyhow::Result<String> {
250 match format.trim_start_matches('.').to_lowercase().as_str() {
251 #[cfg(feature = "yaml")]
252 "yaml" | "yml" => self.to_yaml(),
253 #[cfg(feature = "json")]
254 "json" => self.to_json(),
255 #[cfg(feature = "toml")]
256 "toml" => self.to_toml(),
257 _ => bail!(
258 "Unsupported format {format:?}, must be one of {:?}",
259 Self::ACCEPTED_STR_FORMATS
260 ),
261 }
262 }
263
264 fn from_str<S: AsRef<str>>(contents: S, format: &str, skip_init: bool) -> anyhow::Result<Self> {
272 Ok(
273 match format.trim_start_matches('.').to_lowercase().as_str() {
274 #[cfg(feature = "yaml")]
275 "yaml" | "yml" => Self::from_yaml(contents, skip_init)?,
276 #[cfg(feature = "json")]
277 "json" => Self::from_json(contents, skip_init)?,
278 #[cfg(feature = "toml")]
279 "toml" => Self::from_toml(contents, skip_init)?,
280 _ => bail!(
281 "Unsupported format {format:?}, must be one of {:?}",
282 Self::ACCEPTED_STR_FORMATS
283 ),
284 },
285 )
286 }
287
288 #[cfg(feature = "json")]
290 fn to_json(&self) -> anyhow::Result<String> {
291 Ok(serde_json::to_string(&self)?)
292 }
293
294 #[cfg(feature = "json")]
301 fn from_json<S: AsRef<str>>(json_str: S, skip_init: bool) -> anyhow::Result<Self> {
302 let mut json_de: Self = serde_json::from_str(json_str.as_ref())?;
303 if !skip_init {
304 json_de.init()?;
305 }
306 Ok(json_de)
307 }
308
309 #[cfg(feature = "msgpack")]
311 fn to_msg_pack(&self) -> anyhow::Result<Vec<u8>> {
312 Ok(rmp_serde::encode::to_vec_named(&self)?)
313 }
314
315 #[cfg(feature = "msgpack")]
322 fn from_msg_pack(msg_pack: &[u8], skip_init: bool) -> anyhow::Result<Self> {
323 let mut msg_pack_de: Self = rmp_serde::decode::from_slice(msg_pack)?;
324 if !skip_init {
325 msg_pack_de.init()?;
326 }
327 Ok(msg_pack_de)
328 }
329
330 #[cfg(feature = "toml")]
332 fn to_toml(&self) -> anyhow::Result<String> {
333 Ok(toml::to_string(&self)?)
334 }
335
336 #[cfg(feature = "toml")]
343 fn from_toml<S: AsRef<str>>(toml_str: S, skip_init: bool) -> anyhow::Result<Self> {
344 let mut toml_de: Self = toml::from_str(toml_str.as_ref())?;
345 if !skip_init {
346 toml_de.init()?;
347 }
348 Ok(toml_de)
349 }
350
351 #[cfg(feature = "yaml")]
353 fn to_yaml(&self) -> anyhow::Result<String> {
354 Ok(serde_yaml::to_string(&self)?)
355 }
356
357 #[cfg(feature = "yaml")]
364 fn from_yaml<S: AsRef<str>>(yaml_str: S, skip_init: bool) -> anyhow::Result<Self> {
365 let mut yaml_de: Self = serde_yaml::from_str(yaml_str.as_ref())?;
366 if !skip_init {
367 yaml_de.init()?;
368 }
369 Ok(yaml_de)
370 }
371}
372
373impl<T: SerdeAPI> SerdeAPI for Vec<T> {}
374impl<T: Init> Init for Vec<T> {
375 fn init(&mut self) -> Result<(), Error> {
376 for val in self {
377 val.init()?
378 }
379 Ok(())
380 }
381}