Skip to main content

shuck_server/
workspace.rs

1#![allow(dead_code)]
2
3use std::ops::Deref;
4use std::path::PathBuf;
5
6use lsp_types::{Url, WorkspaceFolder};
7
8use crate::session::{ClientOptions, WorkspaceOptionsMap};
9
10#[derive(Debug)]
11pub struct Workspaces(Vec<Workspace>);
12
13impl Workspaces {
14    pub fn new(workspaces: Vec<Workspace>) -> Self {
15        Self(workspaces)
16    }
17
18    pub(crate) fn from_workspace_folders(
19        workspace_folders: Option<Vec<WorkspaceFolder>>,
20        root_uri: Option<Url>,
21        root_path: Option<String>,
22        mut workspace_options: WorkspaceOptionsMap,
23    ) -> crate::Result<Self> {
24        let mut client_options_for_url =
25            |url: &Url| workspace_options.remove(url).unwrap_or_default();
26
27        let workspaces =
28            if let Some(folders) = workspace_folders.filter(|folders| !folders.is_empty()) {
29                folders
30                    .into_iter()
31                    .map(|folder| {
32                        let options = client_options_for_url(&folder.uri);
33                        Workspace::new(folder.uri).with_options(options)
34                    })
35                    .collect()
36            } else {
37                let uri = default_workspace_uri(root_uri, root_path)?;
38                let options = client_options_for_url(&uri);
39                vec![Workspace::default(uri).with_options(options)]
40            };
41
42        Ok(Self(workspaces))
43    }
44}
45
46fn default_workspace_uri(root_uri: Option<Url>, root_path: Option<String>) -> crate::Result<Url> {
47    if let Some(root_uri) = root_uri {
48        return Ok(root_uri);
49    }
50
51    if let Some(root_path) = root_path
52        && let Ok(root_uri) = Url::from_file_path(PathBuf::from(root_path))
53    {
54        return Ok(root_uri);
55    }
56
57    let current_dir = std::env::current_dir()?;
58    Url::from_file_path(current_dir)
59        .map_err(|()| anyhow::anyhow!("failed to create URL from current directory"))
60}
61
62impl Deref for Workspaces {
63    type Target = [Workspace];
64
65    fn deref(&self) -> &Self::Target {
66        &self.0
67    }
68}
69
70#[derive(Debug)]
71pub struct Workspace {
72    url: Url,
73    options: Option<ClientOptions>,
74    is_default: bool,
75}
76
77impl Workspace {
78    pub fn new(url: Url) -> Self {
79        Self {
80            url,
81            options: None,
82            is_default: false,
83        }
84    }
85
86    pub fn default(url: Url) -> Self {
87        Self {
88            url,
89            options: None,
90            is_default: true,
91        }
92    }
93
94    #[must_use]
95    pub fn with_options(mut self, options: ClientOptions) -> Self {
96        self.options = Some(options);
97        self
98    }
99
100    pub(crate) fn url(&self) -> &Url {
101        &self.url
102    }
103
104    pub(crate) fn options(&self) -> Option<&ClientOptions> {
105        self.options.as_ref()
106    }
107
108    pub(crate) fn is_default(&self) -> bool {
109        self.is_default
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn uses_root_uri_when_workspace_folders_are_missing() {
119        let root_uri = Url::parse("file:///tmp/shuck-root").expect("test URI should parse");
120
121        let workspaces = Workspaces::from_workspace_folders(
122            None,
123            Some(root_uri.clone()),
124            None,
125            WorkspaceOptionsMap::default(),
126        )
127        .expect("workspace fallback should succeed");
128
129        assert_eq!(workspaces.len(), 1);
130        assert_eq!(workspaces[0].url(), &root_uri);
131        assert!(workspaces[0].is_default());
132    }
133
134    #[test]
135    fn uses_root_path_when_root_uri_is_missing() {
136        let root_path = std::env::temp_dir().join("shuck-server-root-path");
137        let expected_uri =
138            Url::from_file_path(&root_path).expect("temporary directory should convert to a URL");
139
140        let workspaces = Workspaces::from_workspace_folders(
141            None,
142            None,
143            Some(root_path.display().to_string()),
144            WorkspaceOptionsMap::default(),
145        )
146        .expect("workspace fallback should succeed");
147
148        assert_eq!(workspaces.len(), 1);
149        assert_eq!(workspaces[0].url(), &expected_uri);
150        assert!(workspaces[0].is_default());
151    }
152}