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}