1use std::path::{Path, PathBuf};
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::time::{SystemTime, UNIX_EPOCH};
4
5pub struct TempFile {
6 dir: TempDir,
8 path: PathBuf,
10}
11
12impl TempFile {
13 pub fn new(file_name: impl AsRef<Path>) -> Self {
15 let dir = TempDir::default();
16 let path = dir.path().join(file_name);
17 std::fs::File::create_new(&path).unwrap();
18 Self { dir, path }
19 }
20
21 pub fn write(&self, data: impl AsRef<[u8]>) {
23 std::fs::write(&self.path, data.as_ref()).unwrap();
24 }
25
26 pub fn assert(&self, expected: impl AsRef<[u8]>) {
28 let actual = std::fs::read(&self.path).unwrap();
29 let expected = expected.as_ref();
30 if actual != expected {
31 println!("expected content: {:?}", expected);
32 println!(" actual content: {:?}", actual);
33 panic!("unexpected content")
34 }
35 }
36
37 pub fn dir(&self) -> &Path {
39 self.dir.path()
40 }
41
42 pub fn path(&self) -> &Path {
44 &self.path
45 }
46
47 pub fn set_readonly(&self, readonly: bool) {
49 let mut permissions = std::fs::metadata(self.path()).unwrap().permissions();
50 permissions.set_readonly(readonly);
51 std::fs::set_permissions(self.path(), permissions).unwrap();
52 }
53}
54
55pub struct TempDir {
56 path: PathBuf,
57}
58
59impl Default for TempDir {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65impl TempDir {
66 pub fn new() -> Self {
68 static COUNTER: AtomicU64 = AtomicU64::new(0);
69 let pid = std::process::id();
70 let nanos = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos();
71 let count = COUNTER.fetch_add(1, Ordering::Relaxed);
72 let name = format!("cli-assert-{pid}-{nanos}-{count}");
73 let path = std::env::temp_dir().join(name);
74 std::fs::create_dir(&path).unwrap();
75 TempDir { path }
76 }
77
78 pub fn path(&self) -> &Path {
80 &self.path
81 }
82}
83
84impl Drop for TempDir {
85 fn drop(&mut self) {
86 _ = std::fs::remove_dir_all(&self.path);
87 }
88}