summaryrefslogtreecommitdiff
path: root/test_util/src/temp_dir.rs
blob: da2b2a06e35d67ca6246842a987b08c205b33c4b (plain)
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
use std::path::Path;
use std::path::PathBuf;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::SystemTime;

use anyhow::Context;
use once_cell::sync::OnceCell;

static TEMP_DIR_SESSION: OnceCell<TempDirSession> = OnceCell::new();

struct TempDirSession {
  default_prefix: String,
  counter: AtomicU32,
}

/// For creating temporary directories in tests.
///
/// This was done because `tempfiles::TempDir` was very slow on Windows.
///
/// Note: Do not use this in actual code as this does not protect against
/// "insecure temporary file" security vulnerabilities.
#[derive(Clone)]
pub struct TempDir(Arc<TempDirInner>);

struct TempDirInner(PathBuf);

impl Drop for TempDirInner {
  fn drop(&mut self) {
    let _ = std::fs::remove_dir_all(&self.0);
  }
}

impl Default for TempDir {
  fn default() -> Self {
    Self::new()
  }
}

impl TempDir {
  pub fn new() -> Self {
    Self::new_inner(&std::env::temp_dir(), None)
  }

  pub fn new_in(path: &Path) -> Self {
    Self::new_inner(path, None)
  }

  pub fn new_with_prefix(prefix: &str) -> Self {
    Self::new_inner(&std::env::temp_dir(), Some(prefix))
  }

  fn new_inner(parent_dir: &Path, prefix: Option<&str>) -> Self {
    let session = TEMP_DIR_SESSION.get_or_init(|| {
      let default_prefix = format!(
        "deno-cli-test-{}",
        SystemTime::now()
          .duration_since(SystemTime::UNIX_EPOCH)
          .unwrap()
          .as_millis()
      );
      TempDirSession {
        default_prefix,
        counter: Default::default(),
      }
    });
    Self({
      let count = session.counter.fetch_add(1, Ordering::SeqCst);
      let path = parent_dir.join(format!(
        "{}{}-{}",
        prefix.unwrap_or(""),
        session.default_prefix,
        count,
      ));
      std::fs::create_dir_all(&path)
        .with_context(|| format!("Error creating temp dir: {}", path.display()))
        .unwrap();
      Arc::new(TempDirInner(path))
    })
  }

  pub fn path(&self) -> &Path {
    let inner = &self.0;
    inner.0.as_path()
  }
}