use std::fmt;
use std::io;
use std::path::PathBuf;
use crate::InstallStatus;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("unknown agent {name:?} (want one of {}, or all, or detected)", valid.join(", "))]
UnknownAgent {
name: String,
valid: Vec<String>,
},
#[error("unknown scope {value:?} (want project or user)")]
UnknownScope {
value: String,
},
#[error("unknown skill {name:?} (embedded: {})", embedded.join(", "))]
UnknownSkill {
name: String,
embedded: Vec<String>,
},
#[error("no agent selected")]
NoAgentSelected,
#[error(transparent)]
NeedsForce(#[from] ForceRequired),
#[error(
"the search landed on the home directory ({}), so pass --scope user to write there \
on purpose or --dir to name another directory",
dir.display()
)]
ProjectIsHome {
dir: PathBuf,
},
#[error(
"the destination is outside the project root ({} is really {}), so pass --dir to \
write there on purpose",
dir.display(),
real.display()
)]
ProjectEscapes {
dir: PathBuf,
real: PathBuf,
},
#[error("{0}")]
Usage(String),
#[error("{0}")]
Skills(String),
#[error("cancelled")]
Cancelled,
#[error("help requested")]
Help,
#[error("{context}: {source}")]
Io {
context: String,
#[source]
source: io::Error,
},
}
impl Error {
pub(crate) fn io(context: impl Into<String>, source: io::Error) -> Self {
Self::Io { context: context.into(), source }
}
}
#[derive(Debug)]
pub struct ForceRequired {
pub blocked: Vec<InstallStatus>,
}
impl fmt::Display for ForceRequired {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(
"refusing to overwrite skills this tool did not install, \
or that were edited after installing:\n",
)?;
for st in &self.blocked {
writeln!(f, " {} ({})", st.path.display(), st.state)?;
}
f.write_str("re-run with --force to overwrite")
}
}
impl std::error::Error for ForceRequired {}
pub type Result<T, E = Error> = std::result::Result<T, E>;