Cargo.lock 🔗
@@ -10137,6 +10137,7 @@ dependencies = [
"pretty_assertions",
"serde_json",
"serde_json_lenient",
+ "settings_content",
"settings_json",
"streaming-iterator",
"tree-sitter",
Richard Feldman created
Previously, some settings migrations only operated on root-level keys
and missed settings nested under platform keys (`macos`, `linux`, etc.),
channel keys (`nightly`, `stable`, etc.), or profile blocks. This fixes
migrations to recurse into those nested locations.
Also fixes `m_2026_02_02` to gracefully skip when `edit_predictions` is
not an object (e.g. `true`) instead of bailing and aborting the entire
migration chain.
Release Notes:
- Fixed settings migrations to correctly handle settings nested under
platform, channel, or profile keys.
Cargo.lock | 1
crates/migrator/Cargo.toml | 1
crates/migrator/src/migrations.rs | 109 ++
crates/migrator/src/migrations/m_2025_10_01/settings.rs | 2
crates/migrator/src/migrations/m_2025_10_02/settings.rs | 2
crates/migrator/src/migrations/m_2025_10_16/settings.rs | 2
crates/migrator/src/migrations/m_2025_10_17/settings.rs | 8
crates/migrator/src/migrations/m_2025_10_21/settings.rs | 8
crates/migrator/src/migrations/m_2025_11_25/settings.rs | 20
crates/migrator/src/migrations/m_2026_02_02/settings.rs | 8
crates/migrator/src/migrations/m_2026_02_03/settings.rs | 13
crates/migrator/src/migrator.rs | 469 +++++++++++
crates/migrator/src/patterns.rs | 1
crates/migrator/src/patterns/settings.rs | 21
crates/settings/src/settings.rs | 17
crates/settings_content/src/settings_content.rs | 54 +
16 files changed, 673 insertions(+), 63 deletions(-)
@@ -10137,6 +10137,7 @@ dependencies = [
"pretty_assertions",
"serde_json",
"serde_json_lenient",
+ "settings_content",
"settings_json",
"streaming-iterator",
"tree-sitter",
@@ -22,6 +22,7 @@ tree-sitter-json.workspace = true
tree-sitter.workspace = true
serde_json_lenient.workspace = true
serde_json.workspace = true
+settings_content.workspace = true
settings_json.workspace = true
[dev-dependencies]
@@ -1,3 +1,112 @@
+use anyhow::Result;
+use serde_json::Value;
+use settings_content::{PlatformOverrides, ReleaseChannelOverrides};
+
+/// Applies a migration callback to the root settings object as well as all
+/// nested platform, release-channel, and profile override objects.
+pub(crate) fn migrate_settings(
+ value: &mut Value,
+ mut migrate_one: impl FnMut(&mut serde_json::Map<String, Value>) -> Result<()>,
+) -> Result<()> {
+ let Some(root_object) = value.as_object_mut() else {
+ return Ok(());
+ };
+
+ migrate_one(root_object)?;
+
+ let override_keys = ReleaseChannelOverrides::OVERRIDE_KEYS
+ .iter()
+ .copied()
+ .chain(PlatformOverrides::OVERRIDE_KEYS.iter().copied());
+
+ for key in override_keys {
+ if let Some(sub_object) = root_object.get_mut(key) {
+ if let Some(sub_map) = sub_object.as_object_mut() {
+ migrate_one(sub_map)?;
+ }
+ }
+ }
+
+ if let Some(profiles) = root_object.get_mut("profiles") {
+ if let Some(profiles_object) = profiles.as_object_mut() {
+ for (_profile_name, profile_settings) in profiles_object.iter_mut() {
+ if let Some(profile_map) = profile_settings.as_object_mut() {
+ migrate_one(profile_map)?;
+ }
+ }
+ }
+ }
+
+ Ok(())
+}
+
+/// Applies a migration callback to a value and its `languages` children,
+/// at the root level as well as all nested platform, release-channel, and
+/// profile override objects.
+pub(crate) fn migrate_language_setting(
+ value: &mut Value,
+ migrate_fn: fn(&mut Value, path: &[&str]) -> Result<()>,
+) -> Result<()> {
+ fn apply_to_value_and_languages(
+ value: &mut Value,
+ prefix: &[&str],
+ migrate_fn: fn(&mut Value, path: &[&str]) -> Result<()>,
+ ) -> Result<()> {
+ migrate_fn(value, prefix)?;
+ let languages = value
+ .as_object_mut()
+ .and_then(|obj| obj.get_mut("languages"))
+ .and_then(|languages| languages.as_object_mut());
+ if let Some(languages) = languages {
+ for (language_name, language) in languages.iter_mut() {
+ let mut path: Vec<&str> = prefix.to_vec();
+ path.push("languages");
+ path.push(language_name);
+ migrate_fn(language, &path)?;
+ }
+ }
+ Ok(())
+ }
+
+ if !value.is_object() {
+ return Ok(());
+ }
+
+ apply_to_value_and_languages(value, &[], migrate_fn)?;
+
+ let Some(root_object) = value.as_object_mut() else {
+ return Ok(());
+ };
+
+ let override_keys = ReleaseChannelOverrides::OVERRIDE_KEYS
+ .iter()
+ .copied()
+ .chain(PlatformOverrides::OVERRIDE_KEYS.iter().copied());
+
+ for key in override_keys {
+ if let Some(sub_value) = root_object.get_mut(key) {
+ apply_to_value_and_languages(sub_value, &[key], migrate_fn)?;
+ }
+ }
+
+ if let Some(profiles) = root_object.get_mut("profiles") {
+ if let Some(profiles_object) = profiles.as_object_mut() {
+ let profile_names: Vec<String> = profiles_object.keys().cloned().collect();
+ for profile_name in &profile_names {
+ if let Some(profile_settings) = profiles_object.get_mut(profile_name.as_str()) {
+ apply_to_value_and_languages(
+ profile_settings,
+ &["profiles", profile_name],
+ migrate_fn,
+ )?;
+ }
+ }
+ }
+ }
+
+ Ok(())
+}
+
pub(crate) mod m_2025_01_02 {
mod settings;
@@ -1,4 +1,4 @@
-use crate::patterns::migrate_language_setting;
+use crate::migrations::migrate_language_setting;
use anyhow::Result;
use serde_json::Value;
@@ -1,7 +1,7 @@
use anyhow::Result;
use serde_json::Value;
-use crate::patterns::migrate_language_setting;
+use crate::migrations::migrate_language_setting;
pub fn remove_formatters_on_save(value: &mut Value) -> Result<()> {
migrate_language_setting(value, remove_formatters_on_save_inner)
@@ -1,7 +1,7 @@
use anyhow::Result;
use serde_json::Value;
-use crate::patterns::migrate_language_setting;
+use crate::migrations::migrate_language_setting;
pub fn restore_code_actions_on_format(value: &mut Value) -> Result<()> {
migrate_language_setting(value, restore_code_actions_on_format_inner)
@@ -1,8 +1,14 @@
use anyhow::Result;
use serde_json::Value;
+use crate::migrations::migrate_settings;
+
pub fn make_file_finder_include_ignored_an_enum(value: &mut Value) -> Result<()> {
- let Some(file_finder) = value.get_mut("file_finder") else {
+ migrate_settings(value, migrate_one)
+}
+
+fn migrate_one(obj: &mut serde_json::Map<String, Value>) -> Result<()> {
+ let Some(file_finder) = obj.get_mut("file_finder") else {
return Ok(());
};
@@ -1,8 +1,14 @@
use anyhow::Result;
use serde_json::Value;
+use crate::migrations::migrate_settings;
+
pub fn make_relative_line_numbers_an_enum(value: &mut Value) -> Result<()> {
- let Some(relative_line_numbers) = value.get_mut("relative_line_numbers") else {
+ migrate_settings(value, migrate_one)
+}
+
+fn migrate_one(obj: &mut serde_json::Map<String, Value>) -> Result<()> {
+ let Some(relative_line_numbers) = obj.get_mut("relative_line_numbers") else {
return Ok(());
};
@@ -1,14 +1,18 @@
use anyhow::Result;
use serde_json::Value;
-pub fn remove_context_server_source(settings: &mut Value) -> Result<()> {
- if let Some(obj) = settings.as_object_mut() {
- if let Some(context_servers) = obj.get_mut("context_servers") {
- if let Some(servers) = context_servers.as_object_mut() {
- for (_, server) in servers.iter_mut() {
- if let Some(server_obj) = server.as_object_mut() {
- server_obj.remove("source");
- }
+use crate::migrations::migrate_settings;
+
+pub fn remove_context_server_source(value: &mut Value) -> Result<()> {
+ migrate_settings(value, migrate_one)
+}
+
+fn migrate_one(obj: &mut serde_json::Map<String, Value>) -> Result<()> {
+ if let Some(context_servers) = obj.get_mut("context_servers") {
+ if let Some(servers) = context_servers.as_object_mut() {
+ for (_, server) in servers.iter_mut() {
+ if let Some(server_obj) = server.as_object_mut() {
+ server_obj.remove("source");
}
}
}
@@ -1,11 +1,13 @@
use anyhow::Result;
use serde_json::Value;
+use crate::migrations::migrate_settings;
+
pub fn move_edit_prediction_provider_to_edit_predictions(value: &mut Value) -> Result<()> {
- let Some(obj) = value.as_object_mut() else {
- return Ok(());
- };
+ migrate_settings(value, migrate_one)
+}
+fn migrate_one(obj: &mut serde_json::Map<String, Value>) -> Result<()> {
let Some(features) = obj.get_mut("features") else {
return Ok(());
};
@@ -1,11 +1,16 @@
use anyhow::Result;
use serde_json::Value;
+use crate::migrations::migrate_settings;
+
pub fn migrate_experimental_sweep_mercury(value: &mut Value) -> Result<()> {
- let Some(obj) = value.as_object_mut() else {
- return Ok(());
- };
+ migrate_settings(value, |obj| {
+ migrate_one(obj);
+ Ok(())
+ })
+}
+fn migrate_one(obj: &mut serde_json::Map<String, Value>) {
if let Some(edit_predictions) = obj.get_mut("edit_predictions") {
if let Some(edit_predictions_obj) = edit_predictions.as_object_mut() {
migrate_provider_field(edit_predictions_obj, "provider");
@@ -17,8 +22,6 @@ pub fn migrate_experimental_sweep_mercury(value: &mut Value) -> Result<()> {
migrate_provider_field(features_obj, "edit_prediction_provider");
}
}
-
- Ok(())
}
fn migrate_provider_field(obj: &mut serde_json::Map<String, Value>, field_name: &str) {
@@ -2219,6 +2219,165 @@ mod tests {
.unindent(),
),
);
+
+ // Platform key: settings nested inside "linux" should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
+ )],
+ &r#"
+ {
+ "linux": {
+ "file_finder": {
+ "include_ignored": true
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "linux": {
+ "file_finder": {
+ "include_ignored": "all"
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
+
+ // Profile: settings nested inside profiles should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
+ )],
+ &r#"
+ {
+ "profiles": {
+ "work": {
+ "file_finder": {
+ "include_ignored": false
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "profiles": {
+ "work": {
+ "file_finder": {
+ "include_ignored": "indexed"
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
+ }
+
+ #[test]
+ fn test_make_relative_line_numbers_an_enum() {
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
+ )],
+ &r#"{ }"#.unindent(),
+ None,
+ );
+
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
+ )],
+ &r#"{
+ "relative_line_numbers": true
+ }"#
+ .unindent(),
+ Some(
+ &r#"{
+ "relative_line_numbers": "enabled"
+ }"#
+ .unindent(),
+ ),
+ );
+
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
+ )],
+ &r#"{
+ "relative_line_numbers": false
+ }"#
+ .unindent(),
+ Some(
+ &r#"{
+ "relative_line_numbers": "disabled"
+ }"#
+ .unindent(),
+ ),
+ );
+
+ // Platform key: settings nested inside "macos" should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
+ )],
+ &r#"
+ {
+ "macos": {
+ "relative_line_numbers": true
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "macos": {
+ "relative_line_numbers": "enabled"
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
+
+ // Profile: settings nested inside profiles should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
+ )],
+ &r#"
+ {
+ "profiles": {
+ "dev": {
+ "relative_line_numbers": false
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "profiles": {
+ "dev": {
+ "relative_line_numbers": "disabled"
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
}
#[test]
@@ -2267,6 +2426,84 @@ mod tests {
.unindent(),
),
);
+
+ // Platform key: settings nested inside "linux" should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2025_11_25::remove_context_server_source,
+ )],
+ &r#"
+ {
+ "linux": {
+ "context_servers": {
+ "my_server": {
+ "source": "extension",
+ "settings": {
+ "key": "value"
+ }
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "linux": {
+ "context_servers": {
+ "my_server": {
+ "settings": {
+ "key": "value"
+ }
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
+
+ // Profile: settings nested inside profiles should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2025_11_25::remove_context_server_source,
+ )],
+ &r#"
+ {
+ "profiles": {
+ "work": {
+ "context_servers": {
+ "my_server": {
+ "source": "custom",
+ "command": "foo",
+ "args": ["bar"]
+ }
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "profiles": {
+ "work": {
+ "context_servers": {
+ "my_server": {
+ "command": "foo",
+ "args": ["bar"]
+ }
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
}
#[test]
@@ -2470,6 +2707,117 @@ mod tests {
.unindent(),
None,
);
+
+ // Platform key: settings nested inside "macos" should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
+ )],
+ &r#"
+ {
+ "macos": {
+ "features": {
+ "edit_prediction_provider": "copilot"
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "macos": {
+ "edit_predictions": {
+ "provider": "copilot"
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
+
+ // Profile: settings nested inside profiles should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
+ )],
+ &r#"
+ {
+ "profiles": {
+ "work": {
+ "features": {
+ "edit_prediction_provider": "copilot"
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "profiles": {
+ "work": {
+ "edit_predictions": {
+ "provider": "copilot"
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
+
+ // Combined: root + platform + profile should all be migrated simultaneously
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
+ )],
+ &r#"
+ {
+ "features": {
+ "edit_prediction_provider": "copilot"
+ },
+ "macos": {
+ "features": {
+ "edit_prediction_provider": "zed"
+ }
+ },
+ "profiles": {
+ "work": {
+ "features": {
+ "edit_prediction_provider": "supermaven"
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "edit_predictions": {
+ "provider": "copilot"
+ },
+ "macos": {
+ "edit_predictions": {
+ "provider": "zed"
+ }
+ },
+ "profiles": {
+ "work": {
+ "edit_predictions": {
+ "provider": "supermaven"
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
}
#[test]
@@ -2591,5 +2939,126 @@ mod tests {
.unindent(),
None,
);
+
+ // Platform key: settings nested inside "linux" should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
+ )],
+ &r#"
+ {
+ "linux": {
+ "edit_predictions": {
+ "provider": {
+ "experimental": "sweep"
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "linux": {
+ "edit_predictions": {
+ "provider": "sweep"
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
+
+ // Profile: settings nested inside profiles should be migrated
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
+ )],
+ &r#"
+ {
+ "profiles": {
+ "dev": {
+ "edit_predictions": {
+ "provider": {
+ "experimental": "mercury"
+ }
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "profiles": {
+ "dev": {
+ "edit_predictions": {
+ "provider": "mercury"
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
+
+ // Combined: root + platform + profile should all be migrated simultaneously
+ assert_migrate_settings_with_migrations(
+ &[MigrationType::Json(
+ migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
+ )],
+ &r#"
+ {
+ "edit_predictions": {
+ "provider": {
+ "experimental": "sweep"
+ }
+ },
+ "linux": {
+ "edit_predictions": {
+ "provider": {
+ "experimental": "mercury"
+ }
+ }
+ },
+ "profiles": {
+ "dev": {
+ "edit_predictions": {
+ "provider": {
+ "experimental": "sweep"
+ }
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ Some(
+ &r#"
+ {
+ "edit_predictions": {
+ "provider": "sweep"
+ },
+ "linux": {
+ "edit_predictions": {
+ "provider": "mercury"
+ }
+ },
+ "profiles": {
+ "dev": {
+ "edit_predictions": {
+ "provider": "sweep"
+ }
+ }
+ }
+ }
+ "#
+ .unindent(),
+ ),
+ );
}
}
@@ -10,5 +10,4 @@ pub(crate) use settings::{
SETTINGS_ASSISTANT_PATTERN, SETTINGS_ASSISTANT_TOOLS_PATTERN,
SETTINGS_DUPLICATED_AGENT_PATTERN, SETTINGS_EDIT_PREDICTIONS_ASSISTANT_PATTERN,
SETTINGS_LANGUAGES_PATTERN, SETTINGS_NESTED_KEY_VALUE_PATTERN, SETTINGS_ROOT_KEY_VALUE_PATTERN,
- migrate_language_setting,
};
@@ -108,24 +108,3 @@ pub const SETTINGS_DUPLICATED_AGENT_PATTERN: &str = r#"(document
(#eq? @agent1 "agent")
(#eq? @agent2 "agent")
)"#;
-
-/// Migrate language settings,
-/// calls `migrate_fn` with the top level object as well as all language settings under the "languages" key
-/// Fails early if `migrate_fn` returns an error at any point
-pub fn migrate_language_setting(
- value: &mut serde_json::Value,
- migrate_fn: fn(&mut serde_json::Value, path: &[&str]) -> anyhow::Result<()>,
-) -> anyhow::Result<()> {
- migrate_fn(value, &[])?;
- let languages = value
- .as_object_mut()
- .and_then(|obj| obj.get_mut("languages"))
- .and_then(|languages| languages.as_object_mut());
- if let Some(languages) = languages {
- for (language_name, language) in languages.iter_mut() {
- let path = vec!["languages", language_name];
- migrate_fn(language, &path)?;
- }
- }
- Ok(())
-}
@@ -24,7 +24,7 @@ pub mod private {
}
use gpui::{App, Global};
-use release_channel::ReleaseChannel;
+
use rust_embed::RustEmbed;
use std::env;
use std::{borrow::Cow, fmt, str};
@@ -73,21 +73,12 @@ impl UserSettingsContentExt for UserSettingsContent {
}
fn for_release_channel(&self) -> Option<&SettingsContent> {
- match *release_channel::RELEASE_CHANNEL {
- ReleaseChannel::Dev => self.dev.as_deref(),
- ReleaseChannel::Nightly => self.nightly.as_deref(),
- ReleaseChannel::Preview => self.preview.as_deref(),
- ReleaseChannel::Stable => self.stable.as_deref(),
- }
+ self.release_channel_overrides
+ .get_by_key(release_channel::RELEASE_CHANNEL.dev_name())
}
fn for_os(&self) -> Option<&SettingsContent> {
- match env::consts::OS {
- "macos" => self.macos.as_deref(),
- "linux" => self.linux.as_deref(),
- "windows" => self.windows.as_deref(),
- _ => None,
- }
+ self.platform_overrides.get_by_key(env::consts::OS)
}
}
@@ -32,6 +32,37 @@ use collections::{HashMap, IndexMap};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings_macros::{MergeFrom, with_fallible_options};
+
+/// Defines a settings override struct where each field is
+/// `Option<Box<SettingsContent>>`, along with:
+/// - `OVERRIDE_KEYS`: a `&[&str]` of the field names (the JSON keys)
+/// - `get_by_key(&self, key) -> Option<&SettingsContent>`: accessor by key
+///
+/// The field list is the single source of truth for the override key strings.
+macro_rules! settings_overrides {
+ (
+ $(#[$attr:meta])*
+ pub struct $name:ident { $($field:ident),* $(,)? }
+ ) => {
+ $(#[$attr])*
+ pub struct $name {
+ $(pub $field: Option<Box<SettingsContent>>,)*
+ }
+
+ impl $name {
+ /// The JSON override keys, derived from the field names on this struct.
+ pub const OVERRIDE_KEYS: &[&str] = &[$(stringify!($field)),*];
+
+ /// Look up an override by its JSON key name.
+ pub fn get_by_key(&self, key: &str) -> Option<&SettingsContent> {
+ match key {
+ $(stringify!($field) => self.$field.as_deref(),)*
+ _ => None,
+ }
+ }
+ }
+ }
+}
use std::collections::BTreeSet;
use std::sync::Arc;
pub use util::serde::default_true;
@@ -217,20 +248,29 @@ impl RootUserSettings for UserSettingsContent {
}
}
+settings_overrides! {
+ #[with_fallible_options]
+ #[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
+ pub struct ReleaseChannelOverrides { dev, nightly, preview, stable }
+}
+
+settings_overrides! {
+ #[with_fallible_options]
+ #[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
+ pub struct PlatformOverrides { macos, linux, windows }
+}
+
#[with_fallible_options]
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
pub struct UserSettingsContent {
#[serde(flatten)]
pub content: Box<SettingsContent>,
- pub dev: Option<Box<SettingsContent>>,
- pub nightly: Option<Box<SettingsContent>>,
- pub preview: Option<Box<SettingsContent>>,
- pub stable: Option<Box<SettingsContent>>,
+ #[serde(flatten)]
+ pub release_channel_overrides: ReleaseChannelOverrides,
- pub macos: Option<Box<SettingsContent>>,
- pub windows: Option<Box<SettingsContent>>,
- pub linux: Option<Box<SettingsContent>>,
+ #[serde(flatten)]
+ pub platform_overrides: PlatformOverrides,
#[serde(default)]
pub profiles: IndexMap<String, SettingsContent>,