Skip to main content

cloudreve_api/
error.rs

1//! Error types for the Cloudreve API client
2
3use crate::api::v4::models::AggregatedItemError;
4use reqwest::Error as ReqwestError;
5use std::collections::HashMap;
6use std::io;
7use thiserror::Error;
8
9/// Main error type for the Cloudreve API client
10#[derive(Error, Debug)]
11pub enum Error {
12    /// HTTP request error
13    #[error("HTTP request error: {0}")]
14    Http(#[from] ReqwestError),
15
16    /// JSON serialization/deserialization error
17    #[error("JSON error: {0}")]
18    Json(#[from] serde_json::Error),
19
20    /// IO error
21    #[error("IO error: {0}")]
22    Io(#[from] io::Error),
23
24    /// API error response
25    #[error("API error: {message} (code: {code})")]
26    Api { code: i32, message: String },
27
28    /// API error whose payload the caller needs to act on
29    ///
30    /// Some business codes put actionable data in `data` — a lock conflict
31    /// (40073) returns the unlock tokens there. [`Error::Api`] drops it, so
32    /// endpoints whose callers must react to the payload report it here.
33    #[error("API error: {message} (code: {code})")]
34    ApiWithData {
35        code: i32,
36        message: String,
37        data: serde_json::Value,
38    },
39
40    /// A batch operation completed only partially (code: 40081)
41    ///
42    /// `errors` is keyed by the URI that was sent; every URI absent from the
43    /// map succeeded. Each entry carries its own business code, so a locked
44    /// file surfaces as a 40073 sub-item with tokens in its `data`.
45    #[error("Batch operation partially failed: {} of the submitted item(s) failed", errors.len())]
46    Aggregate {
47        code: i32,
48        message: String,
49        errors: HashMap<String, AggregatedItemError>,
50    },
51
52    /// Authentication error
53    #[error("Authentication error: {0}")]
54    Auth(String),
55
56    /// Invalid response error
57    #[error("Invalid response: {0}")]
58    InvalidResponse(String),
59
60    /// Invalid timestamp error
61    #[error("Invalid timestamp: {0}")]
62    InvalidTimestamp(String),
63
64    /// Feature not supported in API version
65    #[error("Feature '{0}' not supported in API {1}")]
66    UnsupportedFeature(String, String),
67
68    /// Two-factor authentication required (code: 203)
69    /// Contains the session ID needed for 2FA completion
70    #[error("Two-factor authentication required (session ID: {0})")]
71    TwoFactorRequired(String),
72
73    /// Unauthorized — application-level 401 returned in JSON body
74    #[error("Unauthorized: {0}")]
75    Unauthorized(String),
76
77    /// CAPTCHA required for login
78    #[error("CAPTCHA required for login")]
79    CaptchaRequired,
80
81    /// CAPTCHA validation failed
82    #[error("CAPTCHA validation failed: {0}")]
83    CaptchaInvalid(String),
84}
85
86impl Error {
87    /// Server message of an API-level error, without the code decoration that
88    /// [`std::fmt::Display`] adds.
89    pub fn message(&self) -> Option<&str> {
90        match self {
91            Error::Api { message, .. }
92            | Error::ApiWithData { message, .. }
93            | Error::Aggregate { message, .. } => Some(message),
94            _ => None,
95        }
96    }
97
98    /// Business code carried by an API-level error, if any.
99    pub fn code(&self) -> Option<i32> {
100        match self {
101            Error::Api { code, .. }
102            | Error::ApiWithData { code, .. }
103            | Error::Aggregate { code, .. } => Some(*code),
104            _ => None,
105        }
106    }
107
108    /// Response payload of an API-level error, if the variant kept one.
109    pub fn data(&self) -> Option<&serde_json::Value> {
110        match self {
111            Error::ApiWithData { data, .. } => Some(data),
112            _ => None,
113        }
114    }
115
116    /// Per-item failures of a partially completed batch operation.
117    pub fn aggregated_errors(&self) -> Option<&HashMap<String, AggregatedItemError>> {
118        match self {
119            Error::Aggregate { errors, .. } => Some(errors),
120            _ => None,
121        }
122    }
123}