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
use std::fs::{File, OpenOptions};
use std::path::{Component, Path, PathBuf};
pub use crate::prelude::*;
pub struct Datadir {
inner: Option<DatadirInner>,
}
pub struct DatadirInner {
base: tempfile::TempDir,
}
impl Default for Datadir {
fn default() -> Self {
Self { inner: None }
}
}
impl ContextElement for Datadir {
fn created(&mut self, scenario: &Scenario) {
assert!(self.inner.is_none());
self.inner = DatadirInner::build(scenario.title());
}
}
impl DatadirInner {
fn build(title: &str) -> Option<Self> {
let safe_title: String = title
.chars()
.map(|c| match c {
'a'..='z' | 'A'..='Z' | '0'..='9' => c,
_ => '-',
})
.collect();
let base = tempfile::Builder::new()
.prefix("subplot")
.suffix(&safe_title)
.rand_bytes(5)
.tempdir()
.ok()?;
Some(Self { base })
}
}
impl Datadir {
fn inner(&self) -> &DatadirInner {
self.inner
.as_ref()
.expect("Attempted to access Datadir too early (or late)")
}
pub fn base_path(&self) -> &Path {
self.inner().base.path()
}
#[throws(StepError)]
pub fn canonicalise_filename<S: AsRef<Path>>(&self, subpath: S) -> PathBuf {
let mut ret = self.base_path().to_path_buf();
for component in subpath.as_ref().components() {
match component {
Component::CurDir => {}
Component::ParentDir => {
throw!("embedded filenames may not contain ..");
}
Component::RootDir | Component::Prefix(_) => {
throw!("embedded filenames must be relative");
}
c => ret.push(c),
}
}
ret
}
#[throws(StepError)]
pub fn open_write<S: AsRef<Path>>(&self, subpath: S) -> File {
let full_path = self.canonicalise_filename(subpath)?;
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(full_path)?
}
#[throws(StepError)]
pub fn create_dir_all<S: AsRef<Path>>(&self, subpath: S) {
let full_path = self.canonicalise_filename(subpath)?;
std::fs::create_dir_all(full_path)?;
}
}
#[step]
pub fn datadir_has_enough_space(datadir: &Datadir, bytes: u64) {
let available = fs2::available_space(datadir.base_path())?;
if available < bytes {
throw!(format!(
"Available space check failed, wanted {} bytes, but only {} were available",
bytes, available
));
}
}
#[step]
pub fn datadir_has_enough_space_megabytes(context: &ScenarioContext, megabytes: u64) {
datadir_has_enough_space::call(context, megabytes * 1024 * 1024)
}