1#[derive(Debug, thiserror::Error)]
5pub enum HarmontError {
6 #[error("unauthorized — run `hm login` or set HARMONT_API_TOKEN")]
8 Unauthorized,
9
10 #[error("api error {status} [{code}]: {message}")]
12 Api { status: u16, code: String, message: String },
13
14 #[error("network error: {0}")]
16 Transport(String),
17
18 #[error("decode error: {0}")]
20 Decode(String),
21
22 #[error("log stream error: {0}")]
24 LogStream(String),
25
26 #[error("not found: {0}")]
28 NotFound(String),
29}
30
31impl From<reqwest::Error> for HarmontError {
32 fn from(e: reqwest::Error) -> Self {
33 if e.is_decode() { HarmontError::Decode(e.to_string()) }
34 else { HarmontError::Transport(e.to_string()) }
35 }
36}
37
38pub type Result<T> = std::result::Result<T, HarmontError>;
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn api_error_carries_status_and_code() {
46 let e = HarmontError::Api { status: 422, code: "build_rejected".into(),
47 message: "pipeline_ir invalid".into() };
48 let s = e.to_string();
49 assert!(s.contains("422"));
50 assert!(s.contains("build_rejected"));
51 assert!(s.contains("pipeline_ir invalid"));
52 }
53
54 #[test]
55 fn unauthorized_is_distinguishable() {
56 let e = HarmontError::Unauthorized;
57 assert!(matches!(e, HarmontError::Unauthorized));
58 }
59}