pub mod identity;
pub mod permissions;
pub mod space;
pub mod templates;
pub use identity::{Identity, IdentityProvider};
pub use permissions::{AccessLevel, Permission, ProjectAccess};
pub use space::{UserSpace, SpaceConfig};
pub use templates::Template;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Collaborator {
pub identity: Identity,
pub kind: CollaboratorKind,
pub template: Option<String>,
pub skills: Vec<String>,
pub status: CollaboratorStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum CollaboratorKind {
Human,
Ai { model: String },
System,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum CollaboratorStatus {
Active,
Idle,
Offline,
Invited,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Project {
pub id: String,
pub name: String,
pub owner: Identity,
pub path: PathBuf,
pub collaborators: Vec<(Collaborator, AccessLevel)>,
pub settings: ProjectSettings,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ProjectSettings {
pub allow_containers: bool,
pub default_template: Option<String>,
pub cloud_sync: bool,
pub deny_patterns: Vec<String>,
}
impl Project {
pub fn new(name: &str, owner: Identity, path: PathBuf) -> Self {
let id = format!("{}/{}", owner, name);
Project {
id,
name: name.to_string(),
owner,
path,
collaborators: Vec::new(),
settings: ProjectSettings::default(),
}
}
pub fn add_collaborator(&mut self, collab: Collaborator, access: AccessLevel) {
self.collaborators.push((collab, access));
}
pub fn can_access(&self, identity: &Identity) -> bool {
if &self.owner == identity {
return true;
}
self.collaborators.iter().any(|(c, _)| &c.identity == identity)
}
pub fn access_level(&self, identity: &Identity) -> AccessLevel {
if &self.owner == identity {
return AccessLevel::Owner;
}
self.collaborators
.iter()
.find(|(c, _)| &c.identity == identity)
.map(|(_, level)| level.clone())
.unwrap_or(AccessLevel::None)
}
}
pub async fn join_project(project_id: &str) -> Result<Project> {
let (owner_str, project_name) = project_id
.rsplit_once('/')
.ok_or_else(|| anyhow::anyhow!("Invalid project ID format"))?;
let owner = Identity::parse(owner_str)?;
Ok(Project::new(project_name, owner, PathBuf::from(".")))
}