1use crate::{
2 common::*,
3 dir_stack::{try_rebase_path, with_rebased_dir},
4 error::{Error, FileDumpError, FileLoadError},
5};
6
7#[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#[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}