1use crate::pagination::{ListOptions, QueryEncode};
8use crate::types::enums::{HookType, VisibleType};
9use crate::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Default)]
15pub struct AdminListUsersOptions {
17 pub list_options: ListOptions,
18 pub source_id: i64,
19 pub login_name: String,
20 pub query: String,
21 pub sort: String,
22 pub order: String,
23 pub visibility: String,
24 pub is_active: Option<bool>,
25 pub is_admin: Option<bool>,
26 pub is_restricted: Option<bool>,
27 pub is_2fa_enabled: Option<bool>,
28 pub is_prohibit_login: Option<bool>,
29}
30
31impl QueryEncode for AdminListUsersOptions {
32 fn query_encode(&self) -> String {
33 let mut parts = vec![self.list_options.query_encode()];
34 if self.source_id > 0 {
35 parts.push(format!("source_id={}", self.source_id));
36 }
37 if !self.login_name.is_empty() {
38 parts.push(format!("login_name={}", self.login_name));
39 }
40 if !self.query.is_empty() {
41 parts.push(format!("q={}", self.query));
42 }
43 if !self.sort.is_empty() {
44 parts.push(format!("sort={}", self.sort));
45 }
46 if !self.order.is_empty() {
47 parts.push(format!("order={}", self.order));
48 }
49 if !self.visibility.is_empty() {
50 parts.push(format!("visibility={}", self.visibility));
51 }
52 if let Some(v) = self.is_active {
53 parts.push(format!("is_active={v}"));
54 }
55 if let Some(v) = self.is_admin {
56 parts.push(format!("is_admin={v}"));
57 }
58 if let Some(v) = self.is_restricted {
59 parts.push(format!("is_restricted={v}"));
60 }
61 if let Some(v) = self.is_2fa_enabled {
62 parts.push(format!("is_2fa_enabled={v}"));
63 }
64 if let Some(v) = self.is_prohibit_login {
65 parts.push(format!("is_prohibit_login={v}"));
66 }
67 parts.join("&")
68 }
69}
70
71#[derive(Debug, Clone, Default, Serialize, Deserialize)]
73pub struct CreateUserOption {
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub source_id: Option<i64>,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub login_name: Option<String>,
79 pub username: String,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub full_name: Option<String>,
82 pub email: String,
83 pub password: String,
84 #[serde(
85 skip_serializing_if = "Option::is_none",
86 rename = "must_change_password"
87 )]
88 pub must_change_password: Option<bool>,
89 #[serde(default, rename = "send_notify")]
90 pub send_notify: bool,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub visibility: Option<VisibleType>,
93}
94
95impl CreateUserOption {
96 pub fn validate(&self) -> crate::Result<()> {
98 if self.username.is_empty() {
99 return Err(crate::Error::Validation("username is empty".to_string()));
100 }
101 if self.password.is_empty() {
102 return Err(crate::Error::Validation("password is empty".to_string()));
103 }
104 if self.email.is_empty() {
105 return Err(crate::Error::Validation("email is empty".to_string()));
106 }
107 Ok(())
108 }
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize, Default)]
113pub struct EditUserOption {
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub source_id: Option<i64>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub login_name: Option<String>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub email: Option<String>,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub full_name: Option<String>,
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub password: Option<String>,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub description: Option<String>,
127 #[serde(
128 skip_serializing_if = "Option::is_none",
129 rename = "must_change_password"
130 )]
131 pub must_change_password: Option<bool>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub website: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub location: Option<String>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub active: Option<bool>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub admin: Option<bool>,
140 #[serde(skip_serializing_if = "Option::is_none", rename = "allow_git_hook")]
141 pub allow_git_hook: Option<bool>,
142 #[serde(skip_serializing_if = "Option::is_none", rename = "allow_import_local")]
143 pub allow_import_local: Option<bool>,
144 #[serde(skip_serializing_if = "Option::is_none", rename = "max_repo_creation")]
145 pub max_repo_creation: Option<i32>,
146 #[serde(skip_serializing_if = "Option::is_none", rename = "prohibit_login")]
147 pub prohibit_login: Option<bool>,
148 #[serde(
149 skip_serializing_if = "Option::is_none",
150 rename = "allow_create_organization"
151 )]
152 pub allow_create_organization: Option<bool>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub restricted: Option<bool>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub visibility: Option<VisibleType>,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct RenameUserOption {
163 #[serde(rename = "new_username")]
164 pub new_username: String,
165}
166
167#[derive(Debug, Clone, Default)]
171pub struct ListUnadoptedReposOptions {
173 pub list_options: ListOptions,
174 pub pattern: String,
175}
176
177impl QueryEncode for ListUnadoptedReposOptions {
178 fn query_encode(&self) -> String {
179 let mut out = self.list_options.query_encode();
180 if !self.pattern.is_empty() {
181 out.push_str(&format!("&pattern={}", self.pattern));
182 }
183 out
184 }
185}
186
187#[derive(Debug, Clone, Default)]
191pub struct AdminListOrgsOptions {
193 pub list_options: ListOptions,
194}
195
196impl QueryEncode for AdminListOrgsOptions {
197 fn query_encode(&self) -> String {
198 self.list_options.query_encode()
199 }
200}
201
202#[derive(Debug, Clone, Default)]
206pub struct ListCronTasksOptions {
208 pub list_options: ListOptions,
209}
210
211impl QueryEncode for ListCronTasksOptions {
212 fn query_encode(&self) -> String {
213 self.list_options.query_encode()
214 }
215}
216
217#[derive(Debug, Clone, Default)]
221pub struct ListAdminHooksOptions {
223 pub list_options: ListOptions,
224 pub hook_type: String,
225}
226
227impl QueryEncode for ListAdminHooksOptions {
228 fn query_encode(&self) -> String {
229 let mut out = self.list_options.query_encode();
230 if !self.hook_type.is_empty() {
231 out.push_str(&format!("&type={}", self.hook_type));
232 }
233 out
234 }
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct CreateHookOption {
241 #[serde(rename = "type")]
242 pub hook_type: HookType,
243 pub config: std::collections::HashMap<String, String>,
244 #[serde(default)]
245 pub events: Vec<String>,
246 #[serde(default, rename = "branch_filter")]
247 pub branch_filter: String,
248 #[serde(default)]
249 pub active: bool,
250 #[serde(default, rename = "authorization_header")]
251 pub authorization_header: String,
252}
253
254impl CreateHookOption {
255 pub fn validate(&self) -> crate::Result<()> {
257 if matches!(self.hook_type, HookType::Unknown) {
259 return Err(crate::Error::Validation(
260 "hook type is required".to_string(),
261 ));
262 }
263 Ok(())
264 }
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize, Default)]
269pub struct EditHookOption {
271 #[serde(skip_serializing_if = "Option::is_none")]
272 pub config: Option<std::collections::HashMap<String, String>>,
273 #[serde(default, skip_serializing_if = "Vec::is_empty")]
274 pub events: Vec<String>,
275 #[serde(default, rename = "branch_filter")]
276 pub branch_filter: String,
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub active: Option<bool>,
279 #[serde(default, rename = "authorization_header")]
280 pub authorization_header: String,
281}
282
283#[derive(Debug, Clone, Default)]
287pub struct ListAdminEmailsOptions {
289 pub list_options: ListOptions,
290}
291
292impl QueryEncode for ListAdminEmailsOptions {
293 fn query_encode(&self) -> String {
294 self.list_options.query_encode()
295 }
296}
297
298#[derive(Debug, Clone, Default)]
300pub struct SearchAdminEmailsOptions {
302 pub list_options: ListOptions,
303 pub query: String,
304}
305
306impl QueryEncode for SearchAdminEmailsOptions {
307 fn query_encode(&self) -> String {
308 let mut out = self.list_options.query_encode();
309 if !self.query.is_empty() {
310 out.push_str(&format!("&q={}", self.query));
311 }
312 out
313 }
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
320pub struct UserBadgeOption {
322 #[serde(rename = "badge_slugs")]
323 pub badge_slugs: Vec<String>,
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_create_user_option_validate_success() {
332 let opt = CreateUserOption {
333 username: "testuser".to_string(),
334 email: "test@example.com".to_string(),
335 password: "secret123".to_string(),
336 ..Default::default()
337 };
338 assert!(opt.validate().is_ok());
339 }
340
341 #[test]
342 fn test_create_user_option_validate_empty_username() {
343 let opt = CreateUserOption {
344 username: String::new(),
345 email: "test@example.com".to_string(),
346 password: "secret123".to_string(),
347 ..Default::default()
348 };
349 assert!(opt.validate().is_err());
350 }
351
352 #[test]
353 fn test_create_user_option_validate_empty_password() {
354 let opt = CreateUserOption {
355 username: "testuser".to_string(),
356 email: "test@example.com".to_string(),
357 password: String::new(),
358 ..Default::default()
359 };
360 assert!(opt.validate().is_err());
361 }
362
363 #[test]
364 fn test_create_user_option_validate_empty_email() {
365 let opt = CreateUserOption {
366 username: "testuser".to_string(),
367 email: String::new(),
368 password: "secret123".to_string(),
369 ..Default::default()
370 };
371 assert!(opt.validate().is_err());
372 }
373
374 #[test]
375 fn test_create_hook_option_validate_success() {
376 let opt = CreateHookOption {
377 hook_type: HookType::Gitea,
378 config: std::collections::HashMap::new(),
379 events: Vec::new(),
380 branch_filter: String::new(),
381 active: false,
382 authorization_header: String::new(),
383 };
384 assert!(opt.validate().is_ok());
385 }
386
387 #[test]
388 fn test_create_hook_option_validate_unknown_type() {
389 let opt = CreateHookOption {
390 hook_type: HookType::Unknown,
391 config: std::collections::HashMap::new(),
392 events: Vec::new(),
393 branch_filter: String::new(),
394 active: false,
395 authorization_header: String::new(),
396 };
397 assert!(opt.validate().is_err());
398 }
399}