Skip to main content

gitea_sdk_rs/options/
admin.rs

1// Copyright 2026 infinitete. All rights reserved.
2// Use of this source code is governed by a MIT-style
3// license that can be found in the LICENSE file.
4
5//! Request option types for admin API endpoints.
6
7use crate::pagination::{ListOptions, QueryEncode};
8use crate::types::enums::{HookType, VisibleType};
9use crate::{Deserialize, Serialize};
10
11// ── admin_user.go ───────────────────────────────────────────────────
12
13/// Options for listing admin users
14#[derive(Debug, Clone, Default)]
15/// Options for Admin List Users Option.
16pub 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/// Options for creating a user
72#[derive(Debug, Clone, Default, Serialize, Deserialize)]
73/// Options for Create User Option.
74pub 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    /// Validate this `CreateUserOption` payload.
97    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/// Options for editing a user
112#[derive(Debug, Clone, Serialize, Deserialize, Default)]
113/// Options for Edit User Option.
114pub 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/// Options for renaming a user
160#[derive(Debug, Clone, Serialize, Deserialize)]
161/// Options for Rename User Option.
162pub struct RenameUserOption {
163    #[serde(rename = "new_username")]
164    pub new_username: String,
165}
166
167// ── admin_repo.go ───────────────────────────────────────────────────
168
169/// Options for listing unadopted repositories
170#[derive(Debug, Clone, Default)]
171/// Options for List Unadopted Repos Option.
172pub 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// ── admin_org.go ────────────────────────────────────────────────────
188
189/// Options for listing admin organizations
190#[derive(Debug, Clone, Default)]
191/// Options for Admin List Orgs Option.
192pub 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// ── admin_cron.go ───────────────────────────────────────────────────
203
204/// Options for listing cron tasks
205#[derive(Debug, Clone, Default)]
206/// Options for List Cron Tasks Option.
207pub 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// ── admin_hooks.go ──────────────────────────────────────────────────
218
219/// Options for listing admin hooks
220#[derive(Debug, Clone, Default)]
221/// Options for List Admin Hooks Option.
222pub 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/// Options for creating a hook
238#[derive(Debug, Clone, Serialize, Deserialize)]
239/// Options for Create Hook Option.
240pub 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    /// Validate this `CreateHookOption` payload.
256    pub fn validate(&self) -> crate::Result<()> {
257        // HookType::Unknown means empty, which is invalid
258        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/// Options for editing a hook
268#[derive(Debug, Clone, Serialize, Deserialize, Default)]
269/// Options for Edit Hook Option.
270pub 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// ── admin_email.go ──────────────────────────────────────────────────
284
285/// Options for listing admin emails
286#[derive(Debug, Clone, Default)]
287/// Options for List Admin Emails Option.
288pub 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/// Options for searching admin emails
299#[derive(Debug, Clone, Default)]
300/// Options for Search Admin Emails Option.
301pub 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// ── admin_badges.go ─────────────────────────────────────────────────
317
318/// Options for adding user badges
319#[derive(Debug, Clone, Serialize, Deserialize)]
320/// Options for User Badge Option.
321pub 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}