dir_structure/lib.rs
1//! A library for reading and writing directory structures.
2//!
3//! This library provides a macro for defining directory structures, and a
4//! trait for reading and writing those structures to / from disk.
5//!
6//! # Example
7//!
8//! ## Writing a structure to disk
9//! ```
10//! use std::path::Path;
11//! fn main() -> Result<(), Box<dyn std::error::Error>> {
12//! use dir_structure::DirStructureItem;
13//! #[derive(dir_structure::DirStructure)]
14//! struct Dir {
15//! #[dir_structure(path = "f1.txt")]
16//! f1: String,
17//! #[dir_structure(path = "subdir/f2.txt")]
18//! f2: String,
19//! // default path is just a file name from the field's name.
20//! f3: String,
21//! // also works with nested structures
22//! #[dir_structure(path = "subdir2")]
23//! subdir: Subdir,
24//! }
25//! #[derive(dir_structure::DirStructure)]
26//! struct Subdir {
27//! #[dir_structure(path = "f4.txt")]
28//! f4: String,
29//! }
30//!
31//! let d = Path::new("dir");
32//! Dir {
33//! f1: "f1".to_owned(),
34//! f2: "f2".to_owned(),
35//! f3: "f3".to_owned(),
36//! subdir: Subdir {
37//! f4: "f4".to_owned(),
38//! },
39//! }.write(&d)?;
40//! assert_eq!(std::fs::read_to_string(d.join("f1.txt"))?, "f1");
41//! assert_eq!(std::fs::read_to_string(d.join("subdir/f2.txt"))?, "f2");
42//! assert_eq!(std::fs::read_to_string(d.join("f3"))?, "f3");
43//! assert_eq!(std::fs::read_to_string(d.join("subdir2/f4.txt"))?, "f4");
44//!
45//! # std::fs::remove_dir_all(&d)?;
46//!
47//! Ok(())
48//! }
49//! ```
50//!
51//! ## Reading a structure from disk
52//!
53//! ```
54//! use std::path::Path;
55//! fn main() -> Result<(), Box<dyn std::error::Error>> {
56//! use dir_structure::DirStructureItem;
57//! #[derive(dir_structure::DirStructure)]
58//! struct Dir {
59//! #[dir_structure(path = "f1.txt")]
60//! f1: String,
61//! #[dir_structure(path = "subdir/f2.txt")]
62//! f2: String,
63//! // default path is just a file name from the field's name.
64//! f3: String,
65//! // also works with nested structures
66//! #[dir_structure(path = "subdir2")]
67//! subdir: Subdir,
68//! }
69//! #[derive(dir_structure::DirStructure)]
70//! struct Subdir {
71//! #[dir_structure(path = "f4.txt")]
72//! f4: String,
73//! }
74//! let d = Path::new("dir");
75//! std::fs::create_dir_all(&d)?;
76//! std::fs::create_dir_all(d.join("subdir"))?;
77//! std::fs::create_dir_all(d.join("subdir2"))?;
78//! std::fs::write(d.join("f1.txt"), "f1")?;
79//! std::fs::write(d.join("subdir/f2.txt"), "f2")?;
80//! std::fs::write(d.join("f3"), "f3")?;
81//! std::fs::write(d.join("subdir2/f4.txt"), "f4")?;
82//! let dir = Dir::read(&d)?;
83//! assert_eq!(dir.f1, "f1");
84//! assert_eq!(dir.f2, "f2");
85//! assert_eq!(dir.f3, "f3");
86//! assert_eq!(dir.subdir.f4, "f4");
87//!
88//! # std::fs::remove_dir_all(&d)?;
89//!
90//! Ok(())
91//! }
92//! ```
93
94#![cfg_attr(docsrs, feature(doc_cfg))]
95
96use std::ffi::OsStr;
97use std::ffi::OsString;
98use std::fmt::Display;
99use std::fs::File;
100use std::marker;
101use std::ops::Deref;
102use std::ops::DerefMut;
103use std::path::Path;
104use std::path::PathBuf;
105use std::str::FromStr;
106
107/// The error type for this library.
108#[derive(Debug, thiserror::Error)]
109pub enum Error {
110 /// An IO error.
111 #[error("IO error at {0:?}: {1}")]
112 Io(PathBuf, #[source] std::io::Error),
113 /// Parse error.
114 #[error("Parse error at {0:?}: {1}")]
115 Parse(PathBuf, #[source] Box<dyn std::error::Error + Send + Sync>),
116 /// Serde error.
117 #[error("Serde error at {0:?}: {1}")]
118 Serde(PathBuf, #[source] Box<dyn std::error::Error + Send + Sync>),
119}
120
121trait WrapIoError: Sized {
122 type Output;
123
124 fn wrap_io_error(self, get_path: impl FnOnce() -> PathBuf) -> Result<Self::Output>;
125
126 fn wrap_io_error_with(self, path: &Path) -> Result<Self::Output> {
127 self.wrap_io_error(|| path.to_path_buf())
128 }
129}
130
131impl<T> WrapIoError for std::io::Result<T> {
132 type Output = T;
133
134 fn wrap_io_error(self, get_path: impl FnOnce() -> PathBuf) -> Result<Self::Output> {
135 self.map_err(|e| Error::Io(get_path(), e))
136 }
137}
138
139pub type Result<T> = std::result::Result<T, Error>;
140
141/// The main trait. This is implemented for
142/// all directory structures by the derive macro.
143///
144/// This trait doesn't have any methods, just a supertype:
145/// [`DirStructureItem`].
146pub trait DirStructure: DirStructureItem {}
147
148/// Helper trait, implemented for all types that have a [`ReadFrom`]
149/// and [`WriteTo`] implementation.
150pub trait DirStructureItem: ReadFrom + WriteTo {
151 /// Uses the [`ReadFrom`] implementation to read the structure from
152 /// disk, from the specified path.
153 fn read(path: impl AsRef<Path>) -> Result<Self>
154 where
155 Self: Sized,
156 {
157 Self::read_from(path.as_ref())
158 }
159
160 /// Uses the [`WriteTo`] implementation to write the structure
161 /// to disk at the specified path.
162 fn write(&self, path: impl AsRef<Path>) -> Result<()> {
163 self.write_to(path.as_ref())
164 }
165}
166
167// Blanket impl.
168impl<T> DirStructureItem for T where T: ReadFrom + WriteTo {}
169
170/// Trait for types / structures that can be
171/// read from disk, either from a file or a directory.
172pub trait ReadFrom {
173 /// Reads the structure from the specified path, which
174 /// can be either a file or a directory.
175 fn read_from(path: &Path) -> Result<Self>
176 where
177 Self: Sized;
178}
179
180/// Trait for types / structures that can be
181/// written to disk. All types in the library that
182/// write to files first check that the parent
183/// directories exist, so implementations of
184/// this that create the whole directory are
185/// not necessary (unless used empty children
186/// directories, in which case no directories will
187/// really be created).
188pub trait WriteTo {
189 /// Writes the structure to the specified path.
190 fn write_to(&self, path: &Path) -> Result<()>;
191}
192
193/// Trait to use when using the `with_newtype` attribute.
194///
195/// This is used to convert a reference to a normal type
196/// (like `String`, `Vec<u8>` etc. into a type that is a
197/// reference to them, like `&str`, `&[u8]` etc.), so that
198/// the `WriteTo` implementation can be written only for the
199/// reference types, and all the other [`WriteTo`] impls will
200/// only cast what they have to write to those reference types
201/// (via the function below), and then call the [`WriteTo::write_to`]
202/// method on that reference.
203pub trait FromRefForWriter<'a> {
204 /// The inner type to cast.
205 type Inner: ?Sized;
206 /// The reference type to cast to.
207 type Wr: WriteTo + 'a;
208
209 /// Casts the reference to the inner type to a [`WriteTo`]
210 /// reference type.
211 fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr;
212}
213
214/// Trait to use when using the `with_newtype` attribute.
215///
216/// This is used to convert a newtype to its inner type.
217/// We are using this because we cannot make blanket impls with
218/// [`From`] due to the orphan rules.
219pub trait NewtypeToInner {
220 /// The inner type.
221 type Inner;
222
223 /// Converts the newtype to its inner type.
224 fn into_inner(self) -> Self::Inner;
225}
226
227/// A directory structure where we don't know the names of the folders at compile-time,
228/// and as such we cannot use the derive macro.
229///
230/// Instead we know that all the entries in the directory are folders,
231/// and that they all have the same structure inside (defined by the `T` type parameter),
232/// or they are all files (which can be read with [`DirChildren`]<[`String`]> for example).
233///
234/// In either case, [`ReadFrom::read_from`] must be able to read all the entries in
235/// the directory.
236///
237/// The [`WriteTo`] implementation will directly write the children to the directory it
238/// is passed, with no regards to the path stored in `self_path`.
239#[derive(Debug, PartialEq, Eq)]
240pub struct DirChildren<T, F: Filter = NoFilter>
241where
242 T: DirStructureItem,
243{
244 /// The path to the root directory.
245 ///
246 /// This path doesn't influence writing in any way, it is only to
247 /// point out the directory after it has been read and parsed.
248 pub self_path: PathBuf,
249 /// The children of the root directory.
250 pub children: Vec<DirChild<T>>,
251
252 filter: marker::PhantomData<F>,
253}
254
255impl<T, F> Clone for DirChildren<T, F>
256where
257 T: DirStructureItem + Clone,
258 F: Filter,
259{
260 fn clone(&self) -> Self {
261 Self {
262 self_path: self.self_path.clone(),
263 children: self.children.clone(),
264 filter: marker::PhantomData,
265 }
266 }
267}
268
269/// A filter for the children of a [`DirChildren`] structure.
270///
271/// This is used to filter out children that we don't want to
272/// read into the structure. For example, if we have a directory
273/// with a lot of files, we can use this to only read the
274/// files we want, for example, that have just a certain extension.
275///
276/// # Examples
277///
278/// For example, for a [`Filter`] that only allows `.txt` files:
279///
280/// ```rust
281/// use std::path::Path;
282/// use std::path::PathBuf;
283///
284/// use dir_structure::{DirStructure, DirStructureItem, DirChildren, Filter};
285///
286/// pub struct TextFileFilter;
287///
288/// impl Filter for TextFileFilter {
289/// fn make_filter() -> Self {
290/// Self
291/// }
292///
293/// fn allows(&self, path: &Path) -> bool {
294/// path.extension()
295/// .and_then(|s| s.to_str())
296/// .map_or(false, |s| s == "txt")
297/// }
298/// }
299///
300/// fn main() -> Result<(), Box<dyn std::error::Error>> {
301/// let path = PathBuf::from("dir");
302/// #[derive(DirStructure)]
303/// struct Dir {
304/// #[dir_structure(path = self)]
305/// text_files: DirChildren<String, TextFileFilter>,
306/// }
307///
308/// # std::fs::create_dir_all(&path)?;
309///
310/// std::fs::write(path.join("file1.txt"), "file1")?;
311/// std::fs::write(path.join("file2.txt"), "file2")?;
312/// std::fs::write(path.join("file3.bin"), "aaa")?;
313///
314/// let dir = Dir::read(&path)?;
315/// assert_eq!(dir.text_files.len(), 2);
316/// assert_eq!(dir.text_files.get_value_by_name("file1.txt"), Some(&String::from("file1")));
317/// assert_eq!(dir.text_files.get_value_by_name("file2.txt"), Some(&String::from("file2")));
318/// assert_eq!(dir.text_files.get_value_by_name("file3.bin"), None);
319///
320/// # std::fs::remove_dir_all(&path)?;
321///
322/// Ok(())
323/// }
324/// ```
325pub trait Filter {
326 /// Creates an instance of this filter.
327 fn make_filter() -> Self;
328 /// Checks if the path is allowed by this filter.
329 fn allows(&self, path: &Path) -> bool;
330}
331
332/// A [`Filter`] that allows all paths.
333///
334/// ```rust
335/// # use std::path::Path;
336/// # use dir_structure::{Filter, NoFilter};
337/// #
338/// let filter = NoFilter::make_filter();
339/// assert!(filter.allows(Path::new("foo.txt")));
340/// assert!(filter.allows(Path::new("foo/bar.txt")));
341/// assert!(filter.allows(Path::new("foo/bar/baz.txt")));
342/// assert!(filter.allows(Path::new("foo/bar/baz")));
343/// assert!(filter.allows(Path::new("foo/bar/baz/")));
344/// assert!(filter.allows(Path::new("foo/bar/baz/.")));
345/// assert!(filter.allows(Path::new("foo/bar/baz/..")));
346/// assert!(filter.allows(Path::new("foo/bar/baz/../..")));
347/// assert!(filter.allows(Path::new("foo/bar/baz/../../..")));
348/// assert!(filter.allows(Path::new("foo/bar/baz/../../../..")));
349/// assert!(filter.allows(Path::new("foo/bar/baz/../../../../..")));
350/// assert!(filter.allows(Path::new("foo/bar/baz/../../../../../..")));
351/// ```
352#[derive(Debug, Clone, Copy, PartialEq, Eq)]
353pub struct NoFilter;
354
355impl Filter for NoFilter {
356 fn make_filter() -> Self {
357 Self
358 }
359
360 fn allows(&self, _path: &Path) -> bool {
361 true
362 }
363}
364
365impl<T> Default for DirChildren<T>
366where
367 T: DirStructureItem,
368{
369 fn default() -> Self {
370 Self::new()
371 }
372}
373
374impl<T, F> DirChildren<T, F>
375where
376 T: DirStructureItem,
377 F: Filter,
378{
379 /// Creates an empty [`DirChildren`], with no children.
380 pub fn new() -> Self {
381 Self {
382 self_path: PathBuf::new(),
383 children: Vec::new(),
384 filter: marker::PhantomData,
385 }
386 }
387
388 /// Creates a [`DirChildren`] with the given path and children.
389 pub fn with_children_from_iter(
390 self_path: impl Into<PathBuf>,
391 children: impl IntoIterator<Item = DirChild<T>>,
392 ) -> Self {
393 Self {
394 self_path: self_path.into(),
395 children: children.into_iter().collect(),
396 filter: marker::PhantomData,
397 }
398 }
399
400 /// Maps the children of this [`DirChildren`] to a new type.
401 ///
402 /// This is useful for converting the children to a different type,
403 /// for example, if you want to convert the children to a different
404 /// type of [`DirStructureItem`].
405 ///
406 /// This is a convenience method that allows you to use the
407 /// `map` method on the children of this [`DirChildren`].
408 ///
409 /// # Examples
410 ///
411 /// ```rust
412 /// use std::path::Path;
413 /// use std::path::PathBuf;
414 /// use dir_structure::{DirStructure, DirStructureItem, DirChildren, DirChild, ReadFrom, WriteTo};
415 ///
416 /// #[derive(Debug, PartialEq, Eq)]
417 /// struct NewType(String);
418 ///
419 /// impl ReadFrom for NewType {
420 /// fn read_from(path: &Path) -> dir_structure::Result<Self> {
421 /// String::read_from(path).map(Self)
422 /// }
423 /// }
424 ///
425 /// impl WriteTo for NewType {
426 /// fn write_to(&self, path: &Path) -> dir_structure::Result<()> {
427 /// self.0.write_to(path)
428 /// }
429 /// }
430 ///
431 /// let d = PathBuf::from("dir");
432 /// let dir = DirChildren::<_, dir_structure::NoFilter>::with_children_from_iter(
433 /// d.clone(),
434 /// vec![
435 /// DirChild::new("file1.txt", "file1".to_owned()),
436 /// DirChild::new("file2.txt", "file2".to_owned()),
437 /// DirChild::new("file3.txt", "file3".to_owned()),
438 /// ],
439 /// );
440 /// let dir = dir.map(|child| child.map_value(NewType));
441 /// assert_eq!(
442 /// dir,
443 /// DirChildren::with_children_from_iter(
444 /// d.clone(),
445 /// vec![
446 /// DirChild::new("file1.txt", NewType("file1".to_owned())),
447 /// DirChild::new("file2.txt", NewType("file2".to_owned())),
448 /// DirChild::new("file3.txt", NewType("file3".to_owned())),
449 /// ],
450 /// )
451 /// );
452 /// ```
453 pub fn map<U, MapF>(self, f: MapF) -> DirChildren<U, F>
454 where
455 MapF: FnMut(DirChild<T>) -> DirChild<U>,
456 U: DirStructureItem,
457 {
458 let children = self.children.into_iter().map(f).collect();
459 DirChildren {
460 self_path: self.self_path,
461 children,
462 filter: marker::PhantomData,
463 }
464 }
465
466 /// Maps the filter type. The children remain unchanged.
467 ///
468 /// This is useful if you are trying to pass a DirChildren<T, F1> to
469 /// a function requiring a DirChildren<T, F2>, where F1 and F2 are two
470 /// distinct types implementing [`Filter`].
471 ///
472 /// # Examples
473 ///
474 /// ```rust
475 /// use std::path::Path;
476 /// use dir_structure::{Filter, DirChildren};
477 ///
478 /// struct NewFilter;
479 ///
480 /// impl Filter for NewFilter {
481 /// fn make_filter() -> Self {
482 /// Self
483 /// }
484 ///
485 /// fn allows(&self, _path: &Path) -> bool {
486 /// true
487 /// }
488 /// }
489 ///
490 /// let d = DirChildren::<String, dir_structure::NoFilter>::new();
491 /// let d2: DirChildren<String, NewFilter> = d.map_filter::<NewFilter>();
492 /// ```
493 pub fn map_filter<NewF: Filter>(self) -> DirChildren<T, NewF>
494 where
495 NewF: Filter,
496 {
497 DirChildren {
498 self_path: self.self_path,
499 children: self.children,
500 filter: marker::PhantomData,
501 }
502 }
503
504 /// Returns the number of children.
505 ///
506 /// # Examples
507 ///
508 /// ```rust
509 /// use std::path::{Path, PathBuf};
510 /// use dir_structure::{DirStructure, DirStructureItem, DirChildren, DirChild};
511 ///
512 /// let d = DirChildren::<String, dir_structure::NoFilter>::new();
513 /// assert_eq!(d.len(), 0);
514 ///
515 /// let d = DirChildren::<String, dir_structure::NoFilter>::with_children_from_iter(
516 /// PathBuf::new(),
517 /// vec![
518 /// DirChild::new("file1.txt", "file1".to_owned()),
519 /// DirChild::new("file2.txt", "file2".to_owned()),
520 /// ],
521 /// );
522 /// assert_eq!(d.len(), 2);
523 /// ```
524 pub fn len(&self) -> usize {
525 self.children.len()
526 }
527
528 /// Gets the child at the specified index.
529 ///
530 /// # Examples
531 ///
532 /// ```rust
533 /// use std::path::{Path, PathBuf};
534 /// use dir_structure::{DirStructure, DirStructureItem, DirChildren, DirChild};
535 ///
536 /// let d = DirChildren::<String, dir_structure::NoFilter>::new();
537 /// assert_eq!(d.get(0), None);
538 /// assert_eq!(d.get(1), None);
539 /// assert_eq!(d.get(100), None);
540 ///
541 /// let d = DirChildren::<String, dir_structure::NoFilter>::with_children_from_iter(
542 /// PathBuf::new(),
543 /// vec![
544 /// DirChild::new("file1.txt", "file1".to_owned()),
545 /// DirChild::new("file2.txt", "file2".to_owned()),
546 /// ],
547 /// );
548 /// assert_eq!(d.get(0), Some(&DirChild::new("file1.txt", "file1".to_owned())));
549 /// assert_eq!(d.get(1), Some(&DirChild::new("file2.txt", "file2".to_owned())));
550 /// assert_eq!(d.get(2), None);
551 /// assert_eq!(d.get(100), None);
552 /// ```
553 pub fn get(&self, index: usize) -> Option<&DirChild<T>> {
554 self.children.get(index)
555 }
556
557 /// Gets the child with the specified "file" name (last segment of path).
558 ///
559 /// # Examples
560 ///
561 /// ```rust
562 /// use std::path::{Path, PathBuf};
563 /// use dir_structure::{DirStructure, DirStructureItem, DirChildren, DirChild};
564 ///
565 /// let d = DirChildren::<String, dir_structure::NoFilter>::new();
566 /// assert_eq!(d.get_name(""), None);
567 /// assert_eq!(d.get_name("any_name"), None);
568 /// assert_eq!(d.get_name("aaaa"), None);
569 ///
570 /// let d = DirChildren::<String, dir_structure::NoFilter>::with_children_from_iter(
571 /// PathBuf::new(),
572 /// vec![
573 /// DirChild::new("file1.txt", "file1".to_owned()),
574 /// DirChild::new("file2.txt", "file2".to_owned()),
575 /// ],
576 /// );
577 /// assert_eq!(d.get_name("file1.txt"), Some(&DirChild::new("file1.txt", "file1".to_owned())));
578 /// assert_eq!(d.get_name("file2.txt"), Some(&DirChild::new("file2.txt", "file2".to_owned())));
579 /// assert_eq!(d.get_name("any_name"), None);
580 /// assert_eq!(d.get_name("aaaa"), None);
581 /// ```
582 pub fn get_name(&self, name: impl AsRef<OsStr>) -> Option<&DirChild<T>> {
583 self.children
584 .iter()
585 .find(|child| child.file_name == name.as_ref())
586 }
587
588 /// Gets the value of the child with the specified "file" name (last segment of path).
589 ///
590 /// # Examples
591 ///
592 /// ```rust
593 /// use std::path::{Path, PathBuf};
594 /// use dir_structure::{DirStructure, DirStructureItem, DirChildren, DirChild};
595 ///
596 /// let d = DirChildren::<String, dir_structure::NoFilter>::new();
597 /// assert_eq!(d.get_value_by_name(""), None);
598 /// assert_eq!(d.get_value_by_name("any_name"), None);
599 /// assert_eq!(d.get_value_by_name("aaaa"), None);
600 ///
601 /// let d = DirChildren::<String, dir_structure::NoFilter>::with_children_from_iter(
602 /// PathBuf::new(),
603 /// vec![
604 /// DirChild::new("file1.txt", "file1".to_owned()),
605 /// DirChild::new("file2.txt", "file2".to_owned()),
606 /// ],
607 /// );
608 /// assert_eq!(d.get_value_by_name("file1.txt"), Some(&"file1".to_owned()));
609 /// assert_eq!(d.get_value_by_name("file2.txt"), Some(&"file2".to_owned()));
610 /// assert_eq!(d.get_value_by_name("any_name"), None);
611 /// assert_eq!(d.get_value_by_name("aaaa"), None);
612 /// ```
613 pub fn get_value_by_name(&self, name: impl AsRef<OsStr>) -> Option<&T> {
614 self.get_name(name).map(|child| &child.value)
615 }
616
617 /// Returns an iterator over the children.
618 ///
619 /// # Examples
620 ///
621 /// ```rust
622 /// use std::path::{Path, PathBuf};
623 /// use dir_structure::{DirStructure, DirStructureItem, DirChildren, DirChild};
624 ///
625 /// let d = DirChildren::<String, dir_structure::NoFilter>::new();
626 /// let mut i = d.iter();
627 /// assert_eq!(i.next(), None);
628 ///
629 /// let d = DirChildren::<String, dir_structure::NoFilter>::with_children_from_iter(
630 /// PathBuf::new(),
631 /// vec![
632 /// DirChild::new("file1.txt", "file1".to_owned()),
633 /// DirChild::new("file2.txt", "file2".to_owned()),
634 /// ],
635 /// );
636 /// let mut i = d.iter();
637 /// assert_eq!(i.next(), Some(&DirChild::new("file1.txt", "file1".to_owned())));
638 /// assert_eq!(i.next(), Some(&DirChild::new("file2.txt", "file2".to_owned())));
639 /// assert_eq!(i.next(), None);
640 /// ```
641 pub fn iter(&self) -> DirChildrenIter<'_, T> {
642 DirChildrenIter(self.children.iter())
643 }
644}
645
646impl<T, F> ReadFrom for DirChildren<T, F>
647where
648 T: DirStructureItem,
649 F: Filter,
650{
651 fn read_from(path: &Path) -> Result<Self>
652 where
653 Self: Sized,
654 {
655 let filter = F::make_filter();
656
657 let mut children = Vec::new();
658 for child in path.read_dir().wrap_io_error_with(path)? {
659 let child = child.wrap_io_error_with(path)?;
660 let child_path = child.path();
661
662 if !filter.allows(&child_path) {
663 continue;
664 }
665
666 let value = T::read_from(&child_path)?;
667 let file_name = child.file_name();
668 children.push(DirChild { file_name, value });
669 }
670
671 Ok(DirChildren {
672 self_path: path.to_path_buf(),
673 children,
674 filter: marker::PhantomData,
675 })
676 }
677}
678
679impl<T, F> WriteTo for DirChildren<T, F>
680where
681 T: DirStructureItem,
682 F: Filter,
683{
684 fn write_to(&self, path: &Path) -> Result<()> {
685 for child in &self.children {
686 let child_path = path.join(&child.file_name);
687 child.value.write_to(&child_path)?;
688 }
689
690 Ok(())
691 }
692}
693
694/// A single child of a [`DirChildren`] structure.
695#[derive(Debug, Clone, PartialEq, Eq)]
696pub struct DirChild<T>
697where
698 T: DirStructureItem,
699{
700 /// The file name of the child.
701 file_name: OsString,
702 /// The parsed value of the child.
703 value: T,
704}
705
706impl<T> DirChild<T>
707where
708 T: DirStructureItem,
709{
710 /// Creates a new [`DirChild`] with the specified file name and value.
711 ///
712 /// # Examples
713 ///
714 /// ```rust
715 /// use std::ffi::OsString;
716 /// use dir_structure::DirChild;
717 ///
718 /// let d = DirChild::new("file.txt", "file".to_owned());
719 /// assert_eq!(d.file_name(), &OsString::from("file.txt"));
720 /// assert_eq!(d.value(), &"file".to_owned());
721 /// ```
722 pub fn new(file_name: impl Into<OsString>, value: T) -> Self {
723 Self {
724 file_name: file_name.into(),
725 value,
726 }
727 }
728
729 /// Gets the file name of the child (or the name of the directory; the last segment in the path).
730 ///
731 /// # Examples
732 ///
733 /// ```rust
734 /// use std::ffi::OsString;
735 /// use dir_structure::DirChild;
736 ///
737 /// let d = DirChild::new("file.txt", "file".to_owned());
738 /// assert_eq!(d.file_name(), &OsString::from("file.txt"));
739 /// ```
740 pub fn file_name(&self) -> &OsString {
741 &self.file_name
742 }
743
744 /// Gets the file name of the child (or the name of the directory; the last segment in the path).
745 ///
746 /// Mutable reference version of [`Self::file_name`].
747 ///
748 /// # Examples
749 ///
750 /// ```rust
751 /// use std::ffi::OsString;
752 /// use dir_structure::DirChild;
753 ///
754 /// let mut d = DirChild::new("file.txt", "file".to_owned());
755 /// assert_eq!(d.file_name(), &OsString::from("file.txt"));
756 /// *d.file_name_mut() = OsString::from("new_file.txt");
757 /// assert_eq!(d.file_name(), &OsString::from("new_file.txt"));
758 /// ```
759 pub fn file_name_mut(&mut self) -> &mut OsString {
760 &mut self.file_name
761 }
762
763 /// Gets the value of the child.
764 ///
765 /// This is the parsed value of the file / directory.
766 ///
767 /// # Examples
768 ///
769 /// ```rust
770 /// use std::ffi::OsString;
771 /// use dir_structure::DirChild;
772 ///
773 /// let d = DirChild::new("file.txt", "file".to_owned());
774 /// assert_eq!(d.value(), &"file".to_owned());
775 /// ```
776 pub fn value(&self) -> &T {
777 &self.value
778 }
779
780 /// Gets the value of the child.
781 ///
782 /// This is the parsed value of the file / directory.
783 ///
784 /// Mutable reference version of [`Self::value`].
785 ///
786 /// # Examples
787 ///
788 /// ```rust
789 /// use std::ffi::OsString;
790 /// use dir_structure::DirChild;
791 ///
792 /// let mut d = DirChild::new("file.txt", "file".to_owned());
793 /// assert_eq!(d.value(), &"file".to_owned());
794 /// *d.value_mut() = "new_file".to_owned();
795 /// assert_eq!(d.value(), &"new_file".to_owned());
796 /// ```
797 pub fn value_mut(&mut self) -> &mut T {
798 &mut self.value
799 }
800
801 /// Maps the file name of this [`DirChild`] to a new value.
802 ///
803 /// # Examples
804 ///
805 /// ```rust
806 /// use std::ffi::OsString;
807 /// use dir_structure::DirChild;
808 ///
809 /// let d = DirChild::new("file.txt", "file".to_owned());
810 /// assert_eq!(d.map_file_name(|s| s.to_str().unwrap().to_uppercase()), DirChild::new("FILE.TXT", "file".to_owned()));
811 /// ```
812 pub fn map_file_name<F, O>(self, f: F) -> Self
813 where
814 F: FnOnce(OsString) -> O,
815 O: Into<OsString>,
816 {
817 let file_name = f(self.file_name).into();
818 DirChild {
819 file_name,
820 value: self.value,
821 }
822 }
823
824 /// Maps the value of this [`DirChild`] to a new type.
825 ///
826 /// This is useful for converting the value to a different type,
827 /// for example, if you want to convert the value to a different
828 /// type of [`DirStructureItem`].
829 ///
830 /// # Examples
831 ///
832 /// ```rust
833 /// use std::ffi::OsString;
834 /// use dir_structure::DirChild;
835 /// use dir_structure::FileString;
836 ///
837 /// let d = DirChild::new("file.txt", "file".to_owned());
838 /// assert_eq!(d.map_value(|v| FileString(v)), DirChild::new("file.txt", FileString("file".to_owned())));
839 /// ```
840 pub fn map_value<U, F>(self, f: F) -> DirChild<U>
841 where
842 F: FnOnce(T) -> U,
843 U: DirStructureItem,
844 {
845 let value = f(self.value);
846 DirChild {
847 file_name: self.file_name,
848 value,
849 }
850 }
851}
852
853impl<T> IntoIterator for DirChildren<T>
854where
855 T: DirStructureItem,
856{
857 type Item = DirChild<T>;
858 type IntoIter = std::vec::IntoIter<Self::Item>;
859
860 fn into_iter(self) -> Self::IntoIter {
861 self.children.into_iter()
862 }
863}
864
865/// A [`DirChildren`] iterator. It iterates over the children of a
866/// [`DirChildren`] structure.
867pub struct DirChildrenIter<'a, T: DirStructureItem>(std::slice::Iter<'a, DirChild<T>>);
868
869impl<'a, T> Iterator for DirChildrenIter<'a, T>
870where
871 T: DirStructureItem,
872{
873 type Item = &'a DirChild<T>;
874
875 fn next(&mut self) -> Option<Self::Item> {
876 self.0.next()
877 }
878
879 fn size_hint(&self) -> (usize, Option<usize>) {
880 self.0.size_hint()
881 }
882}
883
884impl<T> ExactSizeIterator for DirChildrenIter<'_, T>
885where
886 T: DirStructureItem,
887{
888 fn len(&self) -> usize {
889 self.0.len()
890 }
891}
892
893impl<T> DoubleEndedIterator for DirChildrenIter<'_, T>
894where
895 T: DirStructureItem,
896{
897 fn next_back(&mut self) -> Option<Self::Item> {
898 self.0.next_back()
899 }
900}
901
902/// A simple macro that generates a [`DirChildren`] newtype, together with
903/// a few impls to make it easy to use.
904#[macro_export]
905macro_rules! dir_children_wrapper {
906 ($vis:vis $name:ident $ty:ty) => {
907 $vis struct $name(pub $crate::DirChildren<$ty>);
908
909 impl $crate::ReadFrom for $name {
910 fn read_from(path: &::std::path::Path) -> $crate::Result<Self>
911 where
912 Self: Sized,
913 {
914 Ok(Self(<$crate::DirChildren<$ty>>::read_from(path)?))
915 }
916 }
917
918 impl $crate::WriteTo for $name {
919 fn write_to(&self, path: &::std::path::Path) -> $crate::Result<()> {
920 self.0.write_to(path)
921 }
922 }
923
924 impl std::ops::Deref for $name {
925 type Target = $crate::DirChildren<$ty>;
926
927 fn deref(&self) -> &Self::Target {
928 &self.0
929 }
930 }
931
932 impl std::ops::DerefMut for $name {
933 fn deref_mut(&mut self) -> &mut Self::Target {
934 &mut self.0
935 }
936 }
937
938 impl std::iter::IntoIterator for $name {
939 type Item = $crate::DirChild<$ty>;
940 type IntoIter = std::vec::IntoIter<Self::Item>;
941
942 fn into_iter(self) -> Self::IntoIter {
943 self.0.into_iter()
944 }
945 }
946 };
947}
948
949pub use dir_structure_macros::DirStructure;
950
951macro_rules! data_format_impl {
952 (
953 $(#[$mod_attr:meta])*
954 $mod_name:ident,
955 $(#[$main_ty_attrs:meta])*
956 $main_ty:ident,
957
958 $from_str_impl:expr,
959 $from_str_error:ty,
960
961 $(#[$to_str_ty_attrs:meta])*
962 $to_str_ty:ident,
963 $to_str_impl:expr,
964 $to_writer_impl:expr,
965 $to_str_error:ty,
966
967 $(#[$writer_ty_attrs:meta])*
968 $writer_ty:ident,
969
970 $extension:literal,
971 $text:literal $(,)?
972 ) => {
973 $(#[$mod_attr])*
974 pub mod $mod_name {
975 #![doc = concat!(r##"
976With the `"##, stringify!($mod_name), r##"` feature, this module provides the [`"##, stringify!($main_ty), r##"`] type,
977
978This allows us to read and parse `"##, stringify!($mod_name), r##"` files to some `serde::Deserialize` type,
979and write them back to disk.
980
981# Examples
982
983## Reading a "##, stringify!($mod_name), r##" file
984
985```
986use std::path::Path;
987
988use dir_structure::DirStructureItem;
989use dir_structure::"##, stringify!($mod_name), "::", stringify!($main_ty), r##";
990
991#[derive(dir_structure::DirStructure)]
992struct Dir {
993 #[dir_structure(path = "f"##, $extension, r##"", with_newtype = "##, stringify!($main_ty), r##"<Obj>)]
994 f: Obj,
995}
996
997#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
998struct Obj {
999 name: String,
1000 age: u32,
1001}
1002
1003fn main() -> Result<(), Box<dyn std::error::Error>> {
1004 let d = Path::new("dir");
1005 std::fs::create_dir_all(&d)?;
1006 std::fs::write(d.join("f"##, $extension, r##""), "##, $text, r##")?;
1007 let dir = Dir::read(&d)?;
1008 assert_eq!(dir.f, Obj { name: "John".to_owned(), age: 30 });
1009 # std::fs::remove_dir_all(&d)?;
1010 Ok(())
1011}
1012```
1013
1014## Writing a "##, stringify!($mod_name), r##" file
1015
1016```
1017use std::path::Path;
1018
1019use dir_structure::DirStructureItem;
1020use dir_structure::"##, stringify!($mod_name), "::", stringify!($main_ty), r##";
1021
1022#[derive(dir_structure::DirStructure)]
1023struct Dir {
1024 #[dir_structure(path = "f"##, $extension, r##"", with_newtype = "##, stringify!($main_ty), r##"<Obj>)]
1025 f: Obj,
1026}
1027
1028#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1029struct Obj {
1030 name: String,
1031 age: u32,
1032}
1033
1034fn main() -> Result<(), Box<dyn std::error::Error>> {
1035 let d = Path::new("dir");
1036 let dir = Dir {
1037 f: Obj {
1038 name: "John".to_owned(),
1039 age: 30,
1040 },
1041 };
1042 dir.write(&d)?;
1043 assert_eq!(std::fs::read_to_string(d.join("f"##, $extension, r##""))?,
1044 "##, $text, r##"
1045 );
1046 # std::fs::remove_dir_all(&d)?;
1047 Ok(())
1048}
1049```
1050"##)]
1051
1052 use std::fmt;
1053 use std::fmt::Formatter;
1054 use std::path::Path;
1055 use std::str::FromStr;
1056
1057 use crate::FromRefForWriter;
1058 use crate::NewtypeToInner;
1059 use crate::ReadFrom;
1060 use crate::WriteTo;
1061
1062 $(#[$main_ty_attrs])*
1063 #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash)]
1064 #[serde(transparent)]
1065 pub struct $main_ty<T>(#[serde(bound = "")] pub T)
1066 where
1067 T: 'static + serde::Serialize + for<'d> serde::Deserialize<'d>;
1068
1069 impl<T> FromStr for $main_ty<T>
1070 where
1071 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
1072 {
1073 type Err = $from_str_error;
1074
1075 fn from_str(s: &str) -> Result<Self, Self::Err> {
1076 $from_str_impl(s).map(Self)
1077 }
1078 }
1079
1080 $(#[$to_str_ty_attrs])*
1081 struct $to_str_ty<'a, T>(&'a T)
1082 where
1083 T: serde::Serialize + 'a;
1084
1085 impl<'a, T> $to_str_ty<'a, T>
1086 where
1087 T: serde::Serialize + 'a
1088 {
1089 fn to_str(&self) -> Result<String, $to_str_error> {
1090 $to_str_impl(&self.0)
1091 }
1092
1093 fn to_writer<W>(&self, writer: &mut W) -> Result<(), ToWriterError>
1094 where
1095 W: std::io::Write,
1096 {
1097 $to_writer_impl(&self.0, writer)
1098 }
1099 }
1100
1101 enum ToWriterError {
1102 #[allow(unused)]
1103 Io(std::io::Error),
1104 Serde($to_str_error),
1105 }
1106
1107 impl<'a, T> fmt::Display for $to_str_ty<'a, T>
1108 where
1109 T: serde::Serialize + 'a,
1110 {
1111 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1112 let s = self.to_str().map_err(|_| fmt::Error)?;
1113 write!(f, "{}", s)
1114 }
1115 }
1116
1117 impl<T> fmt::Display for $main_ty<T>
1118 where
1119 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
1120 {
1121 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1122 $to_str_ty(&self.0).fmt(f)
1123 }
1124 }
1125
1126 impl<T> ReadFrom for $main_ty<T>
1127 where
1128 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
1129 {
1130 fn read_from(path: &Path) -> crate::Result<Self> {
1131 let contents = crate::FileString::read_from(path)?.0;
1132 let v = contents
1133 .parse::<$main_ty<T>>()
1134 .map_err(|e| crate::Error::Parse(path.to_path_buf(), e.into()))?;
1135 Ok(v)
1136 }
1137 }
1138
1139 impl<T> WriteTo for $main_ty<T>
1140 where
1141 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
1142 {
1143 fn write_to(&self, path: &Path) -> crate::Result<()> {
1144 Self::from_ref_for_writer(&self.0).write_to(path)
1145 }
1146 }
1147
1148 impl<T> NewtypeToInner for $main_ty<T>
1149 where
1150 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
1151 {
1152 type Inner = T;
1153
1154 fn into_inner(self) -> Self::Inner {
1155 self.0
1156 }
1157 }
1158
1159 impl<'a, T> FromRefForWriter<'a> for $main_ty<T>
1160 where
1161 T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
1162 {
1163 type Inner = T;
1164 type Wr = $writer_ty<'a, T>;
1165
1166 fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
1167 $writer_ty(value)
1168 }
1169 }
1170
1171 $(#[$writer_ty_attrs])*
1172 pub struct $writer_ty<'a, T>(&'a T)
1173 where
1174 T: serde::Serialize + 'a;
1175
1176 impl<'a, T> WriteTo for $writer_ty<'a, T>
1177 where
1178 T: serde::Serialize + 'a,
1179 {
1180 fn write_to(&self, path: &Path) -> crate::Result<()> {
1181 let mut f = crate::StreamingFileWriter::new(path)?;
1182 $to_str_ty(self.0).to_writer(&mut f)
1183 .map_err(|e| match e {
1184 ToWriterError::Io(e) => crate::Error::Io(path.to_path_buf(), e),
1185 ToWriterError::Serde(e) => crate::Error::Serde(path.to_path_buf(), e.into()),
1186 })?;
1187
1188 Ok(())
1189 }
1190 }
1191 }
1192 };
1193}
1194
1195data_format_impl!(
1196 #[cfg(feature = "json")]
1197 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
1198 json,
1199 /// A wrapper around a type that implements [`serde::Serialize`] and [`serde::Deserialize`],
1200 /// thus allowing us to parse and serialize it from / to json when we read / write a
1201 /// directory structure.
1202 Json,
1203 |s| serde_json::from_str(s),
1204 serde_json::Error,
1205 JsonToStr,
1206 |v| serde_json::to_string(&v),
1207 |v, w| serde_json::to_writer(w, v).map_err(ToWriterError::Serde),
1208 serde_json::Error,
1209 /// [`FromRefForWriter`] implementation for [`Json`].
1210 JsonRefWr,
1211 ".json", r##"r#"{"name":"John","age":30}"#"##,
1212);
1213
1214data_format_impl!(
1215 #[cfg(feature = "toml")]
1216 #[cfg_attr(docsrs, doc(cfg(feature = "toml")))]
1217 toml,
1218 /// A wrapper around a type that implements [`serde::Serialize`] and [`serde::Deserialize`],
1219 /// thus allowing us to parse and serialize it from / to toml when we read / write a
1220 /// directory structure.
1221 Toml,
1222 |s| toml::de::from_str(s),
1223 toml::de::Error,
1224 TomlToStr,
1225 |v| toml::ser::to_string(&v),
1226 |v, w: &mut dyn std::io::Write| {
1227 let s = toml::ser::to_string(&v).map_err(ToWriterError::Serde)?;
1228 w.write_all(s.as_bytes()).map_err(ToWriterError::Io)?;
1229 Ok(())
1230 },
1231 toml::ser::Error,
1232 /// [`FromRefForWriter`] implementation for [`Toml`].
1233 TomlRefWr,
1234 ".toml", r##"r#"
1235name = "John"
1236age = 30
1237"#.trim_start()"##,
1238);
1239
1240data_format_impl!(
1241 #[cfg(feature = "yaml")]
1242 #[cfg_attr(docsrs, doc(cfg(feature = "yaml")))]
1243 yaml,
1244 /// A wrapper around a type that implements [`serde::Serialize`] and [`serde::Deserialize`],
1245 /// thus allowing us to parse and serialize it from / to yaml when we read / write a
1246 /// directory structure.
1247 Yaml,
1248 |s| serde_yaml::from_str(s),
1249 serde_yaml::Error,
1250 YamlToStr,
1251 |v| serde_yaml::to_string(&v),
1252 |v, w| serde_yaml::to_writer(w, v).map_err(ToWriterError::Serde),
1253 serde_yaml::Error,
1254 /// [`FromRefForWriter`] implementation for [`Yaml`].
1255 YamlRefWr,
1256 ".yaml", r##"r#"
1257name: John
1258age: 30
1259"#.trim_start()"##,
1260);
1261
1262data_format_impl!(
1263 #[cfg(feature = "ron")]
1264 #[cfg_attr(docsrs, doc(cfg(feature = "ron")))]
1265 ron,
1266 /// A wrapper around a type that implements [`serde::Serialize`] and [`serde::Deserialize`],
1267 /// thus allowing us to parse and serialize it from / to ron when we read / write a
1268 /// directory structure.
1269 Ron,
1270 |s| ron::de::from_str(s),
1271 ron::error::SpannedError,
1272 RonToStr,
1273 |v| ron::ser::to_string(&v),
1274 |v, w| ron::ser::to_writer(w, v).map_err(ToWriterError::Serde),
1275 ron::error::Error,
1276 /// [`FromRefForWriter`] implementation for [`Ron`].
1277 RonRefWr,
1278 ".ron", r##"r#"(name:"John",age:30)"#"##,
1279);
1280
1281/// A wrapper around a type which will use the [`Display`] and [`FromStr`] implementations
1282/// for serialization / deserialization.
1283///
1284/// For example: u8, i8, i16, u16, all integer types... bool etc.
1285///
1286/// # Examples
1287///
1288/// ```rust
1289/// use std::path::Path;
1290/// use dir_structure::DirStructureItem;
1291///
1292/// use dir_structure::FmtWrapper;
1293///
1294/// #[derive(dir_structure::DirStructure, PartialEq, Debug)]
1295/// struct Dir {
1296/// #[dir_structure(path = "f.txt", with_newtype = FmtWrapper<u8>)]
1297/// f: u8,
1298/// #[dir_structure(path = "b.txt", with_newtype = FmtWrapper<bool>)]
1299/// b: bool,
1300/// }
1301///
1302/// fn main() -> Result<(), Box<dyn std::error::Error>> {
1303/// let d = Path::new("dir");
1304/// std::fs::create_dir_all(&d)?;
1305/// std::fs::write(d.join("f.txt"), "42")?;
1306/// std::fs::write(d.join("b.txt"), "true")?;
1307/// let mut dir = Dir::read(&d)?;
1308/// assert_eq!(dir.f, 42);
1309/// assert_eq!(dir.b, true);
1310/// dir.f = 100;
1311/// dir.b = false;
1312/// dir.write(&d)?;
1313/// assert_eq!(std::fs::read_to_string(d.join("f.txt"))?, "100");
1314/// assert_eq!(std::fs::read_to_string(d.join("b.txt"))?, "false");
1315/// # std::fs::remove_dir_all(&d)?;
1316/// Ok(())
1317/// }
1318/// ```
1319pub struct FmtWrapper<T>(pub T);
1320
1321impl<T> NewtypeToInner for FmtWrapper<T> {
1322 type Inner = T;
1323
1324 fn into_inner(self) -> Self::Inner {
1325 self.0
1326 }
1327}
1328
1329impl<T> ReadFrom for FmtWrapper<T>
1330where
1331 T: FromStr,
1332 T::Err: Into<Box<dyn std::error::Error + Send + Sync>>,
1333{
1334 fn read_from(path: &Path) -> Result<Self>
1335 where
1336 Self: Sized,
1337 {
1338 let contents = FileString::read_from(path)?.0;
1339 match contents.parse::<T>() {
1340 Ok(v) => Ok(Self(v)),
1341 Err(e) => Err(Error::Parse(path.to_path_buf(), e.into())),
1342 }
1343 }
1344}
1345
1346impl<T> WriteTo for FmtWrapper<T>
1347where
1348 T: Display,
1349{
1350 fn write_to(&self, path: &Path) -> Result<()> {
1351 Self::from_ref_for_writer(&self.0).write_to(path)
1352 }
1353}
1354
1355impl<'a, T> FromRefForWriter<'a> for FmtWrapper<T>
1356where
1357 T: Display + 'a,
1358{
1359 type Inner = T;
1360 type Wr = FmtWrapperRefWr<'a, T>;
1361
1362 fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
1363 FmtWrapperRefWr(value)
1364 }
1365}
1366
1367/// A [`WriteTo`] wrapper around a reference to a type which will use the [`Display`]
1368/// implementation to write the value.
1369pub struct FmtWrapperRefWr<'a, T: ?Sized>(pub &'a T);
1370
1371impl<T> WriteTo for FmtWrapperRefWr<'_, T>
1372where
1373 T: Display + ?Sized,
1374{
1375 fn write_to(&self, path: &Path) -> Result<()> {
1376 use std::io::Write;
1377 utils::create_parent_dir(path)?;
1378 let mut f = File::create(path).wrap_io_error_with(path)?;
1379 write!(f, "{}", self.0).wrap_io_error_with(path)?;
1380 Ok(())
1381 }
1382}
1383
1384/// A newtype around a `Vec<u8>`.
1385#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1386pub struct FileBytes(pub Vec<u8>);
1387
1388impl FileBytes {
1389 /// Creates a new [`FileBytes`] from the specified `Vec<u8>`.
1390 pub fn new(v: impl Into<Vec<u8>>) -> Self {
1391 Self(v.into())
1392 }
1393}
1394
1395impl ReadFrom for FileBytes {
1396 fn read_from(path: &Path) -> Result<Self>
1397 where
1398 Self: Sized,
1399 {
1400 std::fs::read(path).wrap_io_error_with(path).map(Self)
1401 }
1402}
1403
1404impl WriteTo for FileBytes {
1405 fn write_to(&self, path: &Path) -> Result<()> {
1406 Self::from_ref_for_writer(&self.0).write_to(path)
1407 }
1408}
1409
1410impl From<FileBytes> for Vec<u8> {
1411 fn from(value: FileBytes) -> Self {
1412 value.0
1413 }
1414}
1415
1416impl From<Vec<u8>> for FileBytes {
1417 fn from(value: Vec<u8>) -> Self {
1418 Self(value)
1419 }
1420}
1421
1422impl NewtypeToInner for FileBytes {
1423 type Inner = Vec<u8>;
1424
1425 fn into_inner(self) -> Self::Inner {
1426 self.0
1427 }
1428}
1429
1430impl<'a> FromRefForWriter<'a> for FileBytes {
1431 type Inner = [u8];
1432 type Wr = FileBytesRefWr<'a>;
1433
1434 fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
1435 FileBytesRefWr(value)
1436 }
1437}
1438
1439/// The [`WriteTo`] wrapper around a reference to a `[u8]`.
1440pub struct FileBytesRefWr<'a>(&'a [u8]);
1441
1442impl WriteTo for FileBytesRefWr<'_> {
1443 fn write_to(&self, path: &Path) -> Result<()> {
1444 utils::create_parent_dir(path)?;
1445 std::fs::write(path, self.0).wrap_io_error_with(path)?;
1446 Ok(())
1447 }
1448}
1449
1450/// A newtype around a [`String`].
1451#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1452pub struct FileString(pub String);
1453
1454impl FileString {
1455 /// Creates a new [`FileString`] from the specified [`String`].
1456 pub fn new(s: impl Into<String>) -> Self {
1457 Self(s.into())
1458 }
1459}
1460
1461impl From<FileString> for String {
1462 fn from(value: FileString) -> Self {
1463 value.0
1464 }
1465}
1466
1467impl From<String> for FileString {
1468 fn from(value: String) -> Self {
1469 Self(value)
1470 }
1471}
1472
1473impl NewtypeToInner for FileString {
1474 type Inner = String;
1475
1476 fn into_inner(self) -> Self::Inner {
1477 self.0
1478 }
1479}
1480
1481impl ReadFrom for FileString {
1482 fn read_from(path: &Path) -> Result<Self>
1483 where
1484 Self: Sized,
1485 {
1486 std::fs::read_to_string(path)
1487 .wrap_io_error_with(path)
1488 .map(Self)
1489 }
1490}
1491
1492impl WriteTo for FileString {
1493 fn write_to(&self, path: &Path) -> Result<()> {
1494 Self::from_ref_for_writer(&self.0).write_to(path)
1495 }
1496}
1497
1498impl<'a> FromRefForWriter<'a> for FileString {
1499 type Inner = str;
1500 type Wr = FileStrWr<'a>;
1501
1502 fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
1503 FileStrWr(value)
1504 }
1505}
1506
1507/// The [`WriteTo`] wrapper around a reference to a [`str`].
1508pub struct FileStrWr<'a>(&'a str);
1509
1510impl WriteTo for FileStrWr<'_> {
1511 fn write_to(&self, path: &Path) -> Result<()> {
1512 FileBytes::from_ref_for_writer(self.0.as_bytes()).write_to(path)
1513 }
1514}
1515
1516impl<T> ReadFrom for Option<T>
1517where
1518 T: ReadFrom,
1519{
1520 fn read_from(path: &Path) -> Result<Self>
1521 where
1522 Self: Sized,
1523 {
1524 if path.exists() {
1525 T::read_from(path).map(Some)
1526 } else {
1527 Ok(None)
1528 }
1529 }
1530}
1531
1532impl<T> WriteTo for Option<T>
1533where
1534 T: WriteTo,
1535{
1536 fn write_to(&self, path: &Path) -> Result<()> {
1537 if let Some(v) = self {
1538 v.write_to(path)
1539 } else {
1540 Ok(())
1541 }
1542 }
1543}
1544
1545/// A wrapper that defers the reading of a file until it is actually needed.
1546///
1547/// The only thing you can do with a [`DeferredRead`] is to call [`DeferredRead::perform_read`],
1548/// which will read the file and return the value.
1549///
1550/// See the [`DeferredRead::perform_read`] method for more details.
1551#[derive(Debug, Clone, Hash)]
1552pub struct DeferredRead<T>(pub PathBuf, marker::PhantomData<T>)
1553where
1554 T: ReadFrom;
1555
1556impl<T> ReadFrom for DeferredRead<T>
1557where
1558 T: ReadFrom,
1559{
1560 fn read_from(path: &Path) -> Result<Self>
1561 where
1562 Self: Sized,
1563 {
1564 Ok(Self(path.to_path_buf(), marker::PhantomData))
1565 }
1566}
1567
1568impl<T> DeferredRead<T>
1569where
1570 T: ReadFrom,
1571{
1572 /// Performs the read and returns the value.
1573 ///
1574 /// If the value changed on disk since the [`DeferredRead`] was created, then the
1575 /// new value will be read from disk and returned.
1576 ///
1577 /// For a cached version see [`DeferredReadOrOwn`].
1578 ///
1579 /// # Examples
1580 ///
1581 /// ```rust
1582 /// use std::path::Path;
1583 /// use dir_structure::DirStructureItem;
1584 /// use dir_structure::DeferredRead;
1585 ///
1586 /// #[derive(dir_structure::DirStructure)]
1587 /// struct Dir {
1588 /// #[dir_structure(path = "f.txt")]
1589 /// f: DeferredRead<String>,
1590 /// }
1591 ///
1592 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1593 /// let d = Path::new("dir");
1594 ///
1595 /// std::fs::create_dir_all(&d)?;
1596 /// std::fs::write(d.join("f.txt"), "Hello, world!")?;
1597 ///
1598 /// let dir = Dir::read(&d)?;
1599 /// assert_eq!(dir.f.perform_read()?, "Hello, world!");
1600 ///
1601 /// std::fs::write(d.join("f.txt"), "Goodbye, world!")?;
1602 /// assert_eq!(dir.f.perform_read()?, "Goodbye, world!");
1603 ///
1604 /// # std::fs::remove_dir_all(&d)?;
1605 /// Ok(())
1606 /// }
1607 /// ```
1608 pub fn perform_read(&self) -> Result<T> {
1609 T::read_from(&self.0)
1610 }
1611}
1612
1613impl<T> WriteTo for DeferredRead<T>
1614where
1615 T: ReadFrom + WriteTo,
1616{
1617 fn write_to(&self, path: &Path) -> Result<()> {
1618 if path == self.0 {
1619 // Optimization: We were asked to write to the same path
1620 // we are supposed to read from. We can just ignore it, since
1621 // the file / directory should already be in the given state.
1622
1623 // If `T` has trivial `ReadFrom` / `WriteTo` implementations,
1624 // this should not be a problem, but if it is, a custom `DeferredRead`
1625 // implementation should be written for it.
1626 return Ok(());
1627 }
1628
1629 let r = self.perform_read()?;
1630 r.write_to(path)
1631 }
1632}
1633
1634/// A wrapper that defers the reading of a file until it is actually needed,
1635/// but can also store the value.
1636///
1637/// It allows us to read the value from disk, and then store it in memory,
1638/// and if we ever need it again, we can just return the stored value.
1639///
1640/// This type exposes 2 functions: [`DeferredReadOrOwn::get`] and
1641/// [`DeferredReadOrOwn::perform_and_store_read`].
1642///
1643/// The table below summarizes the differences between the two functions:
1644///
1645/// | State | [`DeferredReadOrOwn::get`] | [`DeferredReadOrOwn::perform_and_store_read`] |
1646/// |-------------------|------------------------------------------|-----------------------------------------------|
1647/// | New, not cached | Reads the value, does not cache | Reads the value, and caches it |
1648/// | Cached | Returns the cached value | Returns the cached value |
1649///
1650/// As such, [`DeferredReadOrOwn::get`] has the signature of `fn(&self) -> Result<T>` and
1651/// [`DeferredReadOrOwn::perform_and_store_read`] has the signature of `fn(&mut self) -> Result<&T>`.
1652///
1653/// If you never call [`DeferredReadOrOwn::perform_and_store_read`], and only ever call [`DeferredReadOrOwn::get`],
1654/// that would effectively be the same as using a [`DeferredRead`], and that should be preferred instead.
1655#[derive(Debug, Clone, Hash)]
1656pub enum DeferredReadOrOwn<T>
1657where
1658 T: ReadFrom,
1659{
1660 Own(T),
1661 Deferred(DeferredRead<T>),
1662}
1663
1664impl<T> DeferredReadOrOwn<T>
1665where
1666 T: ReadFrom,
1667{
1668 /// Gets the value. If it is not already read, it will read it, but without saving it.
1669 ///
1670 /// This is useful if you want to read the value, but you don't want to store it.
1671 ///
1672 /// Though never calling [`DeferredReadOrOwn::perform_and_store_read`] and only calling
1673 /// [`DeferredReadOrOwn::get`] is equivalent to using a [`DeferredRead`], and that should be preferred.
1674 ///
1675 /// See [`DeferredReadOrOwn`] for more details.
1676 ///
1677 /// # Examples
1678 ///
1679 /// ```rust
1680 /// use std::path::Path;
1681 /// use dir_structure::DirStructureItem;
1682 /// use dir_structure::DeferredRead;
1683 /// use dir_structure::DeferredReadOrOwn;
1684 /// use dir_structure::ReadFrom;
1685 ///
1686 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1687 /// let d = Path::new("dir");
1688 /// std::fs::create_dir_all(&d)?;
1689 /// let deferred = DeferredReadOrOwn::<String>::Deferred(
1690 /// DeferredRead::read_from(&d.join("f.txt")).unwrap()
1691 /// );
1692 /// assert!(deferred.get().is_err());
1693 /// std::fs::write(d.join("f.txt"), "Hello, world!")?;
1694 /// assert_eq!(deferred.get()?, "Hello, world!");
1695 /// std::fs::write(d.join("f.txt"), "Goodbye, world!")?;
1696 /// assert_eq!(deferred.get()?, "Goodbye, world!");
1697 /// # std::fs::remove_dir_all(&d)?;
1698 /// Ok(())
1699 /// }
1700 /// ```
1701 pub fn get(&self) -> Result<T>
1702 where
1703 T: Clone,
1704 {
1705 match self {
1706 DeferredReadOrOwn::Own(own) => Ok(own.clone()),
1707 DeferredReadOrOwn::Deferred(d) => Ok(d.perform_read()?),
1708 }
1709 }
1710
1711 /// Performs the read and stores the value. If the value is already read, it will
1712 /// just return a reference to it.
1713 ///
1714 /// See [`DeferredReadOrOwn`] for more details.
1715 ///
1716 /// # Examples
1717 ///
1718 /// ```rust
1719 /// use std::path::Path;
1720 /// use dir_structure::DirStructureItem;
1721 /// use dir_structure::DeferredRead;
1722 /// use dir_structure::DeferredReadOrOwn;
1723 /// use dir_structure::ReadFrom;
1724 ///
1725 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1726 /// let d = Path::new("dir");
1727 /// std::fs::create_dir_all(&d)?;
1728 /// let mut deferred = DeferredReadOrOwn::<String>::Deferred(
1729 /// DeferredRead::read_from(&d.join("f.txt")).unwrap()
1730 /// );
1731 /// assert!(deferred.perform_and_store_read().is_err());
1732 /// std::fs::write(d.join("f.txt"), "Hello, world!")?;
1733 /// assert_eq!(deferred.perform_and_store_read()?, "Hello, world!");
1734 /// std::fs::write(d.join("f.txt"), "Goodbye, world!")?;
1735 /// assert_eq!(deferred.perform_and_store_read()?, "Hello, world!");
1736 /// # std::fs::remove_dir_all(&d)?;
1737 /// Ok(())
1738 /// }
1739 /// ```
1740 pub fn perform_and_store_read(&mut self) -> Result<&T> {
1741 match self {
1742 DeferredReadOrOwn::Own(own) => Ok(own),
1743 DeferredReadOrOwn::Deferred(d) => {
1744 let value = d.perform_read()?;
1745 *self = DeferredReadOrOwn::Own(value);
1746 let DeferredReadOrOwn::Own(own) = self else {
1747 unreachable!()
1748 };
1749 Ok(own)
1750 }
1751 }
1752 }
1753}
1754
1755impl<T> ReadFrom for DeferredReadOrOwn<T>
1756where
1757 T: ReadFrom,
1758{
1759 fn read_from(path: &Path) -> Result<Self>
1760 where
1761 Self: Sized,
1762 {
1763 ReadFrom::read_from(path).map(Self::Deferred)
1764 }
1765}
1766
1767impl<T> WriteTo for DeferredReadOrOwn<T>
1768where
1769 T: ReadFrom + WriteTo,
1770{
1771 fn write_to(&self, path: &Path) -> Result<()> {
1772 match self {
1773 DeferredReadOrOwn::Own(own) => own.write_to(path),
1774 DeferredReadOrOwn::Deferred(d) => d.write_to(path),
1775 }
1776 }
1777}
1778
1779/// A newtype that will clean the directory it is written to, before writing
1780/// the value.
1781///
1782/// This is useful when we want to write a directory structure, but we want
1783/// to make sure that the directory is clean before writing it, so that there
1784/// are no old files / directories left in it.
1785///
1786/// ```rust
1787/// use std::path::Path;
1788///
1789/// use dir_structure::DirStructureItem;
1790/// use dir_structure::CleanDir;
1791///
1792/// #[derive(dir_structure::DirStructure)]
1793/// struct Dir {
1794/// #[dir_structure(path = "f.txt")]
1795/// f: String,
1796/// }
1797///
1798/// fn main() -> Result<(), Box<dyn std::error::Error>> {
1799/// let d = Path::new("dir");
1800/// std::fs::create_dir_all(&d)?;
1801/// std::fs::write(d.join("f.txt"), "Hello, world!")?;
1802/// std::fs::write(d.join("f2.txt"), "Hello, world! (2)")?;
1803/// let dir = Dir::read(&d)?;
1804/// assert_eq!(dir.f, "Hello, world!");
1805/// assert_eq!(std::fs::read_to_string(d.join("f2.txt"))?, "Hello, world! (2)");
1806/// CleanDir(dir).write(&d)?;
1807/// assert_eq!(std::fs::read_to_string(d.join("f.txt"))?, "Hello, world!");
1808/// assert!(!d.join("f2.txt").exists());
1809/// # std::fs::remove_dir_all(&d)?;
1810/// Ok(())
1811/// }
1812/// ```
1813pub struct CleanDir<T: DirStructureItem>(pub T);
1814
1815impl<T> ReadFrom for CleanDir<T>
1816where
1817 T: DirStructureItem,
1818{
1819 fn read_from(path: &Path) -> Result<Self>
1820 where
1821 Self: Sized,
1822 {
1823 Ok(Self(T::read_from(path)?))
1824 }
1825}
1826
1827impl<T> WriteTo for CleanDir<T>
1828where
1829 T: DirStructureItem,
1830{
1831 fn write_to(&self, path: &Path) -> Result<()> {
1832 Self::from_ref_for_writer(&self.0).write_to(path)
1833 }
1834}
1835
1836impl<'a, T> FromRefForWriter<'a> for CleanDir<T>
1837where
1838 T: DirStructureItem + 'a,
1839{
1840 type Inner = T;
1841 type Wr = CleanDirRefWr<'a, T>;
1842
1843 fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
1844 CleanDirRefWr(value)
1845 }
1846}
1847
1848impl<T> NewtypeToInner for CleanDir<T>
1849where
1850 T: DirStructureItem,
1851{
1852 type Inner = T;
1853
1854 fn into_inner(self) -> Self::Inner {
1855 self.0
1856 }
1857}
1858
1859/// [`WriteTo`] impl for [`CleanDir`]
1860pub struct CleanDirRefWr<'a, T: ?Sized + DirStructureItem>(&'a T);
1861
1862impl<T> WriteTo for CleanDirRefWr<'_, T>
1863where
1864 T: ?Sized + DirStructureItem,
1865{
1866 fn write_to(&self, path: &Path) -> Result<()> {
1867 if path.exists() {
1868 std::fs::remove_dir_all(path).wrap_io_error_with(path)?;
1869 } else {
1870 utils::create_parent_dir(path)?;
1871 }
1872 self.0.write_to(path)
1873 }
1874}
1875
1876/// A versioned value. This is a wrapper around a value that will keep track of
1877/// how many times it has been changed. This is useful to not write the value
1878/// to disk if it hasn't changed.
1879///
1880/// You can get a reference to the value via its [`Deref`] implementation, and
1881/// you can get a mutable reference to the value via its [`DerefMut`] implementation.
1882///
1883/// The version is incremented every time [`DerefMut::deref_mut`] is called.
1884///
1885/// Alternatively, for [`Eq`] types, you can use the [`Versioned::edit_eq_check`]
1886/// method to edit the value, and it will increment the version if the value has changed.
1887///
1888/// # Example
1889///
1890/// ```
1891/// use dir_structure::VersionedString;
1892///
1893/// let mut v = VersionedString::new("value".to_owned(), "path");
1894/// assert!(v.is_clean());
1895/// assert!(!v.is_dirty());
1896///
1897/// *v = "new value".to_owned();
1898/// assert!(v.is_dirty());
1899/// ```
1900#[derive(Debug, Clone, Hash)]
1901pub struct Versioned<T: DirStructureItem> {
1902 value: T,
1903 version: usize,
1904 path: PathBuf,
1905}
1906
1907impl<T: DirStructureItem> Versioned<T> {
1908 const DEFAULT_VERSION: usize = 0;
1909
1910 /// Creates a new [`Versioned`] with the specified value.
1911 ///
1912 /// The version is set to the default value.
1913 pub fn new(value: T, path: impl Into<PathBuf>) -> Self {
1914 Self {
1915 value,
1916 version: Self::DEFAULT_VERSION,
1917 path: path.into(),
1918 }
1919 }
1920
1921 /// Creates a new [`Versioned`] with the specified value, and in a dirty state.
1922 ///
1923 /// # Example
1924 ///
1925 /// ```
1926 /// use dir_structure::VersionedString;
1927 ///
1928 /// let v = VersionedString::new_dirty("value".to_owned(), "path");
1929 /// assert!(v.is_dirty());
1930 /// ```
1931 pub fn new_dirty(value: T, path: impl Into<PathBuf>) -> Self {
1932 Self {
1933 value,
1934 version: Self::DEFAULT_VERSION + 1,
1935 path: path.into(),
1936 }
1937 }
1938
1939 /// Checks if the value has been changed.
1940 pub fn is_dirty(&self) -> bool {
1941 !self.is_clean()
1942 }
1943
1944 /// Checks if the value has not been changed.
1945 pub fn is_clean(&self) -> bool {
1946 self.version == Self::DEFAULT_VERSION
1947 }
1948
1949 /// Edits the value using the provided closure, and increments the version
1950 /// if the value has changed.
1951 ///
1952 /// # Example
1953 ///
1954 /// ```
1955 /// use dir_structure::VersionedString;
1956 ///
1957 /// let mut v = VersionedString::new("value".to_owned(), "path");
1958 ///
1959 /// v.edit_eq_check(|s| *s = "value".to_owned());
1960 /// assert!(v.is_clean());
1961 /// v.edit_eq_check(|s| *s = "new value".to_owned());
1962 /// assert!(v.is_dirty());
1963 /// ```
1964 pub fn edit_eq_check(&mut self, f: impl FnOnce(&mut T))
1965 where
1966 T: Eq + Clone,
1967 {
1968 let copy = self.value.clone();
1969
1970 f(&mut self.value);
1971
1972 if copy != self.value {
1973 self.version += 1;
1974 }
1975 }
1976}
1977
1978impl<T: DirStructureItem> ReadFrom for Versioned<T> {
1979 fn read_from(path: &Path) -> Result<Self>
1980 where
1981 Self: Sized,
1982 {
1983 T::read_from(path).map(|it| Self::new(it, path))
1984 }
1985}
1986
1987impl<T: DirStructureItem> WriteTo for Versioned<T> {
1988 fn write_to(&self, path: &Path) -> Result<()> {
1989 if self.path == path && self.is_clean() {
1990 return Ok(());
1991 }
1992
1993 self.value.write_to(path)
1994 }
1995}
1996
1997impl<T: DirStructureItem> Deref for Versioned<T> {
1998 type Target = T;
1999
2000 fn deref(&self) -> &Self::Target {
2001 &self.value
2002 }
2003}
2004
2005impl<T: DirStructureItem> DerefMut for Versioned<T> {
2006 fn deref_mut(&mut self) -> &mut Self::Target {
2007 // We will assume that the value has changed, if `deref_mut` was called.
2008 // So we increment the version.
2009 self.version += 1;
2010
2011 &mut self.value
2012 }
2013}
2014
2015pub type VersionedString = Versioned<String>;
2016pub type VersionedBytes = Versioned<Vec<u8>>;
2017
2018// Impls for std types.
2019
2020impl ReadFrom for String {
2021 fn read_from(path: &Path) -> Result<Self>
2022 where
2023 Self: Sized,
2024 {
2025 FileString::read_from(path).map(|v| v.0)
2026 }
2027}
2028
2029impl WriteTo for String {
2030 fn write_to(&self, path: &Path) -> Result<()> {
2031 FileString::from_ref_for_writer(self).write_to(path)
2032 }
2033}
2034
2035impl ReadFrom for Vec<u8> {
2036 fn read_from(path: &Path) -> Result<Self>
2037 where
2038 Self: Sized,
2039 {
2040 FileBytes::read_from(path).map(|v| v.0)
2041 }
2042}
2043
2044impl WriteTo for Vec<u8> {
2045 fn write_to(&self, path: &Path) -> Result<()> {
2046 FileBytes::from_ref_for_writer(self).write_to(path)
2047 }
2048}
2049
2050impl WriteTo for str {
2051 fn write_to(&self, path: &Path) -> Result<()> {
2052 FileStrWr(self).write_to(path)
2053 }
2054}
2055
2056impl WriteTo for &str {
2057 fn write_to(&self, path: &Path) -> Result<()> {
2058 FileStrWr(self).write_to(path)
2059 }
2060}
2061
2062impl WriteTo for [u8] {
2063 fn write_to(&self, path: &Path) -> Result<()> {
2064 FileBytesRefWr(self).write_to(path)
2065 }
2066}
2067
2068impl WriteTo for &[u8] {
2069 fn write_to(&self, path: &Path) -> Result<()> {
2070 FileBytesRefWr(self).write_to(path)
2071 }
2072}
2073
2074mod utils {
2075 use crate::WrapIoError;
2076
2077 pub fn create_parent_dir(path: &std::path::Path) -> crate::Result<()> {
2078 if let Some(parent) = path.parent() {
2079 if !parent.exists() {
2080 std::fs::create_dir_all(parent).wrap_io_error_with(parent)?;
2081 }
2082 }
2083 Ok(())
2084 }
2085}
2086
2087struct StreamingFileWriter {
2088 f: File,
2089}
2090
2091impl StreamingFileWriter {
2092 fn new(path: &Path) -> Result<Self> {
2093 utils::create_parent_dir(path)?;
2094 let f = File::create(path).wrap_io_error_with(path)?;
2095 Ok(Self { f })
2096 }
2097}
2098
2099impl std::io::Write for StreamingFileWriter {
2100 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
2101 self.f.write(buf)
2102 }
2103
2104 fn flush(&mut self) -> std::io::Result<()> {
2105 self.f.flush()
2106 }
2107}
2108
2109impl std::fmt::Write for StreamingFileWriter {
2110 fn write_str(&mut self, s: &str) -> std::fmt::Result {
2111 use std::io::Write;
2112
2113 self.f
2114 .write_all(s.as_bytes())
2115 .map_err(|_| std::fmt::Error)?;
2116 Ok(())
2117 }
2118}