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 #[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 }
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}