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