1use std::fs;
4use std::io::Write;
5use std::path;
6
7use super::errors::FixtureError;
8use super::errors::FixtureKind;
9use super::errors::ResultChainExt;
10use super::ChildPath;
11use super::NamedTempFile;
12use super::TempDir;
13
14pub trait PathCreateDir {
17 fn create_dir_all(&self) -> Result<(), FixtureError>;
30}
31
32impl PathCreateDir for ChildPath {
33 fn create_dir_all(&self) -> Result<(), FixtureError> {
34 create_dir_all(self.path())
35 }
36}
37
38pub trait FileTouch {
41 fn touch(&self) -> Result<(), FixtureError>;
54}
55
56impl FileTouch for ChildPath {
57 fn touch(&self) -> Result<(), FixtureError> {
58 touch(self.path())
59 }
60}
61
62impl FileTouch for NamedTempFile {
63 fn touch(&self) -> Result<(), FixtureError> {
64 touch(self.path())
65 }
66}
67
68pub trait FileWriteBin {
71 fn write_binary(&self, data: &[u8]) -> Result<(), FixtureError>;
87}
88
89impl FileWriteBin for ChildPath {
90 fn write_binary(&self, data: &[u8]) -> Result<(), FixtureError> {
91 write_binary(self.path(), data)
92 }
93}
94
95impl FileWriteBin for NamedTempFile {
96 fn write_binary(&self, data: &[u8]) -> Result<(), FixtureError> {
97 write_binary(self.path(), data)
98 }
99}
100
101pub trait FileWriteStr {
104 fn write_str(&self, data: &str) -> Result<(), FixtureError>;
120}
121
122impl FileWriteStr for ChildPath {
123 fn write_str(&self, data: &str) -> Result<(), FixtureError> {
124 write_str(self.path(), data)
125 }
126}
127
128impl FileWriteStr for NamedTempFile {
129 fn write_str(&self, data: &str) -> Result<(), FixtureError> {
130 write_str(self.path(), data)
131 }
132}
133
134pub trait FileWriteFile {
137 fn write_file(&self, data: &path::Path) -> Result<(), FixtureError>;
154}
155
156impl FileWriteFile for ChildPath {
157 fn write_file(&self, data: &path::Path) -> Result<(), FixtureError> {
158 write_file(self.path(), data)
159 }
160}
161
162impl FileWriteFile for NamedTempFile {
163 fn write_file(&self, data: &path::Path) -> Result<(), FixtureError> {
164 write_file(self.path(), data)
165 }
166}
167
168pub trait PathCopy {
171 fn copy_from<P, S>(&self, source: P, patterns: &[S]) -> Result<(), FixtureError>
184 where
185 P: AsRef<path::Path>,
186 S: AsRef<str>;
187}
188
189impl PathCopy for TempDir {
190 fn copy_from<P, S>(&self, source: P, patterns: &[S]) -> Result<(), FixtureError>
191 where
192 P: AsRef<path::Path>,
193 S: AsRef<str>,
194 {
195 copy_files(self.path(), source.as_ref(), patterns)
196 }
197}
198
199impl PathCopy for ChildPath {
200 fn copy_from<P, S>(&self, source: P, patterns: &[S]) -> Result<(), FixtureError>
201 where
202 P: AsRef<path::Path>,
203 S: AsRef<str>,
204 {
205 copy_files(self.path(), source.as_ref(), patterns)
206 }
207}
208
209pub trait SymlinkToFile {
212 fn symlink_to_file<P>(&self, target: P) -> Result<(), FixtureError>
228 where
229 P: AsRef<path::Path>;
230}
231
232impl SymlinkToFile for ChildPath {
233 fn symlink_to_file<P>(&self, target: P) -> Result<(), FixtureError>
234 where
235 P: AsRef<path::Path>,
236 {
237 symlink_to_file(self.path(), target.as_ref())
238 }
239}
240
241impl SymlinkToFile for NamedTempFile {
242 fn symlink_to_file<P>(&self, target: P) -> Result<(), FixtureError>
243 where
244 P: AsRef<path::Path>,
245 {
246 symlink_to_file(self.path(), target.as_ref())
247 }
248}
249
250pub trait SymlinkToDir {
253 fn symlink_to_dir<P>(&self, target: P) -> Result<(), FixtureError>
269 where
270 P: AsRef<path::Path>;
271}
272
273impl SymlinkToDir for ChildPath {
274 fn symlink_to_dir<P>(&self, target: P) -> Result<(), FixtureError>
275 where
276 P: AsRef<path::Path>,
277 {
278 symlink_to_dir(self.path(), target.as_ref())
279 }
280}
281
282impl SymlinkToDir for TempDir {
283 fn symlink_to_dir<P>(&self, target: P) -> Result<(), FixtureError>
284 where
285 P: AsRef<path::Path>,
286 {
287 symlink_to_dir(self.path(), target.as_ref())
288 }
289}
290
291fn ensure_parent_dir(path: &path::Path) -> Result<(), FixtureError> {
292 if let Some(parent) = path.parent() {
293 fs::create_dir_all(parent).chain(FixtureError::new(FixtureKind::CreateDir))?;
294 }
295 Ok(())
296}
297
298fn create_dir_all(path: &path::Path) -> Result<(), FixtureError> {
299 fs::create_dir_all(path).chain(FixtureError::new(FixtureKind::CreateDir))?;
300 Ok(())
301}
302
303fn touch(path: &path::Path) -> Result<(), FixtureError> {
304 ensure_parent_dir(path)?;
305 fs::File::create(path).chain(FixtureError::new(FixtureKind::WriteFile))?;
306 Ok(())
307}
308
309fn write_binary(path: &path::Path, data: &[u8]) -> Result<(), FixtureError> {
310 ensure_parent_dir(path)?;
311 let mut file = fs::File::create(path).chain(FixtureError::new(FixtureKind::WriteFile))?;
312 file.write_all(data)
313 .chain(FixtureError::new(FixtureKind::WriteFile))?;
314 Ok(())
315}
316
317fn write_str(path: &path::Path, data: &str) -> Result<(), FixtureError> {
318 ensure_parent_dir(path)?;
319 write_binary(path, data.as_bytes()).chain(FixtureError::new(FixtureKind::WriteFile))
320}
321
322fn write_file(path: &path::Path, data: &path::Path) -> Result<(), FixtureError> {
323 ensure_parent_dir(path)?;
324 fs::copy(data, path).chain(FixtureError::new(FixtureKind::CopyFile))?;
325 Ok(())
326}
327
328fn copy_files<S>(
329 target: &path::Path,
330 source: &path::Path,
331 patterns: &[S],
332) -> Result<(), FixtureError>
333where
334 S: AsRef<str>,
335{
336 let source = source
338 .canonicalize()
339 .chain(FixtureError::new(FixtureKind::Walk))?;
340 for entry in globwalk::GlobWalkerBuilder::from_patterns(&source, patterns)
341 .follow_links(true)
342 .build()
343 .chain(FixtureError::new(FixtureKind::Walk))?
344 {
345 let entry = entry.chain(FixtureError::new(FixtureKind::Walk))?;
346 let rel = entry
347 .path()
348 .strip_prefix(&source)
349 .expect("entries to be under `source`");
350 let target_path = target.join(rel);
351 if entry.file_type().is_dir() {
352 fs::create_dir_all(target_path).chain(FixtureError::new(FixtureKind::CreateDir))?;
353 } else if entry.file_type().is_file() {
354 fs::create_dir_all(target_path.parent().expect("at least `target` exists"))
355 .chain(FixtureError::new(FixtureKind::CreateDir))?;
356 fs::copy(entry.path(), target_path).chain(FixtureError::new(FixtureKind::CopyFile))?;
357 }
358 }
359 Ok(())
360}
361
362#[cfg(windows)]
363fn symlink_to_file(link: &path::Path, target: &path::Path) -> Result<(), FixtureError> {
364 std::os::windows::fs::symlink_file(target, link)
365 .chain(FixtureError::new(FixtureKind::Symlink))?;
366 Ok(())
367}
368
369#[cfg(windows)]
370fn symlink_to_dir(link: &path::Path, target: &path::Path) -> Result<(), FixtureError> {
371 std::os::windows::fs::symlink_dir(target, link)
372 .chain(FixtureError::new(FixtureKind::Symlink))?;
373 Ok(())
374}
375
376#[cfg(not(windows))]
377fn symlink_to_file(link: &path::Path, target: &path::Path) -> Result<(), FixtureError> {
378 std::os::unix::fs::symlink(target, link).chain(FixtureError::new(FixtureKind::Symlink))?;
379 Ok(())
380}
381
382#[cfg(not(windows))]
383fn symlink_to_dir(link: &path::Path, target: &path::Path) -> Result<(), FixtureError> {
384 std::os::unix::fs::symlink(target, link).chain(FixtureError::new(FixtureKind::Symlink))?;
385 Ok(())
386}