Allow strings as an alternative to numbers
This commit is contained in:
parent
9cb4e2a465
commit
405c65131e
2 changed files with 93 additions and 73 deletions
157
src/predicate.rs
157
src/predicate.rs
|
@ -3,90 +3,115 @@ use std::ffi::OsStr;
|
|||
use clap::{builder::TypedValueParser, Arg, Command};
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::{tag, take_while},
|
||||
character::complete::char,
|
||||
character::{is_digit, is_space},
|
||||
combinator::{cut, map},
|
||||
error::{context, ContextError, ParseError},
|
||||
bytes::complete::{tag, take_while, take_while1},
|
||||
character::is_space,
|
||||
character::{complete::char, is_alphanumeric},
|
||||
combinator::map,
|
||||
multi::separated_list0,
|
||||
number::complete::double,
|
||||
sequence::{delimited, pair, preceded, terminated},
|
||||
Compare, IResult, InputLength, InputTake, InputTakeAtPosition,
|
||||
IResult,
|
||||
};
|
||||
|
||||
use crate::xattr::{parse_xattr, Xattr};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum Relation {
|
||||
Eq(f64),
|
||||
Gt(f64),
|
||||
Gte(f64),
|
||||
Lt(f64),
|
||||
Lte(f64),
|
||||
In(Vec<f64>),
|
||||
enum Value {
|
||||
Number(f64),
|
||||
String(String),
|
||||
}
|
||||
|
||||
const WHITESPACE: &'static [u8] = b" \t\r\n";
|
||||
impl From<f64> for Value {
|
||||
fn from(x: f64) -> Self {
|
||||
Self::Number(x)
|
||||
}
|
||||
}
|
||||
|
||||
fn sp<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
|
||||
let chars = " \t\r\n";
|
||||
impl From<String> for Value {
|
||||
fn from(s: String) -> Self {
|
||||
Self::String(s)
|
||||
}
|
||||
}
|
||||
|
||||
// nom combinators like `take_while` return a function. That function is the
|
||||
// parser,to which we can pass the input
|
||||
take_while(move |c| chars.contains(c))(i)
|
||||
fn string(i: &[u8]) -> IResult<&[u8], String> {
|
||||
alt((
|
||||
delimited(tag("'"), take_while(is_alphanumeric), tag("'")),
|
||||
delimited(tag("\""), take_while(is_alphanumeric), tag("\"")),
|
||||
take_while1(is_alphanumeric),
|
||||
))(i)
|
||||
.map(|(i, b)| (i, String::from_utf8_lossy(b).to_string()))
|
||||
}
|
||||
|
||||
fn parse_value(i: &[u8]) -> IResult<&[u8], Value> {
|
||||
alt((map(double, Value::Number), map(string, Value::String)))(i)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum Relation {
|
||||
Eq(Value),
|
||||
Gt(Value),
|
||||
Gte(Value),
|
||||
Lt(Value),
|
||||
Lte(Value),
|
||||
In(Vec<Value>),
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
impl Relation {
|
||||
fn eq<V: Into<Value>>(x: V) -> Self {
|
||||
Self::Eq(x.into())
|
||||
}
|
||||
fn gt<V: Into<Value>>(x: V) -> Self {
|
||||
Self::Gt(x.into())
|
||||
}
|
||||
fn gte<V: Into<Value>>(x: V) -> Self {
|
||||
Self::Gte(x.into())
|
||||
}
|
||||
fn lt<V: Into<Value>>(x: V) -> Self {
|
||||
Self::Lt(x.into())
|
||||
}
|
||||
fn lte<V: Into<Value>>(x: V) -> Self {
|
||||
Self::Lte(x.into())
|
||||
}
|
||||
fn in_<V: Into<Value>>(x: Vec<V>) -> Self {
|
||||
Self::In(x.into_iter().map(|x| x.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
fn space(i: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
take_while(move |x| is_space(x))(i)
|
||||
}
|
||||
|
||||
// fn array_f64<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
|
||||
// i: &'a str,
|
||||
// ) -> IResult<&'a str, Vec<f64>, E> {
|
||||
// context(
|
||||
// "array",
|
||||
// preceded(
|
||||
// char('['),
|
||||
// cut(terminated(
|
||||
// separated_list0(preceded(sp, char(',')), double),
|
||||
// preceded(sp, char(']')),
|
||||
// )),
|
||||
// ),
|
||||
// )(i)
|
||||
// }
|
||||
|
||||
fn array_f64(i: &[u8]) -> IResult<&[u8], Vec<f64>> {
|
||||
delimited(char('['), separated_list0(char(','), double), char(']'))(i)
|
||||
fn array(i: &[u8]) -> IResult<&[u8], Vec<Value>> {
|
||||
delimited(
|
||||
char('['),
|
||||
separated_list0(char(','), parse_value),
|
||||
char(']'),
|
||||
)(i)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_f64() {
|
||||
fn test_array() {
|
||||
assert_eq!(
|
||||
array_f64(b"[1,2,3,4]".as_slice()),
|
||||
Ok((b"".as_slice(), vec![1f64, 2f64, 3f64, 4f64]))
|
||||
array(b"[1,2,3,4]".as_slice()),
|
||||
Ok((
|
||||
b"".as_slice(),
|
||||
vec![1f64, 2f64, 3f64, 4f64]
|
||||
.into_iter()
|
||||
.map(Value::from)
|
||||
.collect()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
// fn parse_relation<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Relation, E> {
|
||||
// alt((
|
||||
// preceded(tag(">="), map(double, Relation::Gte)),
|
||||
// preceded(tag("<="), map(double, Relation::Lte)),
|
||||
// preceded(tag(" in"), map(array_f64, Relation::In)),
|
||||
// preceded(tag(">"), map(double, Relation::Gt)),
|
||||
// preceded(tag("<"), map(double, Relation::Lt)),
|
||||
// preceded(tag("="), map(double, Relation::Eq)),
|
||||
// ))(i)
|
||||
// }
|
||||
|
||||
/// E.g. `> 5`
|
||||
fn parse_relation(i: &[u8]) -> IResult<&[u8], Relation> {
|
||||
alt((
|
||||
preceded(pair(tag(">="), space), map(double, Relation::Gte)),
|
||||
preceded(pair(tag("<="), space), map(double, Relation::Lte)),
|
||||
preceded(pair(tag("in"), space), map(array_f64, Relation::In)),
|
||||
preceded(pair(tag(">"), space), map(double, Relation::Gt)),
|
||||
preceded(pair(tag("<"), space), map(double, Relation::Lt)),
|
||||
preceded(pair(tag("="), space), map(double, Relation::Eq)),
|
||||
preceded(pair(tag(">="), space), map(parse_value, Relation::gte)),
|
||||
preceded(pair(tag("<="), space), map(parse_value, Relation::Lte)),
|
||||
preceded(pair(tag("in"), space), map(array, Relation::in_)),
|
||||
preceded(pair(tag(">"), space), map(parse_value, Relation::Gt)),
|
||||
preceded(pair(tag("<"), space), map(parse_value, Relation::Lt)),
|
||||
preceded(pair(tag("="), space), map(parse_value, Relation::Eq)),
|
||||
))(i)
|
||||
}
|
||||
|
||||
|
@ -95,20 +120,20 @@ fn test_parse_relation() {
|
|||
assert!(parse_relation(b"a + b").is_err());
|
||||
assert_eq!(
|
||||
parse_relation(b">=-2.4343").unwrap().1,
|
||||
Relation::Gte(-2.4343f64)
|
||||
Relation::gte(-2.4343f64)
|
||||
);
|
||||
assert_eq!(parse_relation(b"<= -2").unwrap().1, Relation::Lte(-2f64));
|
||||
assert_eq!(parse_relation(b"<= -2").unwrap().1, Relation::lte(-2f64));
|
||||
assert_eq!(
|
||||
parse_relation(b"in [1,2,3]"),
|
||||
Ok((b"".as_slice(), Relation::In(vec![1f64, 2f64, 3f64])))
|
||||
Ok((b"".as_slice(), Relation::in_(vec![1f64, 2f64, 3f64])))
|
||||
);
|
||||
assert_eq!(
|
||||
parse_relation(b"> 4"),
|
||||
Ok((b"".as_slice(), Relation::Gt(4f64)))
|
||||
Ok((b"".as_slice(), Relation::gt(4f64)))
|
||||
);
|
||||
assert_eq!(
|
||||
parse_relation(b"< 4"),
|
||||
Ok((b"".as_slice(), Relation::Lt(4f64)))
|
||||
Ok((b"".as_slice(), Relation::lt(4f64)))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -140,7 +165,7 @@ fn test_parse_predicate() {
|
|||
namespace: b"user".to_vec(),
|
||||
attr: b"amplitude".to_vec()
|
||||
},
|
||||
relation: Relation::Gte(-2.4343f64)
|
||||
relation: Relation::gte(-2.4343f64)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -150,7 +175,7 @@ fn test_parse_predicate() {
|
|||
namespace: b"system".to_vec(),
|
||||
attr: b"security".to_vec()
|
||||
},
|
||||
relation: Relation::Lte(-2f64)
|
||||
relation: Relation::lte(-2f64)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -162,7 +187,7 @@ fn test_parse_predicate() {
|
|||
namespace: b"security".to_vec(),
|
||||
attr: b"lmnop".to_vec()
|
||||
},
|
||||
relation: Relation::In(vec![1f64, 2f64, 3f64])
|
||||
relation: Relation::in_(vec![1f64, 2f64, 3f64])
|
||||
}
|
||||
))
|
||||
);
|
||||
|
@ -175,7 +200,7 @@ fn test_parse_predicate() {
|
|||
namespace: b"trusted".to_vec(),
|
||||
attr: b"trustfulness".to_vec()
|
||||
},
|
||||
relation: Relation::Gt(4f64)
|
||||
relation: Relation::gt(4f64)
|
||||
}
|
||||
))
|
||||
);
|
||||
|
@ -188,7 +213,7 @@ fn test_parse_predicate() {
|
|||
namespace: b"user".to_vec(),
|
||||
attr: b"grumpiness".to_vec()
|
||||
},
|
||||
relation: Relation::Lt(4f64)
|
||||
relation: Relation::lt(4f64)
|
||||
}
|
||||
))
|
||||
);
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::{tag, take_while, take_while1},
|
||||
character::complete::char,
|
||||
character::is_alphabetic,
|
||||
error::ParseError,
|
||||
sequence::{pair, separated_pair, terminated},
|
||||
IResult,
|
||||
branch::alt, bytes::complete::take_while1, character::complete::char, character::is_alphabetic,
|
||||
sequence::separated_pair, IResult,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
Loading…
Reference in a new issue