use crate::pred::cmp::parser::{OpParser, OpParserError};
use crate::pred::cmp::{Comparison, Operator, Symbol};
use std::fmt::Display;
use std::str::FromStr;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct FieldComparison<K, V, S = Symbol> {
key: K,
inner: Comparison<V, S>,
}
impl<K, V, S> FieldComparison<K, V, S>
where
V: PartialOrd,
{
pub fn test(&self, value: V) -> bool {
self.inner.test(value)
}
}
impl<K, V, S> FieldComparison<K, V, S> {
pub fn new(key: K, operator: Operator, value: V) -> Self {
Self {
key,
inner: Comparison::new(operator, value),
}
}
fn new_with_inner(key: K, inner: Comparison<V, S>) -> Self {
Self { key, inner }
}
pub fn key(&self) -> &K {
&self.key
}
pub fn operator(&self) -> Operator {
self.inner.operator()
}
pub fn value(&self) -> &V {
self.inner.value()
}
pub fn into_inner(self) -> (K, Comparison<V, S>) {
(self.key, self.inner)
}
}
impl<K, V> FromStr for FieldComparison<K, V, Symbol>
where
K: FromStr,
V: FromStr,
<K as FromStr>::Err: Display,
<V as FromStr>::Err: Display,
Comparison<V, Symbol>: FromStr<Err = String>,
{
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match OpParser::simple_op_parser(s) {
Ok((op, text)) => {
let op = match op {
Operator::Ne => "≠",
Operator::Eq => "=",
Operator::Gt => ">",
Operator::Lt => "<",
Operator::Gte => ">=",
Operator::Lte => "<=",
};
Err(format!(
"missing key in expression `{s}`, should be in the form `key{op}{text}`"
))
}
Err(OpParserError::InvalidCharsBeforeOp {
text_before_op,
op_index: before_op_index,
..
}) => {
let key = text_before_op
.trim_end_matches(' ')
.parse::<K>()
.map_err(|e| e.to_string())?;
let text_after = &s[before_op_index..];
let inner = text_after.parse::<Comparison<V, Symbol>>()?;
Ok(Self::new_with_inner(key, inner))
}
Err(OpParserError::Other(s)) => Err(s),
}
}
}
#[cfg(test)]
mod tests {
use crate::pred::field_cmp::FieldComparison;
use crate::pred::Operator;
macro_rules! test_cases {
(
key_type = $key_type:ty;
value_type = $value_type:ty;
$($operator:expr => [$($input:literal),+] = ($expected_key:literal, $expected_value:literal),)+
) => {$($(
{
let input = $input;
let pred = input.parse::<FieldComparison<$key_type, $value_type>>().unwrap();
assert_eq!(pred.key(), &$expected_key, "input: {input}");
assert_eq!(pred.operator(), $operator, "input: {input}");
assert_eq!(pred.value(), &$expected_value, "input: {input}");
}
)+)+};
}
#[test]
fn test_field_comparison() {
test_cases!(
key_type = String;
value_type = usize;
Operator::Eq => ["size=1024", "size =1024", "size = 1024"] = ("size", 1024),
Operator::Ne => ["size≠1024", "size ≠ 1024", "size!1024", "size ! 1024"] = ("size", 1024),
Operator::Gt => ["size>1024", "size >1024", "size >1024"] = ("size", 1024),
Operator::Gte => ["size>=1024", "size >=1024", "size >= 1024"] = ("size", 1024),
Operator::Lt => ["size<1024", "size <1024", "size <1024"] = ("size", 1024),
Operator::Lte => ["size<=1024", "size <=1024", "size <= 1024"] = ("size", 1024),
);
}
#[test]
fn test_case_in_comment() {
let field_comparison = "size1024".parse::<FieldComparison<String, usize>>();
assert_eq!(
field_comparison,
Err(String::from(
"missing key in expression `size1024`, should be in the form `key=size1024`"
))
);
}
}