Skip to content

Commit 0dfcca0

Browse files
committed
Use fallible options for settings
1 parent 8d176c0 commit 0dfcca0

File tree

19 files changed

+437
-405
lines changed

19 files changed

+437
-405
lines changed

Cargo.lock

Lines changed: 100 additions & 190 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,6 @@ serde_json_lenient = { version = "0.2", features = [
640640
serde_path_to_error = "0.1.17"
641641
serde_repr = "0.1"
642642
serde_urlencoded = "0.7"
643-
serde_with = "3.4.0"
644643
sha2 = "0.10"
645644
shellexpand = "2.1.0"
646645
shlex = "1.3.0"

crates/settings/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ serde.workspace = true
3434
serde_json.workspace = true
3535
serde_json_lenient.workspace = true
3636
serde_repr.workspace = true
37-
serde_with.workspace = true
3837
settings_json.workspace = true
3938
settings_macros.workspace = true
4039
smallvec.workspace = true
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use std::cell::RefCell;
2+
3+
use serde::Deserialize;
4+
5+
use crate::ParseStatus;
6+
7+
thread_local! {
8+
static ERRORS: RefCell<Option<Vec<anyhow::Error>>> = RefCell::new(None);
9+
}
10+
11+
pub(crate) fn parse_json<'de, T>(json: &'de str) -> (Option<T>, ParseStatus)
12+
where
13+
T: Deserialize<'de>,
14+
{
15+
ERRORS.with_borrow_mut(|errors| {
16+
errors.replace(Vec::default());
17+
});
18+
19+
let mut deserializer = serde_json_lenient::Deserializer::from_str(json);
20+
let value = T::deserialize(&mut deserializer);
21+
let value = match value {
22+
Ok(value) => value,
23+
Err(error) => {
24+
return (
25+
None,
26+
ParseStatus::Failed {
27+
error: error.to_string(),
28+
},
29+
);
30+
}
31+
};
32+
33+
if let Some(errors) = ERRORS.with_borrow_mut(|errors| errors.take().filter(|e| !e.is_empty())) {
34+
let error = errors
35+
.into_iter()
36+
.map(|e| e.to_string())
37+
.flat_map(|e| ["\n".to_owned(), e])
38+
.skip(1)
39+
.collect::<String>();
40+
return (Some(value), ParseStatus::Failed { error });
41+
}
42+
43+
(Some(value), ParseStatus::Success)
44+
}
45+
46+
pub(crate) fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
47+
where
48+
D: serde::Deserializer<'de>,
49+
T: serde::Deserialize<'de> + FallibleOption,
50+
{
51+
match T::deserialize(deserializer) {
52+
Ok(value) => Ok(value),
53+
Err(e) => ERRORS.with_borrow_mut(|errors| {
54+
if let Some(errors) = errors {
55+
errors.push(anyhow::anyhow!("{}", e));
56+
Ok(Default::default())
57+
} else {
58+
Err(e)
59+
}
60+
}),
61+
}
62+
}
63+
64+
pub trait FallibleOption: Default {}
65+
impl<T> FallibleOption for Option<T> {}
66+
67+
#[cfg(test)]
68+
mod tests {
69+
use serde::Deserialize;
70+
use settings_macros::with_fallible_options;
71+
72+
use crate::ParseStatus;
73+
74+
#[with_fallible_options]
75+
#[derive(Deserialize, Debug, PartialEq)]
76+
struct Foo {
77+
foo: Option<String>,
78+
bar: Option<usize>,
79+
baz: Option<bool>,
80+
}
81+
82+
#[test]
83+
fn test_fallible() {
84+
let input = r#"
85+
{"foo": "bar",
86+
"bar": "foo",
87+
"baz": 3,
88+
}
89+
"#;
90+
91+
let (settings, result) = crate::fallible_options::parse_json::<Foo>(&input);
92+
assert_eq!(
93+
settings.unwrap(),
94+
Foo {
95+
foo: Some("bar".into()).into(),
96+
bar: None.into(),
97+
baz: None.into(),
98+
}
99+
);
100+
101+
assert!(crate::parse_json_with_comments::<Foo>(&input).is_err());
102+
103+
let ParseStatus::Failed { error } = result else {
104+
panic!("Expected parse to fail")
105+
};
106+
107+
assert_eq!(
108+
error,
109+
"invalid type: string \"foo\", expected usize at line 3 column 24\ninvalid type: integer `3`, expected a boolean at line 4 column 20".to_string()
110+
)
111+
}
112+
}

crates/settings/src/settings.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod base_keymap_setting;
22
mod editable_setting_control;
3+
mod fallible_options;
34
mod keymap_file;
45
pub mod merge_from;
56
mod serde_helper;

crates/settings/src/settings_content.rs

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,15 @@ use gpui::{App, SharedString};
2323
use release_channel::ReleaseChannel;
2424
use schemars::JsonSchema;
2525
use serde::{Deserialize, Serialize};
26-
use serde_with::skip_serializing_none;
27-
use settings_macros::MergeFrom;
26+
use settings_macros::{MergeFrom, with_fallible_options};
2827
use std::collections::BTreeSet;
2928
use std::env;
3029
use std::sync::Arc;
3130
pub use util::serde::default_true;
3231

3332
use crate::{ActiveSettingsProfileName, merge_from};
3433

35-
#[skip_serializing_none]
34+
#[with_fallible_options]
3635
#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
3736
pub struct SettingsContent {
3837
#[serde(flatten)]
@@ -169,7 +168,7 @@ impl SettingsContent {
169168
}
170169
}
171170

172-
#[skip_serializing_none]
171+
#[with_fallible_options]
173172
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
174173
pub struct UserSettingsContent {
175174
#[serde(flatten)]
@@ -260,7 +259,7 @@ impl strum::VariantNames for BaseKeymapContent {
260259
];
261260
}
262261

263-
#[skip_serializing_none]
262+
#[with_fallible_options]
264263
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
265264
pub struct TitleBarSettingsContent {
266265
/// Whether to show the branch icon beside branch switcher in the title bar.
@@ -294,7 +293,7 @@ pub struct TitleBarSettingsContent {
294293
}
295294

296295
/// Configuration of audio in Zed.
297-
#[skip_serializing_none]
296+
#[with_fallible_options]
298297
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
299298
pub struct AudioSettingsContent {
300299
/// Opt into the new audio system.
@@ -338,7 +337,7 @@ pub struct AudioSettingsContent {
338337
}
339338

340339
/// Control what info is collected by Zed.
341-
#[skip_serializing_none]
340+
#[with_fallible_options]
342341
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Debug, MergeFrom)]
343342
pub struct TelemetrySettingsContent {
344343
/// Send debug info like crash reports.
@@ -360,7 +359,7 @@ impl Default for TelemetrySettingsContent {
360359
}
361360
}
362361

363-
#[skip_serializing_none]
362+
#[with_fallible_options]
364363
#[derive(Default, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Clone, MergeFrom)]
365364
pub struct DebuggerSettingsContent {
366365
/// Determines the stepping granularity.
@@ -441,23 +440,23 @@ pub enum DockPosition {
441440
}
442441

443442
/// Settings for slash commands.
444-
#[skip_serializing_none]
443+
#[with_fallible_options]
445444
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, MergeFrom, PartialEq, Eq)]
446445
pub struct SlashCommandSettings {
447446
/// Settings for the `/cargo-workspace` slash command.
448447
pub cargo_workspace: Option<CargoWorkspaceCommandSettings>,
449448
}
450449

