refactor(power): rename sys provider to power (D13)
Per D13 in the v2 plan: 'sys' collides mentally with 'systemd'. The
power & session provider becomes 'power' everywhere. Pre-v2 names
('sys', 'system', badge_sys) remain accepted as serde aliases so
existing user configs keep parsing.
Also fixes a pre-existing bug: filter.rs mapped :sys/:system/:power
prefixes to the type_id 'system', but the provider exposed itself as
Plugin('sys'). The two never matched, so prefix filtering on this
provider was a no-op. Now everything (filter table, ProviderType
FromStr, provider's own provider_type, config key) agrees on 'power'.
Files renamed: providers/system.rs -> providers/power.rs
Struct renamed: SystemProvider -> PowerProvider
type_id: 'sys' -> 'power'
Item IDs: 'sys:shutdown' -> 'power:shutdown' (frecency for the 7
power items resets after upgrade — acceptable for a v2 break)
Config key: providers.system -> providers.power (alias 'system', 'sys')
Theme color: colors.badge_sys -> colors.badge_power (alias 'badge_sys').
theme.rs emits both --owlry-badge-power and --owlry-badge-sys so
existing stylesheets keep rendering.
UI provider_meta: 'system' arm becomes 'power' | 'system'
ProviderType::FromStr: 'power', 'sys', 'system' all -> Plugin('power')
(and 'uuctl', 'systemd' -> Plugin('uuctl') as parallel hygiene)
Tests added (TDD):
- provider_type_from_str_maps_power_aliases
- provider_type_from_str_maps_systemd_aliases
- providers_config_accepts_power_key
- providers_config_accepts_pre_v2_system_alias
- theme_colors_accepts_pre_v2_badge_sys_alias
- all_item_ids_use_power_prefix (in power.rs)
239 tests pass (up from 234) with --features full. Task #8 complete.
This commit is contained in:
@@ -103,7 +103,8 @@ pub struct ThemeColors {
|
|||||||
pub badge_file: Option<String>,
|
pub badge_file: Option<String>,
|
||||||
pub badge_script: Option<String>,
|
pub badge_script: Option<String>,
|
||||||
pub badge_ssh: Option<String>,
|
pub badge_ssh: Option<String>,
|
||||||
pub badge_sys: Option<String>,
|
#[serde(alias = "badge_sys")]
|
||||||
|
pub badge_power: Option<String>,
|
||||||
pub badge_uuctl: Option<String>,
|
pub badge_uuctl: Option<String>,
|
||||||
pub badge_web: Option<String>,
|
pub badge_web: Option<String>,
|
||||||
// Widget badge colors
|
// Widget badge colors
|
||||||
@@ -168,9 +169,10 @@ pub struct ProvidersConfig {
|
|||||||
/// Enable built-in unit/currency converter (> trigger)
|
/// Enable built-in unit/currency converter (> trigger)
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub converter: bool,
|
pub converter: bool,
|
||||||
/// Enable built-in system actions (shutdown, reboot, lock, etc.)
|
/// Enable built-in power actions (shutdown, reboot, lock, etc.)
|
||||||
#[serde(default = "default_true")]
|
/// Pre-v2 config key `system` is still accepted as an alias.
|
||||||
pub system: bool,
|
#[serde(default = "default_true", alias = "system", alias = "sys")]
|
||||||
|
pub power: bool,
|
||||||
/// Enable systemd user units provider (alias: uuctl)
|
/// Enable systemd user units provider (alias: uuctl)
|
||||||
#[serde(default = "default_true", alias = "uuctl")]
|
#[serde(default = "default_true", alias = "uuctl")]
|
||||||
pub systemd: bool,
|
pub systemd: bool,
|
||||||
@@ -212,7 +214,7 @@ impl Default for ProvidersConfig {
|
|||||||
commands: true,
|
commands: true,
|
||||||
calculator: true,
|
calculator: true,
|
||||||
converter: true,
|
converter: true,
|
||||||
system: true,
|
power: true,
|
||||||
systemd: true,
|
systemd: true,
|
||||||
bookmarks: true,
|
bookmarks: true,
|
||||||
clipboard: true,
|
clipboard: true,
|
||||||
@@ -612,3 +614,38 @@ mod tests {
|
|||||||
assert!(!super::command_exists("owlry_nonexistent_binary_abc123"));
|
assert!(!super::command_exists("owlry_nonexistent_binary_abc123"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod v2_rename_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn providers_config_accepts_power_key() {
|
||||||
|
let toml = r#"[providers]
|
||||||
|
power = false
|
||||||
|
"#;
|
||||||
|
let cfg: Config = toml::from_str(toml).expect("must parse");
|
||||||
|
assert!(!cfg.providers.power);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn providers_config_accepts_pre_v2_system_alias() {
|
||||||
|
// Pre-v2 configs used `system = ...`. Serde alias keeps them working.
|
||||||
|
let toml = r#"[providers]
|
||||||
|
system = false
|
||||||
|
"#;
|
||||||
|
let cfg: Config = toml::from_str(toml).expect("must parse");
|
||||||
|
assert!(!cfg.providers.power);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn theme_colors_accepts_pre_v2_badge_sys_alias() {
|
||||||
|
// Pre-v2 stylesheets named the color `badge_sys`. Serde alias keeps
|
||||||
|
// existing user configs working.
|
||||||
|
let toml = r##"[appearance.colors]
|
||||||
|
badge_sys = "#ff8800"
|
||||||
|
"##;
|
||||||
|
let cfg: Config = toml::from_str(toml).expect("must parse");
|
||||||
|
assert_eq!(cfg.appearance.colors.badge_power.as_deref(), Some("#ff8800"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -241,9 +241,9 @@ impl ProviderFilter {
|
|||||||
("script", "scripts"),
|
("script", "scripts"),
|
||||||
("scripts", "scripts"),
|
("scripts", "scripts"),
|
||||||
("ssh", "ssh"),
|
("ssh", "ssh"),
|
||||||
("sys", "system"),
|
("sys", "power"),
|
||||||
("system", "system"),
|
("system", "power"),
|
||||||
("power", "system"),
|
("power", "power"),
|
||||||
("uuctl", "uuctl"),
|
("uuctl", "uuctl"),
|
||||||
("systemd", "uuctl"),
|
("systemd", "uuctl"),
|
||||||
("web", "websearch"),
|
("web", "websearch"),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ mod command;
|
|||||||
pub mod dmenu;
|
pub mod dmenu;
|
||||||
pub(crate) mod calculator;
|
pub(crate) mod calculator;
|
||||||
pub(crate) mod converter;
|
pub(crate) mod converter;
|
||||||
pub(crate) mod system;
|
pub(crate) mod power;
|
||||||
|
|
||||||
// Optional feature-gated providers
|
// Optional feature-gated providers
|
||||||
#[cfg(feature = "bookmarks")]
|
#[cfg(feature = "bookmarks")]
|
||||||
@@ -137,6 +137,10 @@ impl std::str::FromStr for ProviderType {
|
|||||||
"app" | "apps" | "application" | "applications" => Ok(ProviderType::Application),
|
"app" | "apps" | "application" | "applications" => Ok(ProviderType::Application),
|
||||||
"cmd" | "cmds" | "command" | "commands" => Ok(ProviderType::Command),
|
"cmd" | "cmds" | "command" | "commands" => Ok(ProviderType::Command),
|
||||||
"dmenu" => Ok(ProviderType::Dmenu),
|
"dmenu" => Ok(ProviderType::Dmenu),
|
||||||
|
// Power provider — `sys` and `system` are pre-v2 muscle-memory aliases.
|
||||||
|
"power" | "sys" | "system" => Ok(ProviderType::Plugin("power".into())),
|
||||||
|
// systemd provider — type_id is `uuctl` (CLI back-compat).
|
||||||
|
"uuctl" | "systemd" => Ok(ProviderType::Plugin("uuctl".into())),
|
||||||
other => Ok(ProviderType::Plugin(other.to_string())),
|
other => Ok(ProviderType::Plugin(other.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,9 +272,9 @@ impl ProviderManager {
|
|||||||
Box::new(CommandProvider::new()),
|
Box::new(CommandProvider::new()),
|
||||||
];
|
];
|
||||||
|
|
||||||
if cfg_snapshot.system {
|
if cfg_snapshot.power {
|
||||||
core_providers.push(Box::new(system::SystemProvider::new()));
|
core_providers.push(Box::new(power::PowerProvider::new()));
|
||||||
info!("Registered built-in system provider");
|
info!("Registered built-in power provider");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bookmarks")]
|
#[cfg(feature = "bookmarks")]
|
||||||
@@ -937,6 +941,41 @@ mod tests {
|
|||||||
assert_eq!(ProviderPosition::Widget.as_str(), "widget");
|
assert_eq!(ProviderPosition::Widget.as_str(), "widget");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn provider_type_from_str_maps_power_aliases() {
|
||||||
|
// v2 renamed `sys` -> `power`. `sys` and `system` are kept as aliases
|
||||||
|
// for muscle memory; they must all resolve to Plugin("power"), never
|
||||||
|
// Plugin("sys") or Plugin("system").
|
||||||
|
use std::str::FromStr;
|
||||||
|
assert_eq!(
|
||||||
|
ProviderType::from_str("power").unwrap(),
|
||||||
|
ProviderType::Plugin("power".into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ProviderType::from_str("sys").unwrap(),
|
||||||
|
ProviderType::Plugin("power".into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ProviderType::from_str("system").unwrap(),
|
||||||
|
ProviderType::Plugin("power".into())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn provider_type_from_str_maps_systemd_aliases() {
|
||||||
|
// systemd provider's type_id is `uuctl` (CLI back-compat from pre-v2).
|
||||||
|
// Both `uuctl` and `systemd` must resolve to Plugin("uuctl").
|
||||||
|
use std::str::FromStr;
|
||||||
|
assert_eq!(
|
||||||
|
ProviderType::from_str("uuctl").unwrap(),
|
||||||
|
ProviderType::Plugin("uuctl".into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ProviderType::from_str("systemd").unwrap(),
|
||||||
|
ProviderType::Plugin("uuctl".into())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn provider_type_from_str_accepts_plural_aliases() {
|
fn provider_type_from_str_accepts_plural_aliases() {
|
||||||
// After the demolition, FromStr accepts both singular and plural aliases.
|
// After the demolition, FromStr accepts both singular and plural aliases.
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
use super::{ItemSource, LaunchItem, Provider, ProviderType};
|
use super::{ItemSource, LaunchItem, Provider, ProviderType};
|
||||||
|
|
||||||
/// Built-in system provider. Returns a fixed set of power and session management actions.
|
/// Built-in power & session provider. Returns a fixed set of shutdown / reboot /
|
||||||
|
/// suspend / lock / logout actions.
|
||||||
///
|
///
|
||||||
/// This is a static provider — items are populated in `new()` and `refresh()` is a no-op.
|
/// Static provider — items are populated in `new()` and `refresh()` is a no-op.
|
||||||
pub(crate) struct SystemProvider {
|
///
|
||||||
|
/// type_id is `"power"`. CLI aliases `:sys` and `:system` still resolve here for
|
||||||
|
/// muscle-memory back-compat (see [`crate::providers::ProviderType::FromStr`]
|
||||||
|
/// and [`crate::filter::ProviderFilter::parse_query`]).
|
||||||
|
pub(crate) struct PowerProvider {
|
||||||
items: Vec<LaunchItem>,
|
items: Vec<LaunchItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemProvider {
|
const TYPE_ID: &str = "power";
|
||||||
|
|
||||||
|
impl PowerProvider {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let commands: &[(&str, &str, &str, &str, &str)] = &[
|
let commands: &[(&str, &str, &str, &str, &str)] = &[
|
||||||
(
|
(
|
||||||
@@ -64,14 +71,14 @@ impl SystemProvider {
|
|||||||
let items = commands
|
let items = commands
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(action_id, name, description, icon, command)| LaunchItem {
|
.map(|(action_id, name, description, icon, command)| LaunchItem {
|
||||||
id: format!("sys:{}", action_id),
|
id: format!("power:{}", action_id),
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
description: Some(description.to_string()),
|
description: Some(description.to_string()),
|
||||||
icon: Some(icon.to_string()),
|
icon: Some(icon.to_string()),
|
||||||
provider: ProviderType::Plugin("sys".into()),
|
provider: ProviderType::Plugin(TYPE_ID.into()),
|
||||||
command: command.to_string(),
|
command: command.to_string(),
|
||||||
terminal: false,
|
terminal: false,
|
||||||
tags: vec!["system".into()],
|
tags: vec!["power".into()],
|
||||||
source: ItemSource::Core,
|
source: ItemSource::Core,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -80,13 +87,13 @@ impl SystemProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Provider for SystemProvider {
|
impl Provider for PowerProvider {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"System"
|
"Power"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn provider_type(&self) -> ProviderType {
|
fn provider_type(&self) -> ProviderType {
|
||||||
ProviderType::Plugin("sys".into())
|
ProviderType::Plugin(TYPE_ID.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) {
|
||||||
@@ -96,6 +103,22 @@ impl Provider for SystemProvider {
|
|||||||
fn items(&self) -> &[LaunchItem] {
|
fn items(&self) -> &[LaunchItem] {
|
||||||
&self.items
|
&self.items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prefix(&self) -> Option<&str> {
|
||||||
|
Some(":power")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn icon(&self) -> &str {
|
||||||
|
"system-shutdown"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_label(&self) -> Option<&str> {
|
||||||
|
Some("Power")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_noun(&self) -> Option<&str> {
|
||||||
|
Some("power actions")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -104,13 +127,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn has_seven_actions() {
|
fn has_seven_actions() {
|
||||||
let provider = SystemProvider::new();
|
let provider = PowerProvider::new();
|
||||||
assert_eq!(provider.items().len(), 7);
|
assert_eq!(provider.items().len(), 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn contains_expected_action_names() {
|
fn contains_expected_action_names() {
|
||||||
let provider = SystemProvider::new();
|
let provider = PowerProvider::new();
|
||||||
let names: Vec<&str> = provider.items().iter().map(|i| i.name.as_str()).collect();
|
let names: Vec<&str> = provider.items().iter().map(|i| i.name.as_str()).collect();
|
||||||
assert!(names.contains(&"Shutdown"));
|
assert!(names.contains(&"Shutdown"));
|
||||||
assert!(names.contains(&"Reboot"));
|
assert!(names.contains(&"Reboot"));
|
||||||
@@ -119,14 +142,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn provider_type_is_sys_plugin() {
|
fn provider_type_is_power_plugin() {
|
||||||
let provider = SystemProvider::new();
|
let provider = PowerProvider::new();
|
||||||
assert_eq!(provider.provider_type(), ProviderType::Plugin("sys".into()));
|
assert_eq!(provider.provider_type(), ProviderType::Plugin("power".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shutdown_command_is_correct() {
|
fn shutdown_command_is_correct() {
|
||||||
let provider = SystemProvider::new();
|
let provider = PowerProvider::new();
|
||||||
let shutdown = provider
|
let shutdown = provider
|
||||||
.items()
|
.items()
|
||||||
.iter()
|
.iter()
|
||||||
@@ -136,14 +159,28 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn all_items_have_system_tag() {
|
fn all_items_have_power_tag() {
|
||||||
let provider = SystemProvider::new();
|
let provider = PowerProvider::new();
|
||||||
for item in provider.items() {
|
for item in provider.items() {
|
||||||
assert!(
|
assert!(
|
||||||
item.tags.contains(&"system".to_string()),
|
item.tags.contains(&"power".to_string()),
|
||||||
"item '{}' is missing 'system' tag",
|
"item '{}' is missing 'power' tag",
|
||||||
item.name
|
item.name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all_item_ids_use_power_prefix() {
|
||||||
|
// Frecency / IPC compatibility check: every item id must start with
|
||||||
|
// the canonical "power:" prefix after the v2 rename.
|
||||||
|
let provider = PowerProvider::new();
|
||||||
|
for item in provider.items() {
|
||||||
|
assert!(
|
||||||
|
item.id.starts_with("power:"),
|
||||||
|
"item id '{}' should start with 'power:'",
|
||||||
|
item.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -71,8 +71,10 @@ pub fn generate_variables_css(config: &AppearanceConfig) -> String {
|
|||||||
if let Some(ref badge_ssh) = config.colors.badge_ssh {
|
if let Some(ref badge_ssh) = config.colors.badge_ssh {
|
||||||
css.push_str(&format!(" --owlry-badge-ssh: {};\n", badge_ssh));
|
css.push_str(&format!(" --owlry-badge-ssh: {};\n", badge_ssh));
|
||||||
}
|
}
|
||||||
if let Some(ref badge_sys) = config.colors.badge_sys {
|
if let Some(ref badge_power) = config.colors.badge_power {
|
||||||
css.push_str(&format!(" --owlry-badge-sys: {};\n", badge_sys));
|
// Emit both for transition: pre-v2 stylesheets reference --owlry-badge-sys.
|
||||||
|
css.push_str(&format!(" --owlry-badge-power: {};\n", badge_power));
|
||||||
|
css.push_str(&format!(" --owlry-badge-sys: {};\n", badge_power));
|
||||||
}
|
}
|
||||||
if let Some(ref badge_uuctl) = config.colors.badge_uuctl {
|
if let Some(ref badge_uuctl) = config.colors.badge_uuctl {
|
||||||
css.push_str(&format!(" --owlry-badge-uuctl: {};\n", badge_uuctl));
|
css.push_str(&format!(" --owlry-badge-uuctl: {};\n", badge_uuctl));
|
||||||
|
|||||||
@@ -396,8 +396,8 @@ impl MainWindow {
|
|||||||
if config.converter {
|
if config.converter {
|
||||||
parts.push("> conv".to_string());
|
parts.push("> conv".to_string());
|
||||||
}
|
}
|
||||||
if config.system {
|
if config.power {
|
||||||
parts.push(":sys".to_string());
|
parts.push(":power".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
parts.join(" ")
|
parts.join(" ")
|
||||||
@@ -595,7 +595,7 @@ impl MainWindow {
|
|||||||
"pomodoro" => "pomodoro",
|
"pomodoro" => "pomodoro",
|
||||||
"scripts" => "scripts",
|
"scripts" => "scripts",
|
||||||
"ssh" => "SSH hosts",
|
"ssh" => "SSH hosts",
|
||||||
"system" => "system",
|
"power" | "system" => "power actions",
|
||||||
"uuctl" => "uuctl units",
|
"uuctl" => "uuctl units",
|
||||||
"weather" => "weather",
|
"weather" => "weather",
|
||||||
"websearch" => "web",
|
"websearch" => "web",
|
||||||
|
|||||||
@@ -86,10 +86,10 @@ fn hardcoded(provider: &ProviderType) -> ProviderMeta {
|
|||||||
css_class: "owlry-filter-ssh".to_string(),
|
css_class: "owlry-filter-ssh".to_string(),
|
||||||
search_noun: "SSH hosts".to_string(),
|
search_noun: "SSH hosts".to_string(),
|
||||||
},
|
},
|
||||||
"system" => ProviderMeta {
|
"power" | "system" => ProviderMeta {
|
||||||
tab_label: "System".to_string(),
|
tab_label: "Power".to_string(),
|
||||||
css_class: "owlry-filter-sys".to_string(),
|
css_class: "owlry-filter-power".to_string(),
|
||||||
search_noun: "system".to_string(),
|
search_noun: "power actions".to_string(),
|
||||||
},
|
},
|
||||||
"uuctl" => ProviderMeta {
|
"uuctl" => ProviderMeta {
|
||||||
tab_label: "uuctl".to_string(),
|
tab_label: "uuctl".to_string(),
|
||||||
|
|||||||
Reference in New Issue
Block a user