1use std::ffi::OsStr;
2
3use clap::{builder::TypedValueParser, Arg, Command};
4use nom::{
5 sequence::{pair, terminated},
6 IResult,
7};
8
9use crate::Record;
10
11use super::{
12 relation::{parse_relation, Relation},
13 space,
14 xattr::{parse_xattr, Xattr},
15};
16
17#[derive(Clone, Debug, PartialEq)]
18pub struct Predicate {
19 pub xattr: Xattr,
20 pub relation: Relation,
21}
22
23impl Predicate {
24 pub fn new(xattr: Xattr, relation: Relation) -> Self {
25 Self { xattr, relation }
26 }
27 pub fn satisfied(&self, values: &Record) -> bool {
28 values
29 .get(&self.xattr)
30 .map_or(false, |v| self.relation.satisfied_by(v.clone()))
31 }
32}
33
34pub fn parse_predicate(i: &[u8]) -> IResult<&[u8], Predicate> {
40 pair(terminated(parse_xattr, space), parse_relation)(i)
41 .map(|(i, (xattr, relation))| (i, Predicate { xattr, relation }))
42}
43
44#[derive(Clone)]
45pub struct PredicateParser;
46impl TypedValueParser for PredicateParser {
47 type Value = Predicate;
48
49 fn parse_ref(
50 &self,
51 _cmd: &Command,
52 _arg: Option<&Arg>,
53 value: &OsStr,
54 ) -> Result<Self::Value, clap::error::Error<clap::error::RichFormatter>> {
55 parse_predicate(value.to_string_lossy().as_bytes())
56 .map(|(_remainder, predicate)| predicate)
57 .map_err(|e| {
58 clap::error::Error::raw(
59 clap::error::ErrorKind::InvalidValue,
60 format!("Malformed WHERE clause: {}\n", e),
61 )
62 })
63 }
64}
65
66#[cfg(test)]
67mod test {
68 use crate::parser::{
69 relation::Relation,
70 xattr::{Namespace, Xattr},
71 };
72
73 use super::{parse_predicate, Predicate};
74 #[test]
75 fn test_parse_predicate() {
76 assert!(parse_predicate(b"").is_err());
77 assert!(parse_predicate(b"a + b").is_err());
78 assert!(parse_predicate(b"fakenamespace.lmnop").is_err());
79 assert_eq!(
80 parse_predicate(b"amplitude>=-2.4343").unwrap().1,
81 Predicate {
82 xattr: Xattr::new(Namespace::User, "amplitude"),
83 relation: Relation::gte(-2.4343f64)
84 }
85 );
86 assert_eq!(
87 parse_predicate(b"system.security <= -2").unwrap().1,
88 Predicate {
89 xattr: Xattr::new(Namespace::System, "security"),
90 relation: Relation::lte(-2f64)
91 }
92 );
93 assert_eq!(
94 parse_predicate(b"security.lmnop in [1,2,3]"),
95 Ok((
96 b"".as_slice(),
97 Predicate {
98 xattr: Xattr::new(Namespace::Security, "lmnop"),
99 relation: Relation::in_(vec![1f64, 2f64, 3f64])
100 }
101 ))
102 );
103 assert_eq!(
104 parse_predicate(b"trusted.trustfulness > 4"),
105 Ok((
106 b"".as_slice(),
107 Predicate {
108 xattr: Xattr::new(Namespace::Trusted, "trustfulness"),
109 relation: Relation::gt(4f64)
110 }
111 ))
112 );
113 assert_eq!(
114 parse_predicate(b"user.grumpiness < 4"),
115 Ok((
116 b"".as_slice(),
117 Predicate {
118 xattr: Xattr::new(Namespace::User, "grumpiness"),
119 relation: Relation::lt(4f64)
120 }
121 ))
122 );
123 }
124}