use std::collections::HashMap;
use std::sync::LazyLock;
const DEFAULT_WIDGETS_ID: &str = include_str!("../widgets-id");
static DEFAULT_WIDGETS_TABLE: LazyLock<HashMap<String, u64>> =
LazyLock::new(|| Config::parse_widgets_id(DEFAULT_WIDGETS_ID));
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum RestartBehavior {
#[default]
Never,
Always,
Prompt,
}
#[derive(Debug, Clone, Default)]
pub struct Config {
pub system: bool,
pub excluded_packages: Vec<String>,
pub widgets_id_table: HashMap<String, u64>,
pub restart: RestartBehavior,
pub auto_confirm: bool,
pub threads: Option<usize>,
pub skip_plasma_detection: bool,
pub inhibit_idle: bool,
}
impl Config {
pub fn new() -> Self {
Self {
widgets_id_table: DEFAULT_WIDGETS_TABLE.clone(),
inhibit_idle: true,
..Default::default()
}
}
pub fn with_system(mut self, system: bool) -> Self {
self.system = system;
self
}
pub fn with_widgets_id_table(mut self, table: HashMap<String, u64>) -> Self {
self.widgets_id_table = table;
self
}
pub fn with_excluded_packages(mut self, packages: Vec<String>) -> Self {
self.excluded_packages = packages;
self
}
pub fn with_restart(mut self, restart: RestartBehavior) -> Self {
self.restart = restart;
self
}
pub fn parse_widgets_id(content: &str) -> HashMap<String, u64> {
let mut table = HashMap::with_capacity(content.lines().count());
for line in content.lines() {
if let Some((id, name)) = parse_widgets_id_line(line) {
table.insert(name, id);
}
}
table
}
pub fn with_auto_confirm(mut self, auto_confirm: bool) -> Self {
self.auto_confirm = auto_confirm;
self
}
pub fn with_threads(mut self, threads: usize) -> Self {
self.threads = Some(threads);
self
}
pub fn with_skip_plasma_detection(mut self, skip: bool) -> Self {
self.skip_plasma_detection = skip;
self
}
pub fn with_inhibit_idle(mut self, inhibit: bool) -> Self {
self.inhibit_idle = inhibit;
self
}
}
pub(crate) fn parse_widgets_id_line(line: &str) -> Option<(u64, String)> {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
return None;
}
let mut parts = line.splitn(2, ' ');
let id = parts.next()?.parse::<u64>().ok()?;
let name = parts.next()?.trim();
if name.is_empty() {
return None;
}
Some((id, name.to_string()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_widgets_id_line_valid() {
let line = "998890 com.bxabi.bumblebee-indicator";
let result = parse_widgets_id_line(line);
assert_eq!(
result,
Some((998890, "com.bxabi.bumblebee-indicator".to_string()))
);
}
#[test]
fn test_parse_widgets_id_line_comment() {
let line = "#2182964 adhe.menu.11 #Ignored, not a unique ID";
let result = parse_widgets_id_line(line);
assert_eq!(result, None);
}
#[test]
fn test_parse_widgets_id_line_empty() {
let line = "";
let result = parse_widgets_id_line(line);
assert_eq!(result, None);
}
#[test]
fn test_parse_widgets_id_table() {
let content = "998890 com.bxabi.bumblebee-indicator\n\
998913 org.kde.plasma.awesomewidget\n\
# Comment line\n\
1155946 com.dschopf.plasma.qalculate\n";
let table = Config::parse_widgets_id(content);
assert_eq!(table.len(), 3);
assert_eq!(table.get("com.bxabi.bumblebee-indicator"), Some(&998890));
assert_eq!(table.get("org.kde.plasma.awesomewidget"), Some(&998913));
assert_eq!(table.get("com.dschopf.plasma.qalculate"), Some(&1155946));
}
#[test]
fn test_default_widgets_id_table_loads() {
let config = Config::new();
assert!(
!config.widgets_id_table.is_empty(),
"Default widgets_id_table should not be empty"
);
assert_eq!(
config.widgets_id_table.get("com.bxabi.bumblebee-indicator"),
Some(&998890)
);
assert_eq!(
config.widgets_id_table.get("org.kde.plasma.awesomewidget"),
Some(&998913)
);
}
#[test]
fn test_config_with_custom_widgets_id_table() {
let mut custom_table = HashMap::new();
custom_table.insert("custom.widget".to_string(), 123456);
let config = Config::new().with_widgets_id_table(custom_table.clone());
assert_eq!(config.widgets_id_table, custom_table);
assert_eq!(config.widgets_id_table.get("custom.widget"), Some(&123456));
assert_eq!(
config.widgets_id_table.get("com.bxabi.bumblebee-indicator"),
None
);
}
#[test]
fn default_widgets_table_is_cached() {
let config1 = Config::new();
let config2 = Config::new();
assert_eq!(config1.widgets_id_table, config2.widgets_id_table);
assert!(!config1.widgets_id_table.is_empty());
}
#[test]
fn cached_table_matches_fresh_parse() {
let cached = &*DEFAULT_WIDGETS_TABLE;
let fresh = Config::parse_widgets_id(DEFAULT_WIDGETS_ID);
assert_eq!(cached, &fresh);
}
}