dir_structure/
fmt_wrapper.rs

1//! [`ReadFrom`] and [`WriteTo`] implementations using [`FromStr::from_str`] and [`Display::fmt`].
2//!
3//! See [`FmtWrapper`] for more details.
4
5use std::error;
6use std::fmt::Display;
7use std::marker;
8use std::pin::Pin;
9use std::str::FromStr;
10
11use crate::error::Error;
12use crate::error::VfsResult;
13use crate::prelude::*;
14use crate::std_types::FileString;
15#[cfg(feature = "async")]
16use crate::traits::asy::FromRefForWriterAsync;
17#[cfg(feature = "async")]
18use crate::traits::async_vfs::VfsAsync;
19#[cfg(feature = "async")]
20use crate::traits::async_vfs::WriteSupportingVfsAsync;
21use crate::traits::sync::FromRefForWriter;
22use crate::traits::sync::NewtypeToInner;
23use crate::traits::vfs;
24use crate::traits::vfs::PathType;
25#[cfg(feature = "async")]
26use crate::traits::vfs::VfsCore;
27
28/// A wrapper around a type which will use the [`Display`] and [`FromStr`] implementations
29/// for serialization / deserialization.
30///
31/// For example: u8, i8, i16, u16, all integer types... bool etc.
32///
33/// # Examples
34///
35#[cfg_attr(feature = "derive", doc = "```rust")]
36#[cfg_attr(not(feature = "derive"), doc = "```rust,compile_fail")]
37/// use std::path::Path;
38///
39/// use dir_structure::traits::sync::DirStructureItem;
40/// use dir_structure::fmt_wrapper::FmtWrapper;
41///
42/// #[derive(dir_structure::DirStructure, PartialEq, Debug)]
43/// struct Dir {
44///    #[dir_structure(path = "f.txt", with_newtype = FmtWrapper<u8>)]
45///    f: u8,
46///    #[dir_structure(path = "b.txt", with_newtype = FmtWrapper<bool>)]
47///    b: bool,
48/// }
49///
50/// fn main() -> Result<(), Box<dyn std::error::Error>> {
51///     let d = Path::new("dir");
52///     std::fs::create_dir_all(&d)?;
53///     std::fs::write(d.join("f.txt"), "42")?;
54///     std::fs::write(d.join("b.txt"), "true")?;
55///     let mut dir = Dir::read(&d)?;
56///     assert_eq!(dir.f, 42);
57///     assert_eq!(dir.b, true);
58///     dir.f = 100;
59///     dir.b = false;
60///     dir.write(&d)?;
61///     assert_eq!(std::fs::read_to_string(d.join("f.txt"))?, "100");
62///     assert_eq!(std::fs::read_to_string(d.join("b.txt"))?, "false");
63///     # std::fs::remove_dir_all(&d)?;
64///     Ok(())
65/// }
66/// ```
67#[cfg_attr(feature = "assert_eq", derive(assert_eq::AssertEq))]
68pub struct FmtWrapper<T>(pub T);
69
70impl<T> NewtypeToInner for FmtWrapper<T> {
71    type Inner = T;
72
73    fn into_inner(self) -> Self::Inner {
74        self.0
75    }
76}
77
78impl<'a, T, Vfs: vfs::Vfs<'a>> ReadFrom<'a, Vfs> for FmtWrapper<T>
79where
80    T: FromStr + 'a,
81    T::Err: Into<Box<dyn error::Error + Send + Sync>>,
82{
83    fn read_from(path: &Vfs::Path, vfs: Pin<&'a Vfs>) -> VfsResult<Self, Vfs>
84    where
85        Self: Sized,
86    {
87        let contents = FileString::read_from(path, vfs)?.0;
88        match contents.parse::<T>() {
89            Ok(v) => Ok(Self(v)),
90            Err(e) => Err(Error::Parse(path.owned(), e.into())),
91        }
92    }
93}
94
95#[cfg(feature = "async")]
96#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
97impl<'a, T, Vfs: VfsAsync + 'static> ReadFromAsync<'a, Vfs> for FmtWrapper<T>
98where
99    T: FromStr + Send + 'static,
100    T::Err: Into<Box<dyn error::Error + Send + Sync>>,
101{
102    type Future = Pin<Box<dyn Future<Output = VfsResult<Self, Vfs>> + Send + 'a>>;
103
104    fn read_from_async(
105        path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
106        vfs: Pin<&'a Vfs>,
107    ) -> Self::Future {
108        Box::pin(async move {
109            let contents = FileString::read_from_async(path.clone(), vfs).await?.0;
110            match contents.parse::<T>() {
111                Ok(v) => Ok(Self(v)),
112                Err(e) => Err(Error::Parse(path, e.into())),
113            }
114        })
115    }
116}
117
118impl<'vfs, T, Vfs: vfs::WriteSupportingVfs<'vfs>> WriteTo<'vfs, Vfs> for FmtWrapper<T>
119where
120    T: Display,
121{
122    fn write_to(&self, path: &Vfs::Path, vfs: Pin<&'vfs Vfs>) -> VfsResult<(), Vfs> {
123        Self::from_ref_for_writer(&self.0).write_to(path, vfs)
124    }
125}
126
127#[cfg(feature = "async")]
128#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
129impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> WriteToAsync<'a, Vfs> for FmtWrapper<T>
130where
131    T: Display + Send + Sync + 'static,
132{
133    type Future = <FileString as WriteToAsync<'a, Vfs>>::Future;
134
135    fn write_to_async(
136        self,
137        path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
138        vfs: Pin<&'a Vfs>,
139    ) -> Self::Future {
140        let s = self.0.to_string();
141        FileString::new(s).write_to_async(path, vfs)
142    }
143}
144
145#[cfg(feature = "async")]
146#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
147impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> WriteToAsyncRef<'a, Vfs> for FmtWrapper<T>
148where
149    T: Display + Send + Sync + 'a,
150{
151    type Future<'b>
152        = <FileString as WriteToAsync<'b, Vfs>>::Future
153    where
154        Self: 'b,
155        'a: 'b,
156        Vfs: 'b;
157
158    fn write_to_async_ref<'b>(
159        &'b self,
160        path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
161        vfs: Pin<&'b Vfs>,
162    ) -> Self::Future<'b>
163    where
164        'a: 'b,
165    {
166        let s = self.0.to_string();
167        FileString::new(s).write_to_async(path, vfs)
168    }
169}
170
171impl<'a, 'vfs, T, Vfs: vfs::WriteSupportingVfs<'vfs>> FromRefForWriter<'a, 'vfs, Vfs>
172    for FmtWrapper<T>
173where
174    T: Display + 'a,
175    Vfs: 'vfs,
176    'vfs: 'a,
177{
178    type Inner = T;
179    type Wr = FmtWrapperRefWr<'a, 'vfs, T, Vfs>;
180
181    fn from_ref_for_writer(value: &'a Self::Inner) -> Self::Wr {
182        FmtWrapperRefWr(value, marker::PhantomData)
183    }
184}
185
186#[cfg(feature = "async")]
187#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
188impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> FromRefForWriterAsync<'a, Vfs> for FmtWrapper<T>
189where
190    T: Display + Send + 'a,
191{
192    type Inner = T;
193    type Wr = FmtWrapperRefWr<'a, 'a, T, Vfs>;
194
195    fn from_ref_for_writer_async(value: &'a Self::Inner) -> Self::Wr {
196        FmtWrapperRefWr(value, marker::PhantomData)
197    }
198}
199
200/// A [`WriteTo`] wrapper around a reference to a type which will use the [`Display`]
201/// implementation to write the value.
202pub struct FmtWrapperRefWr<'a, 'vfs, T: Display + ?Sized, Vfs: 'vfs>(
203    pub &'a T,
204    marker::PhantomData<&'vfs Vfs>,
205);
206
207impl<'a, 'vfs, T, Vfs: vfs::WriteSupportingVfs<'vfs> + 'vfs> WriteTo<'vfs, Vfs>
208    for FmtWrapperRefWr<'a, 'vfs, T, Vfs>
209where
210    T: Display + ?Sized,
211    'vfs: 'a,
212{
213    fn write_to(&self, path: &Vfs::Path, vfs: Pin<&'vfs Vfs>) -> VfsResult<(), Vfs> {
214        FileString::new(self.0.to_string()).write_to(path, vfs)
215    }
216}
217
218#[cfg(feature = "async")]
219#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
220impl<'a, 'vfs, T, Vfs: WriteSupportingVfsAsync + 'static> WriteToAsync<'a, Vfs>
221    for FmtWrapperRefWr<'a, 'vfs, T, Vfs>
222where
223    T: Display + Send + 'a,
224    'vfs: 'a,
225{
226    type Future = <FileString as WriteToAsync<'a, Vfs>>::Future;
227
228    fn write_to_async(
229        self,
230        path: <<Vfs as VfsCore>::Path as PathType>::OwnedPath,
231        vfs: Pin<&'a Vfs>,
232    ) -> Self::Future {
233        let s = self.0.to_string();
234        FileString::new(s).write_to_async(path, vfs)
235    }
236}