451450
/// Settings for the `/cargo-workspace` slash command.
452-
#[skip_serializing_none]
451+
#[with_fallible_options]
453452
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, MergeFrom, PartialEq, Eq)]
454453
pub struct CargoWorkspaceCommandSettings {
455454
/// Whether `/cargo-workspace` is enabled.
456455
pub enabled: Option<bool>,
457456
}
458457

459458
/// Configuration of voice calls in Zed.
460-
#[skip_serializing_none]
459+
#[with_fallible_options]
461460
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
462461
pub struct CallSettingsContent {
463462
/// Whether the microphone should be muted when joining a channel or a call.
@@ -471,7 +470,7 @@ pub struct CallSettingsContent {
471470
pub share_on_join: Option<bool>,
472471
}
473472

474-
#[skip_serializing_none]
473+
#[with_fallible_options]
475474
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
476475
pub struct GitPanelSettingsContent {
477476
/// Whether to show the panel button in the status bar.
@@ -535,15 +534,15 @@ pub enum StatusStyle {
535534
LabelColor,
536535
}
537536

538-
#[skip_serializing_none]
537+
#[with_fallible_options]
539538
#[derive(
540539
Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
541540
)]
542541
pub struct ScrollbarSettings {
543542
pub show: Option<ShowScrollbar>,
544543
}
545544

