use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum PiError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("session not found: {0}")]
SessionNotFound(String),
#[error("project not found: {0}")]
ProjectNotFound(String),
#[error("invalid session file {path}: {reason}")]
InvalidSessionFile {
path: PathBuf,
reason: String,
},
#[error("malformed session header: {0}")]
MalformedHeader(String),
#[error("conversation error: {0}")]
Convo(#[from] toolpath_convo::ConvoError),
#[error("{0}")]
Anyhow(#[from] anyhow::Error),
#[error("{0}")]
Other(String),
}
impl PiError {
pub fn session_not_found(id: impl Into<String>) -> Self {
Self::SessionNotFound(id.into())
}
pub fn project_not_found(cwd: impl Into<String>) -> Self {
Self::ProjectNotFound(cwd.into())
}
pub fn invalid_session_file(path: impl Into<PathBuf>, reason: impl Into<String>) -> Self {
Self::InvalidSessionFile {
path: path.into(),
reason: reason.into(),
}
}
pub fn malformed_header(reason: impl Into<String>) -> Self {
Self::MalformedHeader(reason.into())
}
pub fn other(msg: impl Into<String>) -> Self {
Self::Other(msg.into())
}
}
pub type Result<T> = std::result::Result<T, PiError>;
#[cfg(test)]
mod tests {
use super::*;
use std::io;
#[test]
fn test_io_error_conversion() {
let io_err = io::Error::new(io::ErrorKind::NotFound, "missing");
let err: PiError = io_err.into();
match err {
PiError::Io(_) => {}
_ => panic!("expected Io variant"),
}
}
#[test]
fn test_json_error_display() {
let json_err = serde_json::from_str::<u32>("x").unwrap_err();
let err: PiError = json_err.into();
let msg = err.to_string();
assert!(
msg.to_lowercase().contains("json"),
"expected 'json' in display: {msg}"
);
}
#[test]
fn test_session_not_found_display() {
let err = PiError::SessionNotFound("abc".into());
assert!(err.to_string().contains("abc"));
}
#[test]
fn test_project_not_found_display() {
let err = PiError::ProjectNotFound("/Users/alex/project".into());
assert!(err.to_string().contains("/Users/alex/project"));
}
#[test]
fn test_other_display() {
let err = PiError::Other("something went wrong".into());
assert!(err.to_string().contains("something went wrong"));
}
#[test]
fn test_invalid_session_file_display() {
let err = PiError::invalid_session_file(PathBuf::from("/tmp/a.jsonl"), "bad line 3");
let msg = err.to_string();
assert!(msg.contains("/tmp/a.jsonl"));
assert!(msg.contains("bad line 3"));
}
#[test]
fn test_malformed_header_display() {
let err = PiError::malformed_header("missing session_id");
assert!(err.to_string().contains("missing session_id"));
}
#[test]
fn test_anyhow_conversion() {
let a: anyhow::Error = anyhow::anyhow!("boom");
let err: PiError = a.into();
assert!(err.to_string().contains("boom"));
}
#[test]
fn test_helper_constructors() {
assert!(matches!(
PiError::session_not_found("s"),
PiError::SessionNotFound(_)
));
assert!(matches!(
PiError::project_not_found("p"),
PiError::ProjectNotFound(_)
));
assert!(matches!(PiError::other("o"), PiError::Other(_)));
}
}