feat(cosmic_config): add ConfigGet::get_{local,system_default}

Required by https://github.com/pop-os/cosmic-settings/pull/975 to a modify a config
containing a HashMap which is used to partially-override the system default config
in the compositor.
This commit is contained in:
Michael Aaron Murphy 2025-02-14 21:44:08 +01:00
parent 0b7e23444a
commit ccc1068d9f
No known key found for this signature in database
GPG key ID: B2732D4240C9212C

View file

@ -33,6 +33,7 @@ pub enum Error {
Io(std::io::Error), Io(std::io::Error),
NoConfigDirectory, NoConfigDirectory,
Notify(notify::Error), Notify(notify::Error),
NotFound,
Ron(ron::Error), Ron(ron::Error),
RonSpanned(ron::error::SpannedError), RonSpanned(ron::error::SpannedError),
GetKey(String, std::io::Error), GetKey(String, std::io::Error),
@ -46,6 +47,7 @@ impl fmt::Display for Error {
Self::Io(err) => err.fmt(f), Self::Io(err) => err.fmt(f),
Self::NoConfigDirectory => write!(f, "cosmic config directory not found"), Self::NoConfigDirectory => write!(f, "cosmic config directory not found"),
Self::Notify(err) => err.fmt(f), Self::Notify(err) => err.fmt(f),
Self::NotFound => write!(f, "cosmic config key not configured"),
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), Self::GetKey(key, err) => write!(f, "failed to get key '{}': {}", key, err),
@ -55,6 +57,15 @@ impl fmt::Display for Error {
impl std::error::Error for Error {} impl std::error::Error for Error {}
impl Error {
/// Whether the reason for the missing config is caused by an error.
///
/// Useful for determining if it is appropriate to log as an error.
pub fn is_err(&self) -> bool {
matches!(self, Self::NoConfigDirectory | Self::NotFound)
}
}
impl From<atomicwrites::Error<std::io::Error>> for Error { impl From<atomicwrites::Error<std::io::Error>> for Error {
fn from(f: atomicwrites::Error<std::io::Error>) -> Self { fn from(f: atomicwrites::Error<std::io::Error>) -> Self {
Self::AtomicWrites(f) Self::AtomicWrites(f)
@ -87,7 +98,15 @@ impl From<ron::error::SpannedError> for Error {
pub trait ConfigGet { pub trait ConfigGet {
/// Get a configuration value /// Get a configuration value
///
/// Fallback to the system default if a local user override is not defined.
fn get<T: DeserializeOwned>(&self, key: &str) -> Result<T, Error>; fn get<T: DeserializeOwned>(&self, key: &str) -> Result<T, Error>;
/// Get a locally-defined configuration value from the user's local config.
fn get_local<T: DeserializeOwned>(&self, key: &str) -> Result<T, Error>;
/// Get the system-defined default configuration value.
fn get_system_default<T: DeserializeOwned>(&self, key: &str) -> Result<T, Error>;
} }
pub trait ConfigSet { pub trait ConfigSet {
@ -216,7 +235,7 @@ impl Config {
} }
// Start a transaction (to set multiple configs at the same time) // Start a transaction (to set multiple configs at the same time)
pub fn transaction<'a>(&'a self) -> ConfigTransaction<'a> { pub fn transaction(&self) -> ConfigTransaction {
ConfigTransaction { ConfigTransaction {
config: self, config: self,
updates: Mutex::new(Vec::new()), updates: Mutex::new(Vec::new()),
@ -288,6 +307,7 @@ impl Config {
Ok(system_path.join(sanitize_name(key)?)) Ok(system_path.join(sanitize_name(key)?))
} }
/// Get the path of the key in the user's local config directory.
fn key_path(&self, key: &str) -> Result<PathBuf, Error> { fn key_path(&self, key: &str) -> Result<PathBuf, Error> {
let Some(user_path) = self.user_path.as_ref() else { let Some(user_path) = self.user_path.as_ref() else {
return Err(Error::NoConfigDirectory); return Err(Error::NoConfigDirectory);
@ -300,22 +320,34 @@ impl Config {
impl ConfigGet for Config { impl ConfigGet for Config {
//TODO: check for transaction //TODO: check for transaction
fn get<T: DeserializeOwned>(&self, key: &str) -> Result<T, Error> { fn get<T: DeserializeOwned>(&self, key: &str) -> Result<T, Error> {
match self.get_local(key) {
Ok(value) => Ok(value),
Err(Error::NotFound) => self.get_system_default(key),
Err(why) => Err(why),
}
}
fn get_local<T: DeserializeOwned>(&self, key: &str) -> Result<T, Error> {
// If key path exists // If key path exists
let key_path = self.key_path(key); match self.key_path(key)? {
let data = match key_path { key_path if key_path.is_file() => {
Ok(key_path) if key_path.is_file() => {
// Load user override // Load user override
fs::read_to_string(key_path).map_err(|err| Error::GetKey(key.to_string(), err))? let data = fs::read_to_string(key_path)
.map_err(|err| Error::GetKey(key.to_string(), err))?;
Ok(ron::from_str(&data)?)
} }
_ => {
// Load system default _ => Err(Error::NotFound),
let default_path = self.default_path(key)?; }
fs::read_to_string(default_path) }
.map_err(|err| Error::GetKey(key.to_string(), err))?
} fn get_system_default<T: DeserializeOwned>(&self, key: &str) -> Result<T, Error> {
}; // Load system default
let t = ron::from_str(&data)?; let default_path = self.default_path(key)?;
Ok(t) let data =
fs::read_to_string(default_path).map_err(|err| Error::GetKey(key.to_string(), err))?;
Ok(ron::from_str(&data)?)
} }
} }