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
94use std::ffi::{OsStr, OsString};
95use std::fmt::Display;
96use std::fs::File;
97use std::ops::{Deref, DerefMut};
98use std::path::{Path, PathBuf};
99use std::str::FromStr;
100
101#[derive(Debug, thiserror::Error)]
102pub enum Error {
103    #[error("IO error at {0:?}: {1}")]
104    Io(PathBuf, #[source] std::io::Error),
105    #[error("Parse error at {0:?}: {1}")]
106    Parse(PathBuf, #[source] Box<dyn std::error::Error + Send + Sync>),
107}
108
109trait WrapIoError: Sized {
110    type Output;
111
112    fn wrap_io_error(self, get_path: impl FnOnce() -> PathBuf) -> Result<Self::Output>;
113
114    fn wrap_io_error_with(self, path: &Path) -> Result<Self::Output> {
115        self.wrap_io_error(|| path.to_path_buf())
116    }
117}
118
119impl<T> WrapIoError for std::io::Result<T> {
120    type Output = T;
121
122    fn wrap_io_error(self, get_path: impl FnOnce() -> PathBuf) -> Result<Self::Output> {
123        self.map_err(|e| Error::Io(get_path(), e))
124    }
125}
126
127pub type Result<T> = std::result::Result<T, Error>;
128
129/// The main trait. This is implemented for
130/// all directory structures by the derive macro.
131///
132/// This trait doesn't have any methods, just a supertype:
133/// [`DirStructureItem`].
134pub trait DirStructure: DirStructureItem {}
135
136/// Helper trait, implemented for all types that have a [`ReadFrom`]
137/// and [`WriteTo`] implementation.
138pub trait DirStructureItem: ReadFrom + WriteTo {
139    /// Uses the [`ReadFrom`] implementation to read the structure from
140    /// disk, from the specified path.
141    fn read(path: impl AsRef<Path>) -> Result<Self>
142    where
143        Self: Sized,
144    {
145        Self::read_from(path.as_ref())
146    }
147
148    /// Uses the [`WriteTo`] implementation to write the structure
149    /// to disk at the specified path.
150    fn write(&self, path: impl AsRef<Path>) -> Result<()> {
151        self.write_to(path.as_ref())
152    }
153}
154
155// Blanket impl.
156impl<T> DirStructureItem for T where T: ReadFrom + WriteTo {}
157
158/// Trait for types / structures that can be
159/// read from disk, either from a file or a directory.
160pub trait ReadFrom {
161    /// Reads the structure from the specified path, which
162    /// can be either a file or a directory.
163    fn read_from(path: &Path) -> Result<Self>
164    where
165        Self: Sized;
166}
167
168/// Trait for types / structures that can be
169/// written to disk. All types in the library that
170/// write to files first check that the parent
171/// directories exist, so implementations of
172/// this that create the whole directory are
173/// not necessary (unless used empty children
174/// directories, in which case no directories will
175/// really be created).
176pub trait WriteTo {
177    /// Writes the structure to the specified path.
178    fn write_to(&self, path: &Path) -> Result<()>;
179}
180
181/// Trait to use when using the `with_newtype` attribute.
182///
183/// This is used to convert a reference to a normal type
184/// (like `String`, `Vec<u8>` etc. into a type that is a
185/// reference to them, like `&str`, `&[u8]` etc.), so that
186/// the `WriteTo` implementation can be written only for the
187/// reference types, and all the other [`WriteTo`] impls will
188/// only cast what they have to write to those reference types
189/// (via the function below), and then call the [`WriteTo::write_to`]
190/// method on that reference.
191pub trait FromRefForWriter<'a> {
192    /// The inner type to cast.
193    type Inner: ?Sized;
194    /// The reference type to cast to.
195    type Wr: WriteTo + 'a;
196
197    /// Casts the reference to the inner type to a [`WriteTo`]
198    /// reference type.
199    fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr;
200}
201
202/// Trait to use when using the `with_newtype` attribute.
203///
204/// This is used to convert a newtype to its inner type.
205/// We are using this because we cannot make blanket impls with
206/// [`From`] due to the orphan rules.
207pub trait NewtypeToInner {
208    /// The inner type.
209    type Inner;
210
211    /// Converts the newtype to its inner type.
212    fn into_inner(self) -> Self::Inner;
213}
214
215/// A directory structure where we don't know the names of the folders at compile-time,
216/// and as such we cannot use the derive macro.
217///
218/// Instead we know that all the entries in the directory are folders,
219/// and that they all have the same structure inside (defined by the [`T`] type parameter),
220/// or they are all files (which can be read with [`DirChildren`]<[`String`]> for example).
221///
222/// In either case, [`<T as ReadFrom>::read_from`] must be able to read all the entries in
223/// the directory.
224///
225/// The [`WriteTo`] implementation will directly write the children to the directory it
226/// is passed, with no regards to the path stored in `self_path`.
227pub struct DirChildren<T>
228where
229    T: DirStructureItem,
230{
231    /// The path to the root directory.
232    ///
233    /// This path doesn't influence writing in any way, it is only to
234    /// point out the directory after it has been read and parsed.
235    pub self_path: PathBuf,
236    /// The children of the root directory.
237    pub children: Vec<DirChild<T>>,
238}
239
240impl<T> DirChildren<T>
241where
242    T: DirStructureItem,
243{
244    /// Creates an empty [`DirChildren`], with no children.
245    pub fn new() -> Self {
246        Self {
247            self_path: PathBuf::new(),
248            children: Vec::new(),
249        }
250    }
251
252    /// Creates a [`DirChildren`] with the given path and children.
253    pub fn with_children_from_iter(
254        self_path: impl Into<PathBuf>,
255        children: impl IntoIterator<Item = DirChild<T>>,
256    ) -> Self {
257        Self {
258            self_path: self_path.into(),
259            children: children.into_iter().collect(),
260        }
261    }
262
263    /// Returns the number of children.
264    pub fn len(&self) -> usize {
265        self.children.len()
266    }
267
268    /// Gets the child at the specified index.
269    pub fn get(&self, index: usize) -> Option<&DirChild<T>> {
270        self.children.get(index)
271    }
272
273    /// Gets the child with the specified "file" name (last segment of path).
274    pub fn get_name(&self, name: impl AsRef<OsStr>) -> Option<&DirChild<T>> {
275        self.children
276            .iter()
277            .find(|child| child.file_name == name.as_ref())
278    }
279
280    /// Returns an iterator over the children.
281    pub fn iter(&self) -> DirChildrenIter<'_, T> {
282        DirChildrenIter(self.children.iter())
283    }
284}
285
286impl<T> ReadFrom for DirChildren<T>
287where
288    T: DirStructureItem,
289{
290    fn read_from(path: &Path) -> Result<Self>
291    where
292        Self: Sized,
293    {
294        let mut children = Vec::new();
295        for child in path.read_dir().wrap_io_error_with(path)? {
296            let child = child.wrap_io_error_with(path)?;
297            let file_name = child.file_name();
298            let value = T::read_from(&child.path())?;
299            children.push(DirChild { file_name, value });
300        }
301
302        Ok(DirChildren {
303            self_path: path.to_path_buf(),
304            children,
305        })
306    }
307}
308
309impl<T> WriteTo for DirChildren<T>
310where
311    T: DirStructureItem,
312{
313    fn write_to(&self, path: &Path) -> Result<()> {
314        for child in &self.children {
315            let child_path = path.join(&child.file_name);
316            child.value.write_to(&child_path)?;
317        }
318
319        Ok(())
320    }
321}
322
323/// A single child of a [`DirChildren`] structure.
324pub struct DirChild<T>
325where
326    T: DirStructureItem,
327{
328    /// The file name of the child.
329    file_name: OsString,
330    /// The parsed value of the child.
331    value: T,
332}
333
334impl<T> DirChild<T>
335where
336    T: DirStructureItem,
337{
338    /// Creates a new [`DirChild`] with the specified file name and value.
339    pub fn new(file_name: impl Into<OsString>, value: T) -> Self {
340        Self {
341            file_name: file_name.into(),
342            value,
343        }
344    }
345
346    /// Gets the file name of the child (or the name of the directory; the last segment in the path).
347    pub fn file_name(&self) -> &OsString {
348        &self.file_name
349    }
350
351    /// Gets the file name of the child (or the name of the directory; the last segment in the path).
352    ///
353    /// Mutable reference version of [`Self::file_name`].
354    pub fn file_name_mut(&mut self) -> &mut OsString {
355        &mut self.file_name
356    }
357
358    /// Gets the value of the child.
359    ///
360    /// This is the parsed value of the file / directory.
361    pub fn value(&self) -> &T {
362        &self.value
363    }
364
365    /// Gets the value of the child.
366    ///
367    /// This is the parsed value of the file / directory.
368    ///
369    /// Mutable reference version of [`Self::value`].
370    pub fn value_mut(&mut self) -> &mut T {
371        &mut self.value
372    }
373}
374
375impl<T> IntoIterator for DirChildren<T>
376where
377    T: DirStructureItem,
378{
379    type Item = DirChild<T>;
380    type IntoIter = std::vec::IntoIter<Self::Item>;
381
382    fn into_iter(self) -> Self::IntoIter {
383        self.children.into_iter()
384    }
385}
386
387/// A [`DirChildren`] iterator. It iterates over the children of a
388/// [`DirChildren`] structure.
389pub struct DirChildrenIter<'a, T: DirStructureItem>(std::slice::Iter<'a, DirChild<T>>);
390
391impl<'a, T> Iterator for DirChildrenIter<'a, T>
392where
393    T: DirStructureItem,
394{
395    type Item = &'a DirChild<T>;
396
397    fn next(&mut self) -> Option<Self::Item> {
398        self.0.next()
399    }
400
401    fn size_hint(&self) -> (usize, Option<usize>) {
402        self.0.size_hint()
403    }
404}
405
406impl<'a, T> ExactSizeIterator for DirChildrenIter<'a, T>
407where
408    T: DirStructureItem,
409{
410    fn len(&self) -> usize {
411        self.0.len()
412    }
413}
414
415/// A simple macro that generates a DirChildren<T> newtype, together with
416/// a few impls to make it easy to use.
417#[macro_export]
418macro_rules! dir_children_wrapper {
419    ($vis:vis $name:ident $ty:ty) => {
420        $vis struct $name(pub $crate::DirChildren<$ty>);
421
422        impl $crate::ReadFrom for $name {
423            fn read_from(path: &::std::path::Path) -> $crate::Result<Self>
424            where
425                Self: Sized,
426            {
427                Ok(Self(<$crate::DirChildren<$ty>>::read_from(path)?))
428            }
429        }
430
431        impl $crate::WriteTo for $name {
432            fn write_to(&self, path: &::std::path::Path) -> $crate::Result<()> {
433                self.0.write_to(path)
434            }
435        }
436
437        impl std::ops::Deref for $name {
438            type Target = $crate::DirChildren<$ty>;
439
440            fn deref(&self) -> &Self::Target {
441                &self.0
442            }
443        }
444
445        impl std::ops::DerefMut for $name {
446            fn deref_mut(&mut self) -> &mut Self::Target {
447                &mut self.0
448            }
449        }
450
451        impl std::iter::IntoIterator for $name {
452            type Item = $crate::DirChild<$ty>;
453            type IntoIter = std::vec::IntoIter<Self::Item>;
454
455            fn into_iter(self) -> Self::IntoIter {
456                self.0.into_iter()
457            }
458        }
459    };
460}
461
462pub use dir_structure_macros::DirStructure;
463
464#[cfg(feature = "json")]
465pub mod json {
466    //! With the `json` feature, this module provides the [`Json`] type,
467    //!
468    //! This allows us to read and parse json files to some `serde::Deserialize` type,
469    //! and write them back to disk.
470    use std::fmt;
471    use std::fmt::Formatter;
472    use std::path::Path;
473    use std::str::FromStr;
474
475    use crate::{FromRefForWriter, NewtypeToInner, ReadFrom, WriteTo};
476
477    /// A wrapper around a type that implements [`serde::Serialize`] and [`serde::Deserialize`],
478    /// thus allowing us to parse and serialize it from / to json when we read / write a
479    /// directory structure.
480    #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash)]
481    #[serde(transparent)]
482    pub struct Json<T>(#[serde(bound = "")] pub T)
483    where
484        T: 'static + serde::Serialize + for<'d> serde::Deserialize<'d>;
485
486    impl<T> FromStr for Json<T>
487    where
488        T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
489    {
490        type Err = serde_json::Error;
491
492        fn from_str(s: &str) -> Result<Self, Self::Err> {
493            serde_json::from_str(s).map(Self)
494        }
495    }
496
497    struct JsonToStr<'a, T>(&'a T)
498    where
499        T: serde::Serialize + 'a;
500
501    impl<'a, T> fmt::Display for JsonToStr<'a, T>
502    where
503        T: serde::Serialize + 'a,
504    {
505        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
506            let s = serde_json::to_string(&self.0).map_err(|_| fmt::Error)?;
507            write!(f, "{}", s)
508        }
509    }
510
511    impl<T> fmt::Display for Json<T>
512    where
513        T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
514    {
515        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
516            JsonToStr(&self.0).fmt(f)
517        }
518    }
519
520    impl<T> ReadFrom for Json<T>
521    where
522        T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
523    {
524        fn read_from(path: &Path) -> crate::Result<Self> {
525            let contents = crate::FileString::read_from(path)?.0;
526            let v = serde_json::from_str::<Self>(&contents)
527                .map_err(|e| crate::Error::Parse(path.to_path_buf(), e.into()))?;
528            Ok(v)
529        }
530    }
531
532    impl<T> WriteTo for Json<T>
533    where
534        T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
535    {
536        fn write_to(&self, path: &Path) -> crate::Result<()> {
537            Self::from_ref_for_writer(&self.0).write_to(path)
538        }
539    }
540
541    impl<T> NewtypeToInner for Json<T>
542    where
543        T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
544    {
545        type Inner = T;
546
547        fn into_inner(self) -> Self::Inner {
548            self.0
549        }
550    }
551
552    impl<'a, T> FromRefForWriter<'a> for Json<T>
553    where
554        T: serde::Serialize + for<'d> serde::Deserialize<'d> + 'static,
555    {
556        type Inner = T;
557        type Wr = JsonWr<'a, T>;
558
559        fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
560            JsonWr(value)
561        }
562    }
563
564    /// [`WriteTo`] impl for [`Json`].
565    pub struct JsonWr<'a, T>(&'a T)
566    where
567        T: serde::Serialize + 'a;
568
569    impl<'a, T> WriteTo for JsonWr<'a, T>
570    where
571        T: serde::Serialize + 'a,
572    {
573        fn write_to(&self, path: &Path) -> crate::Result<()> {
574            crate::FileString::from_ref_for_writer(&format!("{}", JsonToStr(self.0))).write_to(path)
575        }
576    }
577}
578
579/// A wrapper around a type which will use the [`Display`] and [`FromStr`] implementations
580/// for serialization / deserialization.
581///
582/// For example: u8, i8, i16, u16, all integer types... bool etc.
583pub struct FmtWrapper<T>(pub T);
584
585impl<T> NewtypeToInner for FmtWrapper<T> {
586    type Inner = T;
587
588    fn into_inner(self) -> Self::Inner {
589        self.0
590    }
591}
592
593impl<T> ReadFrom for FmtWrapper<T>
594where
595    T: FromStr,
596    T::Err: Into<Box<dyn std::error::Error + Send + Sync>>,
597{
598    fn read_from(path: &Path) -> Result<Self>
599    where
600        Self: Sized,
601    {
602        let contents = FileString::read_from(path)?.0;
603        match contents.parse::<T>() {
604            Ok(v) => Ok(Self(v)),
605            Err(e) => Err(Error::Parse(path.to_path_buf(), e.into())),
606        }
607    }
608}
609
610impl<T> WriteTo for FmtWrapper<T>
611where
612    T: Display,
613{
614    fn write_to(&self, path: &Path) -> Result<()> {
615        Self::from_ref_for_writer(&self.0).write_to(path)
616    }
617}
618
619impl<'a, T> FromRefForWriter<'a> for FmtWrapper<T>
620where
621    T: Display + 'a,
622{
623    type Inner = T;
624    type Wr = FmtWrapperRefWr<'a, T>;
625
626    fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
627        FmtWrapperRefWr(value)
628    }
629}
630
631/// A [`WriteTo`] wrapper around a reference to a type which will use the [`Display`]
632/// implementation to write the value.
633pub struct FmtWrapperRefWr<'a, T: ?Sized>(pub &'a T);
634
635impl<'a, T> WriteTo for FmtWrapperRefWr<'a, T>
636where
637    T: Display + ?Sized,
638{
639    fn write_to(&self, path: &Path) -> Result<()> {
640        use std::io::Write;
641        utils::create_parent_dir(path)?;
642        let mut f = File::create(path).wrap_io_error_with(path)?;
643        write!(f, "{}", self.0).wrap_io_error_with(path)?;
644        Ok(())
645    }
646}
647
648/// A newtype around a [`Vec`]<[`u8`]>.
649#[derive(Debug, Clone, PartialEq, Eq, Hash)]
650pub struct FileBytes(pub Vec<u8>);
651
652impl FileBytes {
653    /// Creates a new [`FileBytes`] from the specified [`Vec`]<[`u8`]>.
654    pub fn new(v: impl Into<Vec<u8>>) -> Self {
655        Self(v.into())
656    }
657}
658
659impl ReadFrom for FileBytes {
660    fn read_from(path: &Path) -> Result<Self>
661    where
662        Self: Sized,
663    {
664        std::fs::read(path).wrap_io_error_with(path).map(Self)
665    }
666}
667
668impl WriteTo for FileBytes {
669    fn write_to(&self, path: &Path) -> Result<()> {
670        Self::from_ref_for_writer(&self.0).write_to(path)
671    }
672}
673
674impl From<FileBytes> for Vec<u8> {
675    fn from(value: FileBytes) -> Self {
676        value.0
677    }
678}
679
680impl NewtypeToInner for FileBytes {
681    type Inner = Vec<u8>;
682
683    fn into_inner(self) -> Self::Inner {
684        self.0
685    }
686}
687
688impl<'a> FromRefForWriter<'a> for FileBytes {
689    type Inner = [u8];
690    type Wr = FileBytesRefWr<'a>;
691
692    fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
693        FileBytesRefWr(value)
694    }
695}
696
697/// The [`WriteTo`] wrapper around a reference to a `[u8]`.
698pub struct FileBytesRefWr<'a>(&'a [u8]);
699
700impl<'a> WriteTo for FileBytesRefWr<'a> {
701    fn write_to(&self, path: &Path) -> Result<()> {
702        utils::create_parent_dir(path)?;
703        std::fs::write(path, self.0).wrap_io_error_with(path)?;
704        Ok(())
705    }
706}
707
708/// A newtype around a [`String`].
709#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
710pub struct FileString(pub String);
711
712impl FileString {
713    /// Creates a new [`FileString`] from the specified [`String`].
714    pub fn new(s: impl Into<String>) -> Self {
715        Self(s.into())
716    }
717}
718
719impl From<FileString> for String {
720    fn from(value: FileString) -> Self {
721        value.0
722    }
723}
724
725impl NewtypeToInner for FileString {
726    type Inner = String;
727
728    fn into_inner(self) -> Self::Inner {
729        self.0
730    }
731}
732
733impl ReadFrom for FileString {
734    fn read_from(path: &Path) -> Result<Self>
735    where
736        Self: Sized,
737    {
738        std::fs::read_to_string(path)
739            .wrap_io_error_with(path)
740            .map(Self)
741    }
742}
743
744impl WriteTo for FileString {
745    fn write_to(&self, path: &Path) -> Result<()> {
746        Self::from_ref_for_writer(&self.0).write_to(path)
747    }
748}
749
750impl<'a> FromRefForWriter<'a> for FileString {
751    type Inner = str;
752    type Wr = FileStrWr<'a>;
753
754    fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
755        FileStrWr(value)
756    }
757}
758
759/// The [`WriteTo`] wrapper around a reference to a [`str`].
760pub struct FileStrWr<'a>(&'a str);
761
762impl WriteTo for FileStrWr<'_> {
763    fn write_to(&self, path: &Path) -> Result<()> {
764        FileBytes::from_ref_for_writer(self.0.as_bytes()).write_to(path)
765    }
766}
767
768impl<T> ReadFrom for Option<T>
769where
770    T: ReadFrom,
771{
772    fn read_from(path: &Path) -> Result<Self>
773    where
774        Self: Sized,
775    {
776        if path.exists() {
777            T::read_from(path).map(Some)
778        } else {
779            Ok(None)
780        }
781    }
782}
783
784impl<T> WriteTo for Option<T>
785where
786    T: WriteTo,
787{
788    fn write_to(&self, path: &Path) -> Result<()> {
789        if let Some(v) = self {
790            v.write_to(path)
791        } else {
792            Ok(())
793        }
794    }
795}
796
797/// A wrapper that defers the reading of a file until it is actually needed.
798#[derive(Debug, Clone, Hash)]
799pub struct DeferredRead<T>(pub PathBuf, std::marker::PhantomData<T>)
800where
801    T: ReadFrom;
802
803impl<T> ReadFrom for DeferredRead<T>
804where
805    T: ReadFrom,
806{
807    fn read_from(path: &Path) -> Result<Self>
808    where
809        Self: Sized,
810    {
811        Ok(Self(path.to_path_buf(), std::marker::PhantomData))
812    }
813}
814
815impl<T> DeferredRead<T>
816where
817    T: ReadFrom,
818{
819    /// Performs the read and returns the value.
820    pub fn perform_read(&self) -> Result<T> {
821        T::read_from(&self.0)
822    }
823}
824
825impl<T> WriteTo for DeferredRead<T>
826where
827    T: ReadFrom + WriteTo,
828{
829    fn write_to(&self, path: &Path) -> Result<()> {
830        if path == self.0 {
831            // Optimization: We were asked to write to the same path
832            // we are supposed to read from. We can just ignore it, since
833            // the file / directory should already be in the given state.
834
835            // If T doesn't have non-trivial ReadFrom / WriteTo implementations,
836            // this should not be a problem, but if it is, a custom DeferredRead
837            // implementation should be written for it.
838            return Ok(());
839        }
840
841        let r = self.perform_read()?;
842        r.write_to(path)
843    }
844}
845
846/// A wrapper that defers the reading of a file until it is actually needed.
847///
848/// It can also store the value.
849#[derive(Debug, Clone, Hash)]
850pub enum DeferredReadOrOwn<T>
851where
852    T: ReadFrom,
853{
854    Own(T),
855    Deferred(DeferredRead<T>),
856}
857
858impl<T> DeferredReadOrOwn<T>
859where
860    T: ReadFrom,
861{
862    /// Gets the value. If it is not already read, it will read it, but without saving it.
863    pub fn get(&self) -> Result<T>
864    where
865        T: Clone,
866    {
867        match self {
868            DeferredReadOrOwn::Own(own) => Ok(own.clone()),
869            DeferredReadOrOwn::Deferred(d) => Ok(d.perform_read()?),
870        }
871    }
872
873    /// Performs the read and stores the value. If the value is already read, it will
874    /// just return a reference to it.
875    pub fn perform_and_store_read(&mut self) -> Result<&T> {
876        match self {
877            DeferredReadOrOwn::Own(own) => Ok(own),
878            DeferredReadOrOwn::Deferred(d) => {
879                let value = d.perform_read()?;
880                *self = DeferredReadOrOwn::Own(value);
881                let DeferredReadOrOwn::Own(own) = self else {
882                    unreachable!()
883                };
884                Ok(own)
885            }
886        }
887    }
888}
889
890impl<T> ReadFrom for DeferredReadOrOwn<T>
891where
892    T: ReadFrom,
893{
894    fn read_from(path: &Path) -> Result<Self>
895    where
896        Self: Sized,
897    {
898        ReadFrom::read_from(path).map(Self::Deferred)
899    }
900}
901
902impl<T> WriteTo for DeferredReadOrOwn<T>
903where
904    T: ReadFrom + WriteTo,
905{
906    fn write_to(&self, path: &Path) -> Result<()> {
907        match self {
908            DeferredReadOrOwn::Own(own) => own.write_to(path),
909            DeferredReadOrOwn::Deferred(d) => d.write_to(path),
910        }
911    }
912}
913
914/// A newtype that will clean the directory it is written to, before writing
915/// the value.
916///
917/// This is useful when we want to write a directory structure, but we want
918/// to make sure that the directory is clean before writing it, so that there
919/// are no old files / directories left in it.
920pub struct CleanDir<T: DirStructureItem>(pub T);
921
922impl<T> ReadFrom for CleanDir<T>
923where
924    T: DirStructureItem,
925{
926    fn read_from(path: &Path) -> Result<Self>
927    where
928        Self: Sized,
929    {
930        Ok(Self(T::read_from(path)?))
931    }
932}
933
934impl<T> WriteTo for CleanDir<T>
935where
936    T: DirStructureItem,
937{
938    fn write_to(&self, path: &Path) -> Result<()> {
939        Self::from_ref_for_writer(&self.0).write_to(path)
940    }
941}
942
943impl<'a, T> FromRefForWriter<'a> for CleanDir<T>
944where
945    T: DirStructureItem + 'a,
946{
947    type Inner = T;
948    type Wr = CleanDirRefWr<'a, T>;
949
950    fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
951        CleanDirRefWr(value)
952    }
953}
954
955impl<T> NewtypeToInner for CleanDir<T>
956where
957    T: DirStructureItem,
958{
959    type Inner = T;
960
961    fn into_inner(self) -> Self::Inner {
962        self.0
963    }
964}
965
966/// [`WriteTo`] impl for [`CleanDir`]
967pub struct CleanDirRefWr<'a, T: ?Sized + DirStructureItem>(&'a T);
968
969impl<'a, T> WriteTo for CleanDirRefWr<'a, T>
970where
971    T: ?Sized + DirStructureItem,
972{
973    fn write_to(&self, path: &Path) -> Result<()> {
974        if path.exists() {
975            std::fs::remove_dir_all(path).wrap_io_error_with(path)?;
976        } else {
977            utils::create_parent_dir(path)?;
978        }
979        self.0.write_to(path)
980    }
981}
982
983/// A versioned value. This is a wrapper around a value that will keep track of
984/// how many times it has been changed. This is useful to not write the value
985/// to disk if it hasn't changed.
986///
987/// You can get a reference to the value via its [`Deref`] implementation, and
988/// you can get a mutable reference to the value via its [`DerefMut`] implementation.
989///
990/// The version is incremented every time [`DerefMut::deref_mut`] is called.
991///
992/// Alternatively, for [`Eq`] types, you can use the [`Versioned::edit_eq_check`]
993/// method to edit the value, and it will increment the version if the value has changed.
994///
995/// # Example
996///
997/// ```
998/// use dir_structure::VersionedString;
999///
1000/// let mut v = VersionedString::new("value".to_owned(), "path");
1001/// assert!(v.is_clean());
1002/// assert!(!v.is_dirty());
1003/// 
1004/// *v = "new value".to_owned();
1005/// assert!(v.is_dirty());
1006/// ```
1007#[derive(Debug, Clone, Hash)]
1008pub struct Versioned<T: DirStructureItem> {
1009    value: T,
1010    version: usize,
1011    path: PathBuf,
1012}
1013
1014impl<T: DirStructureItem> Versioned<T> {
1015    const DEFAULT_VERSION: usize = 0;
1016
1017    /// Creates a new [`Versioned`] with the specified value.
1018    ///
1019    /// The version is set to the default value.
1020    pub fn new(value: T, path: impl Into<PathBuf>) -> Self {
1021        Self {
1022            value: value.into(),
1023            version: Self::DEFAULT_VERSION,
1024            path: path.into(),
1025        }
1026    }
1027
1028    /// Creates a new [`Versioned`] with the specified value, and in a dirty state.
1029    ///
1030    /// # Example
1031    ///
1032    /// ```
1033    /// use dir_structure::VersionedString;
1034    ///
1035    /// let v = VersionedString::new_dirty("value".to_owned(), "path");
1036    /// assert!(v.is_dirty());
1037    /// ```
1038    pub fn new_dirty(value: T, path: impl Into<PathBuf>) -> Self {
1039        Self {
1040            value: value.into(),
1041            version: Self::DEFAULT_VERSION + 1,
1042            path: path.into(),
1043        }
1044    }
1045
1046    /// Checks if the value has been changed.
1047    pub fn is_dirty(&self) -> bool {
1048        !self.is_clean()
1049    }
1050
1051    /// Checks if the value has not been changed.
1052    pub fn is_clean(&self) -> bool {
1053        self.version == Self::DEFAULT_VERSION
1054    }
1055
1056    /// Edits the value using the provided closure, and increments the version
1057    /// if the value has changed.
1058    ///
1059    /// # Example
1060    ///
1061    /// ```
1062    /// use dir_structure::VersionedString;
1063    ///
1064    /// let mut v = VersionedString::new("value".to_owned(), "path");
1065    ///
1066    /// v.edit_eq_check(|s| *s = "value".to_owned());
1067    /// assert!(v.is_clean());
1068    /// v.edit_eq_check(|s| *s = "new value".to_owned());
1069    /// assert!(v.is_dirty());
1070    /// ```
1071    pub fn edit_eq_check(&mut self, f: impl FnOnce(&mut T))
1072    where
1073        T: Eq + Clone,
1074    {
1075        let copy = self.value.clone();
1076
1077        f(&mut self.value);
1078
1079        if copy != self.value {
1080            self.version += 1;
1081        }
1082    }
1083}
1084
1085impl<T: DirStructureItem> ReadFrom for Versioned<T> {
1086    fn read_from(path: &Path) -> Result<Self>
1087    where
1088        Self: Sized,
1089    {
1090        T::read_from(path).map(|it| Self::new(it, path))
1091    }
1092}
1093
1094impl<T: DirStructureItem> WriteTo for Versioned<T> {
1095    fn write_to(&self, path: &Path) -> Result<()> {
1096        if self.path == path && self.is_clean() {
1097            return Ok(());
1098        }
1099
1100        self.value.write_to(path)
1101    }
1102}
1103
1104impl<T: DirStructureItem> Deref for Versioned<T> {
1105    type Target = T;
1106
1107    fn deref(&self) -> &Self::Target {
1108        &self.value
1109    }
1110}
1111
1112impl<T: DirStructureItem> DerefMut for Versioned<T> {
1113    fn deref_mut(&mut self) -> &mut Self::Target {
1114        // We will assume that the value has changed, if `deref_mut` was called.
1115        // So we increment the version.
1116        self.version += 1;
1117
1118        &mut self.value
1119    }
1120}
1121
1122pub type VersionedString = Versioned<String>;
1123pub type VersionedBytes = Versioned<Vec<u8>>;
1124
1125// Impls for std types.
1126
1127impl ReadFrom for String {
1128    fn read_from(path: &Path) -> Result<Self>
1129    where
1130        Self: Sized,
1131    {
1132        FileString::read_from(path).map(|v| v.0)
1133    }
1134}
1135
1136impl WriteTo for String {
1137    fn write_to(&self, path: &Path) -> Result<()> {
1138        FileString::from_ref_for_writer(self).write_to(path)
1139    }
1140}
1141
1142impl ReadFrom for Vec<u8> {
1143    fn read_from(path: &Path) -> Result<Self>
1144    where
1145        Self: Sized,
1146    {
1147        FileBytes::read_from(path).map(|v| v.0)
1148    }
1149}
1150
1151impl WriteTo for Vec<u8> {
1152    fn write_to(&self, path: &Path) -> Result<()> {
1153        FileBytes::from_ref_for_writer(self).write_to(path)
1154    }
1155}
1156
1157impl WriteTo for str {
1158    fn write_to(&self, path: &Path) -> Result<()> {
1159        FileStrWr(self).write_to(path)
1160    }
1161}
1162
1163impl<'a> WriteTo for &'a str {
1164    fn write_to(&self, path: &Path) -> Result<()> {
1165        FileStrWr(self).write_to(path)
1166    }
1167}
1168
1169impl WriteTo for [u8] {
1170    fn write_to(&self, path: &Path) -> Result<()> {
1171        FileBytesRefWr(self).write_to(path)
1172    }
1173}
1174
1175impl<'a> WriteTo for &'a [u8] {
1176    fn write_to(&self, path: &Path) -> Result<()> {
1177        FileBytesRefWr(self).write_to(path)
1178    }
1179}
1180
1181mod utils {
1182    use crate::WrapIoError;
1183
1184    pub fn create_parent_dir(path: &std::path::Path) -> crate::Result<()> {
1185        if let Some(parent) = path.parent() {
1186            if !parent.exists() {
1187                std::fs::create_dir_all(parent).wrap_io_error_with(parent)?;
1188            }
1189        }
1190        Ok(())
1191    }
1192}