fastsim_core/traits/
serde_api.rs1use super::*;
2use include_dir::{include_dir, Dir};
3
4pub trait SerdeAPI: Serialize + for<'a> Deserialize<'a> + Init {
5 const ACCEPTED_BYTE_FORMATS: &'static [&'static str] = &[
6 #[cfg(feature = "yaml")]
7 "yaml",
8 #[cfg(feature = "json")]
9 "json",
10 #[cfg(feature = "msgpack")]
11 "msgpack",
12 #[cfg(feature = "toml")]
13 "toml",
14 ];
15 const ACCEPTED_STR_FORMATS: &'static [&'static str] = &[
16 #[cfg(feature = "yaml")]
17 "yaml",
18 #[cfg(feature = "json")]
19 "json",
20 #[cfg(feature = "toml")]
21 "toml",
22 ];
23 #[cfg(feature = "resources")]
24 const RESOURCES_DIR: &'static Dir<'_> = &include_dir!("$CARGO_MANIFEST_DIR/resources");
25 #[cfg(feature = "resources")]
26 const RESOURCES_SUBDIR: &'static str = "";
27
28 #[cfg(feature = "resources")]
34 fn from_resource<P: AsRef<Path>>(filepath: P, skip_init: bool) -> Result<Self, Error> {
35 let filepath = Path::new(Self::RESOURCES_SUBDIR).join(filepath);
36 let extension = filepath
37 .extension()
38 .and_then(OsStr::to_str)
39 .ok_or_else(|| {
40 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
41 })?;
42 let file = Self::RESOURCES_DIR.get_file(&filepath).ok_or_else(|| {
43 Error::SerdeError(format!("File not found in resources: {filepath:?}"))
44 })?;
45 Self::from_reader(&mut file.contents(), extension, skip_init)
46 }
47
48 #[cfg(feature = "resources")]
52 fn list_resources() -> Result<Vec<PathBuf>, Error> {
53 fn collect_paths(dir: &Dir, paths: &mut Vec<PathBuf>) {
55 for entry in dir.entries() {
56 match entry {
57 include_dir::DirEntry::Dir(subdir) => {
58 collect_paths(subdir, paths);
60 }
61 include_dir::DirEntry::File(file) => {
62 paths.push(file.path().to_path_buf());
64 }
65 }
66 }
67 }
68
69 let mut paths = Vec::new();
70 if let Some(resources_subdir) = Self::RESOURCES_DIR.get_dir(Self::RESOURCES_SUBDIR) {
71 collect_paths(resources_subdir, &mut paths);
72 for p in paths.iter_mut() {
73 *p = p
74 .strip_prefix(Self::RESOURCES_SUBDIR)
75 .map_err(|err| Error::SerdeError(format!("{err}")))?
76 .to_path_buf();
77 }
78 paths.sort();
79 }
80 Ok(paths)
81 }
82
83 #[cfg(feature = "web")]
90 fn from_url<S: AsRef<str>>(url: S, skip_init: bool) -> Result<Self, Error> {
91 let url = url::Url::parse(url.as_ref())?;
92 let format = url
93 .path_segments()
94 .and_then(|segments| segments.last())
95 .and_then(|filename| Path::new(filename).extension())
96 .and_then(OsStr::to_str)
97 .with_context(|| "Could not parse file format from URL: {url:?}")?;
98 let mut response = ureq::get(url.as_ref()).call()?.into_reader();
99 Self::from_reader(&mut response, format, skip_init)
100 }
101
102 fn to_file<P: AsRef<Path>>(&self, filepath: P) -> Result<(), Error> {
111 let filepath = filepath.as_ref();
112 let extension = filepath
113 .extension()
114 .and_then(OsStr::to_str)
115 .ok_or_else(|| {
116 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
117 })?;
118 self.to_writer(
119 File::create(filepath).map_err(|err| Error::SerdeError(format!("{err}")))?,
120 extension,
121 )
122 }
123
124 fn from_file<P: AsRef<Path>>(filepath: P, skip_init: bool) -> Result<Self, Error> {
132 let filepath = filepath.as_ref();
133 let extension = filepath
134 .extension()
135 .and_then(OsStr::to_str)
136 .ok_or_else(|| {
137 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
138 })?;
139 let mut file = File::open(filepath)
140 .with_context(|| {
141 if !filepath.exists() {
142 format!("File not found: {filepath:?}")
143 } else {
144 format!("Could not open file: {filepath:?}")
145 }
146 })
147 .map_err(|err| Error::SerdeError(format!("{err}")))?;
148 Self::from_reader(&mut file, extension, skip_init)
149 }
150
151 fn to_writer<W: std::io::Write>(&self, mut wtr: W, format: &str) -> Result<(), Error> {
159 match format.trim_start_matches('.').to_lowercase().as_str() {
160 #[cfg(feature = "yaml")]
161 "yaml" | "yml" => serde_yaml::to_writer(wtr, self)
162 .map_err(|err| Error::SerdeError(format!("{err}")))?,
163 #[cfg(feature = "json")]
164 "json" => serde_json::to_writer(wtr, self)
165 .map_err(|err| Error::SerdeError(format!("{err}")))?,
166 #[cfg(feature = "msgpack")]
167 "msgpack" => rmp_serde::encode::write(&mut wtr, self)
168 .map_err(|err| Error::SerdeError(format!("{err}")))?,
169 #[cfg(feature = "toml")]
170 "toml" => {
171 let toml_string = self
172 .to_toml()
173 .map_err(|err| Error::SerdeError(format!("{err}")))?;
174 wtr.write_all(toml_string.as_bytes())
175 .map_err(|err| Error::SerdeError(format!("{err}")))?;
176 }
177 _ => Err(Error::SerdeError(format!(
178 "Unsupported format {format:?}, must be one of {:?}",
179 Self::ACCEPTED_BYTE_FORMATS
180 )))?,
181 }
182 Ok(())
183 }
184
185 fn from_reader<R: std::io::Read>(
193 rdr: &mut R,
194 format: &str,
195 skip_init: bool,
196 ) -> Result<Self, Error> {
197 let mut deserialized: Self =
198 match format.trim_start_matches('.').to_lowercase().as_str() {
199 #[cfg(feature = "yaml")]
200 "yaml" | "yml" => serde_yaml::from_reader(rdr)
201 .map_err(|err| Error::SerdeError(format!("{err}")))?,
202 #[cfg(feature = "json")]
203 "json" => serde_json::from_reader(rdr)
204 .map_err(|err| Error::SerdeError(format!("{err}")))?,
205 #[cfg(feature = "msgpack")]
206 "msgpack" => rmp_serde::decode::from_read(rdr)
207 .map_err(|err| Error::SerdeError(format!("{err}")))?,
208 #[cfg(feature = "toml")]
209 "toml" => {
210 let mut buf = String::new();
211 rdr.read_to_string(&mut buf)
212 .map_err(|err| Error::SerdeError(format!("{err}")))?;
213 Self::from_toml(buf, skip_init)
214 .map_err(|err| Error::SerdeError(format!("{err}")))?
215 }
216 _ => Err(Error::SerdeError(format!(
217 "Unsupported format {format:?}, must be one of {:?}",
218 Self::ACCEPTED_BYTE_FORMATS,
219 )))?,
220 };
221 if !skip_init {
222 deserialized.init()?;
223 }
224 Ok(deserialized)
225 }
226
227 fn to_str(&self, format: &str) -> anyhow::Result<String> {
234 match format.trim_start_matches('.').to_lowercase().as_str() {
235 #[cfg(feature = "yaml")]
236 "yaml" | "yml" => self.to_yaml(),
237 #[cfg(feature = "json")]
238 "json" => self.to_json(),
239 #[cfg(feature = "toml")]
240 "toml" => self.to_toml(),
241 _ => bail!(
242 "Unsupported format {format:?}, must be one of {:?}",
243 Self::ACCEPTED_STR_FORMATS
244 ),
245 }
246 }
247
248 fn from_str<S: AsRef<str>>(contents: S, format: &str, skip_init: bool) -> anyhow::Result<Self> {
256 Ok(
257 match format.trim_start_matches('.').to_lowercase().as_str() {
258 #[cfg(feature = "yaml")]
259 "yaml" | "yml" => Self::from_yaml(contents, skip_init)?,
260 #[cfg(feature = "json")]
261 "json" => Self::from_json(contents, skip_init)?,
262 #[cfg(feature = "toml")]
263 "toml" => Self::from_toml(contents, skip_init)?,
264 _ => bail!(
265 "Unsupported format {format:?}, must be one of {:?}",
266 Self::ACCEPTED_STR_FORMATS
267 ),
268 },
269 )
270 }
271
272 #[cfg(feature = "json")]
274 fn to_json(&self) -> anyhow::Result<String> {
275 Ok(serde_json::to_string(&self)?)
276 }
277
278 #[cfg(feature = "json")]
285 fn from_json<S: AsRef<str>>(json_str: S, skip_init: bool) -> anyhow::Result<Self> {
286 let mut json_de: Self = serde_json::from_str(json_str.as_ref())?;
287 if !skip_init {
288 json_de.init()?;
289 }
290 Ok(json_de)
291 }
292
293 #[cfg(feature = "msgpack")]
295 fn to_msg_pack(&self) -> anyhow::Result<Vec<u8>> {
296 Ok(rmp_serde::encode::to_vec_named(&self)?)
297 }
298
299 #[cfg(feature = "msgpack")]
306 fn from_msg_pack(msg_pack: &[u8], skip_init: bool) -> anyhow::Result<Self> {
307 let mut msg_pack_de: Self = rmp_serde::decode::from_slice(msg_pack)?;
308 if !skip_init {
309 msg_pack_de.init()?;
310 }
311 Ok(msg_pack_de)
312 }
313
314 #[cfg(feature = "toml")]
316 fn to_toml(&self) -> anyhow::Result<String> {
317 Ok(toml::to_string(&self)?)
318 }
319
320 #[cfg(feature = "toml")]
327 fn from_toml<S: AsRef<str>>(toml_str: S, skip_init: bool) -> anyhow::Result<Self> {
328 let mut toml_de: Self = toml::from_str(toml_str.as_ref())?;
329 if !skip_init {
330 toml_de.init()?;
331 }
332 Ok(toml_de)
333 }
334
335 #[cfg(feature = "yaml")]
337 fn to_yaml(&self) -> anyhow::Result<String> {
338 Ok(serde_yaml::to_string(&self)?)
339 }
340
341 #[cfg(feature = "yaml")]
348 fn from_yaml<S: AsRef<str>>(yaml_str: S, skip_init: bool) -> anyhow::Result<Self> {
349 let mut yaml_de: Self = serde_yaml::from_str(yaml_str.as_ref())?;
350 if !skip_init {
351 yaml_de.init()?;
352 }
353 Ok(yaml_de)
354 }
355}
356
357impl<T: SerdeAPI> SerdeAPI for Vec<T> {}
358impl<T: Init> Init for Vec<T> {
359 fn init(&mut self) -> Result<(), Error> {
360 for val in self {
361 val.init()?
362 }
363 Ok(())
364 }
365}