1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
9#[sqlx(type_name = "user_role", rename_all = "lowercase")]
10#[serde(rename_all = "lowercase")]
11pub enum UserRole {
12 Admin,
14 Editor,
16 Viewer,
18}
19
20impl UserRole {
21 #[must_use]
23 pub const fn is_admin(&self) -> bool {
24 matches!(self, Self::Admin)
25 }
26
27 #[must_use]
29 pub const fn can_edit(&self) -> bool {
30 matches!(self, Self::Admin | Self::Editor)
31 }
32
33 #[must_use]
35 pub const fn can_view(&self) -> bool {
36 true }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
42pub struct User {
43 pub id: Uuid,
45 pub username: String,
47 pub email: String,
49 #[serde(skip_serializing)]
51 pub password_hash: String,
52 pub display_name: Option<String>,
54 pub avatar_url: Option<String>,
56 pub created_at: DateTime<Utc>,
58 pub updated_at: DateTime<Utc>,
60 pub is_active: bool,
62}
63
64impl User {
65 #[must_use]
67 pub fn new(username: String, email: String, password_hash: String) -> Self {
68 let now = Utc::now();
69 Self {
70 id: Uuid::new_v4(),
71 username,
72 email,
73 password_hash,
74 display_name: None,
75 avatar_url: None,
76 created_at: now,
77 updated_at: now,
78 is_active: true,
79 }
80 }
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
85pub struct TeamWorkspace {
86 pub id: Uuid,
88 pub name: String,
90 pub description: Option<String>,
92 pub owner_id: Uuid,
94 pub config: serde_json::Value,
96 pub version: i64,
98 pub created_at: DateTime<Utc>,
100 pub updated_at: DateTime<Utc>,
102 pub is_archived: bool,
104}
105
106impl TeamWorkspace {
107 #[must_use]
109 pub fn new(name: String, owner_id: Uuid) -> Self {
110 let now = Utc::now();
111 Self {
112 id: Uuid::new_v4(),
113 name,
114 description: None,
115 owner_id,
116 config: serde_json::json!({}),
117 version: 1,
118 created_at: now,
119 updated_at: now,
120 is_archived: false,
121 }
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
127pub struct WorkspaceMember {
128 pub id: Uuid,
130 pub workspace_id: Uuid,
132 pub user_id: Uuid,
134 pub role: UserRole,
136 pub joined_at: DateTime<Utc>,
138 pub last_activity: DateTime<Utc>,
140}
141
142impl WorkspaceMember {
143 #[must_use]
145 pub fn new(workspace_id: Uuid, user_id: Uuid, role: UserRole) -> Self {
146 let now = Utc::now();
147 Self {
148 id: Uuid::new_v4(),
149 workspace_id,
150 user_id,
151 role,
152 joined_at: now,
153 last_activity: now,
154 }
155 }
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
160pub struct WorkspaceInvitation {
161 pub id: Uuid,
163 pub workspace_id: Uuid,
165 pub email: String,
167 pub role: UserRole,
169 pub invited_by: Uuid,
171 pub token: String,
173 pub expires_at: DateTime<Utc>,
175 pub created_at: DateTime<Utc>,
177 pub accepted: bool,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct ActiveSession {
184 pub user_id: Uuid,
186 pub workspace_id: Uuid,
188 pub session_id: Uuid,
190 pub connected_at: DateTime<Utc>,
192 pub last_activity: DateTime<Utc>,
194 pub cursor: Option<CursorPosition>,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct CursorPosition {
201 pub resource: String,
203 pub line: Option<u32>,
205 pub column: Option<u32>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
211pub struct WorkspaceFork {
212 pub id: Uuid,
214 pub source_workspace_id: Uuid,
216 pub forked_workspace_id: Uuid,
218 pub forked_at: DateTime<Utc>,
220 pub forked_by: Uuid,
222 pub fork_point_commit_id: Option<Uuid>,
224}
225
226impl WorkspaceFork {
227 #[must_use]
229 pub fn new(
230 source_workspace_id: Uuid,
231 forked_workspace_id: Uuid,
232 forked_by: Uuid,
233 fork_point_commit_id: Option<Uuid>,
234 ) -> Self {
235 Self {
236 id: Uuid::new_v4(),
237 source_workspace_id,
238 forked_workspace_id,
239 forked_by,
240 fork_point_commit_id,
241 forked_at: Utc::now(),
242 }
243 }
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
248#[sqlx(type_name = "merge_status", rename_all = "lowercase")]
249#[serde(rename_all = "lowercase")]
250pub enum MergeStatus {
251 Pending,
253 InProgress,
255 Completed,
257 Conflict,
259 Cancelled,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
265pub struct WorkspaceMerge {
266 pub id: Uuid,
268 pub source_workspace_id: Uuid,
270 pub target_workspace_id: Uuid,
272 pub base_commit_id: Uuid,
274 pub source_commit_id: Uuid,
276 pub target_commit_id: Uuid,
278 pub merge_commit_id: Option<Uuid>,
280 pub status: MergeStatus,
282 pub conflict_data: Option<serde_json::Value>,
284 pub merged_by: Option<Uuid>,
286 pub merged_at: Option<DateTime<Utc>>,
288 pub created_at: DateTime<Utc>,
290}
291
292impl WorkspaceMerge {
293 #[must_use]
295 pub fn new(
296 source_workspace_id: Uuid,
297 target_workspace_id: Uuid,
298 base_commit_id: Uuid,
299 source_commit_id: Uuid,
300 target_commit_id: Uuid,
301 ) -> Self {
302 Self {
303 id: Uuid::new_v4(),
304 source_workspace_id,
305 target_workspace_id,
306 base_commit_id,
307 source_commit_id,
308 target_commit_id,
309 merge_commit_id: None,
310 status: MergeStatus::Pending,
311 conflict_data: None,
312 merged_by: None,
313 merged_at: None,
314 created_at: Utc::now(),
315 }
316 }
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct MergeConflict {
322 pub path: String,
324 pub base_value: Option<serde_json::Value>,
326 pub source_value: Option<serde_json::Value>,
328 pub target_value: Option<serde_json::Value>,
330 pub conflict_type: ConflictType,
332}
333
334#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
336#[serde(rename_all = "lowercase")]
337pub enum ConflictType {
338 Modified,
340 DeletedModified,
342 BothAdded,
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn test_user_role_permissions() {
352 assert!(UserRole::Admin.is_admin());
353 assert!(UserRole::Admin.can_edit());
354 assert!(UserRole::Admin.can_view());
355
356 assert!(!UserRole::Editor.is_admin());
357 assert!(UserRole::Editor.can_edit());
358 assert!(UserRole::Editor.can_view());
359
360 assert!(!UserRole::Viewer.is_admin());
361 assert!(!UserRole::Viewer.can_edit());
362 assert!(UserRole::Viewer.can_view());
363 }
364
365 #[test]
366 fn test_user_creation() {
367 let user = User::new(
368 "testuser".to_string(),
369 "test@example.com".to_string(),
370 "hashed_password".to_string(),
371 );
372
373 assert_eq!(user.username, "testuser");
374 assert_eq!(user.email, "test@example.com");
375 assert!(user.is_active);
376 }
377
378 #[test]
379 fn test_workspace_creation() {
380 let owner_id = Uuid::new_v4();
381 let workspace = TeamWorkspace::new("Test Workspace".to_string(), owner_id);
382
383 assert_eq!(workspace.name, "Test Workspace");
384 assert_eq!(workspace.owner_id, owner_id);
385 assert_eq!(workspace.version, 1);
386 assert!(!workspace.is_archived);
387 }
388
389 #[test]
390 fn test_workspace_member_creation() {
391 let workspace_id = Uuid::new_v4();
392 let user_id = Uuid::new_v4();
393 let member = WorkspaceMember::new(workspace_id, user_id, UserRole::Editor);
394
395 assert_eq!(member.workspace_id, workspace_id);
396 assert_eq!(member.user_id, user_id);
397 assert_eq!(member.role, UserRole::Editor);
398 }
399}