misskey_core/
model.rs

1//! Object types used in API.
2
3use std::convert::Infallible;
4use std::error::Error;
5use std::fmt::{self, Display};
6use std::str::FromStr;
7
8use serde::{Deserialize, Serialize};
9
10/// ID of API errors.
11#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
12#[serde(transparent)]
13pub struct ApiErrorId(pub String);
14
15impl FromStr for ApiErrorId {
16    type Err = Infallible;
17    fn from_str(s: &str) -> Result<ApiErrorId, Infallible> {
18        Ok(ApiErrorId(s.to_string()))
19    }
20}
21
22impl Display for ApiErrorId {
23    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24        self.0.fmt(f)
25    }
26}
27
28/// Kind of API error.
29#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
30#[serde(rename_all = "camelCase")]
31pub enum ApiErrorKind {
32    /// The error is considered to be on the client side.
33    Client,
34    /// The error is considered to be on the server side.
35    Server,
36}
37
38/// API error returned from Misskey.
39#[derive(Serialize, Deserialize, Debug, Clone)]
40#[serde(rename_all = "camelCase")]
41pub struct ApiError {
42    /// ID of the error.
43    pub id: ApiErrorId,
44    /// Human-readable description of the error.
45    pub message: String,
46    /// The error code, such as `INTERNAL_ERROR`.
47    pub code: String,
48    /// Kind of the error.
49    pub kind: ApiErrorKind,
50    /// Additional information on this error.
51    #[serde(default)]
52    pub info: serde_json::Value,
53}
54
55impl Display for ApiError {
56    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57        match self.kind {
58            ApiErrorKind::Client => write!(f, "Client error: ")?,
59            ApiErrorKind::Server => write!(f, "Server error: ")?,
60        }
61
62        write!(f, "{} ({})", self.message, self.code)
63    }
64}
65
66impl Error for ApiError {}
67
68/// Result type that represents immediate response from Misskey.
69///
70/// [`ApiResult`] is either successful response ([`ApiResult::Ok`]) or an error
71/// ([`ApiResult::Err`]) with [`ApiError`].
72/// We implement this type in a way that distinguishes it from the `Result<T, ApiError>`, since the
73/// [`ApiResult`] is a normal response to a successful request, even if it is an
74/// [`ApiResult::Err`]. (see the return type of [`crate::Client::request`])
75///
76/// You can convert `ApiResult<T>` to `Result<T, ApiError>` by using [`Into::into`] or [`ApiResult::into_result`].
77#[derive(Serialize, Deserialize, Debug, Clone)]
78#[serde(untagged)]
79#[must_use = "this `ApiResult` may be an `Err` variant, which should be handled"]
80pub enum ApiResult<T> {
81    /// Contains the error value, namely [`ApiError`].
82    Err {
83        /// The error returned from Misskey.
84        error: ApiError,
85    },
86    /// Contains the success value.
87    Ok(T),
88}
89
90impl<T> Into<Result<T, ApiError>> for ApiResult<T> {
91    /// Converts [`ApiResult`] to [`Result`] for convenient handling.
92    fn into(self) -> Result<T, ApiError> {
93        self.into_result()
94    }
95}
96
97impl<T> ApiResult<T> {
98    /// Converts [`ApiResult`] to [`Result`] for convenient handling.
99    ///
100    /// You can also use [`Into::into`], but this is preferred as it expresses the intent more clearly.
101    pub fn into_result(self) -> Result<T, ApiError> {
102        match self {
103            ApiResult::Err { error } => Err(error),
104            ApiResult::Ok(x) => Ok(x),
105        }
106    }
107
108    /// Converts [`ApiResult<T>`][`ApiResult`] to [`Option<T>`][`Option`], consuming `self`, and discarding the error, if any.
109    pub fn ok(self) -> Option<T> {
110        match self {
111            ApiResult::Err { .. } => None,
112            ApiResult::Ok(x) => Some(x),
113        }
114    }
115
116    /// Converts [`ApiResult<T>`][`ApiResult`] to [`Option<ApiError>`][`Option`], consuming `self`, and discarding the success
117    /// value, if any.
118    pub fn err(self) -> Option<ApiError> {
119        match self {
120            ApiResult::Err { error } => Some(error),
121            ApiResult::Ok(_) => None,
122        }
123    }
124
125    /// Returns true if the API result is [`ApiResult::Ok`].
126    pub fn is_ok(&self) -> bool {
127        match self {
128            ApiResult::Err { .. } => false,
129            ApiResult::Ok(_) => true,
130        }
131    }
132
133    /// Returns true if the API result is [`ApiResult::Err`].
134    pub fn is_err(&self) -> bool {
135        !self.is_ok()
136    }
137
138    /// Returns the contained [`ApiResult::Ok`] value, consuming the `self` value.
139    ///
140    /// # Panics
141    ///
142    /// Panics if the value is an [`ApiResult::Err`], with a panic message including the
143    /// passed message, and the content of the [`ApiResult::Err`].
144    pub fn expect(self, msg: &str) -> T {
145        match self {
146            ApiResult::Err { error } => panic!("{}: {:?}", msg, error),
147            ApiResult::Ok(x) => x,
148        }
149    }
150
151    /// Returns the contained [`ApiResult::Ok`] value, consuming the `self` value.
152    ///
153    /// # Panics
154    ///
155    /// Panics if the value is an [`ApiResult::Err`], with a panic message provided by the
156    /// [`ApiResult::Err`]'s value.
157    pub fn unwrap(self) -> T {
158        self.expect("called `ApiResult::unwrap()` on an `ApiResult::Err` value")
159    }
160
161    /// Maps a [`ApiResult<T>`][`ApiResult`] to [`ApiResult<U>`][`ApiResult`] by applying a function to a
162    /// contained [`ApiResult::Ok`] value, leaving an [`ApiResult::Err`] value untouched.
163    pub fn map<U, F>(self, op: F) -> ApiResult<U>
164    where
165        F: FnOnce(T) -> U,
166    {
167        match self {
168            ApiResult::Ok(x) => ApiResult::Ok(op(x)),
169            ApiResult::Err { error } => ApiResult::Err { error },
170        }
171    }
172}