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 pub fn is_admin(&self) -> bool {
23 matches!(self, UserRole::Admin)
24 }
25
26 pub fn can_edit(&self) -> bool {
28 matches!(self, UserRole::Admin | UserRole::Editor)
29 }
30
31 pub fn can_view(&self) -> bool {
33 true }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
39pub struct User {
40 pub id: Uuid,
42 pub username: String,
44 pub email: String,
46 #[serde(skip_serializing)]
48 pub password_hash: String,
49 pub display_name: Option<String>,
51 pub avatar_url: Option<String>,
53 pub created_at: DateTime<Utc>,
55 pub updated_at: DateTime<Utc>,
57 pub is_active: bool,
59}
60
61impl User {
62 pub fn new(username: String, email: String, password_hash: String) -> Self {
64 let now = Utc::now();
65 Self {
66 id: Uuid::new_v4(),
67 username,
68 email,
69 password_hash,
70 display_name: None,
71 avatar_url: None,
72 created_at: now,
73 updated_at: now,
74 is_active: true,
75 }
76 }
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
81pub struct TeamWorkspace {
82 pub id: Uuid,
84 pub name: String,
86 pub description: Option<String>,
88 pub owner_id: Uuid,
90 pub config: serde_json::Value,
92 pub version: i64,
94 pub created_at: DateTime<Utc>,
96 pub updated_at: DateTime<Utc>,
98 pub is_archived: bool,
100}
101
102impl TeamWorkspace {
103 pub fn new(name: String, owner_id: Uuid) -> Self {
105 let now = Utc::now();
106 Self {
107 id: Uuid::new_v4(),
108 name,
109 description: None,
110 owner_id,
111 config: serde_json::json!({}),
112 version: 1,
113 created_at: now,
114 updated_at: now,
115 is_archived: false,
116 }
117 }
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
122pub struct WorkspaceMember {
123 pub id: Uuid,
125 pub workspace_id: Uuid,
127 pub user_id: Uuid,
129 pub role: UserRole,
131 pub joined_at: DateTime<Utc>,
133 pub last_activity: DateTime<Utc>,
135}
136
137impl WorkspaceMember {
138 pub fn new(workspace_id: Uuid, user_id: Uuid, role: UserRole) -> Self {
140 let now = Utc::now();
141 Self {
142 id: Uuid::new_v4(),
143 workspace_id,
144 user_id,
145 role,
146 joined_at: now,
147 last_activity: now,
148 }
149 }
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
154pub struct WorkspaceInvitation {
155 pub id: Uuid,
157 pub workspace_id: Uuid,
159 pub email: String,
161 pub role: UserRole,
163 pub invited_by: Uuid,
165 pub token: String,
167 pub expires_at: DateTime<Utc>,
169 pub created_at: DateTime<Utc>,
171 pub accepted: bool,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct ActiveSession {
178 pub user_id: Uuid,
180 pub workspace_id: Uuid,
182 pub session_id: Uuid,
184 pub connected_at: DateTime<Utc>,
186 pub last_activity: DateTime<Utc>,
188 pub cursor: Option<CursorPosition>,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct CursorPosition {
195 pub resource: String,
197 pub line: Option<u32>,
199 pub column: Option<u32>,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
205pub struct WorkspaceFork {
206 pub id: Uuid,
208 pub source_workspace_id: Uuid,
210 pub forked_workspace_id: Uuid,
212 pub forked_at: DateTime<Utc>,
214 pub forked_by: Uuid,
216 pub fork_point_commit_id: Option<Uuid>,
218}
219
220impl WorkspaceFork {
221 pub fn new(
223 source_workspace_id: Uuid,
224 forked_workspace_id: Uuid,
225 forked_by: Uuid,
226 fork_point_commit_id: Option<Uuid>,
227 ) -> Self {
228 Self {
229 id: Uuid::new_v4(),
230 source_workspace_id,
231 forked_workspace_id,
232 forked_by,
233 fork_point_commit_id,
234 forked_at: Utc::now(),
235 }
236 }
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
241#[sqlx(type_name = "merge_status", rename_all = "lowercase")]
242#[serde(rename_all = "lowercase")]
243pub enum MergeStatus {
244 Pending,
246 InProgress,
248 Completed,
250 Conflict,
252 Cancelled,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
258pub struct WorkspaceMerge {
259 pub id: Uuid,
261 pub source_workspace_id: Uuid,
263 pub target_workspace_id: Uuid,
265 pub base_commit_id: Uuid,
267 pub source_commit_id: Uuid,
269 pub target_commit_id: Uuid,
271 pub merge_commit_id: Option<Uuid>,
273 pub status: MergeStatus,
275 pub conflict_data: Option<serde_json::Value>,
277 pub merged_by: Option<Uuid>,
279 pub merged_at: Option<DateTime<Utc>>,
281 pub created_at: DateTime<Utc>,
283}
284
285impl WorkspaceMerge {
286 pub fn new(
288 source_workspace_id: Uuid,
289 target_workspace_id: Uuid,
290 base_commit_id: Uuid,
291 source_commit_id: Uuid,
292 target_commit_id: Uuid,
293 ) -> Self {
294 Self {
295 id: Uuid::new_v4(),
296 source_workspace_id,
297 target_workspace_id,
298 base_commit_id,
299 source_commit_id,
300 target_commit_id,
301 merge_commit_id: None,
302 status: MergeStatus::Pending,
303 conflict_data: None,
304 merged_by: None,
305 merged_at: None,
306 created_at: Utc::now(),
307 }
308 }
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct MergeConflict {
314 pub path: String,
316 pub base_value: Option<serde_json::Value>,
318 pub source_value: Option<serde_json::Value>,
320 pub target_value: Option<serde_json::Value>,
322 pub conflict_type: ConflictType,
324}
325
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
328#[serde(rename_all = "lowercase")]
329pub enum ConflictType {
330 Modified,
332 DeletedModified,
334 BothAdded,
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341
342 #[test]
343 fn test_user_role_permissions() {
344 assert!(UserRole::Admin.is_admin());
345 assert!(UserRole::Admin.can_edit());
346 assert!(UserRole::Admin.can_view());
347
348 assert!(!UserRole::Editor.is_admin());
349 assert!(UserRole::Editor.can_edit());
350 assert!(UserRole::Editor.can_view());
351
352 assert!(!UserRole::Viewer.is_admin());
353 assert!(!UserRole::Viewer.can_edit());
354 assert!(UserRole::Viewer.can_view());
355 }
356
357 #[test]
358 fn test_user_creation() {
359 let user = User::new(
360 "testuser".to_string(),
361 "test@example.com".to_string(),
362 "hashed_password".to_string(),
363 );
364
365 assert_eq!(user.username, "testuser");
366 assert_eq!(user.email, "test@example.com");
367 assert!(user.is_active);
368 }
369
370 #[test]
371 fn test_workspace_creation() {
372 let owner_id = Uuid::new_v4();
373 let workspace = TeamWorkspace::new("Test Workspace".to_string(), owner_id);
374
375 assert_eq!(workspace.name, "Test Workspace");
376 assert_eq!(workspace.owner_id, owner_id);
377 assert_eq!(workspace.version, 1);
378 assert!(!workspace.is_archived);
379 }
380
381 #[test]
382 fn test_workspace_member_creation() {
383 let workspace_id = Uuid::new_v4();
384 let user_id = Uuid::new_v4();
385 let member = WorkspaceMember::new(workspace_id, user_id, UserRole::Editor);
386
387 assert_eq!(member.workspace_id, workspace_id);
388 assert_eq!(member.user_id, user_id);
389 assert_eq!(member.role, UserRole::Editor);
390 }
391}