546-
#[skip_serializing_none]
545+
#[with_fallible_options]
547546
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
548547
pub struct NotificationPanelSettingsContent {
549548
/// Whether to show the panel button in the status bar.
@@ -561,7 +560,7 @@ pub struct NotificationPanelSettingsContent {
561560
pub default_width: Option<f32>,
562561
}
563562

564-
#[skip_serializing_none]
563+
#[with_fallible_options]
565564
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
566565
pub struct PanelSettingsContent {
567566
/// Whether to show the panel button in the status bar.
@@ -579,7 +578,7 @@ pub struct PanelSettingsContent {
579578
pub default_width: Option<f32>,
580579
}
581580

582-
#[skip_serializing_none]
581+
#[with_fallible_options]
583582
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
584583
pub struct MessageEditorSettings {
585584
/// Whether to automatically replace emoji shortcodes with emoji characters.
@@ -589,7 +588,7 @@ pub struct MessageEditorSettings {
589588
pub auto_replace_emoji_shortcode: Option<bool>,
590589
}
591590

592-
#[skip_serializing_none]
591+
#[with_fallible_options]
593592
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
594593
pub struct FileFinderSettingsContent {
595594
/// Whether to show file icons in the file finder.
@@ -664,7 +663,7 @@ pub enum FileFinderWidthContent {
664663
Full,
665664
}
666665

667-
#[skip_serializing_none]
666+
#[with_fallible_options]
668667
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Debug, JsonSchema, MergeFrom)]
669668
pub struct VimSettingsContent {
670669
pub default_mode: Option<ModeContent>,
@@ -697,7 +696,7 @@ pub enum UseSystemClipboard {
697696
}
698697

699698
/// The settings for cursor shape.
700-
#[skip_serializing_none]
699+
#[with_fallible_options]
701700
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
702701
pub struct CursorShapeSettings {
703702
/// Cursor shape for the normal mode.
@@ -719,7 +718,7 @@ pub struct CursorShapeSettings {
719718
}
720719

721720
/// Settings specific to journaling
722-
#[skip_serializing_none]
721+
#[with_fallible_options]
723722
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
724723
pub struct JournalSettingsContent {
725724
/// The path of the directory where journal entries are stored.
@@ -740,7 +739,7 @@ pub enum HourFormat {
740739
Hour24,
741740
}
742741

743-
#[skip_serializing_none]
742+
#[with_fallible_options]
744743
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
745744
pub struct OutlinePanelSettingsContent {
746745
/// Whether to show the outline panel button in the status bar.
@@ -835,7 +834,7 @@ pub enum ShowIndentGuides {
835834
Never,
836835
}
837836

838-
#[skip_serializing_none]
837+
#[with_fallible_options]
839838
#[derive(
840839
Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq, Default,
841840
)]
@@ -853,7 +852,7 @@ pub enum LineIndicatorFormat {
853852
}
854853

855854
/// The settings for the image viewer.
856-
#[skip_serializing_none]
855+
#[with_fallible_options]
857856
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, Default, PartialEq)]
858857
pub struct ImageViewerSettingsContent {
859858
/// The unit to use for displaying image file sizes.
@@ -862,7 +861,7 @@ pub struct ImageViewerSettingsContent {
862861
pub unit: Option<ImageFileSizeUnit>,
863862
}
864863

865-
#[skip_serializing_none]
864+
#[with_fallible_options]
866865
#[derive(
867866
Clone,
868867
Copy,
@@ -885,15 +884,15 @@ pub enum ImageFileSizeUnit {
885884
Decimal,
886885
}
887886

888-
#[skip_serializing_none]
887+
#[with_fallible_options]
889888
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
890889
pub struct RemoteSettingsContent {
891890
pub ssh_connections: Option<Vec<SshConnection>>,
892891
pub wsl_connections: Option<Vec<WslConnection>>,
893892
pub read_ssh_config: Option<bool>,
894893
}
895894

896-
#[skip_serializing_none]
895+
#[with_fallible_options]
897896
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
898897
pub struct SshConnection {
899898
pub host: SharedString,
@@ -922,27 +921,25 @@ pub struct WslConnection {
922921
pub projects: BTreeSet<SshProject>,
923922
}
924923

925-
#[skip_serializing_none]
924+
#[with_fallible_options]
926925
#[derive(
927926
Clone, Debug, Default, Serialize, PartialEq, Eq, PartialOrd, Ord, Deserialize, JsonSchema,
928927
)]
929928
pub struct SshProject {
930929
pub paths: Vec<String>,
931930
}
932931

933-
#[skip_serializing_none]
932+
#[with_fallible_options]
934933
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, JsonSchema, MergeFrom)]
935934
pub struct SshPortForwardOption {
936-
#[serde(skip_serializing_if = "Option::is_none")]
937935
pub local_host: Option<String>,
938936
pub local_port: u16,
939-
#[serde(skip_serializing_if = "Option::is_none")]
940937
pub remote_host: Option<String>,
941938
pub remote_port: u16,
942939
}
943940

944941
/// Settings for configuring REPL display and behavior.
945-
#[skip_serializing_none]
942+
#[with_fallible_options]
946943
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
947944
pub struct ReplSettingsContent {
948945
/// Maximum number of lines to keep in REPL's scrollback buffer.

0 commit comments

Comments
 (0)