1pub mod identity;
13pub mod permissions;
14pub mod space;
15pub mod templates;
16
17pub use identity::{Identity, IdentityProvider};
18pub use permissions::{AccessLevel, Permission, ProjectAccess};
19pub use space::{UserSpace, SpaceConfig};
20pub use templates::Template;
21
22use anyhow::Result;
23use serde::{Deserialize, Serialize};
24use std::path::PathBuf;
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct Collaborator {
29 pub identity: Identity,
31
32 pub kind: CollaboratorKind,
34
35 pub template: Option<String>,
37
38 pub skills: Vec<String>,
40
41 pub status: CollaboratorStatus,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
47pub enum CollaboratorKind {
48 Human,
50 Ai { model: String },
52 System,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
58pub enum CollaboratorStatus {
59 Active,
61 Idle,
63 Offline,
65 Invited,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct Project {
72 pub id: String,
74
75 pub name: String,
77
78 pub owner: Identity,
80
81 pub path: PathBuf,
83
84 pub collaborators: Vec<(Collaborator, AccessLevel)>,
86
87 pub settings: ProjectSettings,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, Default)]
93pub struct ProjectSettings {
94 pub allow_containers: bool,
96
97 pub default_template: Option<String>,
99
100 pub cloud_sync: bool,
102
103 pub deny_patterns: Vec<String>,
105}
106
107impl Project {
108 pub fn new(name: &str, owner: Identity, path: PathBuf) -> Self {
110 let id = format!("{}/{}", owner, name);
111 Project {
112 id,
113 name: name.to_string(),
114 owner,
115 path,
116 collaborators: Vec::new(),
117 settings: ProjectSettings::default(),
118 }
119 }
120
121 pub fn add_collaborator(&mut self, collab: Collaborator, access: AccessLevel) {
123 self.collaborators.push((collab, access));
124 }
125
126 pub fn can_access(&self, identity: &Identity) -> bool {
128 if &self.owner == identity {
130 return true;
131 }
132
133 self.collaborators.iter().any(|(c, _)| &c.identity == identity)
135 }
136
137 pub fn access_level(&self, identity: &Identity) -> AccessLevel {
139 if &self.owner == identity {
140 return AccessLevel::Owner;
141 }
142
143 self.collaborators
144 .iter()
145 .find(|(c, _)| &c.identity == identity)
146 .map(|(_, level)| level.clone())
147 .unwrap_or(AccessLevel::None)
148 }
149}
150
151pub async fn join_project(project_id: &str) -> Result<Project> {
153 let (owner_str, project_name) = project_id
155 .rsplit_once('/')
156 .ok_or_else(|| anyhow::anyhow!("Invalid project ID format"))?;
157
158 let owner = Identity::parse(owner_str)?;
159
160 Ok(Project::new(project_name, owner, PathBuf::from(".")))
163}