-
Notifications
You must be signed in to change notification settings - Fork 397
/
Copy pathtemp.rs
127 lines (108 loc) · 3.06 KB
/
temp.rs
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
use std::fs;
use std::path::{Path, PathBuf};
use crate::errors::Result;
// open temporary directories and files so we ensure we cleanup on exit.
static mut FILES: Vec<tempfile::NamedTempFile> = vec![];
static mut DIRS: Vec<tempfile::TempDir> = vec![];
fn data_dir() -> Option<PathBuf> {
directories::BaseDirs::new().map(|d| d.data_dir().to_path_buf())
}
pub fn dir() -> Result<PathBuf> {
data_dir()
.map(|p| p.join("cross-rs").join("tmp"))
.ok_or(eyre::eyre!("unable to get data directory"))
}
pub(crate) fn has_tempfiles() -> bool {
// SAFETY: safe, since we only check if the stack is empty.
unsafe { !FILES.is_empty() || !DIRS.is_empty() }
}
/// # Safety
/// Safe as long as we have single-threaded execution.
pub(crate) unsafe fn clean() {
// don't expose FILES or DIRS outside this module,
// so we can only add or remove from the stack using
// our wrappers, guaranteeing add/remove in order.
FILES.clear();
DIRS.clear();
}
/// # Safety
/// Safe as long as we have single-threaded execution.
unsafe fn push_tempfile() -> Result<&'static mut tempfile::NamedTempFile> {
let parent = dir()?;
fs::create_dir_all(&parent).ok();
let file = tempfile::NamedTempFile::new_in(&parent)?;
FILES.push(file);
Ok(FILES.last_mut().expect("file list should not be empty"))
}
/// # Safety
/// Safe as long as we have single-threaded execution.
unsafe fn pop_tempfile() -> Option<tempfile::NamedTempFile> {
FILES.pop()
}
#[derive(Debug)]
pub struct TempFile {
file: &'static mut tempfile::NamedTempFile,
}
impl TempFile {
/// # Safety
/// Safe as long as we have single-threaded execution.
pub unsafe fn new() -> Result<Self> {
Ok(Self {
file: push_tempfile()?,
})
}
pub fn file(&mut self) -> &mut tempfile::NamedTempFile {
self.file
}
#[must_use]
pub fn path(&self) -> &Path {
self.file.path()
}
}
impl Drop for TempFile {
fn drop(&mut self) {
// SAFETY: safe if we only modify the stack via `TempFile`.
unsafe {
pop_tempfile();
}
}
}
/// # Safety
/// Safe as long as we have single-threaded execution.
unsafe fn push_tempdir() -> Result<&'static Path> {
let parent = dir()?;
fs::create_dir_all(&parent).ok();
let dir = tempfile::TempDir::new_in(&parent)?;
DIRS.push(dir);
Ok(DIRS.last().expect("should not be empty").path())
}
/// # Safety
/// Safe as long as we have single-threaded execution.
unsafe fn pop_tempdir() -> Option<tempfile::TempDir> {
DIRS.pop()
}
#[derive(Debug)]
pub struct TempDir {
path: &'static Path,
}
impl TempDir {
/// # Safety
/// Safe as long as we have single-threaded execution.
pub unsafe fn new() -> Result<Self> {
Ok(Self {
path: push_tempdir()?,
})
}
#[must_use]
pub fn path(&self) -> &'static Path {
self.path
}
}
impl Drop for TempDir {
fn drop(&mut self) {
// SAFETY: safe if we only modify the stack via `TempDir`.
unsafe {
pop_tempdir();
}
}
}