refactor: config improvements

This commit is contained in:
Ashley Wulber 2024-01-18 19:01:11 -05:00 committed by Jeremy Soller
parent 3eed30f723
commit efe4ce2f5b
7 changed files with 69 additions and 86 deletions

View file

@ -1,6 +1,6 @@
use std::ops::Deref; use std::ops::Deref;
use crate::CosmicConfigEntry; use crate::{CosmicConfigEntry, Update};
use cosmic_settings_daemon::{ConfigProxy, CosmicSettingsDaemonProxy}; use cosmic_settings_daemon::{ConfigProxy, CosmicSettingsDaemonProxy};
use futures_util::SinkExt; use futures_util::SinkExt;
use iced_futures::futures::{future::pending, StreamExt}; use iced_futures::futures::{future::pending, StreamExt};
@ -51,13 +51,6 @@ impl Watcher {
} }
} }
#[derive(Debug)]
pub struct Update<T> {
pub errors: Vec<crate::Error>,
pub keys: Vec<&'static str>,
pub config: T,
}
pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>( pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>(
settings_daemon: CosmicSettingsDaemonProxy<'static>, settings_daemon: CosmicSettingsDaemonProxy<'static>,
config_id: &'static str, config_id: &'static str,
@ -79,7 +72,7 @@ pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'stat
Ok(config) => config, Ok(config) => config,
Err((errors, default)) => { Err((errors, default)) => {
if !errors.is_empty() { if !errors.is_empty() {
eprintln!("Failed to get config: {errors:?}"); eprintln!("Error getting config: {config_id} {errors:?}");
} }
default default
} }

View file

@ -32,6 +32,7 @@ pub enum Error {
Notify(notify::Error), Notify(notify::Error),
Ron(ron::Error), Ron(ron::Error),
RonSpanned(ron::error::SpannedError), RonSpanned(ron::error::SpannedError),
GetKey(String, std::io::Error),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -44,6 +45,7 @@ impl fmt::Display for Error {
Self::Notify(err) => err.fmt(f), Self::Notify(err) => err.fmt(f),
Self::Ron(err) => err.fmt(f), Self::Ron(err) => err.fmt(f),
Self::RonSpanned(err) => err.fmt(f), Self::RonSpanned(err) => err.fmt(f),
Self::GetKey(key, err) => write!(f, "failed to get key '{}': {}", key, err),
} }
} }
} }
@ -264,11 +266,11 @@ impl ConfigGet for Config {
let key_path = self.key_path(key)?; let key_path = self.key_path(key)?;
let data = if key_path.is_file() { let data = if key_path.is_file() {
// Load user override // Load user override
fs::read_to_string(key_path)? fs::read_to_string(key_path).map_err(|err| Error::GetKey(key.to_string(), err))?
} else { } else {
// Load system default // Load system default
let default_path = self.default_path(key)?; let default_path = self.default_path(key)?;
fs::read_to_string(default_path)? fs::read_to_string(default_path).map_err(|err| Error::GetKey(key.to_string(), err))?
}; };
let t = ron::from_str(&data)?; let t = ron::from_str(&data)?;
Ok(t) Ok(t)
@ -339,3 +341,9 @@ where
changed_keys: &[T], changed_keys: &[T],
) -> (Vec<crate::Error>, Vec<&'static str>); ) -> (Vec<crate::Error>, Vec<&'static str>);
} }
pub struct Update<T> {
pub errors: Vec<crate::Error>,
pub keys: Vec<&'static str>,
pub config: T,
}

View file

