FMBuilder

Struct FMBuilder 

Source
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>

Source

pub fn new(ptn: &'a str) -> Result<Self, Box<dyn Error>>

Create a new FMBuilder with default options.

Source

pub fn output_formatter(self, output_formatter: OutputFormatter) -> Self

Source

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.

Source

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.

Source

pub fn name_matching_validator<F>(self, f: F) -> Self
where F: Fn(&HashMap<&str, &str>) -> bool + UnwindSafe + 'static,

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.

Source

pub fn trim_whitespace(self, yes: bool) -> Self

If yes, then:

  1. Blank lines at the start/end of the pattern and text are ignored.
  2. Leading/trailing whitespace is ignored on each line of the pattern and text.

Defaults to “yes”.

Source

pub fn build(self) -> Result<FMatcher<'a>, Box<dyn Error>>

Turn this FMBuilder into a FMatcher.

Trait Implementations§

Source§

impl<'a> Debug for FMBuilder<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'a> Freeze for FMBuilder<'a>

§

impl<'a> !RefUnwindSafe for FMBuilder<'a>

§

impl<'a> !Send for FMBuilder<'a>

§

impl<'a> !Sync for FMBuilder<'a>

§

impl<'a> Unpin for FMBuilder<'a>

§

impl<'a> UnwindSafe for FMBuilder<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.