summaryrefslogtreecommitdiff
path: root/subplotlib/src/steplibrary/files.rs
diff options
context:
space:
mode:
Diffstat (limited to 'subplotlib/src/steplibrary/files.rs')
-rw-r--r--subplotlib/src/steplibrary/files.rs96
1 files changed, 60 insertions, 36 deletions
diff --git a/subplotlib/src/steplibrary/files.rs b/subplotlib/src/steplibrary/files.rs
index 1a55f7a..b1596ae 100644
--- a/subplotlib/src/steplibrary/files.rs
+++ b/subplotlib/src/steplibrary/files.rs
@@ -7,7 +7,7 @@ use std::collections::{HashMap, HashSet};
use std::ffi::OsString;
use std::fs::{self, Metadata, OpenOptions};
use std::io::{self, Write};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};
use filetime::FileTime;
@@ -31,7 +31,7 @@ pub use super::datadir::Datadir;
/// Because files can typically only be named in Subplot documents, we assume they
/// all have names which can be rendered as utf-8 strings.
pub struct Files {
- metadata: HashMap<String, Metadata>,
+ metadata: HashMap<PathBuf, Metadata>,
}
impl ContextElement for Files {
@@ -50,7 +50,7 @@ impl ContextElement for Files {
#[step]
#[context(Datadir)]
pub fn create_from_embedded(context: &ScenarioContext, embedded_file: SubplotDataFile) {
- let filename_on_disk = format!("{}", embedded_file.name().display());
+ let filename_on_disk = PathBuf::from(format!("{}", embedded_file.name().display()));
create_from_embedded_with_other_name::call(context, &filename_on_disk, embedded_file)?;
}
@@ -63,7 +63,7 @@ pub fn create_from_embedded(context: &ScenarioContext, embedded_file: SubplotDat
#[step]
pub fn create_from_embedded_with_other_name(
context: &Datadir,
- filename_on_disk: &str,
+ filename_on_disk: &Path,
embedded_file: SubplotDataFile,
) {
let filename_on_disk = PathBuf::from(filename_on_disk);
@@ -86,11 +86,11 @@ pub fn create_from_embedded_with_other_name(
/// Sets the modification time for the given filename to the provided mtime.
/// If the file does not exist, it will be created.
#[step]
-pub fn touch_with_timestamp(context: &Datadir, filename: &str, mtime: &str) {
+pub fn touch_with_timestamp(context: &Datadir, filename: &Path, mtime: &str) {
let fd = format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour]:[offset_minute]"
);
- let full_time = format!("{} +00:00", mtime);
+ let full_time = format!("{mtime} +00:00");
let ts = OffsetDateTime::parse(&full_time, &fd)?;
let (secs, nanos) = (ts.unix_timestamp(), 0);
let mtime = FileTime::from_unix_time(secs, nanos);
@@ -99,6 +99,7 @@ pub fn touch_with_timestamp(context: &Datadir, filename: &str, mtime: &str) {
drop(
OpenOptions::new()
.create(true)
+ .truncate(false)
.write(true)
.open(&full_path)?,
);
@@ -112,7 +113,7 @@ pub fn touch_with_timestamp(context: &Datadir, filename: &str, mtime: &str) {
///
/// Create/replace the given file with the given content.
#[step]
-pub fn create_from_text(context: &Datadir, text: &str, filename: &str) {
+pub fn create_from_text(context: &Datadir, text: &str, filename: &Path) {
context.open_write(filename)?.write_all(text.as_bytes())?;
}
@@ -125,12 +126,12 @@ pub fn create_from_text(context: &Datadir, text: &str, filename: &str) {
#[step]
#[context(Datadir)]
#[context(Files)]
-pub fn remember_metadata(context: &ScenarioContext, filename: &str) {
+pub fn remember_metadata(context: &ScenarioContext, filename: &Path) {
let full_path = context.with(
|context: &Datadir| context.canonicalise_filename(filename),
false,
)?;
- let metadata = fs::metadata(&full_path)?;
+ let metadata = fs::metadata(full_path)?;
context.with_mut(
|context: &mut Files| {
context.metadata.insert(filename.to_owned(), metadata);
@@ -147,13 +148,14 @@ pub fn remember_metadata(context: &ScenarioContext, filename: &str) {
/// This will create the named file if it does not exist, and then it will ensure that the
/// file's modification time is set to the current time.
#[step]
-pub fn touch(context: &Datadir, filename: &str) {
+pub fn touch(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
let now = FileTime::now();
// If the file doesn't exist, create it
drop(
OpenOptions::new()
.create(true)
+ .truncate(false)
.write(true)
.open(&full_path)?,
);
@@ -167,13 +169,13 @@ pub fn touch(context: &Datadir, filename: &str) {
///
/// This simple step will succeed if the given filename exists in some sense.
#[step]
-pub fn file_exists(context: &Datadir, filename: &str) {
+pub fn file_exists(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
match fs::metadata(full_path) {
Ok(_) => (),
Err(e) => {
if matches!(e.kind(), io::ErrorKind::NotFound) {
- throw!(format!("file '{}' was not found", filename))
+ throw!(format!("file '{}' was not found", filename.display()))
} else {
throw!(e);
}
@@ -187,11 +189,14 @@ pub fn file_exists(context: &Datadir, filename: &str) {
///
/// This simple step will succeed if the given filename does not exist in any sense.
#[step]
-pub fn file_does_not_exist(context: &Datadir, filename: &str) {
+pub fn file_does_not_exist(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
match fs::metadata(full_path) {
Ok(_) => {
- throw!(format!("file '{}' was unexpectedly found", filename))
+ throw!(format!(
+ "file '{}' was unexpectedly found",
+ filename.display()
+ ))
}
Err(e) => {
if !matches!(e.kind(), io::ErrorKind::NotFound) {
@@ -229,10 +234,11 @@ pub fn only_these_exist(context: &Datadir, filenames: &str) {
/// This will load the content of the named file and ensure it contains the given string.
/// Note: this assumes everything is utf-8 encoded. If not, things will fail.
#[step]
-pub fn file_contains(context: &Datadir, filename: &str, data: &str) {
+pub fn file_contains(context: &Datadir, filename: &Path, data: &str) {
let full_path = context.canonicalise_filename(filename)?;
let body = fs::read_to_string(full_path)?;
if !body.contains(data) {
+ println!("file {} contains:\n{}", filename.display(), body);
throw!("expected file content not found");
}
}
@@ -244,10 +250,11 @@ pub fn file_contains(context: &Datadir, filename: &str, data: &str) {
/// This will load the content of the named file and ensure it lacks the given string.
/// Note: this assumes everything is utf-8 encoded. If not, things will fail.
#[step]
-pub fn file_doesnt_contain(context: &Datadir, filename: &str, data: &str) {
+pub fn file_doesnt_contain(context: &Datadir, filename: &Path, data: &str) {
let full_path = context.canonicalise_filename(filename)?;
let body = fs::read_to_string(full_path)?;
if body.contains(data) {
+ println!("file {} contains:\n{}", filename.display(), body);
throw!("unexpected file content found");
}
}
@@ -260,11 +267,12 @@ pub fn file_doesnt_contain(context: &Datadir, filename: &str, data: &str) {
/// matches the given regular expression. This step will fail if the file is not utf-8
/// encoded, or if the regex fails to compile
#[step]
-pub fn file_matches_regex(context: &Datadir, filename: &str, regex: &str) {
+pub fn file_matches_regex(context: &Datadir, filename: &Path, regex: &str) {
let full_path = context.canonicalise_filename(filename)?;
let regex = Regex::new(regex)?;
let body = fs::read_to_string(full_path)?;
if !regex.is_match(&body) {
+ println!("file {} contains:\n{}", filename.display(), body);
throw!("file content does not match given regex");
}
}
@@ -275,12 +283,22 @@ pub fn file_matches_regex(context: &Datadir, filename: &str, regex: &str) {
///
/// This loads the content of the given two files as **bytes** and checks they mach.
#[step]
-pub fn file_match(context: &Datadir, filename1: &str, filename2: &str) {
+pub fn file_match(context: &Datadir, filename1: &Path, filename2: &Path) {
let full_path1 = context.canonicalise_filename(filename1)?;
let full_path2 = context.canonicalise_filename(filename2)?;
let body1 = fs::read(full_path1)?;
let body2 = fs::read(full_path2)?;
if body1 != body2 {
+ println!(
+ "file {} contains:\n{}",
+ filename1.display(),
+ String::from_utf8_lossy(&body1)
+ );
+ println!(
+ "file {} contains:\n{}",
+ filename2.display(),
+ String::from_utf8_lossy(&body2)
+ );
throw!("file contents do not match each other");
}
}
@@ -299,12 +317,12 @@ pub fn file_match(context: &Datadir, filename1: &str, filename2: &str) {
#[step]
#[context(Datadir)]
#[context(Files)]
-pub fn has_remembered_metadata(context: &ScenarioContext, filename: &str) {
+pub fn has_remembered_metadata(context: &ScenarioContext, filename: &Path) {
let full_path = context.with(
|context: &Datadir| context.canonicalise_filename(filename),
false,
)?;
- let metadata = fs::metadata(&full_path)?;
+ let metadata = fs::metadata(full_path)?;
if let Some(remembered) = context.with(
|context: &Files| Ok(context.metadata.get(filename).cloned()),
false,
@@ -314,10 +332,13 @@ pub fn has_remembered_metadata(context: &ScenarioContext, filename: &str) {
|| metadata.len() != remembered.len()
|| metadata.is_file() != remembered.is_file()
{
- throw!(format!("metadata change detected for {}", filename));
+ throw!(format!(
+ "metadata change detected for {}",
+ filename.display()
+ ));
}
} else {
- throw!(format!("no remembered metadata for {}", filename));
+ throw!(format!("no remembered metadata for {}", filename.display()));
}
}
@@ -335,12 +356,12 @@ pub fn has_remembered_metadata(context: &ScenarioContext, filename: &str) {
#[step]
#[context(Datadir)]
#[context(Files)]
-pub fn has_different_metadata(context: &ScenarioContext, filename: &str) {
+pub fn has_different_metadata(context: &ScenarioContext, filename: &Path) {
let full_path = context.with(
|context: &Datadir| context.canonicalise_filename(filename),
false,
)?;
- let metadata = fs::metadata(&full_path)?;
+ let metadata = fs::metadata(full_path)?;
if let Some(remembered) = context.with(
|context: &Files| Ok(context.metadata.get(filename).cloned()),
false,
@@ -350,10 +371,13 @@ pub fn has_different_metadata(context: &ScenarioContext, filename: &str) {
&& metadata.len() == remembered.len()
&& metadata.is_file() == remembered.is_file()
{
- throw!(format!("metadata change not detected for {}", filename));
+ throw!(format!(
+ "metadata change not detected for {}",
+ filename.display()
+ ));
}
} else {
- throw!(format!("no remembered metadata for {}", filename));
+ throw!(format!("no remembered metadata for {}", filename.display()));
}
}
@@ -363,13 +387,13 @@ pub fn has_different_metadata(context: &ScenarioContext, filename: &str) {
///
/// Specifically this checks that the given file has been modified in the past 5 seconds.
#[step]
-pub fn mtime_is_recent(context: &Datadir, filename: &str) {
+pub fn mtime_is_recent(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
let metadata = fs::metadata(full_path)?;
let mtime = metadata.modified()?;
let diff = SystemTime::now().duration_since(mtime)?;
if diff > (Duration::from_secs(5)) {
- throw!(format!("{} is older than 5 seconds", filename));
+ throw!(format!("{} is older than 5 seconds", filename.display()));
}
}
@@ -379,13 +403,13 @@ pub fn mtime_is_recent(context: &Datadir, filename: &str) {
///
/// Specifically this checks that the file was modified at least 39 years ago.
#[step]
-pub fn mtime_is_ancient(context: &Datadir, filename: &str) {
+pub fn mtime_is_ancient(context: &Datadir, filename: &Path) {
let full_path = context.canonicalise_filename(filename)?;
let metadata = fs::metadata(full_path)?;
let mtime = metadata.modified()?;
let diff = SystemTime::now().duration_since(mtime)?;
if diff < (Duration::from_secs(39 * 365 * 24 * 3600)) {
- throw!(format!("{} is younger than 39 years", filename));
+ throw!(format!("{} is younger than 39 years", filename.display()));
}
}
@@ -395,7 +419,7 @@ pub fn mtime_is_ancient(context: &Datadir, filename: &str) {
///
/// This is the equivalent of `mkdir -p` within the data directory for the scenario.
#[step]
-pub fn make_directory(context: &Datadir, path: &str) {
+pub fn make_directory(context: &Datadir, path: &Path) {
context.create_dir_all(path)?;
}
@@ -405,7 +429,7 @@ pub fn make_directory(context: &Datadir, path: &str) {
///
/// This is the equivalent of `rm -rf` within the data directory for the scenario.
#[step]
-pub fn remove_directory(context: &Datadir, path: &str) {
+pub fn remove_directory(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
remove_dir_all::remove_dir_all(full_path)?;
}
@@ -417,7 +441,7 @@ pub fn remove_directory(context: &Datadir, path: &str) {
/// This ensures that the given path exists in the data directory for the scenario and
/// that it is a directory itself.
#[step]
-pub fn path_exists(context: &Datadir, path: &str) {
+pub fn path_exists(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
if !fs::metadata(&full_path)?.is_dir() {
throw!(format!(
@@ -434,7 +458,7 @@ pub fn path_exists(context: &Datadir, path: &str) {
/// This ensures that the given path does not exist in the data directory. If it exists
/// and is not a directory, then this will also fail.
#[step]
-pub fn path_does_not_exist(context: &Datadir, path: &str) {
+pub fn path_does_not_exist(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
match fs::metadata(&full_path) {
Ok(_) => throw!(format!("{} exists", full_path.display())),
@@ -453,7 +477,7 @@ pub fn path_does_not_exist(context: &Datadir, path: &str) {
/// This checks that the given path inside the data directory exists and is an
/// empty directory itself.
#[step]
-pub fn path_is_empty(context: &Datadir, path: &str) {
+pub fn path_is_empty(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
let mut iter = fs::read_dir(&full_path)?;
match iter.next() {
@@ -471,7 +495,7 @@ pub fn path_is_empty(context: &Datadir, path: &str) {
/// directory itself. The step also asserts that the given directory contains at least
/// one entry.
#[step]
-pub fn path_is_not_empty(context: &Datadir, path: &str) {
+pub fn path_is_not_empty(context: &Datadir, path: &Path) {
let full_path = context.canonicalise_filename(path)?;
let mut iter = fs::read_dir(&full_path)?;
match iter.next() {