use std::path::PathBuf;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("failed to read ADR file at {path}")]
FileRead {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("failed to write output to {path}")]
FileWrite {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("invalid frontmatter in {path}: {message}")]
InvalidFrontmatter {
path: PathBuf,
message: String,
},
#[error("YAML parsing failed in {path}")]
YamlParse {
path: PathBuf,
#[source]
source: serde_yaml::Error,
},
#[error("missing required field '{field}' in {path}")]
MissingField {
path: PathBuf,
field: &'static str,
},
#[error("template rendering failed")]
TemplateRender {
#[source]
source: askama::Error,
},
#[error("no ADR files found in {path}")]
NoAdrsFound {
path: PathBuf,
},
#[error("validation failed: {0} error(s) found")]
ValidationFailed(usize),
#[error("invalid ADR filename: {0}")]
InvalidFilename(String),
#[error("invalid glob pattern: {0}")]
GlobPattern(String),
#[error("invalid date format in {path}: {message}")]
DateParse {
path: PathBuf,
message: String,
},
#[error("JSON serialization failed: {0}")]
JsonSerialize(String),
}
impl From<askama::Error> for Error {
fn from(source: askama::Error) -> Self {
Self::TemplateRender { source }
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display_file_read() {
let err = Error::FileRead {
path: PathBuf::from("/test/path.md"),
source: std::io::Error::new(std::io::ErrorKind::NotFound, "file not found"),
};
let display = err.to_string();
assert!(display.contains("failed to read ADR file"));
assert!(display.contains("/test/path.md"));
}
#[test]
fn test_error_display_missing_field() {
let err = Error::MissingField {
path: PathBuf::from("adr_0001.md"),
field: "title",
};
let display = err.to_string();
assert!(display.contains("missing required field"));
assert!(display.contains("title"));
assert!(display.contains("adr_0001.md"));
}
#[test]
fn test_error_display_validation_failed() {
let err = Error::ValidationFailed(5);
assert_eq!(err.to_string(), "validation failed: 5 error(s) found");
}
#[test]
fn test_error_display_no_adrs_found() {
let err = Error::NoAdrsFound {
path: PathBuf::from("docs/decisions"),
};
let display = err.to_string();
assert!(display.contains("no ADR files found"));
assert!(display.contains("docs/decisions"));
}
#[test]
fn test_error_display_file_write() {
let err = Error::FileWrite {
path: PathBuf::from("/output/file.html"),
source: std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied"),
};
let display = err.to_string();
assert!(display.contains("failed to write output"));
assert!(display.contains("/output/file.html"));
}
#[test]
fn test_error_display_invalid_frontmatter() {
let err = Error::InvalidFrontmatter {
path: PathBuf::from("test.md"),
message: "missing closing delimiter".to_string(),
};
let display = err.to_string();
assert!(display.contains("invalid frontmatter"));
assert!(display.contains("test.md"));
assert!(display.contains("missing closing delimiter"));
}
#[test]
fn test_error_display_invalid_filename() {
let err = Error::InvalidFilename("bad_name".to_string());
let display = err.to_string();
assert!(display.contains("invalid ADR filename"));
assert!(display.contains("bad_name"));
}
#[test]
fn test_error_display_glob_pattern() {
let err = Error::GlobPattern("invalid pattern".to_string());
let display = err.to_string();
assert!(display.contains("invalid glob pattern"));
}
#[test]
fn test_error_display_date_parse() {
let err = Error::DateParse {
path: PathBuf::from("adr.md"),
message: "invalid date format".to_string(),
};
let display = err.to_string();
assert!(display.contains("invalid date format"));
assert!(display.contains("adr.md"));
}
#[test]
fn test_error_display_json_serialize() {
let err = Error::JsonSerialize("serialization failed".to_string());
let display = err.to_string();
assert!(display.contains("JSON serialization failed"));
}
#[test]
fn test_error_from_askama() {
let askama_err = askama::Error::Custom(Box::new(std::io::Error::other("template error")));
let err: Error = askama_err.into();
let display = err.to_string();
assert!(display.contains("template rendering failed"));
}
}