feat: add k-launcher-config crate for configuration management and integrate with existing components

This commit is contained in:
2026-03-15 18:20:15 +01:00
parent bc7c896519
commit 3098a4be7c
15 changed files with 494 additions and 46 deletions

View File

@@ -0,0 +1,13 @@
[package]
name = "k-launcher-config"
version = "0.1.0"
edition = "2024"
[lib]
name = "k_launcher_config"
path = "src/lib.rs"
[dependencies]
dirs = { workspace = true }
serde = { workspace = true }
toml = { workspace = true }

View File

@@ -0,0 +1,193 @@
use serde::Deserialize;
// RGBA: [r, g, b, a] where r/g/b are 0255 as f32, a is 0.01.0
pub type Rgba = [f32; 4];
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct Config {
pub window: WindowCfg,
pub appearance: AppearanceCfg,
pub search: SearchCfg,
pub plugins: PluginsCfg,
}
impl Default for Config {
fn default() -> Self {
Self {
window: WindowCfg::default(),
appearance: AppearanceCfg::default(),
search: SearchCfg::default(),
plugins: PluginsCfg::default(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct WindowCfg {
pub width: f32,
pub height: f32,
pub decorations: bool,
pub transparent: bool,
pub resizable: bool,
}
impl Default for WindowCfg {
fn default() -> Self {
Self {
width: 600.0,
height: 400.0,
decorations: false,
transparent: true,
resizable: false,
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct AppearanceCfg {
pub background_rgba: Rgba,
pub border_rgba: Rgba,
pub border_width: f32,
pub border_radius: f32,
pub search_font_size: f32,
pub title_size: f32,
pub desc_size: f32,
pub row_radius: f32,
pub placeholder: String,
}
impl Default for AppearanceCfg {
fn default() -> Self {
Self {
background_rgba: [20.0, 20.0, 30.0, 0.9],
border_rgba: [0.0, 183.0, 235.0, 1.0],
border_width: 1.0,
border_radius: 8.0,
search_font_size: 18.0,
title_size: 15.0,
desc_size: 12.0,
row_radius: 4.0,
placeholder: "Search...".to_string(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct SearchCfg {
pub max_results: usize,
}
impl Default for SearchCfg {
fn default() -> Self {
Self { max_results: 8 }
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct PluginsCfg {
pub calc: bool,
pub cmd: bool,
pub files: bool,
pub apps: bool,
}
impl Default for PluginsCfg {
fn default() -> Self {
Self {
calc: true,
cmd: true,
files: true,
apps: true,
}
}
}
pub fn load() -> Config {
let path = dirs::config_dir()
.map(|d| d.join("k-launcher").join("config.toml"));
let Some(path) = path else {
return Config::default();
};
let Ok(content) = std::fs::read_to_string(&path) else {
return Config::default();
};
toml::from_str(&content).unwrap_or_default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config_has_sane_values() {
let cfg = Config::default();
assert_eq!(cfg.search.max_results, 8);
assert_eq!(cfg.window.width, 600.0);
assert_eq!(cfg.window.height, 400.0);
assert!(!cfg.window.decorations);
assert!(cfg.window.transparent);
assert!(!cfg.window.resizable);
assert!(cfg.plugins.calc);
assert!(cfg.plugins.apps);
assert_eq!(cfg.appearance.search_font_size, 18.0);
assert_eq!(cfg.appearance.placeholder, "Search...");
}
#[test]
fn parse_partial_toml_uses_defaults() {
let toml = "[search]\nmax_results = 5\n";
let cfg: Config = toml::from_str(toml).unwrap();
assert_eq!(cfg.search.max_results, 5);
assert_eq!(cfg.window.width, 600.0);
assert_eq!(cfg.appearance.search_font_size, 18.0);
assert!(cfg.plugins.apps);
}
#[test]
fn parse_full_toml_roundtrip() {
let toml = r#"
[window]
width = 800.0
height = 500.0
decorations = true
transparent = false
resizable = true
[appearance]
background_rgba = [10.0, 10.0, 20.0, 0.8]
border_rgba = [100.0, 200.0, 255.0, 1.0]
border_width = 2.0
border_radius = 12.0
search_font_size = 20.0
title_size = 16.0
desc_size = 13.0
row_radius = 6.0
placeholder = "Type here..."
[search]
max_results = 12
[plugins]
calc = false
cmd = true
files = false
apps = true
"#;
let cfg: Config = toml::from_str(toml).unwrap();
assert_eq!(cfg.window.width, 800.0);
assert_eq!(cfg.window.height, 500.0);
assert!(cfg.window.decorations);
assert!(!cfg.window.transparent);
assert_eq!(cfg.appearance.background_rgba, [10.0, 10.0, 20.0, 0.8]);
assert_eq!(cfg.appearance.search_font_size, 20.0);
assert_eq!(cfg.appearance.placeholder, "Type here...");
assert_eq!(cfg.search.max_results, 12);
assert!(!cfg.plugins.calc);
assert!(!cfg.plugins.files);
}
}