pub struct FMBuilder<'a> { /* private fields */ }Expand description
Build up a FMatcher allowing the setting of options.
use {fm::FMBuilder, regex::Regex};
let ptn_re = Regex::new(r"\$.+?\b").unwrap();
let text_re = Regex::new(r".+?\b").unwrap();
let matcher = FMBuilder::new("$1 $1")
.unwrap()
.name_matcher(ptn_re, text_re)
.build()
.unwrap();
assert!(matcher.matches("a a").is_ok());
assert!(matcher.matches("a b").is_err());Implementations§
Source§impl<'a> FMBuilder<'a>
impl<'a> FMBuilder<'a>
Sourcepub fn new(ptn: &'a str) -> Result<Self, Box<dyn Error>>
pub fn new(ptn: &'a str) -> Result<Self, Box<dyn Error>>
Create a new FMBuilder with default options.
pub fn output_formatter(self, output_formatter: OutputFormatter) -> Self
Sourcepub fn name_matcher(self, ptn_re: Regex, text_re: Regex) -> Self
pub fn name_matcher(self, ptn_re: Regex, text_re: Regex) -> Self
Add a name matcher (ptn_re, text_re). Name matchers allow you to ensure that different
parts of the text match without specifying precisely what they match. For example, if you
have output where you want to ensure that two locations always match the same name, but the
name is non-deterministic you can allow the use of $ wildcards in your pattern:
use {fm::FMBuilder, regex::Regex};
let ptn_re = Regex::new(r"\$[1]+?\b").unwrap();
let text_re = Regex::new(r"[a-b]+?\b").unwrap();
let matcher = FMBuilder::new("$1 b $1")
.unwrap()
.name_matcher(ptn_re, text_re)
.build()
.unwrap();
assert!(matcher.matches("a b a").is_ok());
assert!(matcher.matches("a b b").is_err());Note that if a line in the pattern uses name matching, it can only use the wildcard
operator at the end of the line (so, for the above name matcher, $1... is allowed but
...$1 or ...$1... is not allowed). Invalid combinations of wildcards and name matching
are caught when a pattern is built.
Multiple name matchers are allowed: they are matched in the order they were added to
FMBuilder.
Sourcepub fn name_matcher_ignore(self, ptn_re: Regex, text_re: Regex) -> Self
pub fn name_matcher_ignore(self, ptn_re: Regex, text_re: Regex) -> Self
Add a name matcher that has the same semantics as a name matcher added with Self::name_matcher but which ignores the contents of the matched text. This can be used to ensure that the text follows a certain “shape” but without worrying about either a) the concrete value b) having to generate fresh names for each such instance. This can be combined with “normal” name matching, as in the following example:
use {fm::FMBuilder, regex::Regex};
let ptn_re = Regex::new(r"\$[1]+?\b").unwrap();
let ptn_ignore_re = Regex::new(r"\$_\b").unwrap();
let text_re = Regex::new(r"[a-b]+?\b").unwrap();
let matcher = FMBuilder::new("$1 $_ $1 $_")
.unwrap()
.name_matcher(ptn_re, text_re.clone())
.name_matcher_ignore(ptn_ignore_re, text_re)
.build()
.unwrap();
assert!(matcher.matches("a b a a").is_ok());
assert!(matcher.matches("a b b a").is_err());As this shows, once $1 has matched “a”, all further instances of $1 must also match
“a”, but _ can match different values at different points.
Sourcepub fn name_matching_validator<F>(self, f: F) -> Self
pub fn name_matching_validator<F>(self, f: F) -> Self
Add a name matching validator: this takes a HashMap of (key, value) pairs and must
return true if this is a valid set of pairs or false otherwise. Name matching validators
allow you to customise what names are valid matches. For example, if you want distinct
names to match distinct values you can add a name matching validator which converts values
to a [HashSet] and fails if the resulting set has fewer entries than the input hashmap:
use {fm::FMBuilder, regex::Regex};
use std::collections::HashSet;
let ptn_re = Regex::new(r"\$[0-9]+?\b").unwrap();
let text_re = Regex::new(r"[a-b]+?\b").unwrap();
let matcher = FMBuilder::new("$1 $2")
.unwrap()
.name_matcher(ptn_re, text_re)
.name_matching_validator(|names| {
names.values().collect::<HashSet<_>>().len() == names.len()
})
.build()
.unwrap();
assert!(matcher.matches("a b").is_ok());
assert!(matcher.matches("a a").is_err());As this shows, since $1 matches a, the name matching validator returns false if $2
also matches a.
Note that name matching validators must not confuse “doesn’t match” with “is an error”: just because text doesn’t match at a given point does not mean there is an error.
Name matching validators are called frequently, so their performance can be an issue if you have large inputs. You may need to benchmark carefully.
Multiple name matching validators are allowed: they are matched in the order they were
added to FMBuilder.
Sourcepub fn trim_whitespace(self, yes: bool) -> Self
pub fn trim_whitespace(self, yes: bool) -> Self
If yes, then:
- Blank lines at the start/end of the pattern and text are ignored.
- Leading/trailing whitespace is ignored on each line of the pattern and text.
Defaults to “yes”.