oxios_kernel/kernel_handle/
project_api.rs1use std::sync::Arc;
9
10use anyhow::{Context, Result};
11use chrono::Utc;
12use serde::{Deserialize, Serialize};
13use uuid::Uuid;
14
15use crate::project::{Project, ProjectManager, ProjectSource};
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19#[allow(missing_docs)]
20pub struct ProjectInfo {
21 pub id: String,
22 pub name: String,
23 pub description: String,
24 pub source: String,
25 pub tags: Vec<String>,
26 pub emoji: String,
27 pub memory_visible: bool,
28 pub mount_ids: Vec<String>,
30 pub instructions: String,
32 pub created_at: String,
33 pub updated_at: String,
34 pub last_active_at: String,
35}
36
37impl From<&Project> for ProjectInfo {
38 fn from(project: &Project) -> Self {
39 Self {
40 id: project.id.to_string(),
41 name: project.name.clone(),
42 description: project.description.clone(),
43 source: project.source.to_string(),
44 tags: project.tags.clone(),
45 emoji: project.emoji.clone(),
46 memory_visible: project.memory_visible,
47 mount_ids: project.mount_ids.iter().map(|m| m.to_string()).collect(),
48 instructions: project.instructions.clone(),
49 created_at: project.created_at.to_rfc3339(),
50 updated_at: project.updated_at.to_rfc3339(),
51 last_active_at: project.last_active_at.to_rfc3339(),
52 }
53 }
54}
55
56#[allow(dead_code)]
61pub struct ProjectApi {
62 pub(crate) project_manager: Arc<ProjectManager>,
64}
65
66impl ProjectApi {
67 pub fn new(project_manager: Arc<ProjectManager>) -> Self {
69 Self { project_manager }
70 }
71
72 pub fn list_projects(&self) -> Vec<ProjectInfo> {
74 self.project_manager
75 .list_projects()
76 .iter()
77 .map(ProjectInfo::from)
78 .collect()
79 }
80
81 pub fn get_project(&self, id: &str) -> Option<ProjectInfo> {
83 let project_id = Uuid::parse_str(id).ok()?;
84 self.project_manager
85 .get_project(project_id)
86 .as_ref()
87 .map(ProjectInfo::from)
88 }
89
90 pub fn create_project(
92 &self,
93 name: String,
94 tags: Vec<String>,
95 emoji: Option<String>,
96 description: Option<String>,
97 ) -> Result<ProjectInfo> {
98 let project = self.project_manager.create_project(
99 name,
100 tags,
101 emoji,
102 description,
103 ProjectSource::Manual,
104 )?;
105 Ok(ProjectInfo::from(&project))
106 }
107
108 #[allow(clippy::too_many_arguments)]
110 pub fn update_project(
111 &self,
112 id: &str,
113 name: Option<String>,
114 tags: Option<Vec<String>>,
115 emoji: Option<String>,
116 description: Option<String>,
117 memory_visible: Option<bool>,
118 ) -> Result<ProjectInfo> {
119 let project_id = Uuid::parse_str(id).context("Invalid project ID")?;
120
121 let mut project =
122 self.project_manager
123 .update_project(project_id, name, tags, emoji, description)?;
124
125 if let Some(visible) = memory_visible {
127 project.memory_visible = visible;
128 project.updated_at = Utc::now();
129 self.project_manager.save_project(&project)?;
130 }
131
132 Ok(ProjectInfo::from(&project))
133 }
134
135 pub fn remove_project(&self, id: &str) -> Result<()> {
137 let project_id = Uuid::parse_str(id).context("Invalid project ID")?;
138 self.project_manager.remove_project(project_id)
139 }
140
141 pub fn link_memory(&self, project_id: &str, memory_id: &str) -> Result<()> {
143 let pid = Uuid::parse_str(project_id).context("Invalid project ID")?;
144 self.project_manager.link_memory(pid, memory_id)
145 }
146
147 pub fn unlink_memory(&self, project_id: &str, memory_id: &str) -> Result<()> {
149 let pid = Uuid::parse_str(project_id).context("Invalid project ID")?;
150 self.project_manager.unlink_memory(pid, memory_id)
151 }
152
153 pub fn get_project_memory_ids(&self, project_id: &str) -> Result<Vec<String>> {
155 let pid = Uuid::parse_str(project_id).context("Invalid project ID")?;
156 self.project_manager.get_project_memory_ids(pid)
157 }
158
159 pub fn update_project_bundle(
161 &self,
162 id: &str,
163 mount_ids: Option<Vec<String>>,
164 instructions: Option<String>,
165 ) -> Result<ProjectInfo> {
166 let pid = Uuid::parse_str(id).context("Invalid project ID")?;
167 let mount_ids = match mount_ids {
170 Some(ids) => {
171 let mut parsed = Vec::with_capacity(ids.len());
172 let mut bad = Vec::new();
173 for s in ids {
174 match uuid::Uuid::parse_str(&s) {
175 Ok(u) => parsed.push(u),
176 Err(_) => bad.push(s),
177 }
178 }
179 if !bad.is_empty() {
180 anyhow::bail!("Invalid mount ID(s): {}", bad.join(", "));
181 }
182 Some(parsed)
183 }
184 None => None,
185 };
186 let project = self
187 .project_manager
188 .update_project_bundle(pid, mount_ids, instructions)?;
189 Ok(ProjectInfo::from(&project))
190 }
191}