camino_tempfile_ext/fixture/
tools.rs1use super::{ChildPath, FixtureError, FixtureKind, ResultChainExt};
7use camino::Utf8Path;
8use camino_tempfile::{NamedUtf8TempFile, Utf8TempDir};
9use globwalk::GlobWalkerBuilder;
10use std::{fs, io::Write, path::Path};
11
12pub trait PathCreateDir {
14 fn create_dir_all(&self) -> Result<(), FixtureError>;
26}
27
28impl PathCreateDir for ChildPath {
29 fn create_dir_all(&self) -> Result<(), FixtureError> {
30 create_dir_all(self.as_path())
31 }
32}
33
34pub trait FileTouch {
36 fn touch(&self) -> Result<(), FixtureError>;
48}
49
50impl FileTouch for ChildPath {
51 fn touch(&self) -> Result<(), FixtureError> {
52 touch(self.as_path())
53 }
54}
55
56impl FileTouch for NamedUtf8TempFile {
57 fn touch(&self) -> Result<(), FixtureError> {
58 touch(self.path())
59 }
60}
61
62pub trait FileWriteBin {
64 fn write_binary(&self, data: &[u8]) -> Result<(), FixtureError>;
78}
79
80impl FileWriteBin for ChildPath {
81 fn write_binary(&self, data: &[u8]) -> Result<(), FixtureError> {
82 write_binary(self.as_path(), data)
83 }
84}
85
86impl FileWriteBin for NamedUtf8TempFile {
87 fn write_binary(&self, data: &[u8]) -> Result<(), FixtureError> {
88 write_binary(self.path(), data)
89 }
90}
91
92pub trait FileWriteStr {
94 fn write_str(&self, data: &str) -> Result<(), FixtureError>;
108}
109
110impl FileWriteStr for ChildPath {
111 fn write_str(&self, data: &str) -> Result<(), FixtureError> {
112 write_str(self.as_path(), data)
113 }
114}
115
116impl FileWriteStr for NamedUtf8TempFile {
117 fn write_str(&self, data: &str) -> Result<(), FixtureError> {
118 write_str(self.path(), data)
119 }
120}
121
122pub trait FileWriteFile {
124 fn write_file(&self, data: &Utf8Path) -> Result<(), FixtureError>;
139}
140
141impl FileWriteFile for ChildPath {
142 fn write_file(&self, data: &Utf8Path) -> Result<(), FixtureError> {
143 write_file(self.as_path(), data)
144 }
145}
146
147impl FileWriteFile for NamedUtf8TempFile {
148 fn write_file(&self, data: &Utf8Path) -> Result<(), FixtureError> {
149 write_file(self.path(), data)
150 }
151}
152
153pub trait PathCopy {
155 fn copy_from<P: AsRef<Utf8Path>, S: AsRef<str>>(
168 &self,
169 source: P,
170 patterns: &[S],
171 ) -> Result<(), FixtureError>;
172}
173
174impl PathCopy for Utf8TempDir {
175 fn copy_from<P: AsRef<Utf8Path>, S: AsRef<str>>(
176 &self,
177 source: P,
178 patterns: &[S],
179 ) -> Result<(), FixtureError> {
180 copy_files(self.path(), source.as_ref(), patterns)
181 }
182}
183
184impl PathCopy for ChildPath {
185 fn copy_from<P: AsRef<Utf8Path>, S: AsRef<str>>(
186 &self,
187 source: P,
188 patterns: &[S],
189 ) -> Result<(), FixtureError> {
190 copy_files(self.as_path(), source.as_ref(), patterns)
191 }
192}
193
194pub trait SymlinkToFile {
196 fn symlink_to_file<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError>;
214}
215
216impl SymlinkToFile for ChildPath {
217 fn symlink_to_file<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError> {
218 symlink_to_file(self.as_path(), target.as_ref())
219 }
220}
221
222impl SymlinkToFile for NamedUtf8TempFile {
223 fn symlink_to_file<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError> {
224 symlink_to_file(self.path(), target.as_ref())
225 }
226}
227
228pub trait SymlinkToDir {
230 fn symlink_to_dir<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError>;
248}
249
250impl SymlinkToDir for ChildPath {
251 fn symlink_to_dir<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError> {
252 symlink_to_dir(self.as_path(), target.as_ref())
253 }
254}
255
256impl SymlinkToDir for Utf8TempDir {
257 fn symlink_to_dir<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError> {
258 symlink_to_dir(self.path(), target.as_ref())
259 }
260}
261
262fn ensure_parent_dir(path: &Utf8Path) -> Result<(), FixtureError> {
263 if let Some(parent) = path.parent() {
264 fs::create_dir_all(parent).chain(FixtureError::new(FixtureKind::CreateDir))?;
265 }
266 Ok(())
267}
268
269fn create_dir_all(path: &Utf8Path) -> Result<(), FixtureError> {
270 fs::create_dir_all(path).chain(FixtureError::new(FixtureKind::CreateDir))?;
271 Ok(())
272}
273
274fn touch(path: &Utf8Path) -> Result<(), FixtureError> {
275 ensure_parent_dir(path)?;
276 fs::File::create(path).chain(FixtureError::new(FixtureKind::WriteFile))?;
277 Ok(())
278}
279
280fn write_binary(path: &Utf8Path, data: &[u8]) -> Result<(), FixtureError> {
281 ensure_parent_dir(path)?;
282 let mut file = fs::File::create(path).chain(FixtureError::new(FixtureKind::WriteFile))?;
283 file.write_all(data)
284 .chain(FixtureError::new(FixtureKind::WriteFile))?;
285 Ok(())
286}
287
288fn write_str(path: &Utf8Path, data: &str) -> Result<(), FixtureError> {
289 ensure_parent_dir(path)?;
290 write_binary(path, data.as_bytes()).chain(FixtureError::new(FixtureKind::WriteFile))
291}
292
293fn write_file(path: &Utf8Path, data: &Utf8Path) -> Result<(), FixtureError> {
294 ensure_parent_dir(path)?;
295 fs::copy(data, path).chain(FixtureError::new(FixtureKind::CopyFile))?;
296 Ok(())
297}
298
299fn copy_files<S>(target: &Utf8Path, source: &Utf8Path, patterns: &[S]) -> Result<(), FixtureError>
300where
301 S: AsRef<str>,
302{
303 let source = source
305 .canonicalize()
306 .chain(FixtureError::new(FixtureKind::Walk))?;
307
308 let target = target.as_std_path();
311
312 for entry in GlobWalkerBuilder::from_patterns(&source, patterns)
313 .follow_links(true)
314 .build()
315 .chain(FixtureError::new(FixtureKind::Walk))?
316 {
317 let entry = entry.chain(FixtureError::new(FixtureKind::Walk))?;
318 let rel = entry
319 .path()
320 .strip_prefix(&source)
321 .expect("entries to be under `source`");
322 let target_path = target.join(rel);
323 if entry.file_type().is_dir() {
324 fs::create_dir_all(target_path).chain(FixtureError::new(FixtureKind::CreateDir))?;
325 } else if entry.file_type().is_file() {
326 fs::create_dir_all(target_path.parent().expect("at least `target` exists"))
327 .chain(FixtureError::new(FixtureKind::CreateDir))?;
328 fs::copy(entry.path(), target_path).chain(FixtureError::new(FixtureKind::CopyFile))?;
329 }
330 }
331 Ok(())
332}
333
334#[cfg(windows)]
335fn symlink_to_file(link: &Utf8Path, target: &Path) -> Result<(), FixtureError> {
336 std::os::windows::fs::symlink_file(target, link)
337 .chain(FixtureError::new(FixtureKind::Symlink))?;
338 Ok(())
339}
340
341#[cfg(windows)]
342fn symlink_to_dir(link: &Utf8Path, target: &Path) -> Result<(), FixtureError> {
343 std::os::windows::fs::symlink_dir(target, link)
344 .chain(FixtureError::new(FixtureKind::Symlink))?;
345 Ok(())
346}
347
348#[cfg(not(windows))]
349fn symlink_to_file(link: &Utf8Path, target: &Path) -> Result<(), FixtureError> {
350 std::os::unix::fs::symlink(target, link).chain(FixtureError::new(FixtureKind::Symlink))?;
351 Ok(())
352}
353
354#[cfg(not(windows))]
355fn symlink_to_dir(link: &Utf8Path, target: &Path) -> Result<(), FixtureError> {
356 std::os::unix::fs::symlink(target, link).chain(FixtureError::new(FixtureKind::Symlink))?;
357 Ok(())
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363 use crate::fixture::PathChild;
364
365 #[test]
366 fn test_symlink_to_file() {
367 let temp_dir = Utf8TempDir::new().unwrap();
368 let file = temp_dir.child("file");
369 file.touch().unwrap();
370 let link = temp_dir.child("link");
371 link.symlink_to_file(&file).unwrap();
372
373 assert!(link.exists());
374 assert!(link.is_symlink());
375 assert_eq!(link.read_link_utf8().unwrap(), file);
376 }
377}