1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use std::{
    env,
    fs::{self, File, OpenOptions, ReadDir},
    io,
    path::{Path, PathBuf},
};

/// TmpDir is a struct to manipulate a temporary directory.
///
/// The "path" argument (in some methods) must be relative
/// because it will be joined to the temporary directory path.
///
/// When this struct is dropped, the temporary directory
/// itself is automatically deleted.
pub struct TmpDir(PathBuf);

impl TmpDir {
    pub fn new() -> Result<Self, io::Error> {
        let path = env::temp_dir().join(uuid::Uuid::new_v4().to_string());
        fs::create_dir(&path)?;
        Ok(TmpDir(path))
    }

    pub fn path(&self) -> &PathBuf {
        &self.0
    }

    /// Generates a new unique path in the temporary directory.
    pub fn unique_path(&self) -> PathBuf {
        self.path().join(uuid::Uuid::new_v4().to_string())
    }

    /// Writes to a file (or create it if it doesn't exist)
    /// in the temporary directory. See [`fs::write`].
    pub fn write_file<P, C>(&self, path: P, contents: C) -> io::Result<()>
    where
        P: AsRef<Path>,
        C: AsRef<[u8]>,
    {
        fs::write(self.path().join(path), contents)
    }

    /// Reads a file in the temporary directory. See [`fs::read`].
    pub fn read_file<P>(&self, path: P) -> io::Result<Vec<u8>>
    where
        P: AsRef<Path>,
    {
        fs::read(self.path().join(path))
    }

    /// Creates a directory inside the temporary directory.
    /// See [`fs::create_dir`].
    pub fn create_dir<P>(&self, path: P) -> io::Result<()>
    where
        P: AsRef<Path>,
    {
        fs::create_dir(self.path().join(path))
    }

    /// Creates a directory and all of its parent if they are
    /// missing (inside the temporary directory). See
    /// [`fs::create_dir_all`].
    pub fn create_dir_all<P>(&self, path: P) -> io::Result<()>
    where
        P: AsRef<Path>,
    {
        fs::create_dir_all(self.path().join(path))
    }

    /// Creates a file in the temporary directory. See
    /// [`File::create`].
    pub fn create_file<P>(&self, path: P) -> io::Result<File>
    where
        P: AsRef<Path>,
    {
        File::create(self.path().join(path))
    }

    /// Opens a file in the temporary directory. See
    /// [`File::open`].
    pub fn open_file<P>(&self, path: P) -> io::Result<File>
    where
        P: AsRef<Path>,
    {
        File::open(self.path().join(path))
    }

    /// Opens a file in the temporary directory using the
    /// provided OpenOptions. See [`fs::OpenOptions::open`].
    pub fn open_file_with_opts<P>(&self, opts: &mut OpenOptions, path: P) -> io::Result<File>
    where
        P: AsRef<Path>,
    {
        opts.open(self.path().join(path))
    }

    /// Gets metadata for the given path. Akin to [`fs::metadata`].
    pub fn metadata<P>(&self, path: P) -> io::Result<fs::Metadata>
    where
        P: AsRef<Path>,
    {
        self.path().join(path.as_ref()).metadata()
    }

    /// Checks if a path exists in the current directory. Akin
    /// to [`Path::exists`].
    pub fn exists<P>(&self, path: P) -> bool
    where
        P: AsRef<Path>,
    {
        self.path().join(path.as_ref()).exists()
    }

    /// Reads temporary directory. Akin to [`fs::read_dir`].
    pub fn read_dir(&self) -> io::Result<ReadDir> {
        fs::read_dir(self.path())
    }
}

/// Impl Drop trait so when the TmpDir is dropped, the directory
/// is deleted.
impl Drop for TmpDir {
    fn drop(&mut self) {
        fs::remove_dir_all(&self.0).ok();
    }
}

/// TmpFile is a struct to manipulate a temporary file.
///
/// When this struct is dropped, the temporary file itself is
/// automatically deleted.
pub struct TmpFile(PathBuf);

impl TmpFile {
    pub fn new() -> Result<Self, io::Error> {
        let path = env::temp_dir().join(uuid::Uuid::new_v4().to_string());
        fs::write(&path, &[])?;
        Ok(TmpFile(path))
    }

    pub fn path(&self) -> &PathBuf {
        &self.0
    }

    /// Writes to the temporary file. See [`fs::write`].
    pub fn write_file<C>(&self, contents: C) -> io::Result<()>
    where
        C: AsRef<[u8]>,
    {
        fs::write(self.path(), contents)
    }

    /// Reads a the temporary file. See [`fs::read`].
    pub fn read_file(&self) -> io::Result<Vec<u8>> {
        fs::read(self.path())
    }

    /// Opens the temporary file. See [`File::open`].
    pub fn open(&self) -> io::Result<File> {
        File::open(self.path())
    }

    /// Opens the temporary file using the provided OpenOptions.
    /// See [`fs::OpenOptions::open`].
    pub fn open_with_opts(&self, opts: &mut OpenOptions) -> io::Result<File> {
        opts.open(self.path())
    }
}