use std::{borrow::BorrowMut, str::FromStr}; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; use super::{ parser::{ArgParser, FromCommandArgs, ParseError}, window::Window, Monitor, TagSelect, ToCommandString, }; #[derive(Debug, Clone, Serialize, Deserialize, strum::Display, strum::EnumIter, PartialEq)] #[strum(serialize_all = "snake_case")] pub enum Hook { /// The attribute `path` was changed from `old` to `new`. /// Requires that the attribute `path` has been passed to the [HlwmCommand::Watch] command before. AttributeChanged { path: String, old: String, new: String, }, /// The fullscreen state of `window` was changed to [on|off]. Fullscreen { on: bool, window: Window, }, /// The `tag` was selected on `monitor`. TagChanged { tag: String, monitor: Monitor, }, /// The `window` with title `title` was focused FocusChanged { window: Window, title: String, }, WindowTitleChanged { window: Window, title: String, }, /// The flags (i.e. urgent or filled state) have been changed. TagFlags, TagAdded(TagSelect), TagRenamed { old: String, new: String, }, Urgent { on: bool, window: Window, }, /// A `window` appeared which triggered a rule/hook. Rule { hook: String, window: Window, }, /// Tells a panel to quit. The default panel.sh quits on this hook. Many scripts are using this hook. QuitPanel, /// Tells all daemons that the autostart file is reloaded — and tells them to quit. /// This hook **should** be emitted in the first line of every autostart file. Reload, } impl FromCommandArgs for Hook { fn from_command_args, I: IntoIterator>( cmd: &str, args: I, ) -> Result { let mut command = Self::iter() .find(|c: &Hook| c.to_string() == cmd) .ok_or(ParseError::InvalidCommand(cmd.to_string()))?; let x = args.into_iter().map(|i| i.into()).collect::>(); let mut parser = ArgParser::from_strings(x.into_iter()); match command.borrow_mut() { Hook::QuitPanel | Hook::Reload | Hook::TagFlags => (), Hook::AttributeChanged { path, old, new } => { *path = parser.must_string("attribute_changed(path)")?; *old = parser.must_string("attribute_changed(old)")?; *new = parser.must_string("attribute_changed(new)")?; } Hook::Fullscreen { on, window } => { *on = parser.next_from_str("fullscreen(on)")?; *window = parser.next_from_str("fullscreen(window)")?; } Hook::TagChanged { tag, monitor } => { *tag = parser.must_string("tag_changed(tag)")?; *monitor = parser.next_from_str("tag_changed(monitor)")?; } Hook::FocusChanged { window, title } | Hook::WindowTitleChanged { window, title } => { *window = parser.next_from_str(&format!("{cmd}(window)"))?; *title = parser.must_string(&format!("{cmd}(title)"))?; } Hook::TagAdded(tag) => { *tag = TagSelect::from_str(&parser.must_string(cmd)?).expect("infallible") } Hook::TagRenamed { old, new } => { *old = parser.must_string("tag_renamed(old)")?; *new = parser.must_string("tag_renamed(new)")?; } Hook::Urgent { on, window } => { *on = parser.next_from_str("urgent(on)")?; *window = parser.next_from_str("urgent(window)")?; } Hook::Rule { hook, window } => { *hook = parser.must_string("rule(hook)")?; *window = parser.next_from_str("rule(window)")?; } } Ok(command) } } impl Default for Hook { fn default() -> Self { Self::Reload } } impl ToCommandString for Hook { fn to_command_string(&self) -> String { [self.to_string()] .into_iter() .chain(match self { Hook::AttributeChanged { path, old, new } => { vec![path.to_string(), old.to_string(), new.to_string()].into_iter() } Hook::Fullscreen { on, window } => { vec![on.to_string(), window.to_string()].into_iter() } Hook::TagChanged { tag, monitor } => { vec![tag.to_string(), monitor.to_string()].into_iter() } Hook::WindowTitleChanged { window, title } | Hook::FocusChanged { window, title } => { vec![window.to_string(), title.to_string()].into_iter() } Hook::QuitPanel | Hook::Reload | Hook::TagFlags => vec![].into_iter(), Hook::TagAdded(tag) => vec![tag.to_string()].into_iter(), Hook::TagRenamed { old, new } => vec![old.to_string(), new.to_string()].into_iter(), Hook::Urgent { on, window } => vec![on.to_string(), window.to_string()].into_iter(), Hook::Rule { hook, window } => { vec![hook.to_string(), window.to_string()].into_iter() } }) .collect::>() .join("\t") } }