Skip to content

Commit

Permalink
Add support for watching of the settings file
Browse files Browse the repository at this point in the history
  • Loading branch information
krlvi committed Dec 18, 2024
1 parent 37569d4 commit 2c2cf44
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 17 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/gitbutler-settings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ serde = { workspace = true, features = ["std"] }
serde_json = { version = "1.0", features = ["std", "arbitrary_precision"] }
serde_json_lenient = "0.2.3"
gitbutler-fs.workspace = true
notify = { version = "6.0.1" }
tracing.workspace = true

[[test]]
name = "settings"
Expand Down
6 changes: 0 additions & 6 deletions crates/gitbutler-settings/lib.rs

This file was deleted.

4 changes: 3 additions & 1 deletion crates/gitbutler-settings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ pub use legacy::LegacySettings;

mod app_settings;
mod json;
pub mod persistence;
mod persistence;
mod watch;
pub use app_settings::AppSettings;
pub use watch::SettingsHandle;
12 changes: 3 additions & 9 deletions crates/gitbutler-settings/src/persistence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ use anyhow::Result;
use serde_json::json;

static DEFAULTS: &str = include_str!("../assets/defaults.jsonc");
const SETTINGS_FILE: &str = "settings.json";

impl AppSettings {
/// Load the settings from the configuration directory. If a config file name is not provided, the default `gitbutler_settings.json` one is used.
pub fn load(config_dir: impl Into<PathBuf>) -> Result<Self> {
let config_path = config_dir.into().join(SETTINGS_FILE);

pub fn load(config_path: PathBuf) -> Result<Self> {
// Load the defaults
let mut settings: serde_json::Value = serde_json_lenient::from_str(DEFAULTS)?;

Expand All @@ -25,12 +22,9 @@ impl AppSettings {
}

/// Save the updated fields of the AppSettings in the custom configuration file.
pub fn save(&self, config_dir: impl Into<PathBuf>) -> Result<()> {
let config_dir = config_dir.into();
let config_path = config_dir.clone().join(SETTINGS_FILE);

pub fn save(&self, config_path: PathBuf) -> Result<()> {
// Load the current settings
let current = serde_json::to_value(AppSettings::load(config_dir)?)?;
let current = serde_json::to_value(AppSettings::load(config_path.clone())?)?;

// Derive changed values only compared to the current settings
let update = serde_json::to_value(self)?;
Expand Down
66 changes: 66 additions & 0 deletions crates/gitbutler-settings/src/watch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::{
path::PathBuf,
sync::{mpsc::channel, OnceLock, RwLock},
time::Duration,
};

use crate::{app_settings, AppSettings};
use anyhow::Result;
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher};

pub struct SettingsHandle<'a> {
config_path: PathBuf,
app_settings: &'a RwLock<AppSettings>,
}

const SETTINGS_FILE: &str = "settings.json";

impl<'a> SettingsHandle<'a> {
pub fn create(config_dir: impl Into<PathBuf>) -> Result<Self> {
let config_path = config_dir.into().join(SETTINGS_FILE);
let app_settings = app_settings::AppSettings::load(config_path.clone())?;

static CONFIG: OnceLock<RwLock<AppSettings>> = OnceLock::new();
let app_settings = CONFIG.get_or_init(|| RwLock::new(app_settings));

Ok(Self {
config_path,
app_settings,
})
}

pub fn settings(&self) -> &'a RwLock<AppSettings> {
self.app_settings
}

pub fn watch(&self) -> Result<()> {
let (tx, rx) = channel();
let mut watcher: RecommendedWatcher = Watcher::new(
tx,
notify::Config::default().with_poll_interval(Duration::from_secs(2)),
)?;
watcher.watch(self.config_path.as_path(), RecursiveMode::NonRecursive)?;

loop {
match rx.recv() {
Ok(Ok(Event {
kind: notify::event::EventKind::Modify(_),
..
})) => {
println!("settings.json written; refreshing configuration...");
// TODO: How can we avoid this unwrap?
let mut write = self.app_settings.write().unwrap();
*write = app_settings::AppSettings::load(self.config_path.clone())?;
}

Err(e) => {
tracing::error!("Error watching config file {:?}: {:?}", self.config_path, e)
}

_ => {
// Noop
}
}
}
}
}
3 changes: 2 additions & 1 deletion crates/gitbutler-settings/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use gitbutler_settings::AppSettings;
#[test]
#[allow(clippy::bool_assert_comparison)]
fn test_load_settings() {
let settings = AppSettings::load("tests/fixtures/modify_default_true_to_false").unwrap();
let settings =
AppSettings::load("tests/fixtures/modify_default_true_to_false.json".into()).unwrap();
assert_eq!(settings.telemetry.app_metrics_enabled, false); // modified
assert_eq!(settings.telemetry.app_error_reporting_enabled, true); // default
assert_eq!(settings.telemetry.app_non_anon_metrics_enabled, false); // default
Expand Down

0 comments on commit 2c2cf44

Please sign in to comment.