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>;
27}
28
29impl PathCreateDir for ChildPath {
30 fn create_dir_all(&self) -> Result<(), FixtureError> {
31 create_dir_all(self.as_path())
32 }
33}
34
35pub trait FileTouch {
38 fn touch(&self) -> Result<(), FixtureError>;
51}
52
53impl FileTouch for ChildPath {
54 fn touch(&self) -> Result<(), FixtureError> {
55 touch(self.as_path())
56 }
57}
58
59impl FileTouch for NamedUtf8TempFile {
60 fn touch(&self) -> Result<(), FixtureError> {
61 touch(self.path())
62 }
63}
64
65pub trait FileWriteBin {
68 fn write_binary(&self, data: &[u8]) -> Result<(), FixtureError>;
84}
85
86impl FileWriteBin for ChildPath {
87 fn write_binary(&self, data: &[u8]) -> Result<(), FixtureError> {
88 write_binary(self.as_path(), data)
89 }
90}
91
92impl FileWriteBin for NamedUtf8TempFile {
93 fn write_binary(&self, data: &[u8]) -> Result<(), FixtureError> {
94 write_binary(self.path(), data)
95 }
96}
97
98pub trait FileWriteStr {
100 fn write_str(&self, data: &str) -> Result<(), FixtureError>;
115}
116
117impl FileWriteStr for ChildPath {
118 fn write_str(&self, data: &str) -> Result<(), FixtureError> {
119 write_str(self.as_path(), data)
120 }
121}
122
123impl FileWriteStr for NamedUtf8TempFile {
124 fn write_str(&self, data: &str) -> Result<(), FixtureError> {
125 write_str(self.path(), data)
126 }
127}
128
129pub trait FileWriteFile {
131 fn write_file(&self, data: &Utf8Path) -> Result<(), FixtureError>;
148}
149
150impl FileWriteFile for ChildPath {
151 fn write_file(&self, data: &Utf8Path) -> Result<(), FixtureError> {
152 write_file(self.as_path(), data)
153 }
154}
155
156impl FileWriteFile for NamedUtf8TempFile {
157 fn write_file(&self, data: &Utf8Path) -> Result<(), FixtureError> {
158 write_file(self.path(), data)
159 }
160}
161
162pub trait PathCopy {
164 fn copy_from<P: AsRef<Utf8Path>, S: AsRef<str>>(
177 &self,
178 source: P,
179 patterns: &[S],
180 ) -> Result<(), FixtureError>;
181}
182
183impl PathCopy for Utf8TempDir {
184 fn copy_from<P: AsRef<Utf8Path>, S: AsRef<str>>(
185 &self,
186 source: P,
187 patterns: &[S],
188 ) -> Result<(), FixtureError> {
189 copy_files(self.path(), source.as_ref(), patterns)
190 }
191}
192
193impl PathCopy for ChildPath {
194 fn copy_from<P: AsRef<Utf8Path>, S: AsRef<str>>(
195 &self,
196 source: P,
197 patterns: &[S],
198 ) -> Result<(), FixtureError> {
199 copy_files(self.as_path(), source.as_ref(), patterns)
200 }
201}
202
203pub trait SymlinkToFile {
206 fn symlink_to_file<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError>;
222}
223
224impl SymlinkToFile for ChildPath {
225 fn symlink_to_file<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError> {
226 symlink_to_file(self.as_path(), target.as_ref())
227 }
228}
229
230impl SymlinkToFile for NamedUtf8TempFile {
231 fn symlink_to_file<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError> {
232 symlink_to_file(self.path(), target.as_ref())
233 }
234}
235
236pub trait SymlinkToDir {
238 fn symlink_to_dir<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError>;
254}
255
256impl SymlinkToDir for ChildPath {
257 fn symlink_to_dir<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError> {
258 symlink_to_dir(self.as_path(), target.as_ref())
259 }
260}
261
262impl SymlinkToDir for Utf8TempDir {
263 fn symlink_to_dir<P: AsRef<Path>>(&self, target: P) -> Result<(), FixtureError> {
264 symlink_to_dir(self.path(), target.as_ref())
265 }
266}
267
268fn ensure_parent_dir(path: &Utf8Path) -> Result<(), FixtureError> {
269 if let Some(parent) = path.parent() {
270 fs::create_dir_all(parent).chain(FixtureError::new(FixtureKind::CreateDir))?;
271 }
272 Ok(())
273}
274
275fn create_dir_all(path: &Utf8Path) -> Result<(), FixtureError> {
276 fs::create_dir_all(path).chain(FixtureError::new(FixtureKind::CreateDir))?;
277 Ok(())
278}
279
280fn touch(path: &Utf8Path) -> Result<(), FixtureError> {
281 ensure_parent_dir(path)?;
282 fs::File::create(path).chain(FixtureError::new(FixtureKind::WriteFile))?;
283 Ok(())
284}
285
286fn write_binary(path: &Utf8Path, data: &[u8]) -> Result<(), FixtureError> {
287 ensure_parent_dir(path)?;
288 let mut file = fs::File::create(path).chain(FixtureError::new(FixtureKind::WriteFile))?;
289 file.write_all(data)
290 .chain(FixtureError::new(FixtureKind::WriteFile))?;
291 Ok(())
292}
293
294fn write_str(path: &Utf8Path, data: &str) -> Result<(), FixtureError> {
295 ensure_parent_dir(path)?;
296 write_binary(path, data.as_bytes()).chain(FixtureError::new(FixtureKind::WriteFile))
297}
298
299fn write_file(path: &Utf8Path, data: &Utf8Path) -> Result<(), FixtureError> {
300 ensure_parent_dir(path)?;
301 fs::copy(data, path).chain(FixtureError::new(FixtureKind::CopyFile))?;
302 Ok(())
303}
304
305fn copy_files<S>(target: &Utf8Path, source: &Utf8Path, patterns: &[S]) -> Result<(), FixtureError>
306where
307 S: AsRef<str>,
308{
309 let source = source
311 .canonicalize()
312 .chain(FixtureError::new(FixtureKind::Walk))?;
313
314 let target = target.as_std_path();
317
318 for entry in GlobWalkerBuilder::from_patterns(&source, patterns)
319 .follow_links(true)
320 .build()
321 .chain(FixtureError::new(FixtureKind::Walk))?
322 {
323 let entry = entry.chain(FixtureError::new(FixtureKind::Walk))?;
324 let rel = entry
325 .path()
326 .strip_prefix(&source)
327 .expect("entries to be under `source`");
328 let target_path = target.join(rel);
329 if entry.file_type().is_dir() {
330 fs::create_dir_all(target_path).chain(FixtureError::new(FixtureKind::CreateDir))?;
331 } else if entry.file_type().is_file() {
332 fs::create_dir_all(target_path.parent().expect("at least `target` exists"))
333 .chain(FixtureError::new(FixtureKind::CreateDir))?;
334 fs::copy(entry.path(), target_path).chain(FixtureError::new(FixtureKind::CopyFile))?;
335 }
336 }
337 Ok(())
338}
339
340#[cfg(windows)]
341fn symlink_to_file(link: &Utf8Path, target: &Path) -> Result<(), FixtureError> {
342 std::os::windows::fs::symlink_file(target, link)
343 .chain(FixtureError::new(FixtureKind::Symlink))?;
344 Ok(())
345}
346
347#[cfg(windows)]
348fn symlink_to_dir(link: &Utf8Path, target: &Path) -> Result<(), FixtureError> {
349 std::os::windows::fs::symlink_dir(target, link)
350 .chain(FixtureError::new(FixtureKind::Symlink))?;
351 Ok(())
352}
353
354#[cfg(not(windows))]
355fn symlink_to_file(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(not(windows))]
361fn symlink_to_dir(link: &Utf8Path, target: &Path) -> Result<(), FixtureError> {
362 std::os::unix::fs::symlink(target, link).chain(FixtureError::new(FixtureKind::Symlink))?;
363 Ok(())
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369 use crate::fixture::PathChild;
370
371 #[test]
372 fn test_symlink_to_file() {
373 let temp_dir = Utf8TempDir::new().unwrap();
374 let file = temp_dir.child("file");
375 file.touch().unwrap();
376 let link = temp_dir.child("link");
377 link.symlink_to_file(&file).unwrap();
378
379 assert!(link.exists());
380 assert!(link.is_symlink());
381 assert_eq!(link.read_link_utf8().unwrap(), file);
382 }
383}