@ -12,8 +12,7 @@ pub enum ConfigState<T> {
} }
pub enum ConfigUpdate<T> { pub enum ConfigUpdate<T> {
Update(T), Update(crate::Update<T>),
UpdateError(T, Vec<crate::Error>),
Failed, Failed,
} }
@ -24,7 +23,7 @@ pub fn config_subscription<
id: I, id: I,
config_id: Cow<'static, str>, config_id: Cow<'static, str>,
config_version: u64, config_version: u64,
) -> iced_futures::Subscription<(I, Result<T, (Vec<crate::Error>, T)>)> { ) -> iced_futures::Subscription<crate::Update<T>> {
subscription::channel(id, 100, move |mut output| { subscription::channel(id, 100, move |mut output| {
let config_id = config_id.clone(); let config_id = config_id.clone();
async move { async move {
@ -45,7 +44,7 @@ pub fn config_state_subscription<
id: I, id: I,
config_id: Cow<'static, str>, config_id: Cow<'static, str>,
config_version: u64, config_version: u64,
) -> iced_futures::Subscription<(I, Result<T, (Vec<crate::Error>, T)>)> { ) -> iced_futures::Subscription<crate::Update<T>> {
subscription::channel(id, 100, move |mut output| { subscription::channel(id, 100, move |mut output| {
let config_id = config_id.clone(); let config_id = config_id.clone();
async move { async move {
@ -64,7 +63,7 @@ async fn start_listening<
T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry, T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry,
>( >(
state: ConfigState<T>, state: ConfigState<T>,
output: &mut mpsc::Sender<(I, Result<T, (Vec<crate::Error>, T)>)>, output: &mut mpsc::Sender<crate::Update<T>>,
id: I, id: I,
) -> ConfigState<T> { ) -> ConfigState<T> {
use iced_futures::futures::{future::pending, StreamExt}; use iced_futures::futures::{future::pending, StreamExt};
@ -90,11 +89,21 @@ async fn start_listening<
match T::get_entry(&config) { match T::get_entry(&config) {
Ok(t) => { Ok(t) => {
_ = output.send((id, Ok(t.clone()))).await; let update = crate::Update {
errors: Vec::new(),
keys: Vec::new(),
config: t.clone(),
};
_ = output.send(update).await;
ConfigState::Waiting(t, watcher, rx, config) ConfigState::Waiting(t, watcher, rx, config)
} }
Err((errors, t)) => { Err((errors, t)) => {
_ = output.send((id, Err((errors, t.clone())))).await; let update = crate::Update {
errors: errors,
keys: Vec::new(),
config: t.clone(),
};
_ = output.send(update).await;
ConfigState::Waiting(t, watcher, rx, config) ConfigState::Waiting(t, watcher, rx, config)
} }
} }
@ -104,11 +113,13 @@ async fn start_listening<
let (errors, changed) = conf_data.update_keys(&config, &keys); let (errors, changed) = conf_data.update_keys(&config, &keys);
if !changed.is_empty() { if !changed.is_empty() {
if errors.is_empty() { _ = output
_ = output.send((id, Ok(conf_data.clone()))).await; .send(crate::Update {
} else { errors: errors,
_ = output.send((id, Err((errors, conf_data.clone())))).await; keys: changed,
} config: conf_data.clone(),
})
.await;
} }
ConfigState::Waiting(conf_data, watcher, rx, config) ConfigState::Waiting(conf_data, watcher, rx, config)
} }

View file

@ -13,7 +13,6 @@ const ID: &str = "com.system76.CosmicAppletExample";
pub struct Window { pub struct Window {
core: Core, core: Core,
popup: Option<Id>, popup: Option<Id>,
id_ctr: u128,
example_row: bool, example_row: bool,
} }

View file

@ -214,27 +214,37 @@ impl Core {
self.system_theme_mode self.system_theme_mode
} }
#[cfg(feature = "dbus-config")] pub fn watch_config<
pub fn watch_config<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>( T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone + PartialEq,
>(
&self, &self,
config_id: &'static str, config_id: &'static str,
) -> iced::Subscription<cosmic_config::dbus::Update<T>> { ) -> iced::Subscription<cosmic_config::Update<T>> {
#[cfg(feature = "dbus-config")]
if let Some(settings_daemon) = self.settings_daemon.clone() { if let Some(settings_daemon) = self.settings_daemon.clone() {
cosmic_config::dbus::watcher_subscription(settings_daemon, config_id, false) return cosmic_config::dbus::watcher_subscription(settings_daemon, config_id, false);
} else {
iced::Subscription::none()
} }
cosmic_config::config_subscription(
std::any::TypeId::of::<T>(),
std::borrow::Cow::Borrowed(config_id),
T::VERSION,
)
} }
#[cfg(feature = "dbus-config")] pub fn watch_state<
pub fn watch_state<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>( T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone + PartialEq,
>(
&self, &self,
state_id: &'static str, state_id: &'static str,
) -> iced::Subscription<cosmic_config::dbus::Update<T>> { ) -> iced::Subscription<cosmic_config::Update<T>> {
#[cfg(feature = "dbus-config")]
if let Some(settings_daemon) = self.settings_daemon.clone() { if let Some(settings_daemon) = self.settings_daemon.clone() {
cosmic_config::dbus::watcher_subscription(settings_daemon, state_id, true) return cosmic_config::dbus::watcher_subscription(settings_daemon, state_id, true);
} else {
iced::Subscription::none()
} }
cosmic_config::config_subscription(
std::any::TypeId::of::<T>(),
std::borrow::Cow::Borrowed(state_id),
T::VERSION,
)
} }
} }

View file

@ -22,6 +22,7 @@ use iced::window;
#[cfg(not(any(feature = "multi-window", feature = "wayland")))] #[cfg(not(any(feature = "multi-window", feature = "wayland")))]
use iced::Application as IcedApplication; use iced::Application as IcedApplication;
use iced_futures::event::listen_raw; use iced_futures::event::listen_raw;
use iced_futures::futures::executor::block_on;
#[cfg(not(feature = "wayland"))] #[cfg(not(feature = "wayland"))]
use iced_runtime::command::Action; use iced_runtime::command::Action;
#[cfg(not(feature = "wayland"))] #[cfg(not(feature = "wayland"))]
@ -66,9 +67,6 @@ pub enum Message {
WmCapabilities(window::Id, WindowManagerCapabilities), WmCapabilities(window::Id, WindowManagerCapabilities),
/// Activate the application /// Activate the application
Activate(String), Activate(String),
#[cfg(feature = "dbus-config")]
/// dbus settings daemon setup
SettingsDaemon(zbus::Result<cosmic_settings_daemon::CosmicSettingsDaemonProxy<'static>>),
} }
#[derive(Default)] #[derive(Default)]
@ -85,16 +83,14 @@ where
type Message = super::Message<T::Message>; type Message = super::Message<T::Message>;
type Theme = Theme; type Theme = Theme;
fn new((core, flags): Self::Flags) -> (Self, iced::Command<Self::Message>) { fn new((mut core, flags): Self::Flags) -> (Self, iced::Command<Self::Message>) {
#[cfg(feature = "dbus-config")]
{
core.settings_daemon = block_on(cosmic_config::dbus::settings_daemon_proxy()).ok();
}
let (model, command) = T::init(core, flags); let (model, command) = T::init(core, flags);
#[cfg(feature = "dbus-config")]
let command = iced::Command::batch(vec![
command,
iced::Command::perform(cosmic_config::dbus::settings_daemon_proxy(), |p| {
super::Message::Cosmic(super::cosmic::Message::SettingsDaemon(p))
}),
]);
(Self::new(model), command) (Self::new(model), command)
} }
@ -176,7 +172,6 @@ where
keyboard_nav::subscription() keyboard_nav::subscription()
.map(Message::KeyboardNav) .map(Message::KeyboardNav)
.map(super::Message::Cosmic), .map(super::Message::Cosmic),
#[cfg(feature = "dbus-config")]
self.app self.app
.core() .core()
.watch_config::<cosmic_theme::Theme>(if self.app.core().system_theme_mode.is_dark { .watch_config::<cosmic_theme::Theme>(if self.app.core().system_theme_mode.is_dark {
@ -191,27 +186,6 @@ where
Message::SystemThemeChange(crate::theme::Theme::system(Arc::new(update.config))) Message::SystemThemeChange(crate::theme::Theme::system(Arc::new(update.config)))
}) })
.map(super::Message::Cosmic), .map(super::Message::Cosmic),
#[cfg(not(feature = "dbus-config"))]
theme::subscription(self.app.core().system_theme_mode.is_dark)
.map(Message::SystemThemeChange)
.map(super::Message::Cosmic),
#[cfg(not(feature = "dbus-config"))]
cosmic_config::config_subscription::<_, cosmic_theme::ThemeMode>(
0,
cosmic_theme::THEME_MODE_ID.into(),
cosmic_theme::ThemeMode::version(),
)
.map(|(_, u)| match u {
Ok(t) => Message::SystemThemeModeChange(t),
Err((errors, t)) => {
for e in errors {
tracing::error!("{e}");
}
Message::SystemThemeModeChange(t)
}
})
.map(super::Message::Cosmic),
#[cfg(feature = "dbus-config")]
self.app self.app
.core() .core()
.watch_config::<ThemeMode>(cosmic_theme::THEME_MODE_ID) .watch_config::<ThemeMode>(cosmic_theme::THEME_MODE_ID)
@ -422,15 +396,6 @@ impl<T: Application> Cosmic<T> {
_token, _token,
); );
} }
#[cfg(feature = "dbus-config")]
Message::SettingsDaemon(p) => match p {
Ok(p) => {
self.app.core_mut().settings_daemon = Some(p);
}
Err(e) => {
tracing::error!("Failed to connect to settings daemon: {e}");
}
},
} }
iced::Command::none() iced::Command::none()

View file

@ -85,15 +85,12 @@ pub fn subscription(is_dark: bool) -> Subscription<crate::theme::Theme> {
.into(), .into(),
crate::cosmic_theme::Theme::version(), crate::cosmic_theme::Theme::version(),
) )
.map(|(_, res)| { .map(|res| {
let theme = res.unwrap_or_else(|(errors, theme)| { for err in res.errors {
for err in errors { tracing::error!("{:?}", err);
tracing::error!("{:?}", err); }
}
theme
});
Theme::system(Arc::new(theme)) Theme::system(Arc::new(res.config))
}) })
} }