1use chrono::{DateTime, Utc};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use utoipa::{IntoParams, ToSchema};
5
6use crate::Pagination;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema, JsonSchema)]
9#[serde(rename_all = "snake_case")]
10pub enum Visibility {
11 Public,
12 Private,
13}
14
15impl Visibility {
16 pub fn as_str(self) -> &'static str {
17 match self {
18 Self::Public => "public",
19 Self::Private => "private",
20 }
21 }
22}
23
24impl std::str::FromStr for Visibility {
25 type Err = anyhow::Error;
26
27 fn from_str(value: &str) -> Result<Self, Self::Err> {
28 match value {
29 "public" => Ok(Self::Public),
30 "private" => Ok(Self::Private),
31 other => Err(anyhow::anyhow!("unsupported visibility: {other}")),
32 }
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema, JsonSchema)]
37#[serde(rename_all = "snake_case")]
38pub enum MembershipRole {
39 Owner,
40 Maintainer,
41 Viewer,
42}
43
44impl MembershipRole {
45 pub fn as_str(self) -> &'static str {
46 match self {
47 Self::Owner => "owner",
48 Self::Maintainer => "maintainer",
49 Self::Viewer => "viewer",
50 }
51 }
52
53 pub fn rank(self) -> i16 {
54 match self {
55 Self::Viewer => 1,
56 Self::Maintainer => 2,
57 Self::Owner => 3,
58 }
59 }
60}
61
62impl std::str::FromStr for MembershipRole {
63 type Err = anyhow::Error;
64
65 fn from_str(value: &str) -> Result<Self, Self::Err> {
66 match value {
67 "owner" => Ok(Self::Owner),
68 "maintainer" => Ok(Self::Maintainer),
69 "viewer" => Ok(Self::Viewer),
70 other => Err(anyhow::anyhow!("unsupported membership role: {other}")),
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema, JsonSchema)]
76#[serde(rename_all = "snake_case")]
77pub enum GroupKind {
78 Personal,
79 Shared,
80}
81
82impl GroupKind {
83 pub fn as_str(self) -> &'static str {
84 match self {
85 Self::Personal => "personal",
86 Self::Shared => "shared",
87 }
88 }
89}
90
91impl std::str::FromStr for GroupKind {
92 type Err = anyhow::Error;
93
94 fn from_str(value: &str) -> Result<Self, Self::Err> {
95 match value {
96 "personal" => Ok(Self::Personal),
97 "shared" => Ok(Self::Shared),
98 other => Err(anyhow::anyhow!("unsupported group kind: {other}")),
99 }
100 }
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
104pub struct CreateGroupRequest {
105 #[serde(default, skip_serializing_if = "Option::is_none")]
106 pub parent_group_path: Option<String>,
107 pub group_key: String,
108 pub name: String,
109 pub visibility: Visibility,
110 #[serde(default)]
111 pub kind: Option<GroupKind>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
115pub struct UpdateGroupRequest {
116 #[serde(default, skip_serializing_if = "Option::is_none")]
117 pub name: Option<String>,
118 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub visibility: Option<Visibility>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
123pub struct MoveGroupRequest {
124 #[serde(default, skip_serializing_if = "Option::is_none")]
125 pub target_parent_group_path: Option<String>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
129pub struct UpsertMembershipRequest {
130 pub login_name: String,
131 pub role: MembershipRole,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
135pub struct GroupResponse {
136 pub group_id: i64,
137 pub group_key: String,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub group_path: Option<String>,
140 #[serde(default, skip_serializing_if = "Option::is_none")]
141 pub parent_group_path: Option<String>,
142 pub name: String,
143 pub visibility: Visibility,
144 pub kind: GroupKind,
145 #[serde(default, skip_serializing_if = "Option::is_none")]
146 pub current_role: Option<MembershipRole>,
147 pub created_at: DateTime<Utc>,
148 pub updated_at: DateTime<Utc>,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
152pub struct GroupMemberResponse {
153 pub user_id: i64,
154 pub login_name: String,
155 pub display_name: String,
156 pub role: MembershipRole,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
160pub struct UserDirectoryEntryResponse {
161 pub user_id: i64,
162 pub login_name: String,
163 pub display_name: String,
164}
165
166fn default_page() -> u32 {
167 1
168}
169fn default_page_size() -> u32 {
170 50
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize, IntoParams, ToSchema)]
174#[into_params(parameter_in = Query)]
175pub struct NamespacePageQuery {
176 #[serde(default = "default_page")]
177 pub page: u32,
178 #[serde(default = "default_page_size")]
179 pub page_size: u32,
180 #[serde(default)]
181 pub query: Option<String>,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
185pub struct GroupPageResponse {
186 pub items: Vec<GroupResponse>,
187 pub pagination: Pagination,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
191pub struct GroupMemberPageResponse {
192 pub items: Vec<GroupMemberResponse>,
193 pub pagination: Pagination,
194}
195
196#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
197#[into_params(parameter_in = Query)]
198pub struct GroupSearchQuery {
199 #[serde(default)]
200 pub query: String,
201 #[serde(default = "default_search_limit")]
202 pub limit: u32,
203}
204
205const fn default_search_limit() -> u32 {
206 20
207}