rover/summarizer/
error.rs1use thiserror::Error;
4
5#[derive(Debug, Error)]
7pub enum BackendError {
8 #[error("backend unavailable: {0}")]
9 Unavailable(String),
10
11 #[error("rate limited")]
12 RateLimited,
13
14 #[error("auth failed: {0}")]
15 AuthFailed(String),
16
17 #[error("model error: {0}")]
18 ModelError(String),
19
20 #[error("model file {file} has been modified (expected {expected}, got {actual})")]
21 ModelIntegrityFailure {
22 file: String,
23 expected: String,
24 actual: String,
25 },
26
27 #[error("invalid request: {0}")]
32 Invalid(String),
33}
34
35#[derive(Debug, Error)]
39pub enum SummarizerError {
40 #[error("no such backend: {name}")]
41 NoSuchBackend { name: String },
42
43 #[error("no extractive backend configured for fallback")]
44 NoExtractiveBackendForFallback,
45
46 #[error("backend {name} unavailable: {reason}")]
47 BackendUnavailable { name: String, reason: String },
48
49 #[error("backend {name} rate limited")]
50 RateLimited { name: String },
51
52 #[error("backend {name} auth failed: {reason}")]
53 AuthFailed { name: String, reason: String },
54
55 #[error("backend {name} model error: {reason}")]
56 ModelError { name: String, reason: String },
57
58 #[error("invalid request to backend {name}: {reason}")]
59 InvalidRequest { name: String, reason: String },
60
61 #[error("local-inference backend requires the `local-inference` cargo feature")]
62 LocalFeatureNotCompiled,
63
64 #[error("storage error: {0}")]
65 Storage(#[from] crate::storage::StorageError),
66
67 #[error("token counting error: {0}")]
68 Tokenizer(#[from] crate::tokenizer::TokenizerError),
69}
70
71impl SummarizerError {
72 pub fn from_backend(name: &str, e: BackendError) -> Self {
75 match e {
76 BackendError::Unavailable(r) => SummarizerError::BackendUnavailable {
77 name: name.to_string(),
78 reason: r,
79 },
80 BackendError::Invalid(r) => SummarizerError::InvalidRequest {
81 name: name.to_string(),
82 reason: r,
83 },
84 BackendError::RateLimited => SummarizerError::RateLimited {
85 name: name.to_string(),
86 },
87 BackendError::AuthFailed(r) => SummarizerError::AuthFailed {
88 name: name.to_string(),
89 reason: r,
90 },
91 BackendError::ModelError(r) => SummarizerError::ModelError {
92 name: name.to_string(),
93 reason: r,
94 },
95 BackendError::ModelIntegrityFailure {
96 file,
97 expected,
98 actual,
99 } => SummarizerError::ModelError {
100 name: name.to_string(),
101 reason: format!(
102 "model file {file} has been modified (expected {expected}, got {actual})"
103 ),
104 },
105 }
106 }
107
108 pub fn fallback_reason(&self) -> &'static str {
115 match self {
116 SummarizerError::BackendUnavailable { .. } => "backend_unavailable",
117 SummarizerError::InvalidRequest { .. } => "invalid_request",
118 SummarizerError::RateLimited { .. } => "rate_limited",
119 SummarizerError::AuthFailed { .. } => "auth_failed",
120 SummarizerError::ModelError { .. } => "model_error",
121 other => {
122 debug_assert!(
123 false,
124 "fallback_reason called on non-fallback-path variant: {other}",
125 );
126 "other"
127 }
128 }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn from_backend_maps_each_variant() {
138 let cases = [
139 (
140 BackendError::Unavailable("net".into()),
141 "backend_unavailable",
142 ),
143 (BackendError::RateLimited, "rate_limited"),
144 (BackendError::AuthFailed("401".into()), "auth_failed"),
145 (BackendError::ModelError("bad".into()), "model_error"),
146 (BackendError::Invalid("empty".into()), "invalid_request"),
147 ];
148 for (be, expected_reason) in cases {
149 let e = SummarizerError::from_backend("fast", be);
150 assert_eq!(e.fallback_reason(), expected_reason, "for {e}");
151 }
152 }
153
154 #[test]
155 fn storage_error_converts_via_from() {
156 let storage_err =
159 crate::storage::StorageError::Backend(tokio_rusqlite::Error::ConnectionClosed);
160 let e: SummarizerError = storage_err.into();
161 assert!(matches!(e, SummarizerError::Storage(_)));
162 }
163
164 #[test]
165 fn tokenizer_error_converts_via_from() {
166 let tok_err = crate::tokenizer::TokenizerError::UnknownFamily("test".to_string());
167 let e: SummarizerError = tok_err.into();
168 assert!(matches!(e, SummarizerError::Tokenizer(_)));
169 }
170}