Skip to content

Commit

Permalink
improv(cosmic-config): remove hardcoded paths
Browse files Browse the repository at this point in the history
This commit changes the hardcoded /usr/share paths in cosmic-config to
become performed via XDG lookups using the `xdg` crate.

This allows the installed files to be discovered on non-FHS Linux, e.g.
NixOS.

Hardcoded /var/lib/ is removed entirely because
1. nothing installs to it yet (only user of new_state is cosmic_bg
   currently and it does not install to /var/lib)
2. it's intended for system states, not template for user state.
3. it's not part of XDG spec.

On Windows the known folder crate is used.

Signed-off-by: Gary Guo <gary@garyguo.net>
  • Loading branch information
nbdd0121 authored and mmstick committed Jan 24, 2024
1 parent 912e8b0 commit 3aef16b
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 59 deletions.
7 changes: 6 additions & 1 deletion cosmic-config/Cargo.toml
Expand Up @@ -10,7 +10,6 @@ macro = ["cosmic-config-derive"]
subscription = ["iced_futures"]

[dependencies]
# For redox support
zbus = { version = "3.14.1", default-features = false, optional = true }
atomicwrites = { git = "https://github.com/jackpot51/rust-atomicwrites" }
calloop = { version = "0.12.2", optional = true }
Expand All @@ -24,3 +23,9 @@ iced_futures = { path = "../iced/futures/", default-features = false, optional =
once_cell = "1.19.0"
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", branch = "cosmic-settings-daemon", optional = true }
futures-util = { version = "0.3", optional = true }

[target.'cfg(unix)'.dependencies]
xdg = "2.1"

[target.'cfg(windows)'.dependencies]
known-folders = "1.1.0"
111 changes: 53 additions & 58 deletions cosmic-config/src/lib.rs
Expand Up @@ -94,10 +94,23 @@ pub trait ConfigSet {

#[derive(Clone, Debug)]
pub struct Config {
system_path: PathBuf,
system_path: Option<PathBuf>,
user_path: PathBuf,
}

/// Check that the name is relative and doesn't contain . or ..
fn sanitize_name(name: &str) -> Result<&Path, Error> {
let path = Path::new(name);
if path
.components()
.all(|x| matches!(x, std::path::Component::Normal(_)))
{
Ok(path)
} else {
Err(Error::InvalidName(name.to_owned()))
}
}

impl Config {
/// Get the config for the libcosmic toolkit
pub fn libcosmic() -> Result<Self, Error> {
Expand All @@ -108,33 +121,34 @@ impl Config {
// Use folder at XDG config/name for config storage, return Config if successful
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
pub fn new(name: &str, version: u64) -> Result<Self, Error> {
// Get libcosmic system defaults path
//TODO: support non-UNIX OS
let cosmic_system_path = Path::new("/usr/share/cosmic");
// Append [name]/v[version]
let system_path = cosmic_system_path.join(name).join(format!("v{}", version));
// Look for [name]/v[version]
let path = sanitize_name(name)?.join(format!("v{}", version));

// Search data file, which provides default (e.g. /usr/share)
#[cfg(unix)]
let system_path = xdg::BaseDirectories::with_prefix("cosmic")
.map_err(std::io::Error::from)?
.find_data_file(&path);

#[cfg(windows)]
let system_path =
known_folders::get_known_folder_path(known_folders::KnownFolder::ProgramFilesCommon)
.map(|x| x.join("COSMIC").join(&path));

// Get libcosmic user configuration directory
let cosmic_user_path = dirs::config_dir()
.ok_or(Error::NoConfigDirectory)?
.join("cosmic");
// Append [name]/v[version]
let user_path = cosmic_user_path.join(name).join(format!("v{}", version));

// If the app paths are children of the cosmic paths
if system_path.starts_with(&cosmic_system_path) && user_path.starts_with(&cosmic_user_path)
{
// Create app user path
fs::create_dir_all(&user_path)?;
// Return Config
Ok(Self {
system_path,
user_path,
})
} else {
// Return error for invalid name
Err(Error::InvalidName(name.to_string()))
}
let user_path = cosmic_user_path.join(path);
// Create new configuration directory if not found.
fs::create_dir_all(&user_path)?;

// Return Config
Ok(Self {
system_path,
user_path,
})
}

/// Get state for the given application name and config version. State is meant to be used to
Expand All @@ -143,33 +157,22 @@ impl Config {
// Use folder at XDG config/name for config storage, return Config if successful
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
pub fn new_state(name: &str, version: u64) -> Result<Self, Error> {
// Get libcosmic system defaults path
//TODO: support non-UNIX OS
let cosmic_system_path = Path::new("/var/lib/cosmic");
// Append [name]/v[version]
let system_path = cosmic_system_path.join(name).join(format!("v{}", version));
// Look for [name]/v[version]
let path = sanitize_name(name)?.join(format!("v{}", version));

// Get libcosmic user configuration directory
// Get libcosmic user state directory
let cosmic_user_path = dirs::state_dir()
.ok_or(Error::NoConfigDirectory)?
.join("cosmic");
// Append [name]/v[version]
let user_path = cosmic_user_path.join(name).join(format!("v{}", version));

// If the app paths are children of the cosmic paths
if system_path.starts_with(&cosmic_system_path) && user_path.starts_with(&cosmic_user_path)
{
// Create app user path
fs::create_dir_all(&user_path)?;
// Return Config
Ok(Self {
system_path,
user_path,
})
} else {
// Return error for invalid name
Err(Error::InvalidName(name.to_string()))
}
let user_path = cosmic_user_path.join(path);
// Create new state directory if not found.
fs::create_dir_all(&user_path)?;

Ok(Self {
system_path: None,
user_path,
})
}

// Start a transaction (to set multiple configs at the same time)
Expand Down Expand Up @@ -238,23 +241,15 @@ impl Config {
}

fn default_path(&self, key: &str) -> Result<PathBuf, Error> {
let default_path = self.system_path.join(key);
// Ensure key path is a direct child of config directory
if default_path.parent() == Some(&self.system_path) {
Ok(default_path)
} else {
Err(Error::InvalidName(key.to_string()))
}
let Some(system_path) = self.system_path.as_ref() else {
return Err(Error::NoConfigDirectory);
};

Ok(system_path.join(sanitize_name(key)?))
}

fn key_path(&self, key: &str) -> Result<PathBuf, Error> {
let key_path = self.user_path.join(key);
// Ensure key path is a direct child of config directory
if key_path.parent() == Some(&self.user_path) {
Ok(key_path)
} else {
Err(Error::InvalidName(key.to_string()))
}
Ok(self.user_path.join(sanitize_name(key)?))
}
}

Expand Down

0 comments on commit 3aef16b

Please sign in to comment.