Skip to content

Commit

Permalink
Restructure matcher module (#833)
Browse files Browse the repository at this point in the history
Signed-off-by: Nico Wagner <[email protected]>
  • Loading branch information
nwagner84 committed Sep 19, 2024
1 parent 603cb53 commit cc92e6f
Show file tree
Hide file tree
Showing 11 changed files with 614 additions and 560 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub use record::{ByteRecord, StringRecord};

mod error;
pub mod matcher;
mod parser;
pub mod prelude;
pub mod primitives;
mod record;
11 changes: 2 additions & 9 deletions src/matcher.rs → src/matcher/mod.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
//! Various matcher against records (and record primitives).
//! Various matcher against record primitives.

pub use error::ParseMatcherError;
pub use occurrence::OccurrenceMatcher;
pub use operator::{BooleanOp, RelationalOp};
pub use options::MatcherOptions;
pub use quantifier::Quantifier;
pub use subfield::{
CardinalityMatcher, ExistsMatcher, InMatcher, RegexMatcher,
RegexSetMatcher, RelationMatcher, SingletonMatcher,
SubfieldMatcher,
};
pub use tag::TagMatcher;

mod error;
mod occurrence;
mod operator;
mod options;
mod parse;
mod quantifier;
mod string;
mod subfield;
pub mod subfield;
mod tag;
113 changes: 110 additions & 3 deletions src/matcher/occurrence.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
//! Matcher that can be applied on a list of [OccurrenceRef].

use std::fmt::{self, Display};

use winnow::Parser;
use winnow::combinator::{alt, empty, preceded, separated_pair};
use winnow::prelude::*;

use super::parse::parse_occurrence_matcher;
use super::ParseMatcherError;
use crate::primitives::parse::parse_occurrence_ref;
use crate::primitives::{Occurrence, OccurrenceRef};

/// A matcher that checks for occurrences (or no occurrence).
/// A matcher that matches against a [OccurrenceRef].
#[derive(Debug, Clone, PartialEq)]
pub enum OccurrenceMatcher {
Exact(Occurrence),
Expand Down Expand Up @@ -97,3 +100,107 @@ impl Display for OccurrenceMatcher {
Ok(())
}
}

#[inline]
fn parse_occurrence_matcher_inner(
i: &mut &[u8],
) -> PResult<Occurrence> {
parse_occurrence_ref.map(Occurrence::from).parse_next(i)
}

#[inline]
fn parse_occurrence_matcher_exact(
i: &mut &[u8],
) -> PResult<OccurrenceMatcher> {
parse_occurrence_matcher_inner
.verify(|occurrence| occurrence.as_bytes() != b"00")
.map(OccurrenceMatcher::Exact)
.parse_next(i)
}

#[inline]
fn parse_occurrence_matcher_range(
i: &mut &[u8],
) -> PResult<OccurrenceMatcher> {
separated_pair(
parse_occurrence_matcher_inner,
'-',
parse_occurrence_matcher_inner,
)
.verify(|(min, max)| min.len() == max.len() && min < max)
.map(|(min, max)| OccurrenceMatcher::Range(min, max))
.parse_next(i)
}

pub(crate) fn parse_occurrence_matcher(
i: &mut &[u8],
) -> PResult<OccurrenceMatcher> {
alt((
preceded(
'/',
alt((
parse_occurrence_matcher_range,
parse_occurrence_matcher_exact,
"00".value(OccurrenceMatcher::None),
"*".value(OccurrenceMatcher::Any),
)),
),
empty.value(OccurrenceMatcher::None),
))
.parse_next(i)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_occurrence_matcher() -> anyhow::Result<()> {
macro_rules! parse_success {
($i:expr, $o:expr) => {
let o = parse_occurrence_matcher
.parse($i.as_bytes())
.unwrap();

assert_eq!(o.to_string(), $i);
assert_eq!(o, $o);
};
}

parse_success!("", OccurrenceMatcher::None);
parse_success!("/*", OccurrenceMatcher::Any);

parse_success!(
"/01",
OccurrenceMatcher::Exact(Occurrence::new("01")?)
);

parse_success!(
"/999",
OccurrenceMatcher::Exact(Occurrence::new("999")?)
);

parse_success!(
"/01-99",
OccurrenceMatcher::Range(
Occurrence::new("01")?,
Occurrence::new("99")?
)
);

parse_success!(
"/01-99",
OccurrenceMatcher::Range(
Occurrence::new("01")?,
Occurrence::new("99")?
)
);

assert_eq!(
parse_occurrence_matcher.parse(b"/00").unwrap(),
OccurrenceMatcher::None
);

Ok(())
}
}
61 changes: 61 additions & 0 deletions src/matcher/operator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::fmt::{self, Display};

use winnow::combinator::alt;
use winnow::{PResult, Parser};

/// Relational Operator
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RelationalOp {
Expand Down Expand Up @@ -130,6 +133,30 @@ impl quickcheck::Arbitrary for RelationalOp {
}
}

/// Parse RelationalOp which can be used for string comparisons.
#[inline]
pub(crate) fn parse_relational_operator(
i: &mut &[u8],
) -> PResult<RelationalOp> {
use RelationalOp::*;

alt((
"==".value(Equal),
"!=".value(NotEqual),
"=^".value(StartsWith),
"!^".value(StartsNotWith),
"=$".value(EndsWith),
"!$".value(EndsNotWith),
"=*".value(Similar),
"=?".value(Contains),
">=".value(GreaterThanOrEqual),
">".value(GreaterThan),
"<=".value(LessThanOrEqual),
"<".value(LessThan),
))
.parse_next(i)
}

/// Boolean Operators.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BooleanOp {
Expand All @@ -154,3 +181,37 @@ impl quickcheck::Arbitrary for BooleanOp {
g.choose(&[Self::And, Self::Or, Self::Xor]).unwrap().clone()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_relational_operator() {
use RelationalOp::*;

macro_rules! parse_success {
($input:expr, $expected:expr) => {
assert_eq!(
parse_relational_operator
.parse($input.as_bytes())
.unwrap(),
$expected
);
};
}

parse_success!("==", Equal);
parse_success!("!=", NotEqual);
parse_success!(">=", GreaterThanOrEqual);
parse_success!(">", GreaterThan);
parse_success!("<=", LessThanOrEqual);
parse_success!("<", LessThan);
parse_success!("=^", StartsWith);
parse_success!("!^", StartsNotWith);
parse_success!("=$", EndsWith);
parse_success!("!$", EndsNotWith);
parse_success!("=*", Similar);
parse_success!("=?", Contains);
}
}
22 changes: 22 additions & 0 deletions src/matcher/quantifier.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::fmt::{self, Display};

use winnow::combinator::alt;
use winnow::{PResult, Parser};

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum Quantifier {
All,
Expand All @@ -25,3 +28,22 @@ impl quickcheck::Arbitrary for Quantifier {
}
}
}

#[inline]
pub(crate) fn parse_quantifier(i: &mut &[u8]) -> PResult<Quantifier> {
alt(("ALL".value(Quantifier::All), "ANY".value(Quantifier::Any)))
.parse_next(i)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_quantifier() {
use Quantifier::*;

assert_eq!(parse_quantifier.parse(b"ALL").unwrap(), All);
assert_eq!(parse_quantifier.parse(b"ANY").unwrap(), Any);
}
}
Loading

0 comments on commit cc92e6f

Please sign in to comment.