1#![allow(dead_code)]
2
3use std::sync::Arc;
4
5use indexmap::IndexMap;
6use tokio::sync::RwLock;
7
8use crate::front::Error;
9use crate::front::FileId;
10use crate::front::LifecycleApi;
11use crate::front::ProjectId;
12use crate::front::Result;
13use crate::front::SceneGraph;
14use crate::front::Version;
15
16#[derive(Debug, Clone)]
17pub struct Project {
18 id: ProjectId,
19 files: IndexMap<FileId, File>,
20 open_file: FileId,
21 cur_version: Version,
22}
23
24impl Project {
25 fn scene_graph(&self) -> SceneGraph {
26 SceneGraph::empty(self.id, self.open_file, self.cur_version)
27 }
28}
29
30#[derive(Debug, Clone)]
31struct File {
32 version: Version,
33 path: String,
34 text: String,
35}
36
37lazy_static::lazy_static! {
38 static ref PROJECT: Arc<RwLock<Option<Project>>> = Default::default();
39}
40
41#[derive(Debug, Clone)]
42pub struct ProjectManager;
43
44impl ProjectManager {
45 async fn with_project<T>(f: impl FnOnce(&Option<Project>) -> T) -> T {
46 f(&*PROJECT.read().await)
47 }
48
49 async fn with_project_mut<T>(f: impl FnOnce(&mut Option<Project>) -> T) -> T {
50 f(&mut *PROJECT.write().await)
51 }
52}
53
54impl LifecycleApi for ProjectManager {
55 async fn open_project(&self, id: ProjectId, files: Vec<crate::front::File>, open_file: FileId) -> Result<()> {
56 Self::with_project_mut(move |project| {
57 *project = Some(Project {
58 id,
59 files: files
60 .into_iter()
61 .map(|f| {
62 (
63 f.id,
64 File {
65 version: Version(0),
66 path: f.path,
67 text: f.text,
68 },
69 )
70 })
71 .collect(),
72 open_file,
73 cur_version: Version(0),
74 });
75 Ok(())
76 })
77 .await
78 }
79
80 async fn get_project(&self, id: ProjectId) -> Result<Vec<crate::front::File>> {
81 Self::with_project(move |project| {
82 let Some(project) = project else {
83 return Err(Error::bad_project(id, None));
84 };
85 if project.id != id {
86 return Err(Error::bad_project(id, Some(project.id)));
87 }
88 Ok(project
89 .files
90 .iter()
91 .map(|(file_id, file)| to_front_file(*file_id, file))
92 .collect())
93 })
94 .await
95 }
96
97 async fn add_file(&self, project_id: ProjectId, file: crate::front::File) -> 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 if project.files.contains_key(&file.id) {
106 return Err(Error::file_id_in_use(file.id, &project.files[&file.id].path));
107 }
108 project.files.insert(
109 file.id,
110 File {
111 version: Version(0),
112 path: file.path,
113 text: file.text,
114 },
115 );
116 Ok(())
117 })
118 .await
119 }
120
121 async fn get_file(&self, project_id: ProjectId, file_id: FileId) -> Result<crate::front::File> {
122 Self::with_project(move |project| {
123 let Some(project) = project else {
124 return Err(Error::bad_project(project_id, None));
125 };
126 if project.id != project_id {
127 return Err(Error::bad_project(project_id, Some(project.id)));
128 }
129 project
130 .files
131 .get(&file_id)
132 .map(|file| to_front_file(file_id, file))
133 .ok_or_else(|| Error::file_id_not_found(project_id, file_id))
134 })
135 .await
136 }
137
138 async fn remove_file(&self, project_id: ProjectId, file_id: FileId) -> Result<()> {
139 Self::with_project_mut(move |project| {
140 let Some(project) = project else {
141 return Err(Error::bad_project(project_id, None));
142 };
143 if project.id != project_id {
144 return Err(Error::bad_project(project_id, Some(project.id)));
145 }
146 let old = project.files.swap_remove(&file_id);
147 if old.is_none() {
148 return Err(Error::bad_file(file_id, None));
149 }
150 Ok(())
151 })
152 .await
153 }
154
155 async fn update_file(&self, project_id: ProjectId, file_id: FileId, text: String) -> Result<()> {
156 Self::with_project_mut(move |project| {
157 let Some(project) = project else {
158 return Err(Error::bad_project(project_id, None));
159 };
160 if project.id != project_id {
161 return Err(Error::bad_project(project_id, Some(project.id)));
162 }
163 let Some(file) = project.files.get_mut(&file_id) else {
164 return Err(Error::bad_file(file_id, None));
165 };
166 file.text = text;
167 Ok(())
168 })
169 .await
170 }
171
172 async fn switch_file(&self, project_id: ProjectId, file_id: FileId) -> Result<()> {
173 Self::with_project_mut(move |project| {
174 let Some(project) = project else {
175 return Err(Error::bad_project(project_id, None));
176 };
177 if project.id != project_id {
178 return Err(Error::bad_project(project_id, Some(project.id)));
179 }
180 let Some(file) = project.files.get(&file_id) else {
181 return Err(Error::bad_file(file_id, None));
182 };
183 project.open_file = file_id;
184 project.cur_version = file.version;
185 Ok(())
186 })
187 .await
188 }
189
190 async fn refresh(&self, project_id: ProjectId) -> Result<()> {
191 Self::with_project_mut(move |project| {
192 let Some(project) = project else {
193 return Err(Error::bad_project(project_id, None));
194 };
195 if project.id != project_id {
196 return Err(Error::bad_project(project_id, Some(project.id)));
197 }
198 Ok(())
199 })
200 .await
201 }
202}
203
204fn to_front_file(file_id: FileId, file: &File) -> crate::front::File {
205 crate::front::File {
206 id: file_id,
207 path: file.path.clone(),
208 text: file.text.clone(),
209 }
210}
211
212#[derive(ts_rs::TS, serde::Serialize)]
214#[ts(export, export_to = "FrontendApi.ts")]
215pub struct IgnoreMe {
216 pub a: crate::front::File,
217}