diff --git a/src/config.rs b/src/config.rs index 5757730..ffbe98b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use anyhow::{bail, Result}; use regex::Regex; use serde_derive::Deserialize; use tracing::trace; @@ -66,6 +67,36 @@ pub trait Act { fn apply_action(&self, input: &str) -> String; } +impl Replacements<'_> { + pub fn validate(&self) -> Result<()> { + for subst in self.substitutors.iter() { + match &subst.matcher { + MatcherType::Single(matcher) => match matcher { + Matcher::Regex { pattern } => { + if let Err(e) = Regex::from_str(pattern) { + bail!(e); + } + } + _ => {} + }, + MatcherType::Multiple(matchers) => { + for matcher in matchers.iter() { + match matcher { + Matcher::Regex { pattern } => { + if let Err(e) = Regex::from_str(pattern) { + bail!(e); + } + } + _ => {} + } + } + } + } + } + Ok(()) + } +} + impl Match for Matcher<'_> { fn check_match(&self, string: &str) -> bool { trace!(?self, ?string, "Checking for match"); diff --git a/src/test.rs b/src/test.rs index 1c5f0ec..09cb8bc 100644 --- a/src/test.rs +++ b/src/test.rs @@ -72,3 +72,27 @@ fn parse_with_single_matcher() { assert!(matches!(subst.matcher, MatcherType::Single(_))); assert!(matches!(subst.action, Action::Prefix { .. })); } + +#[assay] +fn config_validation_success() { + let config = r#" +[[substitutor]] +name = "vxTwitter" +matcher = { regex = { pattern = "^https://(?P(?:mobile.)?twitter.com)/.*/status/[0-9]+.*" } } +action = { replace = { from = "twitter.com", to = "vxtwitter.com" } } + "#; + let config: Replacements<'_> = toml::from_str(config)?; + assert!(matches!(config.validate(), Ok(_))); +} + +#[assay] +fn config_validation_failure() { + let config = r#" +[[substitutor]] +name = "vxTwitter" +matcher = { regex = { pattern = "^https://(?P<>(?:mobile.)?twitter.com)/.*/status/[0-9]+.*" } } +action = { replace = { from = "twitter.com", to = "vxtwitter.com" } } + "#; + let config: Replacements<'_> = toml::from_str(config)?; + assert!(matches!(config.validate(), Err(_))); +}