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 =
92 url::Url::parse(url.as_ref()).map_err(|err| Error::SerdeError(format!("{err}")))?;
93 let format = url
94 .path_segments()
95 .and_then(|segments| segments.last())
96 .and_then(|filename| Path::new(filename).extension())
97 .and_then(OsStr::to_str)
98 .with_context(|| "Could not parse file format from URL: {url:?}")
99 .map_err(|err| Error::SerdeError(format!("{err}")))?;
100 let mut response = ureq::get(url.as_ref())
101 .call()
102 .map_err(|err| Error::SerdeError(format!("{err}")))?
103 .into_reader();
104 Self::from_reader(&mut response, format, skip_init)
105 }
106
107 fn to_file<P: AsRef<Path>>(&self, filepath: P) -> Result<(), Error> {
116 let filepath = filepath.as_ref();
117 let extension = filepath
118 .extension()
119 .and_then(OsStr::to_str)
120 .ok_or_else(|| {
121 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
122 })?;
123 self.to_writer(
124 File::create(filepath).map_err(|err| Error::SerdeError(format!("{err}")))?,
125 extension,
126 )
127 }
128
129 fn from_file<P: AsRef<Path>>(filepath: P, skip_init: bool) -> Result<Self, Error> {
137 let filepath = filepath.as_ref();
138 let extension = filepath
139 .extension()
140 .and_then(OsStr::to_str)
141 .ok_or_else(|| {
142 Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
143 })?;
144 let mut file = File::open(filepath)
145 .with_context(|| {
146 if !filepath.exists() {
147 format!("File not found: {filepath:?}")
148 } else {
149 format!("Could not open file: {filepath:?}")
150 }
151 })
152 .map_err(|err| Error::SerdeError(format!("{err}")))?;
153 Self::from_reader(&mut file, extension, skip_init)
154 }
155
156 fn to_writer<W: std::io::Write>(&self, mut wtr: W, format: &str) -> Result<(), Error> {
164 match format.trim_start_matches('.').to_lowercase().as_str() {
165 #[cfg(feature = "yaml")]
166 "yaml" | "yml" => serde_yaml::to_writer(wtr, self)
167 .map_err(|err| Error::SerdeError(format!("{err}")))?,
168 #[cfg(feature = "json")]
169 "json" => serde_json::to_writer(wtr, self)
170 .map_err(|err| Error::SerdeError(format!("{err}")))?,
171 #[cfg(feature = "msgpack")]
172 "msgpack" => rmp_serde::encode::write(&mut wtr, self)
173 .map_err(|err| Error::SerdeError(format!("{err}")))?,
174 #[cfg(feature = "toml")]
175 "toml" => {
176 let toml_string = self
177 .to_toml()
178 .map_err(|err| Error::SerdeError(format!("{err}")))?;
179 wtr.write_all(toml_string.as_bytes())
180 .map_err(|err| Error::SerdeError(format!("{err}")))?;
181 }
182 _ => Err(Error::SerdeError(format!(
183 "Unsupported format {format:?}, must be one of {:?}",
184 Self::ACCEPTED_BYTE_FORMATS
185 )))?,
186 }
187 Ok(())
188 }
189
190 fn from_reader<R: std::io::Read>(
198 rdr: &mut R,
199 format: &str,
200 skip_init: bool,
201 ) -> Result<Self, Error> {
202 let mut deserialized: Self =
203 match format.trim_start_matches('.').to_lowercase().as_str() {
204 #[cfg(feature = "yaml")]
205 "yaml" | "yml" => serde_yaml::from_reader(rdr)
206 .map_err(|err| Error::SerdeError(format!("{err}")))?,
207 #[cfg(feature = "json")]
208 "json" => serde_json::from_reader(rdr)
209 .map_err(|err| Error::SerdeError(format!("{err}")))?,
210 #[cfg(feature = "msgpack")]
211 "msgpack" => rmp_serde::decode::from_read(rdr)
212 .map_err(|err| Error::SerdeError(format!("{err}")))?,
213 #[cfg(feature = "toml")]
214 "toml" => {
215 let mut buf = String::new();
216 rdr.read_to_string(&mut buf)
217 .map_err(|err| Error::SerdeError(format!("{err}")))?;
218 Self::from_toml(buf, skip_init)
219 .map_err(|err| Error::SerdeError(format!("{err}")))?
220 }
221 _ => Err(Error::SerdeError(format!(
222 "Unsupported format {format:?}, must be one of {:?}",
223 Self::ACCEPTED_BYTE_FORMATS,
224 )))?,
225 };
226 if !skip_init {
227 deserialized.init()?;
228 }
229 Ok(deserialized)
230 }
231
232 fn to_str(&self, format: &str) -> anyhow::Result<String> {
239 match format.trim_start_matches('.').to_lowercase().as_str() {
240 #[cfg(feature = "yaml")]
241 "yaml" | "yml" => self.to_yaml(),
242 #[cfg(feature = "json")]
243 "json" => self.to_json(),
244 #[cfg(feature = "toml")]
245 "toml" => self.to_toml(),
246 _ => bail!(
247 "Unsupported format {format:?}, must be one of {:?}",
248 Self::ACCEPTED_STR_FORMATS
249 ),
250 }
251 }
252
253 fn from_str<S: AsRef<str>>(contents: S, format: &str, skip_init: bool) -> anyhow::Result<Self> {
261 Ok(
262 match format.trim_start_matches('.').to_lowercase().as_str() {
263 #[cfg(feature = "yaml")]
264 "yaml" | "yml" => Self::from_yaml(contents, skip_init)?,
265 #[cfg(feature = "json")]
266 "json" => Self::from_json(contents, skip_init)?,
267 #[cfg(feature = "toml")]
268 "toml" => Self::from_toml(contents, skip_init)?,
269 _ => bail!(
270 "Unsupported format {format:?}, must be one of {:?}",
271 Self::ACCEPTED_STR_FORMATS
272 ),
273 },
274 )
275 }
276
277 #[cfg(feature = "json")]
279 fn to_json(&self) -> anyhow::Result<String> {
280 Ok(serde_json::to_string(&self)?)
281 }
282
283 #[cfg(feature = "json")]
290 fn from_json<S: AsRef<str>>(json_str: S, skip_init: bool) -> anyhow::Result<Self> {
291 let mut json_de: Self = serde_json::from_str(json_str.as_ref())?;
292 if !skip_init {
293 json_de.init()?;
294 }
295 Ok(json_de)
296 }
297
298 #[cfg(feature = "msgpack")]
300 fn to_msg_pack(&self) -> anyhow::Result<Vec<u8>> {
301 Ok(rmp_serde::encode::to_vec_named(&self)?)
302 }
303
304 #[cfg(feature = "msgpack")]
311 fn from_msg_pack(msg_pack: &[u8], skip_init: bool) -> anyhow::Result<Self> {
312 let mut msg_pack_de: Self = rmp_serde::decode::from_slice(msg_pack)?;
313 if !skip_init {
314 msg_pack_de.init()?;
315 }
316 Ok(msg_pack_de)
317 }
318
319 #[cfg(feature = "toml")]
321 fn to_toml(&self) -> anyhow::Result<String> {
322 Ok(toml::to_string(&self)?)
323 }
324
325 #[cfg(feature = "toml")]
332 fn from_toml<S: AsRef<str>>(toml_str: S, skip_init: bool) -> anyhow::Result<Self> {
333 let mut toml_de: Self = toml::from_str(toml_str.as_ref())?;
334 if !skip_init {
335 toml_de.init()?;
336 }
337 Ok(toml_de)
338 }
339
340 #[cfg(feature = "yaml")]
342 fn to_yaml(&self) -> anyhow::Result<String> {
343 Ok(serde_yaml::to_string(&self)?)
344 }
345
346 #[cfg(feature = "yaml")]
353 fn from_yaml<S: AsRef<str>>(yaml_str: S, skip_init: bool) -> anyhow::Result<Self> {
354 let mut yaml_de: Self = serde_yaml::from_str(yaml_str.as_ref())?;
355 if !skip_init {
356 yaml_de.init()?;
357 }
358 Ok(yaml_de)
359 }
360}
361
362impl<T: SerdeAPI> SerdeAPI for Vec<T> {}
363impl<T: Init> Init for Vec<T> {
364 fn init(&mut self) -> Result<(), Error> {
365 for val in self {
366 val.init()?
367 }
368 Ok(())
369 }
370}