1use loonfs_api::{ErrorCode, ErrorDetails, ErrorKind};
4use thiserror::Error;
5
6#[derive(Debug, Error)]
11#[non_exhaustive]
12pub enum ClientError {
13 #[error("failed to read config: {0}")]
14 ConfigIo(String),
15 #[error("failed to decode config: {0}")]
16 ConfigDecode(String),
17 #[error("missing `{field}`")]
18 MissingConfigField { field: &'static str },
19 #[error("invalid `{field}`: {reason}")]
20 ConfigValidation { field: &'static str, reason: String },
21 #[error("invalid namespace path `{0}`")]
22 InvalidNamespacePath(String),
23 #[error("invalid commit_id `{0}`")]
24 InvalidCommitId(String),
25 #[error("invalid checkpoint_id `{0}`")]
26 InvalidCheckpointId(String),
27 #[error("http error: {0}")]
28 Http(String),
29 #[error("server returned {status} {code}: {message}")]
30 Api {
31 status: u16,
32 code: String,
33 feature: Option<String>,
35 message: String,
36 request_id: Option<String>,
38 details: Option<Box<ErrorDetails>>,
41 },
42 #[error("i/o error: {0}")]
43 Io(String),
44 #[error("json error: {0}")]
45 Json(String),
46}
47
48impl ClientError {
49 pub fn code(&self) -> Option<ErrorCode> {
53 match self {
54 ClientError::Api { code, .. } => ErrorCode::parse(code),
55 _ => None,
56 }
57 }
58
59 pub fn kind(&self) -> Option<ErrorKind> {
66 match self {
67 ClientError::Api { status, code, .. } => match ErrorCode::parse(code) {
68 Some(code) => Some(code.kind()),
69 None => kind_for_status_class(*status),
70 },
71 _ => None,
72 }
73 }
74}
75
76pub(crate) fn kind_for_status_class(status: u16) -> Option<ErrorKind> {
78 match status {
79 503 => Some(ErrorKind::Unavailable),
81 400..=499 => Some(ErrorKind::InvalidRequest),
82 500..=599 => Some(ErrorKind::Internal),
83 _ => None,
84 }
85}