use thiserror::Error;
#[must_use]
pub fn is_auth_failure(err: &anyhow::Error) -> bool {
matches!(
err.downcast_ref::<JevError>(),
Some(JevError::ApiRequestFailed {
status: 401 | 403,
..
})
)
}
#[derive(Error, Debug)]
pub enum JevError {
#[error(
"Jev credentials not configured. Set TYPESAFE_API_KEY (or OMNI_DEV_JEV_API_KEY, or \
either's _FILE companion), or add one to ~/.omni-dev/settings.json"
)]
CredentialsNotFound,
#[error("Jev API request failed: HTTP {status}: {body}")]
ApiRequestFailed {
status: u16,
body: String,
},
#[error("Invalid question specification: {0}")]
InvalidQuestionSpec(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_auth_failure_matches_401_and_403() {
for status in [401, 403] {
let err = anyhow::Error::new(JevError::ApiRequestFailed {
status,
body: "nope".to_string(),
});
assert!(is_auth_failure(&err), "status {status}");
}
}
#[test]
fn is_auth_failure_rejects_other_statuses_and_errors() {
let err = anyhow::Error::new(JevError::ApiRequestFailed {
status: 500,
body: "boom".to_string(),
});
assert!(!is_auth_failure(&err));
assert!(!is_auth_failure(&anyhow::anyhow!("unrelated")));
}
#[test]
fn credentials_not_found_display() {
let err = JevError::CredentialsNotFound;
let msg = err.to_string();
assert!(msg.contains("TYPESAFE_API_KEY"));
assert!(msg.contains("OMNI_DEV_JEV_API_KEY"));
assert!(msg.contains("settings.json"));
}
#[test]
fn api_request_failed_display() {
let err = JevError::ApiRequestFailed {
status: 401,
body: "Unauthorized".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("401"));
assert!(msg.contains("Unauthorized"));
}
#[test]
fn invalid_question_spec_display() {
let err = JevError::InvalidQuestionSpec("missing name".to_string());
assert!(err.to_string().contains("missing name"));
}
}