use std::path::{Path, PathBuf};
use tracing::*;
use crate::Strategy;
pub struct FileExists;
impl<O> Strategy<O> for FileExists {
type E = ();
fn skip(&self, output: &Path, _args_hash: u64, _code_hash: u64) -> bool {
let exists = output.exists();
if exists {
warn!(?output, "Skipping as output exists");
}
exists
}
}
pub fn append_ext(ext: impl AsRef<std::ffi::OsStr>, path: &Path) -> PathBuf {
let mut os_string: std::ffi::OsString = path.into();
os_string.push(".");
os_string.push(ext.as_ref());
os_string.into()
}
pub struct Markers<E> {
pub failure_marker: bool,
pub retriable: Box<dyn Fn(&E) -> bool + Sync + Send>,
pub success_marker: bool,
pub hashes: bool,
pub folder: bool,
}
impl<E> Default for Markers<E> {
fn default() -> Self {
Self {
failure_marker: true,
retriable: Box::new(|_| true),
success_marker: true,
hashes: true,
folder: false,
}
}
}
impl<E> Markers<E> {
fn marker_path(&self, success: bool, output: &Path) -> PathBuf {
let name = if success { "success" } else { "failure" };
if self.folder {
output.join(name)
} else {
append_ext(name, output)
}
}
fn hashes_str(&self, args_hash: u64, code_hash: u64) -> String {
if self.hashes {
format!("{}\n{}", args_hash, code_hash)
} else {
Default::default()
}
}
pub fn folder(mut self) -> Self {
self.folder = true;
self
}
pub fn retriable(mut self, retriable: impl Fn(&E) -> bool + Sync + Send + 'static) -> Self {
self.retriable = Box::new(retriable);
self
}
}
impl<T, E> Strategy<Result<T, E>> for Markers<E> {
type E = std::io::Error;
fn skip(&self, output: &Path, args_hash: u64, code_hash: u64) -> bool {
let check_marker = |path: &Path| {
if let Ok(s) = std::fs::read_to_string(path) {
if s == self.hashes_str(args_hash, code_hash) {
return true;
}
}
false
};
if self.failure_marker {
let marker = self.marker_path(false, output);
if check_marker(&marker) {
warn!(?marker, "Skipping due to failure marker");
return true;
}
}
if self.success_marker {
let marker = self.marker_path(true, output);
match (check_marker(&marker), output.exists()) {
(true, false) => {
warn!(?marker, "Success marker exists, but not the output file");
false
}
(true, true) => {
warn!(?marker, "Skipping due to success marker");
true
}
(false, _) => false,
}
} else {
output.exists()
}
}
fn callback(
&self,
result: &Result<T, E>,
output: &Path,
args_hash: u64,
code_hash: u64,
) -> Result<(), std::io::Error> {
let write_markers = |success: bool| {
if self.folder {
std::fs::create_dir_all(output)?;
}
let path = self.marker_path(success, output);
debug!(
?path,
"Writing {} marker",
if success { "success" } else { "failure" }
);
std::fs::write(path, self.hashes_str(args_hash, code_hash))?;
let _ = std::fs::remove_file(self.marker_path(!success, output));
std::io::Result::Ok(())
};
match result {
Ok(_) if self.success_marker => {
write_markers(true)?;
}
Err(e) if self.failure_marker => {
if (self.retriable)(e) {
debug!("Not writing a failure marker as the error is retriable");
} else {
write_markers(false)?;
}
}
_ => {}
}
Ok(())
}
}