use std::fmt;
#[derive(Debug)]
pub struct InputError(pub String);
impl InputError {
pub fn new(msg: impl Into<String>) -> Self {
Self(msg.into())
}
}
impl fmt::Display for InputError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for InputError {}
pub fn is_input_error(err: &anyhow::Error) -> bool {
err.downcast_ref::<InputError>().is_some()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_input_error_finds_head_marker() {
let err: anyhow::Error = InputError::new("bad corpus").into();
assert!(is_input_error(&err));
}
#[test]
fn is_input_error_finds_marker_under_further_context_layers() {
let err: anyhow::Error =
anyhow::Error::new(InputError::new("bad corpus")).context("while running ground");
assert!(is_input_error(&err));
}
#[test]
fn is_input_error_returns_false_when_marker_absent() {
let err: anyhow::Error = anyhow::anyhow!("disk IO blew up");
assert!(!is_input_error(&err));
}
#[test]
fn is_input_error_returns_false_for_unrelated_context_layers() {
let err: anyhow::Error = anyhow::anyhow!("disk IO blew up")
.context("opening ground dir")
.context("during index");
assert!(!is_input_error(&err));
}
#[test]
fn display_forwards_inner_message_so_existing_callers_keep_working() {
let err: anyhow::Error = InputError::new("corpus \"docs\" not found in config").into();
assert!(
err.to_string().contains("docs") && err.to_string().contains("not found"),
"InputError must surface its message via Display: {err}"
);
}
}