1use crate::fs::Metadata;
2use crate::fs::ReadDir;
3use anyhow::Context;
4use std::ffi::OsStr;
5use std::fs::{File, Permissions};
6use std::path::{Path, PathBuf};
7
8pub trait PathAnyhow {
10 fn to_str_anyhow(&self) -> anyhow::Result<&str>;
12
13 fn parent_anyhow(&self) -> anyhow::Result<&Path>;
15
16 fn file_name_anyhow(&self) -> anyhow::Result<&OsStr>;
18
19 fn strip_prefix_anyhow<P>(&self, base: P) -> anyhow::Result<&Path>
21 where
22 P: AsRef<Path>;
23
24 fn file_stem_anyhow(&self) -> anyhow::Result<&OsStr>;
26
27 fn extension_anyhow(&self) -> anyhow::Result<&OsStr>;
29
30 fn metadata_anyhow(&self) -> anyhow::Result<Metadata>;
32
33 fn symlink_metadata_anyhow(&self) -> anyhow::Result<Metadata>;
35
36 fn canonicalize_anyhow(&self) -> anyhow::Result<PathBuf>;
38
39 fn read_link_anyhow(&self) -> anyhow::Result<PathBuf>;
41
42 fn read_dir_anyhow(&self) -> anyhow::Result<ReadDir>;
44
45 fn copy_anyhow<P>(&self, to: P) -> anyhow::Result<u64>
49 where
50 P: AsRef<Path>;
51
52 fn create_dir_anyhow(&self) -> anyhow::Result<()>;
54
55 fn create_dir_all_anyhow(&self) -> anyhow::Result<()>;
57
58 fn hard_link_anyhow<P>(&self, link: P) -> anyhow::Result<()>
60 where
61 P: AsRef<Path>;
62
63 fn read_anyhow(&self) -> anyhow::Result<Vec<u8>>;
65
66 fn read_to_string_anyhow(&self) -> anyhow::Result<String>;
68
69 fn remove_dir_anyhow(&self) -> anyhow::Result<()>;
71
72 fn remove_dir_all_anyhow(&self) -> anyhow::Result<()>;
74
75 fn remove_file_anyhow(&self) -> anyhow::Result<()>;
77
78 fn rename_anyhow<P>(&self, to: P) -> anyhow::Result<()>
80 where
81 P: AsRef<Path>;
82
83 fn set_permissions_anyhow(&self, perm: Permissions) -> anyhow::Result<()>;
85
86 fn set_readonly_anyhow(&self, readonly: bool) -> anyhow::Result<()>;
91
92 fn write_anyhow<C>(&self, contents: C) -> anyhow::Result<()>
94 where
95 C: AsRef<[u8]>;
96
97 fn set_to_current_dir_anyhow(&self) -> anyhow::Result<()>;
99
100 fn open_file_anyhow(&self) -> anyhow::Result<File>;
103
104 fn create_file_anyhow(&self) -> anyhow::Result<File>;
106}
107
108macro_rules! wrap_method {
109 ( $method:ident, $cb:expr, $ret:ty, None: $errordesc:expr ) => {
110 fn $method(&self) -> anyhow::Result<$ret> {
111 let p = self.as_ref();
112 $cb(p)
113 .ok_or_else(|| anyhow::Error::msg($errordesc))
114 .with_context(|| format!("while processing path {:?}", p.display()))
115 }
116 };
117
118 ( $method:ident, $cb:expr, $ret:ty ) => {
119 fn $method(&self) -> anyhow::Result<$ret> {
120 $cb(self).with_context(|| format!("while processing path {:?}", self.display()))
121 }
122 };
123
124 ( $method:ident, $cb:expr, AsRefPath: $arg:ident, $ret:ty ) => {
125 fn $method<Q>(&self, $arg: Q) -> anyhow::Result<$ret>
126 where
127 Q: AsRef<Path>,
128 {
129 let argref = $arg.as_ref();
130 $cb(self, argref)
131 .with_context(|| format!("with {} {:?}", stringify!($arg), argref.display()))
132 .with_context(|| format!("while processing path {:?}", self.display()))
133 }
134 };
135}
136
137impl PathAnyhow for Path {
138 wrap_method!(to_str_anyhow, Path::to_str, &str, None: "invalid UTF8");
139
140 wrap_method!(
141 parent_anyhow,
142 Path::parent,
143 &Path,
144 None: "expected parent directory"
145 );
146
147 wrap_method!(
148 file_name_anyhow,
149 Path::file_name,
150 &OsStr,
151 None: "missing expected filename"
152 );
153
154 wrap_method!(
155 strip_prefix_anyhow,
156 Path::strip_prefix,
157 AsRefPath: prefix,
158 &Path
159 );
160
161 wrap_method!(
162 file_stem_anyhow,
163 Path::file_stem,
164 &OsStr,
165 None: "missing expected filename"
166 );
167
168 wrap_method!(
169 extension_anyhow,
170 Path::extension,
171 &OsStr,
172 None: "missing expected extension"
173 );
174
175 wrap_method!(
176 metadata_anyhow,
177 |p: &Path| p.metadata().map(|md| Metadata::from((md, p.to_path_buf()))),
178 Metadata
179 );
180 wrap_method!(
181 symlink_metadata_anyhow,
182 |p: &Path| p
183 .symlink_metadata()
184 .map(|md| Metadata::from((md, p.to_path_buf()))),
185 Metadata
186 );
187 wrap_method!(canonicalize_anyhow, Path::canonicalize, PathBuf);
188 wrap_method!(read_link_anyhow, Path::read_link, PathBuf);
189 wrap_method!(read_dir_anyhow, ReadDir::from_path, ReadDir);
190 wrap_method!(copy_anyhow, std::fs::copy, AsRefPath: copy_to, u64);
191 wrap_method!(create_dir_anyhow, std::fs::create_dir, ());
192 wrap_method!(create_dir_all_anyhow, std::fs::create_dir_all, ());
193 wrap_method!(hard_link_anyhow, std::fs::hard_link, AsRefPath: link_to, ());
194 wrap_method!(read_anyhow, std::fs::read, Vec<u8>);
195 wrap_method!(read_to_string_anyhow, std::fs::read_to_string, String);
196 wrap_method!(remove_dir_anyhow, std::fs::remove_dir, ());
197 wrap_method!(remove_dir_all_anyhow, std::fs::remove_dir_all, ());
198 wrap_method!(remove_file_anyhow, std::fs::remove_file, ());
199 wrap_method!(rename_anyhow, std::fs::rename, AsRefPath: rename_to, ());
200
201 fn set_permissions_anyhow(&self, perms: Permissions) -> anyhow::Result<()> {
202 std::fs::set_permissions(self, perms.clone())
203 .with_context(|| format!("with permissions {:?}", perms))
204 .with_context(|| format!("while processing path {:?}", self.display()))
205 }
206
207 fn set_readonly_anyhow(&self, readonly: bool) -> anyhow::Result<()> {
208 let mut perms = self.metadata_anyhow()?.permissions();
209 perms.set_readonly(readonly);
210 self.set_permissions_anyhow(perms)
211 }
212
213 fn write_anyhow<C>(&self, contents: C) -> anyhow::Result<()>
214 where
215 C: AsRef<[u8]>,
216 {
217 std::fs::write(self, contents)
218 .with_context(|| format!("while writing to {:?}", self.display()))
219 }
220
221 wrap_method!(set_to_current_dir_anyhow, std::env::set_current_dir, ());
222 wrap_method!(open_file_anyhow, File::open, File);
223 wrap_method!(create_file_anyhow, File::create, File);
224}
225
226#[cfg(test)]
227mod tests;