1#![allow(dead_code)]
2
3use std::sync::Arc;
4
5use indexmap::IndexMap;
6use kcl_api::{Error, FileId, LifecycleApi, ProjectId, Result, SceneGraph, Version};
7use tokio::sync::RwLock;
8
9#[derive(Debug, Clone)]
10pub struct Project {
11 id: ProjectId,
12 files: IndexMap<FileId, File>,
13 open_file: FileId,
14 cur_version: Version,
15}
16
17impl Project {
18 fn scene_graph(&self) -> SceneGraph {
19 SceneGraph::empty(self.id, self.open_file, self.cur_version)
20 }
21}
22
23#[derive(Debug, Clone)]
24struct File {
25 version: Version,
26 path: String,
27 text: String,
28}
29
30lazy_static::lazy_static! {
31 static ref PROJECT: Arc<RwLock<Option<Project>>> = Default::default();
32}
33
34#[derive(Debug, Clone)]
35pub struct ProjectManager;
36
37impl ProjectManager {
38 async fn with_project<T>(f: impl FnOnce(&Option<Project>) -> T) -> T {
39 f(&*PROJECT.read().await)
40 }
41
42 async fn with_project_mut<T>(f: impl FnOnce(&mut Option<Project>) -> T) -> T {
43 f(&mut *PROJECT.write().await)
44 }
45}
46
47impl LifecycleApi for ProjectManager {
48 async fn open_project(&self, id: ProjectId, files: Vec<kcl_api::File>, open_file: FileId) -> Result<()> {
49 Self::with_project_mut(move |project| {
50 *project = Some(Project {
51 id,
52 files: files
53 .into_iter()
54 .map(|f| {
55 (
56 f.id,
57 File {
58 version: Version(0),
59 path: f.path,
60 text: f.text,
61 },
62 )
63 })
64 .collect(),
65 open_file,
66 cur_version: Version(0),
67 });
68 Ok(())
69 })
70 .await
71 }
72
73 async fn add_file(&self, project_id: ProjectId, file: kcl_api::File) -> Result<()> {
74 Self::with_project_mut(move |project| {
75 let Some(project) = project else {
76 return Err(Error::bad_project(project_id, None));
77 };
78 if project.id != project_id {
79 return Err(Error::bad_project(project_id, Some(project.id)));
80 }
81 if project.files.contains_key(&file.id) {
82 return Err(Error::file_id_in_use(file.id, &project.files[&file.id].path));
83 }
84 project.files.insert(
85 file.id,
86 File {
87 version: Version(0),
88 path: file.path,
89 text: file.text,
90 },
91 );
92 Ok(())
93 })
94 .await
95 }
96
97 async fn remove_file(&self, project_id: ProjectId, file_id: FileId) -> Result<()> {
98 Self::with_project_mut(move |project| {
99 let Some(project) = project else {
100 return Err(Error::bad_project(project_id, None));
101 };
102 if project.id != project_id {
103 return Err(Error::bad_project(project_id, Some(project.id)));
104 }
105 let old = project.files.swap_remove(&file_id);
106 if old.is_none() {
107 return Err(Error::bad_file(file_id, None));
108 }
109 Ok(())
110 })
111 .await
112 }
113
114 async fn update_file(&self, project_id: ProjectId, file_id: FileId, text: String) -> Result<()> {
115 Self::with_project_mut(move |project| {
116 let Some(project) = project else {
117 return Err(Error::bad_project(project_id, None));
118 };
119 if project.id != project_id {
120 return Err(Error::bad_project(project_id, Some(project.id)));
121 }
122 let Some(file) = project.files.get_mut(&file_id) else {
123 return Err(Error::bad_file(file_id, None));
124 };
125 file.text = text;
126 Ok(())
127 })
128 .await
129 }
130
131 async fn switch_file(&self, project_id: ProjectId, file_id: FileId) -> Result<()> {
132 Self::with_project_mut(move |project| {
133 let Some(project) = project else {
134 return Err(Error::bad_project(project_id, None));
135 };
136 if project.id != project_id {
137 return Err(Error::bad_project(project_id, Some(project.id)));
138 }
139 let Some(file) = project.files.get(&file_id) else {
140 return Err(Error::bad_file(file_id, None));
141 };
142 project.open_file = file_id;
143 project.cur_version = file.version;
144 Ok(())
145 })
146 .await
147 }
148
149 async fn refresh(&self, project_id: ProjectId) -> Result<()> {
150 Self::with_project_mut(move |project| {
151 let Some(project) = project else {
152 return Err(Error::bad_project(project_id, None));
153 };
154 if project.id != project_id {
155 return Err(Error::bad_project(project_id, Some(project.id)));
156 }
157 Ok(())
158 })
159 .await
160 }
161}
162
163#[derive(ts_rs::TS, serde::Serialize, serde::Deserialize)]
165#[ts(export)]
166pub struct IgnoreMe {
167 pub a: kcl_api::Error,
168 pub b: kcl_api::SceneGraphDelta,
169 pub c: kcl_api::File,
170}