1macro_rules! data_format_impl {
4 (
5 $(#[$mod_attr:meta])*
6 $mod_name:ident,
7 $(#[$main_ty_attrs:meta])*
8 $main_ty:ident,
9
10 $from_str_impl:expr,
11 $from_str_error:ty,
12
13 $(#[$to_str_ty_attrs:meta])*
14 $to_str_ty:ident,
15 $to_str_impl:expr,
16 $to_writer_impl:expr,
17 $to_str_error:ty,
18
19 $(#[$writer_ty_attrs:meta])*
20 $writer_ty:ident,
21
22 $extension:literal,
23 $text:literal $(,)?
24 ) => {
25 $(#[$mod_attr])*
26 pub mod $mod_name {
27 #![doc = concat!(r##"
28With the `"##, stringify!($mod_name), r##"` feature, this module provides the [`"##, stringify!($main_ty), r##"`] type.
29
30This allows us to read and parse `"##, stringify!($mod_name), r##"` files to some `serde::Deserialize` type,
31and write them back to disk."##
32 )]
33 #![doc = concat!(r##"## Reading a "##, stringify!($mod_name), r##" file"##)]
37 #![cfg_attr(feature = "derive", doc = "```rust")]
39 #![cfg_attr(not(feature = "derive"), doc = "```rust,compile_fail")]
40 #![doc = concat!(r##"use dir_structure::data_formats::"##, stringify!($mod_name), "::", stringify!($main_ty), r##";"##)]
44 #![doc = concat!(r##" #[dir_structure(path = "f"##, $extension, r##"", with_newtype = "##, stringify!($main_ty), r##"<Obj>)]"##)]
48 #![doc = concat!(r##" std::fs::write(d.join("f"##, $extension, r##""), "##, $text, r##")?;"##)]
61 #![doc = concat!(r##"## Writing a "##, stringify!($mod_name), r##" file"##)]
69 #![cfg_attr(feature = "derive", doc = "```rust")]
71 #![cfg_attr(not(feature = "derive"), doc = "```rust,compile_fail")]
72 #![doc = concat!(r##"use dir_structure::data_formats::"##, stringify!($mod_name), "::", stringify!($main_ty), r##";"##)]
76 #![doc = concat!(r##" #[dir_structure(path = "f"##, $extension, r##"", with_newtype = "##, stringify!($main_ty), r##"<Obj>)]"##)]
80 #![doc = concat!(r##" assert_eq!(std::fs::read_to_string(d.join("f"##, $extension, r##""))?,"##)]
99 #![doc = concat!(r##" "##, $text)]
100 use std::fmt;
107 use std::fmt::Formatter;
108 use std::str::FromStr;
109
110 use std::pin::Pin;
111 use std::marker;
112 use std::result::Result as StdResult;
113
114 use crate::traits::sync::FromRefForWriter;
115 use crate::traits::vfs;
116 #[cfg(feature = "async")]
117 use crate::traits::async_vfs::VfsAsync;
118 #[cfg(feature = "async")]
119 use crate::traits::async_vfs::WriteSupportingVfsAsync;
120 #[cfg(feature = "async")]
121 use crate::traits::asy::FromRefForWriterAsync;
122 use crate::traits::sync::NewtypeToInner;
123 use crate::prelude::*;
124 use crate::std_types::FileString;
125 use crate::error::VfsResult;
126 use crate::error::Error;
127 #[cfg(feature = "async")]
128 use crate::traits::vfs::VfsCore;
129 use crate::traits::vfs::PathType;
130
131 $(#[$main_ty_attrs])*
132 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, Hash)]
133 #[serde(transparent)]
134 #[cfg_attr(feature = "assert_eq", derive(assert_eq::AssertEq))]
135 pub struct $main_ty<T>(#[serde(bound = "")] pub T)
136 where
137 T: 'static + serde::Serialize + for<'d> serde::Deserialize<'d>;
138
139 impl<T> FromStr for $main_ty<T>
140 where
141 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
142 {
143 type Err = $from_str_error;
144
145 fn from_str(s: &str) -> StdResult<Self, Self::Err> {
146 $from_str_impl(s).map(Self)
147 }
148 }
149
150 $(#[$to_str_ty_attrs])*
151 struct $to_str_ty<'a, T>(&'a T)
152 where
153 T: serde::Serialize + 'a;
154
155 impl<'a, T> $to_str_ty<'a, T>
156 where
157 T: serde::Serialize + 'a
158 {
159 fn to_str(&self) -> StdResult<String, $to_str_error> {
160 $to_str_impl(&self.0)
161 }
162
163 fn to_writer<W>(&self, writer: &mut W) -> StdResult<(), ToWriterError>
164 where
165 W: std::io::Write,
166 {
167 $to_writer_impl(&self.0, writer)
168 }
169 }
170
171 enum ToWriterError {
172 #[allow(clippy::allow_attributes, unused)]
173 Io(std::io::Error),
174 Serde($to_str_error),
175 }
176
177 impl<'a, T> fmt::Display for $to_str_ty<'a, T>
178 where
179 T: serde::Serialize + 'a,
180 {
181 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
182 let s = self.to_str().map_err(|_| fmt::Error)?;
183 write!(f, "{s}")
184 }
185 }
186
187 impl<T> fmt::Display for $main_ty<T>
188 where
189 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
190 {
191 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
192 $to_str_ty(&self.0).fmt(f)
193 }
194 }
195
196 impl<'a, T, Vfs: vfs::Vfs<'a>> ReadFrom<'a, Vfs> for $main_ty<T>
197 where
198 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
199 {
200 fn read_from(path: &Vfs::Path, vfs: Pin<&'a Vfs>) -> VfsResult<Self, Vfs> {
201 let contents = FileString::read_from(path, vfs)?.0;
202 let v = contents
203 .parse::<$main_ty<T>>()
204 .map_err(|e| Error::Parse(path.owned(), e.into()))?;
205 Ok(v)
206 }
207 }
208
209 #[cfg(feature = "async")]
210 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
211 impl<'a, T, Vfs: VfsAsync + 'static> ReadFromAsync<'a, Vfs> for $main_ty<T>
212 where
213 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
214 {
215 type Future = Pin<Box<dyn Future<Output = VfsResult<Self, Vfs>> + Send + 'a>>;
216
217 fn read_from_async(path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath, vfs: Pin<&'a Vfs>) -> Self::Future {
218 Box::pin(async move {
219 let contents = FileString::read_from_async(path.clone(), vfs).await?.0;
220 let v = contents
221 .parse::<$main_ty<T>>()
222 .map_err(|e| Error::Parse(path.clone(), e.into()))?;
223 Ok(v)
224 })
225 }
226 }
227
228 impl<'a, T, Vfs: vfs::WriteSupportingVfs<'a>> WriteTo<'a, Vfs> for $main_ty<T>
229 where
230 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
231 {
232 fn write_to(&self, path: &Vfs::Path, vfs: Pin<&'a Vfs>) -> VfsResult<(), Vfs> {
233 Self::from_ref_for_writer(&self.0).write_to(path, vfs)
234 }
235 }
236
237 #[cfg(feature = "async")]
238 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
239 impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> WriteToAsync<'a, Vfs> for $main_ty<T>
240 where
241 T: serde::Serialize + for<'d> serde::Deserialize<'d> + Send + Sync + 'static,
242 {
243 type Future = Pin<Box<dyn Future<Output = VfsResult<(), Vfs>> + Send + 'a>>;
244
245 fn write_to_async(self, path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath, vfs: Pin<&'a Vfs>) -> Self::Future {
246 Box::pin(async move {
247 let s = $to_str_ty(&self.0).to_str()
248 .map_err(|e| Error::Serde(path.clone(), e.into()))?;
249 FileString::new(s).write_to_async(path, vfs).await
250 })
251 }
252 }
253
254 impl<T> NewtypeToInner for $main_ty<T>
255 where
256 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
257 {
258 type Inner = T;
259
260 fn into_inner(self) -> Self::Inner {
261 self.0
262 }
263 }
264
265 impl<'a, 'vfs, T, Vfs: vfs::WriteSupportingVfs<'vfs> + 'vfs> FromRefForWriter<'a, 'vfs, Vfs> for $main_ty<T>
266 where
267 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
268 'vfs: 'a,
269 {
270 type Inner = T;
271 type Wr = $writer_ty<'a, 'vfs, T, Vfs>;
272
273 fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
274 $writer_ty(value, marker::PhantomData)
275 }
276 }
277
278 #[cfg(feature = "async")]
279 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
280 impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> FromRefForWriterAsync<'a, Vfs> for $main_ty<T>
281 where
282 T: serde::Serialize + for<'d> serde::Deserialize<'d> + Send + Sync + 'static,
283 {
284 type Inner = T;
285 type Wr = $writer_ty<'a, 'a, T, Vfs>;
286
287 fn from_ref_for_writer_async(value: &'a <Self as FromRefForWriterAsync<'a, Vfs>>::Inner) -> Self::Wr {
288 $writer_ty(value, marker::PhantomData)
289 }
290 }
291
292 $(#[$writer_ty_attrs])*
293 pub struct $writer_ty<'a, 'vfs, T, Vfs: 'vfs>(&'a T, marker::PhantomData<&'vfs Vfs>)
294 where
295 T: serde::Serialize + 'a,
296 'vfs: 'a;
297
298 impl<'a, 'vfs, T, Vfs: vfs::WriteSupportingVfs<'vfs>> WriteTo<'vfs, Vfs> for $writer_ty<'a, 'vfs, T, Vfs>
299 where
300 T: serde::Serialize + 'a,
301 'vfs: 'a,
302 {
303 fn write_to(&self, path: &Vfs::Path, vfs: Pin<&'vfs Vfs>) -> VfsResult<(), Vfs> {
304 vfs.create_parent_dir(path)?;
305
306 $to_str_ty(self.0).to_writer(&mut vfs.open_write(path)?)
307 .map_err(|e| match e {
308 ToWriterError::Io(e) => Error::Io(path.owned(), e),
309 ToWriterError::Serde(e) => Error::Serde(path.owned(), e.into()),
310 })?;
311
312 Ok(())
313 }
314 }
315
316 #[cfg(feature = "async")]
317 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
318 impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> WriteToAsync<'a, Vfs> for $writer_ty<'a, 'a, T, Vfs>
319 where
320 T: serde::Serialize + Send + Sync + 'a,
321 {
322 type Future = Pin<Box<dyn Future<Output = VfsResult<(), Vfs>> + Send + 'a>> where Self: 'a, Vfs: 'a;
323
324 fn write_to_async(self, path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath, vfs: Pin<&'a Vfs>) -> Self::Future {
325 Box::pin(async move {
326 let s = $to_str_ty(self.0).to_str()
327 .map_err(|e| Error::Serde(path.clone(), e.into()))?;
328 FileString::new(s).write_to_async(path, vfs).await
329 })
330 }
331 }
332
333 impl<T> std::ops::Deref for $main_ty<T>
334 where
335 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
336 {
337 type Target = T;
338
339 fn deref(&self) -> &Self::Target {
340 &self.0
341 }
342 }
343
344 impl<T> std::ops::DerefMut for $main_ty<T>
345 where
346 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
347 {
348 fn deref_mut(&mut self) -> &mut Self::Target {
349 &mut self.0
350 }
351 }
352 }
353 };
354}
355
356data_format_impl!(
357 #[cfg(feature = "json")]
358 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
359 #[allow(clippy::absolute_paths)]
360 json,
361 Json,
365 |s| serde_json::from_str(s),
366 serde_json::Error,
367 JsonToStr,
368 |v| serde_json::to_string(&v),
369 |v, w| serde_json::to_writer(w, v).map_err(ToWriterError::Serde),
370 serde_json::Error,
371 JsonRefWr,
373 ".json", r##"r#"{"name":"John","age":30}"#"##,
374);
375
376data_format_impl!(
377 #[cfg(feature = "json")]
378 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
379 #[allow(clippy::absolute_paths)]
380 json_pretty,
381 JsonPretty,
387 |s| serde_json::from_str(s),
388 serde_json::Error,
389 JsonPrettyToStr,
390 |v| serde_json::to_string_pretty(&v),
391 |v, w| serde_json::to_writer_pretty(w, v).map_err(ToWriterError::Serde),
392 serde_json::Error,
393 JsonPrettyRefWr,
395 ".json", r##"r#"{
396 "name": "John",
397 "age": 30
398}"#"##,
399);
400
401data_format_impl!(
402 #[cfg(feature = "toml")]
403 #[cfg_attr(docsrs, doc(cfg(feature = "toml")))]
404 #[allow(clippy::absolute_paths)]
405 toml,
406 Toml,
410 |s| toml::de::from_str(s),
411 toml::de::Error,
412 TomlToStr,
413 |v| toml::ser::to_string(&v),
414 |v, w: &mut dyn std::io::Write| {
415 let s = toml::ser::to_string(&v).map_err(ToWriterError::Serde)?;
416 w.write_all(s.as_bytes()).map_err(ToWriterError::Io)?;
417 Ok(())
418 },
419 toml::ser::Error,
420 TomlRefWr,
422 ".toml", r##"r#"
423name = "John"
424age = 30
425"#.trim_start()"##,
426);
427
428data_format_impl!(
429 #[cfg(feature = "yaml")]
430 #[cfg_attr(docsrs, doc(cfg(feature = "yaml")))]
431 #[allow(clippy::absolute_paths)]
432 yaml,
433 Yaml,
437 |s| serde_yaml::from_str(s),
438 serde_yaml::Error,
439 YamlToStr,
440 |v| serde_yaml::to_string(&v),
441 |v, w| serde_yaml::to_writer(w, v).map_err(ToWriterError::Serde),
442 serde_yaml::Error,
443 YamlRefWr,
445 ".yaml", r##"r#"
446name: John
447age: 30
448"#.trim_start()"##,
449);
450
451data_format_impl!(
452 #[cfg(feature = "ron")]
453 #[cfg_attr(docsrs, doc(cfg(feature = "ron")))]
454 #[allow(clippy::absolute_paths)]
455 ron,
456 Ron,
460 |s| ron::de::from_str(s),
461 ron::error::SpannedError,
462 RonToStr,
463 |v| ron::ser::to_string(&v),
464 |v, w| ron::options::Options::default().to_io_writer(w, v).map_err(ToWriterError::Serde),
465 ron::error::Error,
466 RonRefWr,
468 ".ron", r##"r#"(name:"John",age:30)"#"##,
469);
470
471data_format_impl!(
472 #[cfg(feature = "ron")]
473 #[cfg_attr(docsrs, doc(cfg(feature = "ron")))]
474 #[allow(clippy::absolute_paths)]
475 ron_pretty,
476 RonPretty,
482 |s| ron::de::from_str(s),
483 ron::error::SpannedError,
484 RonToStr,
485 |v| ron::ser::to_string_pretty(&v, ron::ser::PrettyConfig::default()),
486 |v, w| ron::options::Options::default().to_io_writer_pretty(w, v, ron::ser::PrettyConfig::default()).map_err(ToWriterError::Serde),
487 ron::error::Error,
488 RonPrettyRefWr,
490 ".ron", r##"r#"(
491 name: "John",
492 age: 30,
493)"#"##,
494);