Define auto_update setting in the auto_update crate

Max Brunsfeld created

Change summary

crates/auto_update/src/auto_update.rs | 29 +++++++++++++++++++++++------
crates/settings/src/settings.rs       | 10 +---------
crates/settings/src/settings_file.rs  | 21 +++++++++++++++++++--
crates/settings/src/settings_store.rs | 11 +++++++++++
4 files changed, 54 insertions(+), 17 deletions(-)

Detailed changes

crates/auto_update/src/auto_update.rs 🔗

@@ -10,7 +10,7 @@ use gpui::{
 use isahc::AsyncBody;
 use serde::Deserialize;
 use serde_derive::Serialize;
-use settings::Settings;
+use settings::{Setting, Settings, SettingsStore};
 use smol::{fs::File, io::AsyncReadExt, process::Command};
 use std::{ffi::OsString, sync::Arc, time::Duration};
 use update_notification::UpdateNotification;
@@ -58,18 +58,35 @@ impl Entity for AutoUpdater {
     type Event = ();
 }
 
+struct AutoUpdateSetting(bool);
+
+impl Setting for AutoUpdateSetting {
+    const KEY: Option<&'static str> = Some("auto_update");
+
+    type FileContent = Option<bool>;
+
+    fn load(default_value: &Option<bool>, user_values: &[&Option<bool>], _: &AppContext) -> Self {
+        Self(
+            Self::json_merge(default_value, user_values)
+                .unwrap()
+                .unwrap(),
+        )
+    }
+}
+
 pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppContext) {
+    settings::register_setting::<AutoUpdateSetting>(cx);
+
     if let Some(version) = (*ZED_APP_VERSION).or_else(|| cx.platform().app_version().ok()) {
         let auto_updater = cx.add_model(|cx| {
             let updater = AutoUpdater::new(version, http_client, server_url);
 
-            let mut update_subscription = cx
-                .global::<Settings>()
-                .auto_update
+            let mut update_subscription = settings::get_setting::<AutoUpdateSetting>(None, cx)
+                .0
                 .then(|| updater.start_polling(cx));
 
-            cx.observe_global::<Settings, _>(move |updater, cx| {
-                if cx.global::<Settings>().auto_update {
+            cx.observe_global::<SettingsStore, _>(move |updater, cx| {
+                if settings::get_setting::<AutoUpdateSetting>(None, cx).0 {
                     if update_subscription.is_none() {
                         update_subscription = Some(updater.start_polling(cx))
                     }

crates/settings/src/settings.rs 🔗

@@ -14,7 +14,6 @@ use schemars::{
 };
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
-use settings_store::Setting;
 use sqlez::{
     bindable::{Bind, Column, StaticColumnCount},
     statement::Statement,
@@ -25,7 +24,7 @@ use util::ResultExt as _;
 
 pub use keymap_file::{keymap_file_json_schema, KeymapFileContent};
 pub use settings_file::*;
-pub use settings_store::{SettingsJsonSchemaParams, SettingsStore};
+pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore};
 
 pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json";
 pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json";
@@ -62,7 +61,6 @@ pub struct Settings {
     pub theme: Arc<Theme>,
     pub telemetry_defaults: TelemetrySettings,
     pub telemetry_overrides: TelemetrySettings,
-    pub auto_update: bool,
     pub base_keymap: BaseKeymap,
 }
 
@@ -137,7 +135,6 @@ impl Setting for Settings {
             theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),
             telemetry_defaults: defaults.telemetry,
             telemetry_overrides: Default::default(),
-            auto_update: defaults.auto_update.unwrap(),
             base_keymap: Default::default(),
             features: Features {
                 copilot: defaults.features.copilot.unwrap(),
@@ -576,8 +573,6 @@ pub struct SettingsFileContent {
     #[serde(default)]
     pub telemetry: TelemetrySettings,
     #[serde(default)]
-    pub auto_update: Option<bool>,
-    #[serde(default)]
     pub base_keymap: Option<BaseKeymap>,
     #[serde(default)]
     pub features: FeaturesContent,
@@ -695,7 +690,6 @@ impl Settings {
             theme: themes.get(&defaults.theme.unwrap()).unwrap(),
             telemetry_defaults: defaults.telemetry,
             telemetry_overrides: Default::default(),
-            auto_update: defaults.auto_update.unwrap(),
             base_keymap: Default::default(),
             features: Features {
                 copilot: defaults.features.copilot.unwrap(),
@@ -770,7 +764,6 @@ impl Settings {
         self.language_overrides = data.languages;
         self.telemetry_overrides = data.telemetry;
         self.lsp = data.lsp;
-        merge(&mut self.auto_update, data.auto_update);
     }
 
     pub fn with_language_defaults(
@@ -980,7 +973,6 @@ impl Settings {
                 metrics: Some(true),
             },
             telemetry_overrides: Default::default(),
-            auto_update: true,
             base_keymap: Default::default(),
             features: Features { copilot: true },
         }

crates/settings/src/settings_file.rs 🔗

@@ -1,15 +1,32 @@
 use crate::{
     settings_store::parse_json_with_comments, settings_store::SettingsStore, KeymapFileContent,
-    Settings, SettingsFileContent, DEFAULT_SETTINGS_ASSET_PATH,
+    Setting, Settings, SettingsFileContent, DEFAULT_SETTINGS_ASSET_PATH,
 };
 use anyhow::Result;
 use assets::Assets;
 use fs::Fs;
 use futures::{channel::mpsc, StreamExt};
 use gpui::{executor::Background, AppContext, AssetSource};
-use std::{borrow::Cow, io::ErrorKind, path::PathBuf, str, sync::Arc, time::Duration};
+use std::{
+    borrow::Cow,
+    io::ErrorKind,
+    path::{Path, PathBuf},
+    str,
+    sync::Arc,
+    time::Duration,
+};
 use util::{paths, ResultExt};
 
+pub fn register_setting<T: Setting>(cx: &mut AppContext) {
+    cx.update_global::<SettingsStore, _, _>(|store, cx| {
+        store.register_setting::<T>(cx);
+    });
+}
+
+pub fn get_setting<'a, T: Setting>(path: Option<&Path>, cx: &'a AppContext) -> &'a T {
+    cx.global::<SettingsStore>().get(path)
+}
+
 pub fn default_settings() -> Cow<'static, str> {
     match Assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap() {
         Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()),

crates/settings/src/settings_store.rs 🔗

@@ -43,6 +43,17 @@ pub trait Setting: 'static {
         generator.root_schema_for::<Self::FileContent>()
     }
 
+    fn json_merge(
+        default_value: &Self::FileContent,
+        user_values: &[&Self::FileContent],
+    ) -> Result<Self::FileContent> {
+        let mut merged = serde_json::Value::Null;
+        for value in [default_value].iter().chain(user_values) {
+            merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);
+        }
+        Ok(serde_json::from_value(merged)?)
+    }
+
     fn load_via_json_merge(
         default_value: &Self::FileContent,
         user_values: &[&Self::FileContent],