use std::path::PathBuf;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error(
"no llmlint config found (looked for {names} from {dir} upward); \
run `llmlint init` to create one"
)]
ConfigNotFound { names: String, dir: String },
#[error("config {path}: {message}")]
ConfigParse { path: String, message: String },
#[error("invalid config: {0}")]
InvalidConfig(String),
#[error("unknown bundled plugin '{0}' (known: llmlint:config-lint)")]
UnknownPlugin(String),
#[error("config already exists at {0}; pass --force to overwrite")]
ConfigExists(PathBuf),
#[error(
"oneharness not found ({0}); install it \
(https://github.com/nickderobertis/oneharness) or pass --oneharness-bin"
)]
OneharnessNotFound(String),
#[error("oneharness run failed: {0}")]
Oneharness(String),
#[error("{0}")]
Io(String),
#[error("template error: {0}")]
Template(String),
}
impl Error {
pub fn exit_code(&self) -> i32 {
2
}
}
pub fn io_err(context: impl Into<String>, e: std::io::Error) -> Error {
Error::Io(format!("{}: {e}", context.into()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_variants_exit_two() {
assert_eq!(Error::InvalidConfig("x".into()).exit_code(), 2);
assert_eq!(Error::Oneharness("y".into()).exit_code(), 2);
}
#[test]
fn io_err_includes_context_and_cause() {
let e = io_err(
"writing file",
std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied"),
);
let msg = e.to_string();
assert!(msg.contains("writing file"));
assert!(msg.contains("denied"));
}
#[test]
fn display_messages_are_actionable() {
assert!(Error::OneharnessNotFound("oneharness".into())
.to_string()
.contains("not found"));
assert!(Error::ConfigNotFound {
names: "llmlint.yml".into(),
dir: "/x".into()
}
.to_string()
.contains("no llmlint config"));
assert!(Error::UnknownPlugin("llmlint:x".into())
.to_string()
.contains("unknown bundled plugin"));
assert!(Error::ConfigExists("/tmp/llmlint.yml".into())
.to_string()
.contains("already exists"));
assert!(Error::Template("bad".into())
.to_string()
.contains("template error"));
assert!(Error::ConfigParse {
path: "f".into(),
message: "m".into()
}
.to_string()
.contains("f: m"));
}
}