From 34ba1e39e4fc0ba5a410d2ba8f8c267a56d72741 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sun, 7 Nov 2021 15:29:45 +0100 Subject: [PATCH] feat(config): refactor YAML match parsing to account for new form syntax #856 --- espanso-config/src/legacy/mod.rs | 2 +- .../src/matches/group/loader/yaml/mod.rs | 174 ++++++++++++++++-- 2 files changed, 160 insertions(+), 16 deletions(-) diff --git a/espanso-config/src/legacy/mod.rs b/espanso-config/src/legacy/mod.rs index 674b08a..7c3126e 100644 --- a/espanso-config/src/legacy/mod.rs +++ b/espanso-config/src/legacy/mod.rs @@ -102,7 +102,7 @@ fn split_config(config: LegacyConfig) -> (LegacyInteropConfig, LegacyMatchGroup) .iter() .filter_map(|var| { let m: YAMLMatch = serde_yaml::from_value(var.clone()).ok()?; - let (m, warnings) = try_convert_into_match(m).ok()?; + let (m, warnings) = try_convert_into_match(m, true).ok()?; warnings.into_iter().for_each(|warning| { warn!("{}", warning); }); diff --git a/espanso-config/src/matches/group/loader/yaml/mod.rs b/espanso-config/src/matches/group/loader/yaml/mod.rs index 1d3a61c..6e1e3d1 100644 --- a/espanso-config/src/matches/group/loader/yaml/mod.rs +++ b/espanso-config/src/matches/group/loader/yaml/mod.rs @@ -43,6 +43,8 @@ mod util; lazy_static! { static ref VAR_REGEX: Regex = Regex::new("\\{\\{\\s*(\\w+)(\\.\\w+)?\\s*\\}\\}").unwrap(); + static ref FORM_CONTROL_REGEX: Regex = + Regex::new("\\[\\[\\s*(\\w+)(\\.\\w+)?\\s*\\]\\]").unwrap(); } // Create an alias to make the meaning more explicit @@ -85,7 +87,7 @@ impl Importer for YAMLImporter { let mut matches = Vec::new(); for yaml_match in yaml_group.matches.as_ref().cloned().unwrap_or_default() { - match try_convert_into_match(yaml_match) { + match try_convert_into_match(yaml_match, false) { Ok((m, warnings)) => { matches.push(m); non_fatal_errors.extend(warnings.into_iter().map(ErrorRecord::warn)); @@ -119,7 +121,10 @@ impl Importer for YAMLImporter { } } -pub fn try_convert_into_match(yaml_match: YAMLMatch) -> Result<(Match, Vec)> { +pub fn try_convert_into_match( + yaml_match: YAMLMatch, + use_compatibility_mode: bool, +) -> Result<(Match, Vec)> { let mut warnings = Vec::new(); if yaml_match.uppercase_style.is_some() && yaml_match.propagate_case.is_none() { @@ -216,21 +221,45 @@ pub fn try_convert_into_match(yaml_match: YAMLMatch) -> Result<(Match, Vec Result<(Match, Vec Result<(Match, Vec)> { + fn create_match_with_warnings( + yaml: &str, + use_compatibility_mode: bool, + ) -> Result<(Match, Vec)> { let yaml_match: YAMLMatch = serde_yaml::from_str(yaml)?; - let (mut m, warnings) = try_convert_into_match(yaml_match)?; + let (mut m, warnings) = try_convert_into_match(yaml_match, use_compatibility_mode)?; // Reset the IDs to correctly compare them m.id = 0; @@ -309,7 +341,7 @@ mod tests { } fn create_match(yaml: &str) -> Result { - let (m, warnings) = create_match_with_warnings(yaml)?; + let (m, warnings) = create_match_with_warnings(yaml, false)?; if !warnings.is_empty() { panic!("warnings were detected but not handled: {:?}", warnings); } @@ -529,6 +561,7 @@ mod tests { replace: "world" uppercase_style: "capitalize" "#, + false, ) .unwrap(); assert_eq!( @@ -545,6 +578,7 @@ mod tests { uppercase_style: "invalid" propagate_case: true "#, + false, ) .unwrap(); assert_eq!( @@ -554,6 +588,116 @@ mod tests { assert_eq!(warnings.len(), 1); } + #[test] + fn form_maps_correctly() { + let mut params = Params::new(); + params.insert( + "layout".to_string(), + Value::String("Hi [[name]]!".to_string()), + ); + + assert_eq!( + create_match( + r#" + trigger: "Hello" + form: "Hi [[name]]!" + "# + ) + .unwrap(), + Match { + cause: MatchCause::Trigger(TriggerCause { + triggers: vec!["Hello".to_string()], + ..Default::default() + }), + effect: MatchEffect::Text(TextEffect { + replace: "Hi {{form1.name}}!".to_string(), + vars: vec![Variable { + id: 0, + name: "form1".to_string(), + var_type: "form".to_string(), + params + }], + ..Default::default() + }), + ..Default::default() + } + ) + } + + #[test] + fn form_maps_correctly_with_variable_injection() { + let mut params = Params::new(); + params.insert( + "layout".to_string(), + Value::String("Hi [[name]]! {{signature}}".to_string()), + ); + + assert_eq!( + create_match( + r#" + trigger: "Hello" + form: "Hi [[name]]! {{signature}}" + "# + ) + .unwrap(), + Match { + cause: MatchCause::Trigger(TriggerCause { + triggers: vec!["Hello".to_string()], + ..Default::default() + }), + effect: MatchEffect::Text(TextEffect { + replace: "Hi {{form1.name}}! {{signature}}".to_string(), + vars: vec![Variable { + id: 0, + name: "form1".to_string(), + var_type: "form".to_string(), + params + }], + ..Default::default() + }), + ..Default::default() + } + ) + } + + #[test] + fn form_maps_correctly_legacy_format() { + let mut params = Params::new(); + params.insert( + "layout".to_string(), + Value::String("Hi [[name]]!".to_string()), + ); + + assert_eq!( + create_match_with_warnings( + r#" + trigger: "Hello" + form: "Hi {{name}}!" + "#, + true + ) + .unwrap() + .0, + Match { + cause: MatchCause::Trigger(TriggerCause { + triggers: vec!["Hello".to_string()], + ..Default::default() + }), + effect: MatchEffect::Text(TextEffect { + replace: "Hi {{form1.name}}!".to_string(), + vars: vec![Variable { + id: 0, + name: "form1".to_string(), + var_type: "form".to_string(), + params + }], + ..Default::default() + }), + ..Default::default() + } + ) + } + #[test] fn vars_maps_correctly() { let mut params = Params::new();