gveditor_core_api/
state.rs

1use crate::extensions::base::ExtensionInfo;
2use crate::extensions::manager::{ExtensionsManager, LoadedExtension};
3use crate::filesystems::{FileFormat, Filesystem, LocalFilesystem};
4use crate::messaging::ExtensionMessages;
5pub use crate::state_persistors::memory::MemoryPersistor;
6use crate::state_persistors::Persistor;
7use crate::{Errors, ExtensionErrors, LanguageServer, ManifestInfo};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::fmt;
11use std::sync::Arc;
12use tokio::sync::Mutex;
13
14#[derive(Clone)]
15pub enum TokenFlags {
16    All(String),
17}
18
19/// Internal list of states
20#[derive(Clone, Default)]
21pub struct StatesList {
22    states: HashMap<u8, Arc<Mutex<State>>>,
23    provided_tokens: Vec<TokenFlags>,
24}
25
26impl StatesList {
27    /// Create a new empty states list
28    pub fn new() -> Self {
29        Self {
30            states: HashMap::new(),
31            provided_tokens: Vec::new(),
32        }
33    }
34
35    pub fn with_tokens(mut self, tokens: &[TokenFlags]) -> Self {
36        self.provided_tokens = tokens.to_vec();
37        self
38    }
39
40    /// Return the state by the given ID if found
41    pub fn get_state_by_id(&self, id: u8) -> Option<Arc<Mutex<State>>> {
42        self.states.get(&id).cloned()
43    }
44
45    /// Return the state by the given ID if found
46    pub fn with_state(mut self, state: State) -> Self {
47        let mut state = state;
48
49        for token in &self.provided_tokens {
50            match token {
51                TokenFlags::All(token) => {
52                    state.tokens.push(token.clone());
53                }
54            }
55        }
56
57        self.states
58            .insert(state.data.id, Arc::new(Mutex::new(state.to_owned())));
59
60        self
61    }
62
63    /// Notify all the extensions in a state about a message
64    pub async fn notify_extensions(&self, message: ExtensionMessages) {
65        let state_id = message.get_state_id();
66        let state = self.states.get(&state_id);
67        if let Some(state) = state {
68            let state = state.lock().await;
69            state.notify_extensions(message);
70        }
71    }
72}
73
74/// A Tab data
75#[derive(Serialize, Deserialize, Debug, Clone)]
76#[serde(tag = "tab_type")]
77pub enum TabData {
78    // Text Editor tab
79    TextEditor {
80        path: String,
81        filesystem: String,
82        format: FileFormat,
83        filename: String,
84        id: String,
85    },
86    // Basic tab (e.g. Settings)
87    Basic {
88        title: String,
89        id: String,
90    },
91}
92#[derive(Serialize, Deserialize, Clone, Debug, Default)]
93struct ViewPanel {
94    selected_tab_id: Option<String>,
95    tabs: Vec<TabData>,
96}
97
98type TabsViews = Vec<ViewPanel>;
99
100/// The data of a state
101#[derive(Serialize, Deserialize, Clone, Debug)]
102pub struct StateData {
103    pub id: u8,
104    opened_tabs: Vec<TabsViews>,
105}
106
107impl Default for StateData {
108    fn default() -> Self {
109        Self {
110            id: 1,
111            opened_tabs: Vec::default(),
112        }
113    }
114}
115
116/// A state is like a small configuration, like a profile
117#[derive(Clone)]
118pub struct State {
119    filesystems: HashMap<String, Arc<Mutex<Box<dyn Filesystem + Send>>>>,
120    extensions_manager: ExtensionsManager,
121    persistor: Option<Arc<Mutex<Box<dyn Persistor + Send>>>>,
122    pub data: StateData,
123    tokens: Vec<String>,
124    language_servers: HashMap<String, LanguageServer>,
125}
126
127impl fmt::Debug for State {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        f.debug_struct("State")
130            .field("opened_tabs", &self.data.opened_tabs)
131            .field("id", &self.data.id)
132            .finish()
133    }
134}
135
136impl Default for State {
137    /// The default constructor will include:
138    /// - LocalFilesystem
139    ///
140    /// But will not persist the state
141    fn default() -> Self {
142        let mut filesystems = HashMap::new();
143
144        // Support the local filesystem by default
145        let local_fs: Box<dyn Filesystem + Send> = Box::new(LocalFilesystem::new());
146        filesystems.insert("local".to_string(), Arc::new(Mutex::new(local_fs)));
147
148        Self {
149            data: StateData::default(),
150            filesystems,
151            extensions_manager: ExtensionsManager::default(),
152            tokens: Vec::new(),
153            persistor: None,
154            language_servers: HashMap::new(),
155        }
156    }
157}
158
159impl State {
160    pub fn new(
161        id: u8,
162        extensions_manager: ExtensionsManager,
163        mut persistor: Box<dyn Persistor + Send>,
164    ) -> Self {
165        // Retrieve opened tabs from the persistor
166        let state = persistor.load();
167
168        State {
169            data: StateData { id, ..state },
170            extensions_manager,
171            persistor: Some(Arc::new(Mutex::new(persistor))),
172            ..Default::default()
173        }
174    }
175
176    /// Retrieve the specified filesystem by the given name
177    pub fn get_fs_by_name(
178        &self,
179        filesystem: &str,
180    ) -> Option<Arc<Mutex<Box<dyn Filesystem + Send>>>> {
181        return self.filesystems.get(filesystem).cloned();
182    }
183
184    // Check if the state can be used with the specified token
185    pub fn has_token(&self, token: &str) -> bool {
186        self.tokens.contains(&token.to_owned())
187    }
188
189    /// Run all the extensions in the manager
190    pub async fn run_extensions(&self) {
191        for ext in &self.extensions_manager.extensions {
192            if let LoadedExtension::ExtensionInstance { plugin, .. } = ext {
193                let mut ext_plugin = plugin.lock().await;
194                ext_plugin.unload();
195                ext_plugin.init();
196            }
197        }
198    }
199
200    /// Notify all the extensions in a state about a message, asynchronously and independently
201    pub fn notify_extensions(&self, message: ExtensionMessages) {
202        for ext in &self.extensions_manager.extensions {
203            if let LoadedExtension::ExtensionInstance { plugin, .. } = ext {
204                let ext_plugin = plugin.clone();
205                let message = message.clone();
206                tokio::spawn(async move {
207                    let mut ext_plugin = ext_plugin.lock().await;
208                    ext_plugin.notify(message.clone());
209                });
210            }
211        }
212    }
213
214    /// Try to retrieve info about a perticular loaded extension
215    pub fn get_ext_info_by_id(&self, ext_id: &str) -> Result<ManifestInfo, Errors> {
216        let extensions = &self.extensions_manager.extensions;
217        let result = extensions.iter().find_map(|extension| {
218            if let LoadedExtension::ManifestFile { manifest } = extension {
219                if manifest.info.extension.id == ext_id {
220                    Some(manifest.info.clone())
221                } else {
222                    None
223                }
224            } else if let LoadedExtension::ManifestBuiltin { info, .. } = extension {
225                if info.extension.id == ext_id {
226                    Some(info.clone())
227                } else {
228                    None
229                }
230            } else {
231                None
232            }
233        });
234
235        result.ok_or(Errors::Ext(ExtensionErrors::ExtensionNotFound))
236    }
237
238    /// Try to retrieve info about a perticular loaded extension
239    pub fn get_ext_run_info_by_id(&self, ext_id: &str) -> Result<ExtensionInfo, Errors> {
240        let extensions = &self.extensions_manager.extensions;
241        let result = extensions.iter().find_map(|extension| {
242            if let LoadedExtension::ExtensionInstance { info, .. } = extension {
243                if info.id == ext_id {
244                    Some(info.clone())
245                } else {
246                    None
247                }
248            } else {
249                None
250            }
251        });
252
253        result.ok_or(Errors::Ext(ExtensionErrors::ExtensionNotFound))
254    }
255
256    /// Return the list of loaded extensions
257    pub fn get_ext_list_by_id(&self) -> Vec<String> {
258        let extensions = &self.extensions_manager.extensions;
259
260        extensions
261            .iter()
262            .filter_map(|extension| {
263                if let LoadedExtension::ManifestBuiltin { info, .. } = extension {
264                    Some(info.extension.id.to_string())
265                } else if let LoadedExtension::ManifestFile { manifest } = extension {
266                    Some(manifest.info.extension.id.to_string())
267                } else {
268                    None
269                }
270            })
271            .collect::<Vec<String>>()
272    }
273
274    // Merge a new state data
275    pub async fn update(&mut self, new_data: StateData) {
276        self.data.opened_tabs = new_data.opened_tabs;
277
278        if let Some(persistor) = &self.persistor {
279            persistor.lock().await.save(&self.data);
280        }
281    }
282
283    // Register a new language server
284    pub async fn register_language_servers(
285        &mut self,
286        language_servers: HashMap<String, LanguageServer>,
287    ) {
288        self.language_servers.extend(language_servers);
289    }
290
291    // Register a new language server
292    pub async fn get_all_language_servers(&self) -> Vec<LanguageServer> {
293        self.language_servers
294            .values()
295            .cloned()
296            .collect::<Vec<LanguageServer>>()
297    }
298}
299
300// NOTE: It would be interesting to implement https://doc.rust-lang.org/std/ops/trait.AddAssign.html
301// So it's easier to merge 2 states, old + new
302
303#[cfg(test)]
304mod tests {
305
306    use crate::extensions::base::{Extension, ExtensionInfo};
307    use crate::extensions::manager::ExtensionsManager;
308    use crate::messaging::ExtensionMessages;
309    use crate::state::MemoryPersistor;
310
311    use super::State;
312
313    fn get_sample_extension_info() -> ExtensionInfo {
314        ExtensionInfo {
315            id: "sample".to_string(),
316            name: "sample".to_string(),
317        }
318    }
319
320    fn get_sample_extension() -> Box<dyn Extension + Send> {
321        struct SampleExtension;
322
323        impl Extension for SampleExtension {
324            fn get_info(&self) -> ExtensionInfo {
325                get_sample_extension_info()
326            }
327
328            fn init(&mut self) {
329                todo!()
330            }
331
332            fn unload(&mut self) {
333                todo!()
334            }
335
336            fn notify(&mut self, _message: ExtensionMessages) {
337                todo!()
338            }
339        }
340
341        Box::new(SampleExtension)
342    }
343
344    #[test]
345    fn get_info() {
346        let mut manager = ExtensionsManager::default();
347        manager.register("sample", get_sample_extension());
348        let test_state = State::new(0, manager, Box::new(MemoryPersistor::new()));
349
350        let ext_info = test_state.get_ext_run_info_by_id("sample");
351        assert!(ext_info.is_ok());
352
353        let ext_info = ext_info.unwrap();
354        assert_eq!(get_sample_extension_info(), ext_info);
355    }
356}