serde_loader/
file.rs

1use crate::{
2    common::*,
3    dir_stack::{try_rebase_path, with_rebased_dir},
4    error::{Error, FileDumpError, FileLoadError},
5};
6
7/// A wrapper type that opens and deserializes a JSON file to type `T`.
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct FilePath<T, Ser, De> {
10    inner: Option<Inner<T>>,
11    _phantom: PhantomData<(Ser, De)>,
12}
13
14/// A wrapper type that opens and deserializes a JSON file to type `T`.
15#[derive(Debug, Clone, Derivative)]
16#[derivative(PartialEq, Eq, Hash)]
17struct Inner<T> {
18    data: T,
19    #[derivative(PartialEq = "ignore", Hash = "ignore")]
20    ref_path: PathBuf,
21    #[derivative(PartialEq = "ignore", Hash = "ignore")]
22    abs_path: PathBuf,
23}
24
25impl<T, Ser, De> FilePath<T, Ser, De>
26where
27    De: FileLoader<T>,
28{
29    pub fn open<P>(path: P) -> Result<Self, Error<Infallible, De::Error>>
30    where
31        P: AsRef<Path>,
32        T: DeserializeOwned,
33    {
34        let ref_path = path.as_ref();
35        let abs_path = try_rebase_path(ref_path).into_owned();
36
37        let parent = abs_path
38            .parent()
39            .ok_or_else(|| {
40                io::Error::new(
41                    io::ErrorKind::NotFound,
42                    format_err!(
43                        "unable to find parent directory of '{}'",
44                        abs_path.display()
45                    ),
46                )
47            })?
48            .to_owned();
49
50        let data = with_rebased_dir(parent, || {
51            De::load(&abs_path).map_err(|error| FileLoadError {
52                path: abs_path.clone(),
53                error,
54            })
55        })?;
56
57        Ok(Self {
58            inner: Some(Inner {
59                data,
60                ref_path: ref_path.to_path_buf(),
61                abs_path,
62            }),
63            _phantom: PhantomData,
64        })
65    }
66
67    pub fn open_and_take<P>(path: P) -> Result<T, Error<Infallible, De::Error>>
68    where
69        P: AsRef<Path>,
70        T: DeserializeOwned,
71    {
72        Ok(Self::open(path)?.take())
73    }
74}
75
76impl<T, Ser, De> FilePath<T, Ser, De>
77where
78    Ser: FileDumper<T>,
79{
80    pub fn create<P>(path: P, data: T) -> Result<Self, Error<Ser::Error, Infallible>>
81    where
82        P: AsRef<Path>,
83        T: DeserializeOwned,
84    {
85        let ref_path = path.as_ref();
86        let abs_path = try_rebase_path(ref_path).into_owned();
87
88        let parent = abs_path
89            .parent()
90            .ok_or_else(|| {
91                io::Error::new(
92                    io::ErrorKind::NotFound,
93                    format_err!(
94                        "unable to find parent directory of '{}'",
95                        abs_path.display()
96                    ),
97                )
98            })?
99            .to_owned();
100
101        with_rebased_dir(parent, || {
102            Ser::dump(&abs_path, &data).map_err(|error| FileDumpError {
103                error,
104                path: abs_path.to_path_buf(),
105            })
106        })?;
107
108        Ok(Self {
109            inner: Some(Inner {
110                data,
111                ref_path: ref_path.to_path_buf(),
112                abs_path,
113            }),
114            _phantom: PhantomData,
115        })
116    }
117
118    pub fn save(&self) -> Result<(), Error<Ser::Error, Infallible>>
119    where
120        T: Serialize,
121    {
122        let abs_path = &self.inner().abs_path;
123        let parent = abs_path
124            .parent()
125            .ok_or_else(|| {
126                io::Error::new(
127                    io::ErrorKind::NotFound,
128                    format_err!(
129                        "unable to find parent directory of '{}'",
130                        abs_path.display()
131                    ),
132                )
133            })?
134            .to_owned();
135
136        with_rebased_dir(parent, || {
137            Ser::dump(&abs_path, &self.inner().data).map_err(|error| FileDumpError {
138                error,
139                path: abs_path.to_path_buf(),
140            })
141        })?;
142
143        Ok(())
144    }
145}
146
147impl<T, Ser, De> FilePath<T, Ser, De> {
148    pub fn take(mut self) -> T {
149        self.inner.take().unwrap().data
150    }
151
152    pub fn abs_path(&self) -> &Path {
153        &self.inner().abs_path
154    }
155
156    pub fn ref_path(&self) -> &Path {
157        &self.inner().ref_path
158    }
159
160    fn inner(&self) -> &Inner<T> {
161        self.inner.as_ref().unwrap()
162    }
163
164    fn inner_mut(&mut self) -> &mut Inner<T> {
165        self.inner.as_mut().unwrap()
166    }
167}
168
169impl<T, Ser, De> AsRef<T> for FilePath<T, Ser, De> {
170    fn as_ref(&self) -> &T {
171        &self.inner().data
172    }
173}
174
175impl<T, Ser, De> Deref for FilePath<T, Ser, De> {
176    type Target = T;
177
178    fn deref(&self) -> &Self::Target {
179        &self.inner().data
180    }
181}
182
183impl<T, Ser, De> DerefMut for FilePath<T, Ser, De> {
184    fn deref_mut(&mut self) -> &mut Self::Target {
185        &mut self.inner_mut().data
186    }
187}
188
189impl<T, Ser, De> Serialize for FilePath<T, Ser, De>
190where
191    T: Serialize,
192    Ser: FileDumper<T>,
193{
194    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
195    where
196        S: Serializer,
197    {
198        self.save().map_err(S::Error::custom)?;
199        self.inner().ref_path.serialize(serializer)
200    }
201}
202
203impl<'de, T, Ser, De> Deserialize<'de> for FilePath<T, Ser, De>
204where
205    T: DeserializeOwned,
206    De: FileLoader<T>,
207{
208    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
209    where
210        D: Deserializer<'de>,
211    {
212        let ref_path = PathBuf::deserialize(deserializer)?;
213        Self::open(ref_path).map_err(D::Error::custom)
214    }
215}
216
217pub trait FileLoader<T>
218where
219    Self::Error: fmt::Display,
220{
221    type Error;
222
223    fn load<P>(p: P) -> Result<T, Self::Error>
224    where
225        P: AsRef<Path>;
226}
227
228pub trait FileDumper<T>
229where
230    Self::Error: fmt::Display,
231{
232    type Error;
233
234    fn dump<P>(p: P, value: &T) -> Result<(), Self::Error>
235    where
236        P: AsRef<Path>;
237}