use std::fmt;
use super::types::{
CanonEntry, CanonMatchRequest, CanonMatchResponse, RequestVerifyBody, RequestVerifyResponse,
};
pub trait CanonClient: Send + Sync {
fn match_annotations(&self, req: &CanonMatchRequest) -> Result<CanonMatchResponse, CanonError>;
fn get_entry(&self, canon_id: &str, version: Option<&str>) -> Result<CanonEntry, CanonError>;
fn request_verify(&self, body: &RequestVerifyBody)
-> Result<RequestVerifyResponse, CanonError>;
}
#[derive(Debug)]
pub enum CanonError {
NotEnabled,
Auth(AuthError),
Network(String),
Timeout,
BadRequest { status: u16, message: String },
Server { status: u16, message: String },
Decode(String),
Fixture(String),
}
impl fmt::Display for CanonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CanonError::NotEnabled => write!(
f,
"canon disabled via aristo.toml `[canon] enabled = false`"
),
CanonError::Auth(e) => write!(f, "canon auth error: {e}"),
CanonError::Network(msg) => write!(f, "canon network error: {msg}"),
CanonError::Timeout => write!(f, "canon request timed out (>2s)"),
CanonError::BadRequest { status, message } => {
write!(f, "canon server returned HTTP {status}: {message}")
}
CanonError::Server { status, message } => {
write!(f, "canon server error (HTTP {status}): {message}")
}
CanonError::Decode(msg) => write!(f, "canon response decode error: {msg}"),
CanonError::Fixture(msg) => write!(f, "canon fixture error: {msg}"),
}
}
}
impl std::error::Error for CanonError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
CanonError::Auth(e) => Some(e),
_ => None,
}
}
}
pub use crate::auth::AuthError;
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
#[test]
fn canon_error_displays_render_useful_messages() {
let e = CanonError::NotEnabled;
assert!(e.to_string().contains("aristo.toml"));
assert!(e.to_string().contains("enabled"));
let e = CanonError::Auth(AuthError::NoToken);
assert!(e.to_string().contains("aristo auth login"));
let e = CanonError::Timeout;
assert!(e.to_string().contains("timed out"));
let e = CanonError::BadRequest {
status: 400,
message: "threshold too low".into(),
};
assert!(e.to_string().contains("400"));
assert!(e.to_string().contains("threshold too low"));
}
#[test]
fn auth_error_chains_through_canon_error_source() {
let e = CanonError::Auth(AuthError::NoToken);
let src = e.source().expect("auth error should be sourced");
assert!(src.to_string().contains("aristo auth login"));
}
}