use std::path::{Component, PathBuf};
use serde::{Deserialize, Serialize};
use crate::completion::ToolAnnotations;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Touches {
Reads(Scope),
Writes(Scope),
Independent,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Scope {
Paths(Vec<PathBuf>),
Anything,
}
impl Scope {
pub fn overlaps(&self, other: &Scope) -> bool {
match (self, other) {
(Self::Paths(ours), Self::Paths(theirs)) => ours.iter().any(|ours| {
theirs
.iter()
.any(|theirs| ours.starts_with(theirs) || theirs.starts_with(ours))
}),
(Self::Anything, _) | (_, Self::Anything) => true,
}
}
pub fn normalized(self) -> Scope {
match self {
Self::Paths(paths) => paths
.into_iter()
.map(normal)
.collect::<Option<Vec<_>>>()
.map_or(Self::Anything, Self::Paths),
Self::Anything => Self::Anything,
}
}
}
fn normal(path: PathBuf) -> Option<PathBuf> {
if !path.is_absolute() {
return None;
}
let mut normal = PathBuf::new();
for component in path.components() {
match component {
Component::ParentDir => {
normal.pop();
}
Component::CurDir => {}
Component::Prefix(_) | Component::RootDir | Component::Normal(_) => {
normal.push(component);
}
}
}
Some(normal)
}
impl Touches {
pub fn conflicts_with(&self, other: &Touches) -> bool {
let (ours, we_write) = self.parts();
let (theirs, they_write) = other.parts();
(we_write || they_write) && ours.overlaps(theirs)
}
pub fn reads_only(&self) -> bool {
matches!(self, Self::Reads(_))
}
pub fn normalized(self) -> Touches {
match self {
Self::Reads(scope) => Self::Reads(scope.normalized()),
Self::Writes(scope) => Self::Writes(scope.normalized()),
Self::Independent => Self::Independent,
}
}
pub fn from_annotations(annotations: &ToolAnnotations) -> Self {
match annotations.read_only() {
true => Self::Reads(Scope::Anything),
false => Self::Writes(Scope::Anything),
}
}
pub fn from_hints(annotations: &ToolAnnotations) -> Self {
match annotations.read_only() {
true => Self::Independent,
false => Self::Writes(Scope::Anything),
}
}
fn parts(&self) -> (&Scope, bool) {
match self {
Self::Reads(scope) => (scope, false),
Self::Writes(scope) => (scope, true),
Self::Independent => (&Scope::Anything, false),
}
}
}
#[cfg(test)]
mod tests;