fixed spawn and rules not parsing/passing correctly
This commit is contained in:
		
							parent
							
								
									aaf053e070
								
							
						
					
					
						commit
						3ded017eee
					
				| 
						 | 
				
			
			@ -25,4 +25,3 @@ pretty_assertions = "1.4.0"
 | 
			
		|||
[profile.release]
 | 
			
		||||
opt-level = 3
 | 
			
		||||
strip = "debuginfo"
 | 
			
		||||
lto = "fat"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@ use thiserror::Error;
 | 
			
		|||
 | 
			
		||||
use crate::{
 | 
			
		||||
    hlwm::{
 | 
			
		||||
        self,
 | 
			
		||||
        attribute::Attribute,
 | 
			
		||||
        color::Color,
 | 
			
		||||
        command::{CommandError, HlwmCommand},
 | 
			
		||||
| 
						 | 
				
			
			@ -329,6 +330,13 @@ impl Config {
 | 
			
		|||
        .chain(self.settings_command_set())
 | 
			
		||||
        .chain(self.rule_command_set())
 | 
			
		||||
        .chain(self.service_command_set())
 | 
			
		||||
        .chain([
 | 
			
		||||
            HlwmCommand::Unlock,
 | 
			
		||||
            HlwmCommand::Spawn {
 | 
			
		||||
                executable: "hlctl".into(),
 | 
			
		||||
                args: vec!["panel".into()],
 | 
			
		||||
            },
 | 
			
		||||
        ])
 | 
			
		||||
        .collect())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -358,10 +366,14 @@ impl Config {
 | 
			
		|||
                HlwmCommand::And {
 | 
			
		||||
                    separator: Separator::Comma,
 | 
			
		||||
                    commands: vec![
 | 
			
		||||
                        HlwmCommand::Compare {
 | 
			
		||||
                            attribute: "tags.by-name.default.index".into(),
 | 
			
		||||
                            operator: Operator::Equal,
 | 
			
		||||
                            value: "tags.focus.index".into(),
 | 
			
		||||
                        HlwmCommand::Substitute {
 | 
			
		||||
                            identifier: "CURRENT_INDEX".into(),
 | 
			
		||||
                            attribute_path: "tags.focus.index".into(),
 | 
			
		||||
                            command: Box::new(HlwmCommand::Compare {
 | 
			
		||||
                                attribute: "tags.by-name.default.index".into(),
 | 
			
		||||
                                operator: Operator::Equal,
 | 
			
		||||
                                value: "CURRENT_INDEX".into(),
 | 
			
		||||
                            }),
 | 
			
		||||
                        },
 | 
			
		||||
                        HlwmCommand::UseIndex {
 | 
			
		||||
                            index: Index::Relative(1),
 | 
			
		||||
| 
						 | 
				
			
			@ -372,9 +384,8 @@ impl Config {
 | 
			
		|||
                .to_try(),
 | 
			
		||||
                HlwmCommand::MergeTag {
 | 
			
		||||
                    tag: "default".to_string(),
 | 
			
		||||
                    target: None,
 | 
			
		||||
                }
 | 
			
		||||
                .to_try(),
 | 
			
		||||
                    target: Some(hlwm::Tag::Name(self.tags.first().unwrap().to_string())),
 | 
			
		||||
                },
 | 
			
		||||
            ])
 | 
			
		||||
            .collect()
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -588,12 +588,6 @@ impl HlwmCommand {
 | 
			
		|||
impl HlwmCommand {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub(crate) fn args(&self) -> Vec<String> {
 | 
			
		||||
        if let Self::Spawn { executable, args } = self {
 | 
			
		||||
            return vec!["spawn".to_string(), executable.to_string()]
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .chain(args.into_iter().cloned())
 | 
			
		||||
                .collect();
 | 
			
		||||
        }
 | 
			
		||||
        self.to_command_string()
 | 
			
		||||
            .split('\t')
 | 
			
		||||
            .map(|a| a.to_string())
 | 
			
		||||
| 
						 | 
				
			
			@ -652,14 +646,7 @@ impl ToCommandString for HlwmCommand {
 | 
			
		|||
                window.as_ref().map(|w| w.to_string()).unwrap_or_default(),
 | 
			
		||||
            ),
 | 
			
		||||
            HlwmCommand::Spawn { executable, args } => {
 | 
			
		||||
                format!(
 | 
			
		||||
                    "{self}\t\"{executable}\"\t{}",
 | 
			
		||||
                    (&args)
 | 
			
		||||
                        .into_iter()
 | 
			
		||||
                        .map(|arg| format!("\"{arg}\""))
 | 
			
		||||
                        .collect::<Vec<String>>()
 | 
			
		||||
                        .join("\t")
 | 
			
		||||
                )
 | 
			
		||||
                format!("{self}\t{executable}\t{}", (&args).join("\t"))
 | 
			
		||||
            }
 | 
			
		||||
            HlwmCommand::GetAttr(attr) => format!("{self}\t{attr}"),
 | 
			
		||||
            HlwmCommand::SetAttr {
 | 
			
		||||
| 
						 | 
				
			
			@ -900,7 +887,7 @@ mod test {
 | 
			
		|||
                        executable: "grep".into(),
 | 
			
		||||
                        args: vec!["content".into()],
 | 
			
		||||
                    },
 | 
			
		||||
                    "spawn\t\"grep\"\t\"content\"".into(),
 | 
			
		||||
                    "spawn\tgrep\tcontent".into(),
 | 
			
		||||
                ),
 | 
			
		||||
                HlwmCommand::GetAttr(_) => (
 | 
			
		||||
                    HlwmCommand::GetAttr("my_attr".into()),
 | 
			
		||||
| 
						 | 
				
			
			@ -1175,4 +1162,16 @@ mod test {
 | 
			
		|||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn rule_fixedsize_not_omitted() {
 | 
			
		||||
        let cmd = HlwmCommand::Rule(Rule::new(
 | 
			
		||||
            Some(Condition::FixedSize),
 | 
			
		||||
            vec![Consequence::Floating(true)],
 | 
			
		||||
            None,
 | 
			
		||||
            None,
 | 
			
		||||
        ))
 | 
			
		||||
        .to_command_string();
 | 
			
		||||
        assert_eq!("rule\tfixedsize\t--floating=on", cmd);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,9 +56,6 @@ impl Client {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// Run the command and wait for it to finish.
 | 
			
		||||
    ///
 | 
			
		||||
    /// To run the command and return a handle instead of waiting,
 | 
			
		||||
    /// see [Client::spawn]
 | 
			
		||||
    pub fn execute(&self, command: HlwmCommand) -> Result<process::Output, CommandError> {
 | 
			
		||||
        let args = command.args();
 | 
			
		||||
        debug!("running command: [{}]", (&args).join(" "),);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										149
									
								
								src/hlwm/rule.rs
								
								
								
								
							
							
						
						
									
										149
									
								
								src/hlwm/rule.rs
								
								
								
								
							| 
						 | 
				
			
			@ -88,47 +88,19 @@ impl FromStr for Rule {
 | 
			
		|||
        let mut consequences = vec![];
 | 
			
		||||
        let mut label: Option<String> = None;
 | 
			
		||||
        let mut flag: Option<Flag> = None;
 | 
			
		||||
 | 
			
		||||
        for arg in (&parts)
 | 
			
		||||
        let mut args = parts
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .map(|a| a.strip_prefix("--"))
 | 
			
		||||
            .filter(|a| a.is_some())
 | 
			
		||||
        {
 | 
			
		||||
            let arg = arg.unwrap();
 | 
			
		||||
            if condition.is_none() && arg == Condition::FixedSize.to_string() {
 | 
			
		||||
                condition = Some(Condition::FixedSize);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            let parts = arg.split(['=', '~']).collect::<Vec<_>>();
 | 
			
		||||
            if parts.len() != 2 {
 | 
			
		||||
                return Err(StringParseError::InvalidLength(parts.len(), "rule=parts"));
 | 
			
		||||
            }
 | 
			
		||||
            let mut parts = parts.into_iter();
 | 
			
		||||
            let (name, value) = (parts.next().unwrap(), parts.next().unwrap());
 | 
			
		||||
            .map(|part| part.strip_prefix("--").unwrap_or(part.as_str()).to_string());
 | 
			
		||||
 | 
			
		||||
            if name == "label" && label.is_none() {
 | 
			
		||||
                label = Some(value.to_string());
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if condition.is_none() && Condition::iter().any(|prop| prop.to_string() == name) {
 | 
			
		||||
                condition = Some(match Condition::from_str(arg) {
 | 
			
		||||
                    Ok(arg) => arg,
 | 
			
		||||
                    Err(err) => panic!("<<condition>>\n\n{err}\n\n\n"),
 | 
			
		||||
                });
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if Consequence::iter().any(|cons| cons.to_string() == name) {
 | 
			
		||||
                consequences.push(match Consequence::from_str(arg) {
 | 
			
		||||
                    Ok(arg) => arg,
 | 
			
		||||
                    Err(err) => panic!("<<consequence>>\n\n{err}\n\n\n"),
 | 
			
		||||
                });
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let mut args = parts.into_iter().filter(|a| !a.starts_with("--"));
 | 
			
		||||
        while let Some(arg) = args.next() {
 | 
			
		||||
            if label.is_none() {
 | 
			
		||||
                if let Some((name, value)) = arg.split_once('=') {
 | 
			
		||||
                    if name.trim() == "label" {
 | 
			
		||||
                        label = Some(value.trim().to_string());
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if flag.is_none() {
 | 
			
		||||
                if let Ok(flag_res) = Flag::from_str(&arg) {
 | 
			
		||||
                    flag = Some(flag_res);
 | 
			
		||||
| 
						 | 
				
			
			@ -325,7 +297,9 @@ impl FromStr for Condition {
 | 
			
		|||
    fn from_str(s: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
        // Handle the case for fixedsize first so that we can treat the rest
 | 
			
		||||
        // of the variants as having arguments
 | 
			
		||||
        if s == Self::FixedSize.to_string() {
 | 
			
		||||
        //
 | 
			
		||||
        // Also, sometimes `fixedsize=0` could come back from `herbstclient list_rules`
 | 
			
		||||
        if s.starts_with(&Self::FixedSize.to_string()) {
 | 
			
		||||
            return Ok(Self::FixedSize);
 | 
			
		||||
        }
 | 
			
		||||
        let ((name, match_val), match_char) =
 | 
			
		||||
| 
						 | 
				
			
			@ -669,8 +643,13 @@ impl FromStr for Unrule {
 | 
			
		|||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use std::str::FromStr;
 | 
			
		||||
 | 
			
		||||
    use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
    use crate::hlwm::rule::FloatPlacement;
 | 
			
		||||
    use pretty_assertions::assert_eq;
 | 
			
		||||
 | 
			
		||||
    use super::{Condition, Consequence, Flag, Rule, RuleOperator};
 | 
			
		||||
 | 
			
		||||
    #[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
| 
						 | 
				
			
			@ -680,15 +659,23 @@ mod test {
 | 
			
		|||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn rule_serialize_deserialize() {
 | 
			
		||||
        for rule in [Rule::new(
 | 
			
		||||
            Some(Condition::Class {
 | 
			
		||||
                operator: RuleOperator::Equal,
 | 
			
		||||
                value: "Netscape".into(),
 | 
			
		||||
            }),
 | 
			
		||||
            vec![Consequence::Tag(1.to_string())],
 | 
			
		||||
            Some("label".into()),
 | 
			
		||||
            Some(Flag::Not),
 | 
			
		||||
        )] {
 | 
			
		||||
        for rule in [
 | 
			
		||||
            Rule::new(
 | 
			
		||||
                Some(Condition::Class {
 | 
			
		||||
                    operator: RuleOperator::Equal,
 | 
			
		||||
                    value: "Netscape".into(),
 | 
			
		||||
                }),
 | 
			
		||||
                vec![Consequence::Tag(1.to_string())],
 | 
			
		||||
                Some("label".into()),
 | 
			
		||||
                Some(Flag::Not),
 | 
			
		||||
            ),
 | 
			
		||||
            Rule::new(
 | 
			
		||||
                Some(Condition::FixedSize),
 | 
			
		||||
                vec![Consequence::Floating(true)],
 | 
			
		||||
                None,
 | 
			
		||||
                None,
 | 
			
		||||
            ),
 | 
			
		||||
        ] {
 | 
			
		||||
            let serialized = toml::to_string_pretty(&RuleWrapper { rule: rule.clone() })
 | 
			
		||||
                .expect("serializing rule");
 | 
			
		||||
            let deserialized: RuleWrapper =
 | 
			
		||||
| 
						 | 
				
			
			@ -696,4 +683,70 @@ mod test {
 | 
			
		|||
            assert_eq!(rule, deserialized.rule);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn rules_from_list_rules_parse() {
 | 
			
		||||
        const INPUT: &str = r#"label=0	windowtype~_NET_WM_WINDOW_TYPE_(DIALOG|UTILITY|SPLASH)	floating=on
 | 
			
		||||
label=1	focus=on
 | 
			
		||||
label=2	floatplacement=smart
 | 
			
		||||
label=3	windowtype~_NET_WM_WINDOW_TYPE_(DIALOG)	floating=on
 | 
			
		||||
label=4	windowtype~_NET_WM_WINDOW_TYPE_(NOTIFICATION|DOCK|DESKTOP)	manage=off
 | 
			
		||||
label=5	fixedsize=0	floating=true"#;
 | 
			
		||||
 | 
			
		||||
        let parsed = INPUT
 | 
			
		||||
            .split('\n')
 | 
			
		||||
            .map(|l| Rule::from_str(l))
 | 
			
		||||
            .collect::<Result<Vec<_>, _>>()
 | 
			
		||||
            .expect("parsing error");
 | 
			
		||||
 | 
			
		||||
        let expected = [
 | 
			
		||||
            Rule::new(
 | 
			
		||||
                Some(Condition::WindowType {
 | 
			
		||||
                    operator: RuleOperator::Regex,
 | 
			
		||||
                    value: "_NET_WM_WINDOW_TYPE_(DIALOG|UTILITY|SPLASH)".into(),
 | 
			
		||||
                }),
 | 
			
		||||
                vec![Consequence::Floating(true)],
 | 
			
		||||
                Some("0".into()),
 | 
			
		||||
                None,
 | 
			
		||||
            ),
 | 
			
		||||
            Rule::new(None, vec![Consequence::Focus(true)], Some("1".into()), None),
 | 
			
		||||
            Rule::new(
 | 
			
		||||
                None,
 | 
			
		||||
                vec![Consequence::FloatPlacement(FloatPlacement::Smart)],
 | 
			
		||||
                Some("2".into()),
 | 
			
		||||
                None,
 | 
			
		||||
            ),
 | 
			
		||||
            Rule::new(
 | 
			
		||||
                Some(Condition::WindowType {
 | 
			
		||||
                    operator: RuleOperator::Regex,
 | 
			
		||||
                    value: "_NET_WM_WINDOW_TYPE_(DIALOG)".into(),
 | 
			
		||||
                }),
 | 
			
		||||
                vec![Consequence::Floating(true)],
 | 
			
		||||
                Some("3".to_string()),
 | 
			
		||||
                None,
 | 
			
		||||
            ),
 | 
			
		||||
            Rule::new(
 | 
			
		||||
                Some(Condition::WindowType {
 | 
			
		||||
                    operator: RuleOperator::Regex,
 | 
			
		||||
                    value: "_NET_WM_WINDOW_TYPE_(NOTIFICATION|DOCK|DESKTOP)".into(),
 | 
			
		||||
                }),
 | 
			
		||||
                vec![Consequence::Manage(false)],
 | 
			
		||||
                Some("4".to_string()),
 | 
			
		||||
                None,
 | 
			
		||||
            ),
 | 
			
		||||
            Rule::new(
 | 
			
		||||
                Some(Condition::FixedSize),
 | 
			
		||||
                vec![Consequence::Floating(true)],
 | 
			
		||||
                Some("5".into()),
 | 
			
		||||
                None,
 | 
			
		||||
            ),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        parsed
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .zip(expected)
 | 
			
		||||
            .for_each(|(parsed, expected)| {
 | 
			
		||||
                assert_eq!(expected, parsed);
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue