use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ManifestError {
#[error("agents.yml io error{path}: {source}", path = fmt_path(.path.as_ref()))]
Io {
#[source]
source: std::io::Error,
path: Option<PathBuf>,
},
#[error("agents.yml is not valid YAML{path}: {source}", path = fmt_path(.path.as_ref()))]
ParseYaml {
#[source]
source: serde_yaml_ng::Error,
path: Option<PathBuf>,
},
#[error("agents.yml failed schema validation{path}: {message}", path = fmt_path(.path.as_ref()))]
Schema {
message: String,
path: Option<PathBuf>,
},
}
impl ManifestError {
#[must_use]
pub fn with_path(mut self, p: impl Into<PathBuf>) -> Self {
let new_path = p.into();
match &mut self {
Self::Io { path, .. } | Self::ParseYaml { path, .. } | Self::Schema { path, .. } => {
*path = Some(new_path);
}
}
self
}
pub const fn path(&self) -> Option<&PathBuf> {
match self {
Self::Io { path, .. } | Self::ParseYaml { path, .. } | Self::Schema { path, .. } => {
path.as_ref()
}
}
}
}
#[derive(Debug, Error)]
pub enum LockfileError {
#[error("agents.lock io error{path}: {source}", path = fmt_path(.path.as_ref()))]
Io {
#[source]
source: std::io::Error,
path: Option<PathBuf>,
},
#[error("agents.lock is not valid JSON{path}: {source}", path = fmt_path(.path.as_ref()))]
ParseJson {
#[source]
source: serde_json::Error,
path: Option<PathBuf>,
},
#[error("agents.lock failed schema validation{path}: {message}", path = fmt_path(.path.as_ref()))]
Schema {
message: String,
path: Option<PathBuf>,
},
}
impl LockfileError {
#[must_use]
pub fn with_path(mut self, p: impl Into<PathBuf>) -> Self {
let new_path = p.into();
match &mut self {
Self::Io { path, .. } | Self::ParseJson { path, .. } | Self::Schema { path, .. } => {
*path = Some(new_path);
}
}
self
}
pub const fn path(&self) -> Option<&PathBuf> {
match self {
Self::Io { path, .. } | Self::ParseJson { path, .. } | Self::Schema { path, .. } => {
path.as_ref()
}
}
}
}
fn fmt_path(p: Option<&PathBuf>) -> String {
p.map_or_else(String::new, |path| format!(" at {}", redact(path)))
}
fn redact(path: &std::path::Path) -> String {
if let Ok(cwd) = std::env::current_dir() {
if let Ok(rel) = path.strip_prefix(&cwd) {
return rel.to_string_lossy().replace('\\', "/");
}
}
path.file_name().map_or_else(
|| path.display().to_string(),
|n| n.to_string_lossy().into_owned(),
)
}