Skip to content

Commit

Permalink
Add !^ and !$ operator (#644)
Browse files Browse the repository at this point in the history
  • Loading branch information
nwagner84 authored Jul 7, 2023
1 parent 1fbb0ae commit 5d132b9
Show file tree
Hide file tree
Showing 14 changed files with 93 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* #637 Stabilize `print` command
* #641 Stabilize `sample` command
* #642 Add `--squash` and `--merge` option
* #644 Add `!^` and `!$` operator

### Changed

Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,11 @@ between `/01` and `/10`.

Simple subfield filter consists of the subfield code (single
alpha-numerical character, ex `0`) a comparison operator (equal `==`,
not equal `!=` not equal, starts with prefix `=^`, ends with suffix
`=$`, regex `=~`/`!~`, `in` and `not in`) and a value enclosed in single
quotes. These simple subfield expressions can be grouped in parentheses
and combined with boolean connectives (ex. `(0 == 'abc' || 0 == 'def')`).
not equal `!=` not equal, starts with prefix `=^`, starts not with
prefix `!^`, ends with suffix `=$`, regex `=~`/`!~`, `in` and `not in`)
and a value enclosed in single quotes. These simple subfield expressions
can be grouped in parentheses and combined with boolean connectives (ex.
`(0 == 'abc' || 0 == 'def')`).

A special existence operator can be used to check if a given field
(`012A/00?`) or a subfield (`[email protected]?` or `002@$0?`) exists. To test for
Expand Down
32 changes: 23 additions & 9 deletions pica-matcher/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ where
/// Relational Operator
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum RelationalOp {
Eq, // equal, "=="
Ne, // not equal, "!="
Gt, // greater than, ">"
Ge, // greater than or equal, ">="
Lt, // less than, "<"
Le, // less than or equal, "<="
StartsWith, // starts with, "=^"
EndsWith, // ends with, "=$"
Similar, // similar, "=*"
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, "=*"
}

impl Display for RelationalOp {
Expand All @@ -50,7 +52,9 @@ impl Display for RelationalOp {
RelationalOp::Lt => write!(f, "<"),
RelationalOp::Le => write!(f, "<="),
RelationalOp::StartsWith => write!(f, "=^"),
RelationalOp::StartsNotWith => write!(f, "!^"),
RelationalOp::EndsWith => write!(f, "=$"),
RelationalOp::EndsNotWith => write!(f, "!$"),
RelationalOp::Similar => write!(f, "=*"),
}
}
Expand All @@ -64,7 +68,9 @@ pub(crate) fn parse_relational_op_str(
value(RelationalOp::Eq, tag("==")),
value(RelationalOp::Ne, tag("!=")),
value(RelationalOp::StartsWith, tag("=^")),
value(RelationalOp::StartsNotWith, tag("!^")),
value(RelationalOp::EndsWith, tag("=$")),
value(RelationalOp::EndsNotWith, tag("!$")),
value(RelationalOp::Similar, tag("=*")),
))(i)
}
Expand Down Expand Up @@ -216,10 +222,18 @@ mod tests {
parse_relational_op_str(b"=^"),
RelationalOp::StartsWith
);
assert_finished_and_eq!(
parse_relational_op_str(b"!^"),
RelationalOp::StartsNotWith
);
assert_finished_and_eq!(
parse_relational_op_str(b"=$"),
RelationalOp::EndsWith
);
assert_finished_and_eq!(
parse_relational_op_str(b"!$"),
RelationalOp::EndsNotWith
);
assert_finished_and_eq!(
parse_relational_op_str(b"=*"),
RelationalOp::Similar
Expand Down
44 changes: 40 additions & 4 deletions pica-matcher/src/subfield_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,19 @@ impl RelationMatcher {
&self,
value: &[u8],
options: &MatcherOptions,
invert: bool,
) -> bool {
if options.case_ignore {
let mut result = if options.case_ignore {
value.to_lowercase().starts_with(&self.value.to_lowercase())
} else {
value.starts_with(&self.value)
};

if invert {
result = !result
}

result
}

/// Returns `true` if the given values is a suffix of the matcher's
Expand All @@ -226,12 +233,19 @@ impl RelationMatcher {
&self,
value: &[u8],
options: &MatcherOptions,
invert: bool,
) -> bool {
if options.case_ignore {
let mut result = if options.case_ignore {
value.to_lowercase().ends_with(&self.value.to_lowercase())
} else {
value.ends_with(&self.value)
};

if invert {
result = !result;
}

result
}

/// Returns `true` if the given value is similar to the matcher's
Expand Down Expand Up @@ -279,10 +293,16 @@ impl Matcher for RelationMatcher {
RelationalOp::Eq => self.compare(value, options),
RelationalOp::Ne => !self.compare(value, options),
RelationalOp::StartsWith => {
self.starts_with(value, options)
self.starts_with(value, options, false)
}
RelationalOp::StartsNotWith => {
self.starts_with(value, options, true)
}
RelationalOp::EndsWith => {
self.ends_with(value, options)
self.ends_with(value, options, false)
}
RelationalOp::EndsNotWith => {
self.ends_with(value, options, true)
}
RelationalOp::Similar => {
self.is_similar(value, options)
Expand Down Expand Up @@ -877,6 +897,14 @@ mod tests {
value: "abc".into()
}
);
assert_finished_and_eq!(
parse_relation_matcher(b"0 !^ 'T'"),
RelationMatcher {
codes: vec!['0'],
op: RelationalOp::StartsNotWith,
value: "T".into()
}
);
assert_finished_and_eq!(
parse_relation_matcher(b"0 =$ 'abc'"),
RelationMatcher {
Expand All @@ -885,6 +913,14 @@ mod tests {
value: "abc".into()
}
);
assert_finished_and_eq!(
parse_relation_matcher(b"0 !$ 'z'"),
RelationMatcher {
codes: vec!['0'],
op: RelationalOp::EndsNotWith,
value: "z".into()
}
);
assert_finished_and_eq!(
parse_relation_matcher(b"0 =* 'abc'"),
RelationMatcher {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
bin.name = "pica"
args = "filter \"002@{ 0 !^ 'T' }\""
status = "success"
stdout = ""
stderr = ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bin.name = "pica"
args = "filter \"002@{ 0 !^ 'A' }\""
status = "success"
stderr = ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bin.name = "pica"
args = "filter \"[email protected] !^ 'A'\""
status = "success"
stderr = ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
bin.name = "pica"
args = "filter \"[email protected] !^ 'T'\""
status = "success"
stdout = ""
stderr = ""

0 comments on commit 5d132b9

Please sign in to comment.