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
169
170
171
172
173
174
175
176
177
// Copyright (c) The camino-tempfile Contributors
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Temporary files and directories with UTF-8 paths.
//!
//! `camino-tempfile` is a wrapper around the [`tempfile`](mod@::tempfile) crate that returns
//! [`Utf8Path`](camino::Utf8Path) rather than [`Path`](std::path::Path).
//!
//! If your code mostly uses [`camino`], it can be annoying to have to convert temporary paths to
//! `Utf8Path` over and over again. This crate takes care of that for you.
//!
//! This library and its documentation are adapted from the [`tempfile`](mod@::tempfile) crate, and
//! are used under the MIT and Apache 2.0 licenses.
//!
//! # Synopsis
//!
//! - Use the [`tempfile()`] function for temporary files
//! - Use the [`tempdir()`] function for temporary directories.
//!
//! # Design
//!
//! This crate provides several approaches to creating temporary files and directories.
//! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed.
//! [`Utf8TempDir`] and [`NamedUtf8TempFile`] both rely on Rust destructors for cleanup.
//!
//! When choosing between the temporary file variants, prefer `tempfile` unless you either need to
//! know the file's path or to be able to persist it.
//!
//! ## Resource Leaking
//!
//! [`tempfile()`] will (almost) never fail to cleanup temporary resources. However, [`Utf8TempDir`]
//! and [`NamedUtf8TempFile`] will fail if their destructors don't run. This is because
//! [`tempfile()`] relies on the OS to cleanup the underlying file, while [`Utf8TempDir`] and
//! [`NamedUtf8TempFile`] rely on Rust destructors to do so. Destructors may fail to run if the
//! process exits through an unhandled signal interrupt (like `SIGINT`), or if the instance is
//! declared statically, among other possible reasons.
//!
//! ## Security
//!
//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
//!
//! [`tempfile()`] doesn't rely on file paths so this isn't an issue. However, [`NamedUtf8TempFile`]
//! does rely on file paths for _some_ operations. See the security documentation on the
//! [`NamedUtf8TempFile`] type for more information.
//!
//! ## Early drop pitfall
//!
//! Because [`Utf8TempDir`] and [`NamedUtf8TempFile`] rely on their destructors for cleanup, this
//! can lead to an unexpected early removal of the directory/file, usually when working with APIs
//! which are generic over `AsRef<Utf8Path>` or `AsRef<Path>`. Consider the following example:
//!
//! ```no_run
//! # use camino_tempfile::tempdir;
//! # use std::io;
//! # use std::process::Command;
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a directory inside of `std::env::temp_dir()`.
//! let temp_dir = tempdir()?;
//!
//! // Spawn the `touch` command inside the temporary directory and collect the exit status
//! // Note that `temp_dir` is **not** moved into `current_dir`, but passed as a reference
//! let exit_status = Command::new("touch").arg("tmp").current_dir(&temp_dir).status()?;
//! assert!(exit_status.success());
//!
//! # Ok(())
//! # }
//! ```
//!
//! This works because a reference to `temp_dir` is passed to `current_dir`, resulting in the
//! destructor of `temp_dir` being run after the `Command` has finished execution. Moving the
//! [`Utf8TempDir`] into the `current_dir` call would result in the [`Utf8TempDir`] being converted
//! into an internal representation, with the original value being dropped and the directory thus
//! being deleted, before the command can be executed.
//!
//! The `touch` command would fail with an `No such file or directory` error.
//!
//! ## Examples
//!
//! Create a temporary file and write some data into it:
//!
//! ```
//! use camino_tempfile::tempfile;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a file inside of `std::env::temp_dir()`.
//! let mut file = tempfile()?;
//!
//! writeln!(file, "Brian was here. Briefly.")?;
//! # Ok(())
//! # }
//! ```
//!
//! Create a named temporary file and open an independent file handle:
//!
//! ```
//! use camino_tempfile::NamedUtf8TempFile;
//! use std::io::{self, Write, Read};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! let text = "Brian was here. Briefly.";
//!
//! // Create a file inside of `std::env::temp_dir()`.
//! let mut file1 = NamedUtf8TempFile::new()?;
//!
//! // Re-open it.
//! let mut file2 = file1.reopen()?;
//!
//! // Write some test data to the first handle.
//! file1.write_all(text.as_bytes())?;
//!
//! // Read the test data using the second handle.
//! let mut buf = String::new();
//! file2.read_to_string(&mut buf)?;
//! assert_eq!(buf, text);
//! # Ok(())
//! # }
//! ```
//!
//! Create a temporary directory and add a file to it:
//!
//! ```
//! use camino_tempfile::tempdir;
//! use std::fs::File;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a directory inside of `std::env::temp_dir()`.
//! let dir = tempdir()?;
//!
//! let file_path = dir.path().join("my-temporary-note.txt");
//! let mut file = File::create(file_path)?;
//! writeln!(file, "Brian was here. Briefly.")?;
//!
//! // By closing the `Utf8TempDir` explicitly, we can check that it has
//! // been deleted successfully. If we don't close it explicitly,
//! // the directory will still be deleted when `dir` goes out
//! // of scope, but we won't know whether deleting the directory
//! // succeeded.
//! drop(file);
//! dir.close()?;
//! # Ok(())
//! # }
//! ```

#![deny(rust_2018_idioms)]

mod builder;
mod dir;
mod errors;
mod file;
mod helpers;

pub use builder::*;
pub use dir::*;
pub use file::*;