misskey_api/endpoint/
users.rs

1use crate::model::{
2    sort::SortOrder,
3    user::{User, UserOrigin, UserSortKey},
4};
5
6use serde::Serialize;
7use thiserror::Error;
8use typed_builder::TypedBuilder;
9
10pub mod followers;
11pub mod following;
12pub mod get_frequently_replied_users;
13pub mod groups;
14pub mod lists;
15pub mod notes;
16pub mod recommendation;
17pub mod relation;
18pub mod report_abuse;
19pub mod search;
20pub mod search_by_username_and_host;
21pub mod show;
22
23#[cfg(feature = "12-60-0")]
24#[cfg_attr(docsrs, doc(cfg(feature = "12-60-0")))]
25pub mod stats;
26
27#[cfg(feature = "12-61-0")]
28#[cfg_attr(docsrs, doc(cfg(feature = "12-61-0")))]
29pub mod clips;
30
31#[cfg(feature = "12-61-0")]
32#[cfg_attr(docsrs, doc(cfg(feature = "12-61-0")))]
33pub mod pages;
34
35#[derive(Serialize, PartialEq, Eq, Clone, Debug, Copy)]
36#[serde(rename_all = "camelCase")]
37pub enum UserState {
38    All,
39    Alive,
40    Admin,
41    Moderator,
42    AdminOrModerator,
43}
44
45#[derive(Debug, Error, Clone)]
46#[error("invalid user state")]
47pub struct ParseUserStateError {
48    _priv: (),
49}
50
51impl std::str::FromStr for UserState {
52    type Err = ParseUserStateError;
53
54    fn from_str(s: &str) -> Result<UserState, Self::Err> {
55        match s {
56            "all" | "All" => Ok(UserState::All),
57            "alive" | "Alive" => Ok(UserState::Alive),
58            "admin" | "Admin" => Ok(UserState::Admin),
59            "moderator" | "Moderator" => Ok(UserState::Moderator),
60            "adminOrModerator" | "AdminOrModerator" => Ok(UserState::AdminOrModerator),
61            _ => Err(ParseUserStateError { _priv: () }),
62        }
63    }
64}
65
66#[derive(Serialize, Default, Debug, Clone, TypedBuilder)]
67#[serde(rename_all = "camelCase")]
68#[builder(doc)]
69pub struct Request {
70    /// 1 .. 100
71    #[serde(skip_serializing_if = "Option::is_none")]
72    #[builder(default, setter(strip_option))]
73    pub limit: Option<u8>,
74    #[serde(skip_serializing_if = "Option::is_none")]
75    #[builder(default, setter(strip_option))]
76    pub offset: Option<u64>,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    #[builder(default, setter(strip_option))]
79    pub sort: Option<SortOrder<UserSortKey>>,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    #[builder(default, setter(strip_option))]
82    pub state: Option<UserState>,
83    #[serde(skip_serializing_if = "Option::is_none")]
84    #[builder(default, setter(strip_option))]
85    pub origin: Option<UserOrigin>,
86}
87
88impl misskey_core::Request for Request {
89    type Response = Vec<User>;
90    const ENDPOINT: &'static str = "users";
91}
92
93impl_offset_pagination!(Request, User);
94
95#[cfg(test)]
96mod tests {
97    use super::{Request, UserState};
98    use crate::test::{ClientExt, TestClient};
99
100    #[tokio::test]
101    async fn request() {
102        let client = TestClient::new();
103        client.test(Request::default()).await;
104    }
105
106    #[tokio::test]
107    async fn request_with_limit() {
108        let client = TestClient::new();
109        client
110            .test(Request {
111                limit: Some(100),
112                offset: None,
113                sort: None,
114                state: None,
115                origin: None,
116            })
117            .await;
118    }
119
120    #[tokio::test]
121    async fn request_with_offset() {
122        let client = TestClient::new();
123        client
124            .test(Request {
125                limit: None,
126                offset: Some(5),
127                sort: None,
128                state: None,
129                origin: None,
130            })
131            .await;
132    }
133
134    #[tokio::test]
135    async fn request_with_sort() {
136        use crate::model::{sort::SortOrder, user::UserSortKey};
137
138        let client = TestClient::new();
139
140        client
141            .test(Request {
142                limit: None,
143                offset: None,
144                sort: Some(SortOrder::Ascending(UserSortKey::Follower)),
145                state: None,
146                origin: None,
147            })
148            .await;
149        client
150            .test(Request {
151                limit: None,
152                offset: None,
153                sort: Some(SortOrder::Ascending(UserSortKey::CreatedAt)),
154                state: None,
155                origin: None,
156            })
157            .await;
158        client
159            .test(Request {
160                limit: None,
161                offset: None,
162                sort: Some(SortOrder::Descending(UserSortKey::UpdatedAt)),
163                state: None,
164                origin: None,
165            })
166            .await;
167    }
168
169    #[tokio::test]
170    async fn request_with_state() {
171        let client = TestClient::new();
172
173        client
174            .test(Request {
175                limit: None,
176                offset: None,
177                sort: None,
178                state: Some(UserState::All),
179                origin: None,
180            })
181            .await;
182        client
183            .test(Request {
184                limit: None,
185                offset: None,
186                sort: None,
187                state: Some(UserState::Admin),
188                origin: None,
189            })
190            .await;
191        client
192            .test(Request {
193                limit: None,
194                offset: None,
195                sort: None,
196                state: Some(UserState::Alive),
197                origin: None,
198            })
199            .await;
200        client
201            .test(Request {
202                limit: None,
203                offset: None,
204                sort: None,
205                state: Some(UserState::Moderator),
206                origin: None,
207            })
208            .await;
209        // TODO: Uncomment with cfg when `adminOrModerator` value is fixed in Misskey
210        // client
211        //     .test(Request {
212        //         limit: None,
213        //         offset: None,
214        //         sort: None,
215        //         state: Some(UserState::AdminOrModerator),
216        //         origin: None,
217        //     })
218        //     .await;
219    }
220
221    #[tokio::test]
222    async fn request_with_origin() {
223        use crate::model::user::UserOrigin;
224
225        let client = TestClient::new();
226
227        client
228            .test(Request {
229                limit: None,
230                offset: None,
231                sort: None,
232                state: None,
233                origin: Some(UserOrigin::Local),
234            })
235            .await;
236        client
237            .test(Request {
238                limit: None,
239                offset: None,
240                sort: None,
241                state: None,
242                origin: Some(UserOrigin::Remote),
243            })
244            .await;
245        client
246            .test(Request {
247                limit: None,
248                offset: None,
249                sort: None,
250                state: None,
251                origin: Some(UserOrigin::Combined),
252            })
253            .await;
254    }
255}