1use anyhow::Result;
2use fs::Fs;
3use serde_json::{Map, Value};
4
5use std::sync::Arc;
6
7#[derive(Clone, Copy, PartialEq, Eq, Debug)]
8pub enum VsCodeSettingsSource {
9 VsCode,
10 Cursor,
11}
12
13impl std::fmt::Display for VsCodeSettingsSource {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 match self {
16 VsCodeSettingsSource::VsCode => write!(f, "VS Code"),
17 VsCodeSettingsSource::Cursor => write!(f, "Cursor"),
18 }
19 }
20}
21
22pub struct VsCodeSettings {
23 pub source: VsCodeSettingsSource,
24 content: Map<String, Value>,
25}
26
27impl VsCodeSettings {
28 pub fn from_str(content: &str, source: VsCodeSettingsSource) -> Result<Self> {
29 Ok(Self {
30 source,
31 content: serde_json_lenient::from_str(content)?,
32 })
33 }
34
35 pub async fn load_user_settings(source: VsCodeSettingsSource, fs: Arc<dyn Fs>) -> Result<Self> {
36 let path = match source {
37 VsCodeSettingsSource::VsCode => paths::vscode_settings_file(),
38 VsCodeSettingsSource::Cursor => paths::cursor_settings_file(),
39 };
40 let content = fs.load(path).await?;
41 Ok(Self {
42 source,
43 content: serde_json_lenient::from_str(&content)?,
44 })
45 }
46
47 pub fn read_value(&self, setting: &str) -> Option<&Value> {
48 if let Some(value) = self.content.get(setting) {
49 return Some(value);
50 }
51 // TODO: maybe check if it's in [platform] settings for current platform as a fallback
52 // TODO: deal with language specific settings
53 None
54 }
55
56 pub fn read_string(&self, setting: &str) -> Option<&str> {
57 self.read_value(setting).and_then(|v| v.as_str())
58 }
59
60 pub fn read_bool(&self, setting: &str) -> Option<bool> {
61 self.read_value(setting).and_then(|v| v.as_bool())
62 }
63
64 pub fn string_setting(&self, key: &str, setting: &mut Option<String>) {
65 if let Some(s) = self.content.get(key).and_then(Value::as_str) {
66 *setting = Some(s.to_owned())
67 }
68 }
69
70 pub fn bool_setting(&self, key: &str, setting: &mut Option<bool>) {
71 if let Some(s) = self.content.get(key).and_then(Value::as_bool) {
72 *setting = Some(s)
73 }
74 }
75
76 pub fn u32_setting(&self, key: &str, setting: &mut Option<u32>) {
77 if let Some(s) = self.content.get(key).and_then(Value::as_u64) {
78 *setting = Some(s as u32)
79 }
80 }
81
82 pub fn u64_setting(&self, key: &str, setting: &mut Option<u64>) {
83 if let Some(s) = self.content.get(key).and_then(Value::as_u64) {
84 *setting = Some(s)
85 }
86 }
87
88 pub fn usize_setting(&self, key: &str, setting: &mut Option<usize>) {
89 if let Some(s) = self.content.get(key).and_then(Value::as_u64) {
90 *setting = Some(s.try_into().unwrap())
91 }
92 }
93
94 pub fn f32_setting(&self, key: &str, setting: &mut Option<f32>) {
95 if let Some(s) = self.content.get(key).and_then(Value::as_f64) {
96 *setting = Some(s as f32)
97 }
98 }
99
100 pub fn enum_setting<T>(
101 &self,
102 key: &str,
103 setting: &mut Option<T>,
104 f: impl FnOnce(&str) -> Option<T>,
105 ) {
106 if let Some(s) = self.content.get(key).and_then(Value::as_str).and_then(f) {
107 *setting = Some(s)
108 }
109 }
110}