Skip to content

Commit

Permalink
Refactor field matcher (#834)
Browse files Browse the repository at this point in the history
Signed-off-by: Nico Wagner <[email protected]>
  • Loading branch information
nwagner84 committed Sep 20, 2024
1 parent cc92e6f commit edc500f
Show file tree
Hide file tree
Showing 9 changed files with 1,898 additions and 91 deletions.
1,323 changes: 1,323 additions & 0 deletions src/matcher/field/mod.rs

Large diffs are not rendered by default.

476 changes: 476 additions & 0 deletions src/matcher/field/parser.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/matcher/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub use quantifier::Quantifier;
pub use tag::TagMatcher;

mod error;
pub mod field;
mod occurrence;
mod operator;
mod options;
Expand Down
28 changes: 17 additions & 11 deletions src/matcher/occurrence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,26 @@ impl OccurrenceMatcher {
/// use pica_record::primitives::OccurrenceRef;
///
/// let matcher = OccurrenceMatcher::new("/01-03")?;
/// assert!(matcher.is_match(&OccurrenceRef::new("02")?));
/// assert!(!matcher.is_match(&OccurrenceRef::new("04")?));
/// assert!(matcher.is_match(Some(&OccurrenceRef::new("02")?)));
/// assert!(!matcher.is_match(Some(&OccurrenceRef::new("04")?)));
///
/// let matcher = OccurrenceMatcher::new("/*")?;
/// assert!(matcher.is_match(None));
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn is_match(&self, other: &OccurrenceRef) -> bool {
match self {
Self::Any => true,
Self::None => other == b"00",
Self::Exact(rhs) => other == rhs,
Self::Range(min, max) => {
(other.as_bytes() >= min.as_bytes())
&& (other.as_bytes() <= max.as_bytes())
}
pub fn is_match(&self, other: Option<&OccurrenceRef>) -> bool {
match other {
None => matches!(self, Self::Any | Self::None),
Some(occ) => match self {
Self::Any => true,
Self::None => occ == b"00",
Self::Exact(rhs) => occ == rhs,
Self::Range(min, max) => {
(occ.as_bytes() >= min.as_bytes())
&& (occ.as_bytes() <= max.as_bytes())
}
},
}
}
}
Expand Down
116 changes: 58 additions & 58 deletions src/matcher/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ use winnow::{PResult, Parser};
/// Relational Operator
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RelationalOp {
Equal, // equal, "=="
NotEqual, // not equal, "!="
GreaterThan, // greater than, ">"
GreaterThanOrEqual, // greater than or equal, ">="
LessThan, // less than, "<"
LessThanOrEqual, // less than or equal, "<="
StartsWith, // starts with, "=^"
StartsNotWith, // starts not with, "!^"
EndsWith, // ends with, "=$"
EndsNotWith, // ends not with, "!$"
Similar, // similar, "=*"
Contains, // contains, "=?"
Eq, // equal, "=="
Ne, // not equal, "!="
Gt, // greater than, ">"
Ge, // greater than or equal, ">="
Lt, // less than, "<"
Le, // less than or equal, "<="
StartsWith, // starts with, "=^"
StartsNotWith, // starts not with, "!^"
EndsWith, // ends with, "=$"
EndsNotWith, // ends not with, "!$"
Similar, // similar, "=*"
Contains, // contains, "=?"
}

impl RelationalOp {
Expand All @@ -29,12 +29,12 @@ impl RelationalOp {
/// ```rust
/// use pica_record::matcher::RelationalOp;
///
/// assert!(RelationalOp::Equal.is_usize_applicable());
/// assert!(RelationalOp::NotEqual.is_usize_applicable());
/// assert!(RelationalOp::GreaterThanOrEqual.is_usize_applicable());
/// assert!(RelationalOp::GreaterThan.is_usize_applicable());
/// assert!(RelationalOp::LessThanOrEqual.is_usize_applicable());
/// assert!(RelationalOp::LessThan.is_usize_applicable());
/// assert!(RelationalOp::Eq.is_usize_applicable());
/// assert!(RelationalOp::Ne.is_usize_applicable());
/// assert!(RelationalOp::Ge.is_usize_applicable());
/// assert!(RelationalOp::Gt.is_usize_applicable());
/// assert!(RelationalOp::Le.is_usize_applicable());
/// assert!(RelationalOp::Lt.is_usize_applicable());
///
/// assert!(!RelationalOp::Contains.is_usize_applicable());
/// assert!(!RelationalOp::EndsNotWith.is_usize_applicable());
Expand All @@ -48,12 +48,12 @@ impl RelationalOp {
pub fn is_usize_applicable(&self) -> bool {
matches!(
self,
RelationalOp::Equal
| RelationalOp::NotEqual
| RelationalOp::GreaterThanOrEqual
| RelationalOp::GreaterThan
| RelationalOp::LessThan
| RelationalOp::LessThanOrEqual
RelationalOp::Eq
| RelationalOp::Ne
| RelationalOp::Ge
| RelationalOp::Gt
| RelationalOp::Lt
| RelationalOp::Le
)
}

Expand All @@ -65,42 +65,42 @@ impl RelationalOp {
/// ```rust
/// use pica_record::matcher::RelationalOp;
///
/// assert!(RelationalOp::Eq.is_str_applicable());
/// assert!(RelationalOp::Ne.is_str_applicable());
/// assert!(RelationalOp::Contains.is_str_applicable());
/// assert!(RelationalOp::EndsNotWith.is_str_applicable());
/// assert!(RelationalOp::EndsWith.is_str_applicable());
/// assert!(RelationalOp::Equal.is_str_applicable());
/// assert!(RelationalOp::NotEqual.is_str_applicable());
/// assert!(RelationalOp::Similar.is_str_applicable());
/// assert!(RelationalOp::StartsNotWith.is_str_applicable());
/// assert!(RelationalOp::StartsWith.is_str_applicable());
///
/// assert!(!RelationalOp::GreaterThanOrEqual.is_str_applicable());
/// assert!(!RelationalOp::GreaterThan.is_str_applicable());
/// assert!(!RelationalOp::LessThanOrEqual.is_str_applicable());
/// assert!(!RelationalOp::LessThan.is_str_applicable());
/// assert!(!RelationalOp::Ge.is_str_applicable());
/// assert!(!RelationalOp::Gt.is_str_applicable());
/// assert!(!RelationalOp::Le.is_str_applicable());
/// assert!(!RelationalOp::Lt.is_str_applicable());
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn is_str_applicable(&self) -> bool {
!matches!(
self,
RelationalOp::GreaterThanOrEqual
| RelationalOp::GreaterThan
| RelationalOp::LessThan
| RelationalOp::LessThanOrEqual
RelationalOp::Ge
| RelationalOp::Gt
| RelationalOp::Lt
| RelationalOp::Le
)
}
}

impl Display for RelationalOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
RelationalOp::Equal => write!(f, "=="),
RelationalOp::NotEqual => write!(f, "!="),
RelationalOp::GreaterThan => write!(f, ">"),
RelationalOp::GreaterThanOrEqual => write!(f, ">="),
RelationalOp::LessThan => write!(f, "<"),
RelationalOp::LessThanOrEqual => write!(f, "<="),
RelationalOp::Eq => write!(f, "=="),
RelationalOp::Ne => write!(f, "!="),
RelationalOp::Gt => write!(f, ">"),
RelationalOp::Ge => write!(f, ">="),
RelationalOp::Lt => write!(f, "<"),
RelationalOp::Le => write!(f, "<="),
RelationalOp::StartsWith => write!(f, "=^"),
RelationalOp::StartsNotWith => write!(f, "!^"),
RelationalOp::EndsWith => write!(f, "=$"),
Expand All @@ -115,12 +115,12 @@ impl Display for RelationalOp {
impl quickcheck::Arbitrary for RelationalOp {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
g.choose(&[
Self::Equal,
Self::NotEqual,
Self::GreaterThan,
Self::GreaterThanOrEqual,
Self::LessThan,
Self::LessThanOrEqual,
Self::Eq,
Self::Ne,
Self::Gt,
Self::Ge,
Self::Lt,
Self::Le,
Self::StartsWith,
Self::StartsNotWith,
Self::EndsWith,
Expand All @@ -141,18 +141,18 @@ pub(crate) fn parse_relational_operator(
use RelationalOp::*;

alt((
"==".value(Equal),
"!=".value(NotEqual),
"==".value(Eq),
"!=".value(Ne),
"=^".value(StartsWith),
"!^".value(StartsNotWith),
"=$".value(EndsWith),
"!$".value(EndsNotWith),
"=*".value(Similar),
"=?".value(Contains),
">=".value(GreaterThanOrEqual),
">".value(GreaterThan),
"<=".value(LessThanOrEqual),
"<".value(LessThan),
">=".value(Ge),
">".value(Gt),
"<=".value(Le),
"<".value(Lt),
))
.parse_next(i)
}
Expand Down Expand Up @@ -201,12 +201,12 @@ mod tests {
};
}

parse_success!("==", Equal);
parse_success!("!=", NotEqual);
parse_success!(">=", GreaterThanOrEqual);
parse_success!(">", GreaterThan);
parse_success!("<=", LessThanOrEqual);
parse_success!("<", LessThan);
parse_success!("==", Eq);
parse_success!("!=", Ne);
parse_success!(">=", Ge);
parse_success!(">", Gt);
parse_success!("<=", Le);
parse_success!("<", Lt);
parse_success!("=^", StartsWith);
parse_success!("!^", StartsNotWith);
parse_success!("=$", EndsWith);
Expand Down
16 changes: 8 additions & 8 deletions src/matcher/subfield/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ impl RelationMatcher {
let value = subfield.value().as_ref();

match self.op {
Equal => self.compare(value, options),
NotEqual => !self.compare(value, options),
Eq => self.compare(value, options),
Ne => !self.compare(value, options),
StartsWith => self.starts_with(value, options, false),
StartsNotWith => self.starts_with(value, options, true),
EndsWith => self.ends_with(value, options, false),
Expand Down Expand Up @@ -813,12 +813,12 @@ impl CardinalityMatcher {
.count();

match self.op {
RelationalOp::Equal => count == self.value,
RelationalOp::NotEqual => count != self.value,
RelationalOp::GreaterThanOrEqual => count >= self.value,
RelationalOp::GreaterThan => count > self.value,
RelationalOp::LessThanOrEqual => count <= self.value,
RelationalOp::LessThan => count < self.value,
RelationalOp::Eq => count == self.value,
RelationalOp::Ne => count != self.value,
RelationalOp::Ge => count >= self.value,
RelationalOp::Gt => count > self.value,
RelationalOp::Le => count <= self.value,
RelationalOp::Lt => count < self.value,
_ => unreachable!(),
}
}
Expand Down
26 changes: 13 additions & 13 deletions src/matcher/subfield/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ pub(crate) fn parse_singleton_matcher(
}

#[inline(always)]
fn parse_subfield_singleton_matcher(
pub(crate) fn parse_subfield_singleton_matcher(
i: &mut &[u8],
) -> PResult<SubfieldMatcher> {
parse_singleton_matcher
Expand Down Expand Up @@ -299,8 +299,8 @@ fn parse_subfield_or_matcher(
let atom = |i: &mut &[u8]| -> PResult<SubfieldMatcher> {
ws(alt((
parse_subfield_and_matcher,
parse_subfield_group_matcher,
parse_subfield_xor_matcher,
parse_subfield_group_matcher,
parse_subfield_singleton_matcher,
parse_subfield_not_matcher,
)))
Expand All @@ -319,8 +319,8 @@ fn parse_subfield_xor_matcher(
) -> PResult<SubfieldMatcher> {
let atom = |i: &mut &[u8]| -> PResult<SubfieldMatcher> {
ws(alt((
parse_subfield_group_matcher,
parse_subfield_and_matcher,
parse_subfield_group_matcher,
parse_subfield_singleton_matcher,
parse_subfield_not_matcher,
)))
Expand Down Expand Up @@ -370,9 +370,9 @@ pub(crate) fn parse_subfield_matcher(
) -> PResult<SubfieldMatcher> {
alt((
parse_subfield_composite_matcher,
parse_subfield_singleton_matcher,
parse_subfield_group_matcher,
parse_subfield_not_matcher,
parse_subfield_singleton_matcher,
))
.map(|matcher| {
group_level_reset();
Expand Down Expand Up @@ -456,9 +456,9 @@ mod tests {
};
}

parse_success!("0 == 'abc'", Any, "0", Equal, b"abc");
parse_success!("ALL 0 != 'abc'", All, "0", NotEqual, b"abc");
parse_success!("ANY [ab] == 'abc'", Any, "ab", Equal, b"abc");
parse_success!("0 == 'abc'", Any, "0", Eq, b"abc");
parse_success!("ALL 0 != 'abc'", All, "0", Ne, b"abc");
parse_success!("ANY [ab] == 'abc'", Any, "ab", Eq, b"abc");
parse_success!("a =^ 'abc'", Any, "a", StartsWith, b"abc");
parse_success!("a !^ 'abc'", Any, "a", StartsNotWith, b"abc");
parse_success!("a =$ 'abc'", Any, "a", EndsWith, b"abc");
Expand Down Expand Up @@ -677,12 +677,12 @@ mod tests {
};
}

parse_success!("#a == 1", 'a', Equal, 1);
parse_success!("#a != 2", 'a', NotEqual, 2);
parse_success!("#a >= 3", 'a', GreaterThanOrEqual, 3);
parse_success!("#a > 4", 'a', GreaterThan, 4);
parse_success!("#a <= 5", 'a', LessThanOrEqual, 5);
parse_success!("#a < 6", 'a', LessThan, 6);
parse_success!("#a == 1", 'a', Eq, 1);
parse_success!("#a != 2", 'a', Ne, 2);
parse_success!("#a >= 3", 'a', Ge, 3);
parse_success!("#a > 4", 'a', Gt, 4);
parse_success!("#a <= 5", 'a', Le, 5);
parse_success!("#a < 6", 'a', Lt, 6);

assert!(parse_cardinality_matcher.parse(b"#a > -1").is_err());
assert!(parse_cardinality_matcher.parse(b"#a > '2'").is_err());
Expand Down
2 changes: 1 addition & 1 deletion src/matcher/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ fn parse_tag_matcher_pattern(i: &mut &[u8]) -> PResult<TagMatcher> {
.parse_next(i)
}

fn parse_tag_matcher(i: &mut &[u8]) -> PResult<TagMatcher> {
pub(crate) fn parse_tag_matcher(i: &mut &[u8]) -> PResult<TagMatcher> {
alt((parse_tag_matcher_tag, parse_tag_matcher_pattern))
.parse_next(i)
}
Expand Down
1 change: 1 addition & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```

pub use crate::matcher::field::FieldMatcher;
pub use crate::matcher::subfield::SubfieldMatcher;
pub use crate::matcher::{
MatcherOptions, OccurrenceMatcher, TagMatcher,
Expand Down

0 comments on commit edc500f

Please sign in to comment.