mirror of
https://github.com/RGBCube/superfreq
synced 2025-07-27 17:07:44 +00:00
Merge pull request #22 from NotAShelf/complete-config-watcher
config: streamline hotreload; don't search in `$XDG_CONFIG_HOME`
This commit is contained in:
commit
39736f2925
4 changed files with 64 additions and 28 deletions
|
@ -32,22 +32,27 @@ pub fn load_config_from_path(specific_path: Option<&str>) -> Result<AppConfig, C
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise try the standard paths
|
// Check for SUPERFREQ_CONFIG environment variable
|
||||||
let mut config_paths: Vec<PathBuf> = Vec::new();
|
if let Ok(env_path) = std::env::var("SUPERFREQ_CONFIG") {
|
||||||
|
let env_path = Path::new(&env_path);
|
||||||
// User-specific path
|
if env_path.exists() {
|
||||||
if let Some(home_dir) = dirs::home_dir() {
|
println!(
|
||||||
let user_config_path = home_dir.join(".config/superfreq/config.toml");
|
"Loading config from SUPERFREQ_CONFIG: {}",
|
||||||
config_paths.push(user_config_path);
|
env_path.display()
|
||||||
} else {
|
);
|
||||||
|
return load_and_parse_config(env_path);
|
||||||
|
}
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Warning: Could not determine home directory. User-specific config will not be loaded."
|
"Warning: Config file specified by SUPERFREQ_CONFIG not found: {}",
|
||||||
|
env_path.display()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// System-wide paths
|
// System-wide paths
|
||||||
config_paths.push(PathBuf::from("/etc/xdg/superfreq/config.toml"));
|
let config_paths = vec![
|
||||||
config_paths.push(PathBuf::from("/etc/superfreq.toml"));
|
PathBuf::from("/etc/xdg/superfreq/config.toml"),
|
||||||
|
PathBuf::from("/etc/superfreq.toml"),
|
||||||
|
];
|
||||||
|
|
||||||
for path in config_paths {
|
for path in config_paths {
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
|
|
|
@ -87,8 +87,6 @@ pub struct AppConfig {
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
IoError(std::io::Error),
|
IoError(std::io::Error),
|
||||||
TomlError(toml::de::Error),
|
TomlError(toml::de::Error),
|
||||||
NoValidConfigFound,
|
|
||||||
HomeDirNotFound,
|
|
||||||
ValidationError(String),
|
ValidationError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,8 +107,6 @@ impl std::fmt::Display for ConfigError {
|
||||||
match self {
|
match self {
|
||||||
Self::IoError(e) => write!(f, "I/O error: {e}"),
|
Self::IoError(e) => write!(f, "I/O error: {e}"),
|
||||||
Self::TomlError(e) => write!(f, "TOML parsing error: {e}"),
|
Self::TomlError(e) => write!(f, "TOML parsing error: {e}"),
|
||||||
Self::NoValidConfigFound => write!(f, "No valid configuration file found."),
|
|
||||||
Self::HomeDirNotFound => write!(f, "Could not determine user home directory."),
|
|
||||||
Self::ValidationError(s) => write!(f, "Configuration validation error: {s}"),
|
Self::ValidationError(s) => write!(f, "Configuration validation error: {s}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use log::{debug, error, warn};
|
||||||
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -26,6 +27,8 @@ impl ConfigWatcher {
|
||||||
// Start watching the config file
|
// Start watching the config file
|
||||||
watcher.watch(Path::new(config_path), RecursiveMode::NonRecursive)?;
|
watcher.watch(Path::new(config_path), RecursiveMode::NonRecursive)?;
|
||||||
|
|
||||||
|
debug!("Started watching config file: {config_path}");
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
rx,
|
rx,
|
||||||
_watcher: watcher,
|
_watcher: watcher,
|
||||||
|
@ -46,15 +49,34 @@ impl ConfigWatcher {
|
||||||
loop {
|
loop {
|
||||||
match self.rx.try_recv() {
|
match self.rx.try_recv() {
|
||||||
Ok(Ok(event)) => {
|
Ok(Ok(event)) => {
|
||||||
// Only process write/modify events
|
// Process various file events that might indicate configuration changes
|
||||||
if matches!(event.kind, EventKind::Modify(_)) {
|
match event.kind {
|
||||||
should_reload = true;
|
EventKind::Modify(_) => {
|
||||||
|
debug!("Detected modification to config file: {}", self.config_path);
|
||||||
|
should_reload = true;
|
||||||
|
}
|
||||||
|
EventKind::Create(_) => {
|
||||||
|
debug!("Detected recreation of config file: {}", self.config_path);
|
||||||
|
should_reload = true;
|
||||||
|
}
|
||||||
|
EventKind::Remove(_) => {
|
||||||
|
// Some editors delete then recreate the file when saving
|
||||||
|
// Just log this event and wait for the create event
|
||||||
|
debug!(
|
||||||
|
"Detected removal of config file: {} - waiting for recreation",
|
||||||
|
self.config_path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {} // Ignore other event types
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_reload {
|
||||||
self.last_event_time = std::time::Instant::now();
|
self.last_event_time = std::time::Instant::now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
// File watcher error, log but continue
|
// File watcher error, log but continue
|
||||||
eprintln!("Error watching config file: {e}");
|
warn!("Error watching config file: {e}");
|
||||||
}
|
}
|
||||||
Err(TryRecvError::Empty) => {
|
Err(TryRecvError::Empty) => {
|
||||||
// No more events
|
// No more events
|
||||||
|
@ -62,7 +84,7 @@ impl ConfigWatcher {
|
||||||
}
|
}
|
||||||
Err(TryRecvError::Disconnected) => {
|
Err(TryRecvError::Disconnected) => {
|
||||||
// Channel disconnected, watcher is dead
|
// Channel disconnected, watcher is dead
|
||||||
eprintln!("Config watcher channel disconnected");
|
error!("Config watcher channel disconnected");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,18 +100,31 @@ impl ConfigWatcher {
|
||||||
thread::sleep(debounce_time - time_since_last_event);
|
thread::sleep(debounce_time - time_since_last_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure the file exists before attempting to reload
|
||||||
|
let config_path = Path::new(&self.config_path);
|
||||||
|
if !config_path.exists() {
|
||||||
|
warn!(
|
||||||
|
"Config file does not exist after change events: {}",
|
||||||
|
self.config_path
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Reloading configuration from {}", self.config_path);
|
||||||
|
|
||||||
// Attempt to reload the config from the specific path being watched
|
// Attempt to reload the config from the specific path being watched
|
||||||
match load_config_from_path(Some(&self.config_path)) {
|
match load_config_from_path(Some(&self.config_path)) {
|
||||||
Ok(config) => Some(Ok(config)),
|
Ok(config) => {
|
||||||
Err(e) => Some(Err(Box::new(e))),
|
debug!("Successfully reloaded configuration");
|
||||||
|
Some(Ok(config))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to reload configuration: {e}");
|
||||||
|
Some(Err(Box::new(e)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the path of the config file being watched
|
|
||||||
pub const fn config_path(&self) -> &String {
|
|
||||||
&self.config_path
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ fn main() {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error loading configuration: {e}. Using default values.");
|
error!("Error loading configuration: {e}. Using default values.");
|
||||||
// Proceed with default config if loading fails, as per previous steps
|
// Proceed with default config if loading fails
|
||||||
AppConfig::default()
|
AppConfig::default()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue