use std::path::{Path, PathBuf};
use sprawl_guard_lib::{RatchetFile, RelativePath};
use crate::error::Result;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum BaselineScopeRequest {
All,
Paths,
}
pub(super) enum RatchetScope {
All,
Paths(Vec<RelativePath>),
}
impl RatchetScope {
pub(super) fn contains(&self, path: &RelativePath) -> bool {
match self {
Self::All => true,
Self::Paths(paths) => paths.iter().any(|scope| path.is_at_or_under(scope)),
}
}
}
pub(super) fn replacement_scope(
root: &Path,
paths: &[PathBuf],
request: BaselineScopeRequest,
) -> Result<RatchetScope> {
if request == BaselineScopeRequest::All || paths.is_empty() {
return Ok(RatchetScope::All);
}
let paths = paths
.iter()
.map(|path| normalize_scope_path(root, path))
.collect::<Result<Vec<_>>>()?;
Ok(RatchetScope::Paths(paths))
}
pub(super) fn ratchet_update_scope(
root: &Path,
paths: &[PathBuf],
previous: &RatchetFile,
) -> Result<(RatchetScope, Vec<PathBuf>)> {
if paths.is_empty() {
return Ok((RatchetScope::All, Vec::new()));
}
let previous_paths = RatchetPaths::from_file(previous);
let mut scopes = Vec::new();
let mut measured_paths = Vec::new();
for path in paths {
let filesystem_path = if path.is_absolute() {
path.clone()
} else {
root.join(path)
};
let path_exists = filesystem_path.try_exists().map_err(|source| {
sprawl_guard_lib::SprawlError::ReadFileType {
path: filesystem_path.clone(),
source,
}
})?;
if path_exists {
scopes.push(normalize_scope_path(root, path)?);
measured_paths.push(path.clone());
continue;
}
if path.is_absolute() {
return Err(
sprawl_guard_lib::SprawlError::MissingCheckScope { path: path.clone() }.into(),
);
}
let scope = RelativePath::new(path)?;
if !previous_paths.any_at_or_under(&scope) {
return Err(
sprawl_guard_lib::SprawlError::MissingCheckScope { path: path.clone() }.into(),
);
}
scopes.push(scope);
}
Ok((RatchetScope::Paths(scopes), measured_paths))
}
fn normalize_scope_path(root: &Path, path: &Path) -> Result<RelativePath> {
if !path.is_absolute() {
return Ok(RelativePath::new(path)?);
}
let root =
root.canonicalize()
.map_err(|source| sprawl_guard_lib::SprawlError::ResolveRoot {
path: root.to_path_buf(),
source,
})?;
let path =
path.canonicalize()
.map_err(|source| sprawl_guard_lib::SprawlError::ReadFileType {
path: path.to_path_buf(),
source,
})?;
let relative =
path.strip_prefix(&root)
.map_err(|_| sprawl_guard_lib::SprawlError::PathOutsideRoot {
path: path.to_path_buf(),
root,
})?;
Ok(RelativePath::new(relative)?)
}
struct RatchetPaths {
paths: Vec<RelativePath>,
}
impl RatchetPaths {
fn from_file(file: &RatchetFile) -> Self {
let paths = file
.files
.iter()
.map(|entry| entry.path.clone())
.chain(file.test_files.iter().map(|entry| entry.path.clone()))
.chain(file.directories.iter().map(|entry| entry.path.clone()))
.chain(file.test_directories.iter().map(|entry| entry.path.clone()))
.collect();
Self { paths }
}
fn any_at_or_under(&self, scope: &RelativePath) -> bool {
self.paths.iter().any(|path| path.is_at_or_under(scope))
}
}