1use std::fmt;
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum ModelsErrorCode {
15 ModelSource,
17 ModelValidation,
19 Provider,
21 Stream,
23 Auth,
25 Oauth,
27}
28
29impl ModelsErrorCode {
30 #[must_use]
32 pub const fn as_str(self) -> &'static str {
33 match self {
34 Self::ModelSource => "model_source",
35 Self::ModelValidation => "model_validation",
36 Self::Provider => "provider",
37 Self::Stream => "stream",
38 Self::Auth => "auth",
39 Self::Oauth => "oauth",
40 }
41 }
42}
43
44impl fmt::Display for ModelsErrorCode {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 f.write_str(self.as_str())
47 }
48}
49
50impl std::str::FromStr for ModelsErrorCode {
51 type Err = ModelsErrorCodeParseError;
52
53 fn from_str(value: &str) -> Result<Self, Self::Err> {
54 match value {
55 "model_source" => Ok(Self::ModelSource),
56 "model_validation" => Ok(Self::ModelValidation),
57 "provider" => Ok(Self::Provider),
58 "stream" => Ok(Self::Stream),
59 "auth" => Ok(Self::Auth),
60 "oauth" => Ok(Self::Oauth),
61 other => Err(ModelsErrorCodeParseError {
62 value: other.to_owned(),
63 }),
64 }
65 }
66}
67
68#[derive(Clone, Debug, Eq, PartialEq)]
70pub struct ModelsErrorCodeParseError {
71 value: String,
72}
73
74impl fmt::Display for ModelsErrorCodeParseError {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 write!(f, "unknown models error code: {}", self.value)
77 }
78}
79
80impl std::error::Error for ModelsErrorCodeParseError {}
81
82#[derive(Clone, Debug, thiserror::Error)]
84#[error("{message}")]
85pub struct ModelsError {
86 pub code: ModelsErrorCode,
88 message: String,
89 cancelled: bool,
90}
91
92impl ModelsError {
93 #[must_use]
95 pub fn new(code: ModelsErrorCode, message: impl Into<String>) -> Self {
96 Self {
97 code,
98 message: message.into(),
99 cancelled: false,
100 }
101 }
102
103 #[must_use]
106 pub fn cancelled() -> Self {
107 Self {
108 code: ModelsErrorCode::Oauth,
109 message: "Login cancelled".to_owned(),
110 cancelled: true,
111 }
112 }
113
114 #[must_use]
116 pub const fn is_cancelled(&self) -> bool {
117 self.cancelled
118 }
119
120 #[must_use]
122 pub fn message(&self) -> &str {
123 &self.message
124 }
125}
126
127#[derive(Clone, Debug, thiserror::Error)]
129pub enum AuthError {
130 #[error("Login cancelled")]
132 Cancelled,
133 #[error("{0}")]
135 Message(String),
136}
137
138impl AuthError {
139 #[must_use]
141 pub fn message(message: impl Into<String>) -> Self {
142 Self::Message(message.into())
143 }
144}
145
146#[derive(Clone, Debug, thiserror::Error)]
148pub enum StoreError {
149 #[error("{0}")]
151 Message(String),
152 #[error(transparent)]
154 Auth(#[from] AuthError),
155}
156
157impl StoreError {
158 #[must_use]
160 pub fn message(message: impl Into<String>) -> Self {
161 Self::Message(message.into())
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn models_error_code_roundtrips_snake_case() -> Result<(), ModelsErrorCodeParseError> {
171 for code in [
172 ModelsErrorCode::ModelSource,
173 ModelsErrorCode::ModelValidation,
174 ModelsErrorCode::Provider,
175 ModelsErrorCode::Stream,
176 ModelsErrorCode::Auth,
177 ModelsErrorCode::Oauth,
178 ] {
179 let text = code.as_str();
180 let parsed: ModelsErrorCode = text.parse()?;
181 assert_eq!(parsed, code);
182 assert_eq!(parsed.to_string(), text);
183 }
184 Ok(())
185 }
186
187 #[test]
188 fn models_error_preserves_code_and_message() {
189 let err = ModelsError::new(ModelsErrorCode::Oauth, "refresh failed");
190 assert_eq!(err.code, ModelsErrorCode::Oauth);
191 assert_eq!(err.message(), "refresh failed");
192 assert_eq!(err.to_string(), "refresh failed");
193 }
194}