1use core::{fmt, str::FromStr};
8
9use time::{
10 format_description::well_known::Rfc3339,
11 macros::{format_description, offset},
12 Date, OffsetDateTime,
13};
14
15use crate::{prelude::*, serializers::timestamp, Error};
16
17#[derive(Debug, Clone, PartialEq)]
58pub struct Query {
59 pub event_type: Option<EventType>,
61 pub conditions: Vec<Condition>,
64}
65
66impl Query {
67 pub fn eq(key: impl ToString, value: impl Into<Operand>) -> Self {
69 Self {
70 event_type: None,
71 conditions: vec![Condition::eq(key.to_string(), value.into())],
72 }
73 }
74
75 pub fn lt(key: impl ToString, value: impl Into<Operand>) -> Self {
77 Self {
78 event_type: None,
79 conditions: vec![Condition::lt(key.to_string(), value.into())],
80 }
81 }
82
83 pub fn lte(key: impl ToString, value: impl Into<Operand>) -> Self {
85 Self {
86 event_type: None,
87 conditions: vec![Condition::lte(key.to_string(), value.into())],
88 }
89 }
90
91 pub fn gt(key: impl ToString, value: impl Into<Operand>) -> Self {
93 Self {
94 event_type: None,
95 conditions: vec![Condition::gt(key.to_string(), value.into())],
96 }
97 }
98
99 pub fn gte(key: impl ToString, value: impl Into<Operand>) -> Self {
101 Self {
102 event_type: None,
103 conditions: vec![Condition::gte(key.to_string(), value.into())],
104 }
105 }
106
107 pub fn contains(key: impl ToString, value: impl ToString) -> Self {
111 Self {
112 event_type: None,
113 conditions: vec![Condition::contains(key.to_string(), value.to_string())],
114 }
115 }
116
117 pub fn exists(key: impl ToString) -> Self {
119 Self {
120 event_type: None,
121 conditions: vec![Condition::exists(key.to_string())],
122 }
123 }
124
125 pub fn and_eq(mut self, key: impl ToString, value: impl Into<Operand>) -> Self {
127 self.conditions
128 .push(Condition::eq(key.to_string(), value.into()));
129 self
130 }
131
132 pub fn and_lt(mut self, key: impl ToString, value: impl Into<Operand>) -> Self {
134 self.conditions
135 .push(Condition::lt(key.to_string(), value.into()));
136 self
137 }
138
139 pub fn and_lte(mut self, key: impl ToString, value: impl Into<Operand>) -> Self {
141 self.conditions
142 .push(Condition::lte(key.to_string(), value.into()));
143 self
144 }
145
146 pub fn and_gt(mut self, key: impl ToString, value: impl Into<Operand>) -> Self {
148 self.conditions
149 .push(Condition::gt(key.to_string(), value.into()));
150 self
151 }
152
153 pub fn and_gte(mut self, key: impl ToString, value: impl Into<Operand>) -> Self {
155 self.conditions
156 .push(Condition::gte(key.to_string(), value.into()));
157 self
158 }
159
160 pub fn and_contains(mut self, key: impl ToString, value: impl ToString) -> Self {
162 self.conditions
163 .push(Condition::contains(key.to_string(), value.to_string()));
164 self
165 }
166
167 pub fn and_exists(mut self, key: impl ToString) -> Self {
169 self.conditions.push(Condition::exists(key.to_string()));
170 self
171 }
172}
173
174impl Default for Query {
175 fn default() -> Self {
179 Self {
180 event_type: None,
181 conditions: Vec::new(),
182 }
183 }
184}
185
186impl From<EventType> for Query {
187 fn from(t: EventType) -> Self {
188 Self {
189 event_type: Some(t),
190 conditions: Vec::new(),
191 }
192 }
193}
194
195impl fmt::Display for Query {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 if let Some(t) = &self.event_type {
198 write!(f, "tm.event = '{t}'")?;
199
200 if !self.conditions.is_empty() {
201 write!(f, " AND ")?;
202 }
203 }
204
205 join(f, " AND ", &self.conditions)?;
206
207 Ok(())
208 }
209}
210
211peg::parser! {
212 grammar query_parser() for str {
213 rule _() = quiet!{[' ']*}
215
216 rule __() = quiet!{[' ']+}
218
219 rule string() -> &'input str
220 = "'" s:$([^'\'']*) "'" { s }
221
222 rule unsigned() -> u64
223 = s:$(['0'..='9']+) {?
224 u64::from_str(s)
225 .map_err(|_| "failed to parse as an unsigned integer")
226 }
227
228 rule signed() -> i64
229 = s:$("-" ['1'..='9'] ['0'..='9']*) {?
230 i64::from_str(s)
231 .map_err(|_| "failed to parse as a signed integer")
232 }
233
234 rule year() -> &'input str
235 = $(['0'..='9']*<4>)
236
237 rule month() -> &'input str
238 = $(['0' | '1'] ['0'..='9'])
239
240 rule day() -> &'input str
241 = $(['0'..='3'] ['0'..='9'])
242
243 rule date() -> &'input str
244 = $(year() "-" month() "-" day())
245
246 rule hour() -> &'input str
247 = $(['0'..='2'] ['0'..='9'])
248
249 rule min_sec() -> &'input str
250 = $(['0'..='5'] ['0'..='9'])
251
252 rule nanosec() -> &'input str
253 = $("." ['0'..='9']+)
254
255 rule time() -> &'input str
256 = $(hour() ":" min_sec() ":" min_sec() nanosec()? "Z")
257
258 rule datetime() -> &'input str
259 = dt:$(date() "T" time()) { dt }
260
261 rule float() -> f64
262 = s:$("-"? ['0'..='9']+ "." ['0'..='9']+) {?
263 f64::from_str(s)
264 .map_err(|_| "failed to parse as a 64-bit floating point number")
265 }
266
267 rule string_op() -> Operand
268 = s:string() { Operand::String(s.to_owned()) }
269
270 rule unsigned_op() -> Operand
271 = u:unsigned() { Operand::Unsigned(u) }
272
273 rule signed_op() -> Operand
274 = s:signed() { Operand::Signed(s) }
275
276 rule datetime_op() -> Operand
277 = "TIME" __ dt:datetime() {?
278 OffsetDateTime::parse(dt, &Rfc3339)
279 .map(|dt| Operand::DateTime(dt.to_offset(offset!(UTC))))
280 .map_err(|_| "failed to parse as RFC3339-compatible date/time")
281 }
282
283 rule date_op() -> Operand
284 = "DATE" __ dt:date() {?
285 let date = Date::parse(dt, &format_description!("[year]-[month]-[day]"))
286 .map_err(|_| "failed to parse as RFC3339-compatible date")?;
287 Ok(Operand::Date(date))
288 }
289
290 rule float_op() -> Operand
291 = f:float() { Operand::Float(f) }
292
293 rule tag() -> &'input str
294 = $(['a'..='z' | 'A'..='Z'] ['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.']*)
295
296 rule operand() -> Operand
297 = datetime_op() / date_op() / string_op() / float_op() / signed_op() / unsigned_op()
298
299 rule eq() -> Condition
300 = t:tag() _ "=" _ op:operand() { Condition::eq(t.to_owned(), op) }
301
302 rule lte() -> Condition
303 = t:tag() _ "<=" _ op:operand() { Condition::lte(t.to_owned(), op) }
304
305 rule lt() -> Condition
306 = t:tag() _ "<" _ op:operand() { Condition::lt(t.to_owned(), op) }
307
308 rule gte() -> Condition
309 = t:tag() _ ">=" _ op:operand() { Condition::gte(t.to_owned(), op) }
310
311 rule gt() -> Condition
312 = t:tag() _ ">" _ op:operand() { Condition::gt(t.to_owned(), op) }
313
314 rule contains() -> Condition
315 = t:tag() __ "CONTAINS" __ op:string() { Condition::contains(t.to_owned(), op.to_owned()) }
316
317 rule exists() -> Condition
318 = t:tag() __ "EXISTS" { Condition::exists(t.to_owned()) }
319
320 rule event_type() -> Term
321 = "tm.event" _ "=" _ "'" et:$("NewBlock" / "Tx") "'" {
322 Term::EventType(EventType::from_str(et).unwrap())
323 }
324
325 rule condition() -> Term
326 = c:(eq() / lte() / lt() / gte() / gt() / contains() / exists()) { Term::Condition(c) }
327
328 rule term() -> Term
329 = event_type() / condition()
330
331 pub rule query() -> Vec<Term>
332 = t:term() ** ( __ "AND" __ ) { t }
333 }
334}
335
336#[derive(Debug)]
339pub enum Term {
340 EventType(EventType),
341 Condition(Condition),
342}
343
344fn separate_terms(terms: Vec<Term>) -> (Vec<EventType>, Vec<Condition>) {
346 terms
347 .into_iter()
348 .fold((Vec::new(), Vec::new()), |mut v, t| {
349 match t {
350 Term::EventType(et) => v.0.push(et),
351 Term::Condition(c) => v.1.push(c),
352 }
353 v
354 })
355}
356
357impl FromStr for Query {
358 type Err = Error;
359
360 fn from_str(s: &str) -> Result<Self, Self::Err> {
361 let (event_types, conditions) = separate_terms(
362 query_parser::query(s)
363 .map_err(|e| Error::invalid_params(format!("failed to parse query: {e}")))?,
364 );
365 if event_types.len() > 1 {
366 return Err(Error::invalid_params(
367 "tm.event can only be used once in a query".to_owned(),
368 ));
369 }
370 Ok(Query {
371 event_type: event_types.first().cloned(),
372 conditions,
373 })
374 }
375}
376
377fn join<S, I>(f: &mut fmt::Formatter<'_>, separator: S, iterable: I) -> fmt::Result
378where
379 S: fmt::Display,
380 I: IntoIterator,
381 I::Item: fmt::Display,
382{
383 let mut iter = iterable.into_iter();
384 if let Some(first) = iter.next() {
385 write!(f, "{first}")?;
386 }
387
388 for item in iter {
389 write!(f, "{separator}{item}")?;
390 }
391
392 Ok(())
393}
394
395#[derive(Debug, Clone, PartialEq, Eq)]
397pub enum EventType {
398 NewBlock,
399 Tx,
400}
401
402impl fmt::Display for EventType {
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 match self {
405 EventType::NewBlock => write!(f, "NewBlock"),
406 EventType::Tx => write!(f, "Tx"),
407 }
408 }
409}
410
411impl FromStr for EventType {
412 type Err = Error;
413
414 fn from_str(s: &str) -> Result<Self, Error> {
415 match s {
416 "NewBlock" => Ok(Self::NewBlock),
417 "Tx" => Ok(Self::Tx),
418 invalid => Err(Error::unrecognized_event_type(invalid.to_string())),
419 }
420 }
421}
422
423#[derive(Debug, Clone, PartialEq)]
425pub struct Condition {
426 pub key: String,
428 pub operation: Operation,
431}
432
433impl Condition {
434 pub fn new(key: String, operation: Operation) -> Self {
436 Self { key, operation }
437 }
438
439 pub fn eq(key: String, op: Operand) -> Self {
441 Self::new(key, Operation::Eq(op))
442 }
443
444 pub fn lt(key: String, op: Operand) -> Self {
446 Self::new(key, Operation::Lt(op))
447 }
448
449 pub fn lte(key: String, op: Operand) -> Self {
451 Self::new(key, Operation::Lte(op))
452 }
453
454 pub fn gt(key: String, op: Operand) -> Self {
456 Self::new(key, Operation::Gt(op))
457 }
458
459 pub fn gte(key: String, op: Operand) -> Self {
461 Self::new(key, Operation::Gte(op))
462 }
463
464 pub fn contains(key: String, op: String) -> Self {
466 Self::new(key, Operation::Contains(op))
467 }
468
469 pub fn exists(key: String) -> Self {
471 Self::new(key, Operation::Exists)
472 }
473}
474
475impl fmt::Display for Condition {
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 match &self.operation {
478 Operation::Eq(op) => write!(f, "{} = {}", self.key, op),
479 Operation::Lt(op) => write!(f, "{} < {}", self.key, op),
480 Operation::Lte(op) => write!(f, "{} <= {}", self.key, op),
481 Operation::Gt(op) => write!(f, "{} > {}", self.key, op),
482 Operation::Gte(op) => write!(f, "{} >= {}", self.key, op),
483 Operation::Contains(op) => write!(f, "{} CONTAINS {}", self.key, escape(op)),
484 Operation::Exists => write!(f, "{} EXISTS", self.key),
485 }
486 }
487}
488
489#[derive(Debug, Clone, PartialEq)]
494pub enum Operation {
495 Eq(Operand),
497 Lt(Operand),
499 Lte(Operand),
501 Gt(Operand),
503 Gte(Operand),
505 Contains(String),
507 Exists,
509}
510
511#[derive(Debug, Clone, PartialEq)]
520pub enum Operand {
521 String(String),
522 Signed(i64),
523 Unsigned(u64),
524 Float(f64),
525 Date(Date),
526 DateTime(OffsetDateTime),
527}
528
529impl fmt::Display for Operand {
530 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531 match self {
532 Operand::String(s) => write!(f, "{}", escape(s)),
533 Operand::Signed(i) => write!(f, "{i}"),
534 Operand::Unsigned(u) => write!(f, "{u}"),
535 Operand::Float(h) => write!(f, "{h}"),
536 Operand::Date(d) => {
537 write!(f, "DATE ")?;
538 fmt_date(*d, f)?;
539 Ok(())
540 },
541 Operand::DateTime(dt) => {
542 write!(f, "TIME ")?;
543 timestamp::fmt_as_rfc3339_nanos(*dt, f)?;
544 Ok(())
545 },
546 }
547 }
548}
549
550fn fmt_date(d: Date, mut f: impl fmt::Write) -> fmt::Result {
551 write!(f, "{:04}-{:02}-{:02}", d.year(), d.month() as u8, d.day())
552}
553
554impl From<String> for Operand {
555 fn from(source: String) -> Self {
556 Operand::String(source)
557 }
558}
559
560impl From<char> for Operand {
561 fn from(source: char) -> Self {
562 Operand::String(source.to_string())
563 }
564}
565
566impl From<&str> for Operand {
567 fn from(source: &str) -> Self {
568 Operand::String(source.to_string())
569 }
570}
571
572impl From<i64> for Operand {
573 fn from(source: i64) -> Self {
574 Operand::Signed(source)
575 }
576}
577
578impl From<i32> for Operand {
579 fn from(source: i32) -> Self {
580 Operand::Signed(source as i64)
581 }
582}
583
584impl From<i16> for Operand {
585 fn from(source: i16) -> Self {
586 Operand::Signed(source as i64)
587 }
588}
589
590impl From<i8> for Operand {
591 fn from(source: i8) -> Self {
592 Operand::Signed(source as i64)
593 }
594}
595
596impl From<u64> for Operand {
597 fn from(source: u64) -> Self {
598 Operand::Unsigned(source)
599 }
600}
601
602impl From<u32> for Operand {
603 fn from(source: u32) -> Self {
604 Operand::Unsigned(source as u64)
605 }
606}
607
608impl From<u16> for Operand {
609 fn from(source: u16) -> Self {
610 Operand::Unsigned(source as u64)
611 }
612}
613
614impl From<u8> for Operand {
615 fn from(source: u8) -> Self {
616 Operand::Unsigned(source as u64)
617 }
618}
619
620impl From<usize> for Operand {
621 fn from(source: usize) -> Self {
622 Operand::Unsigned(source as u64)
623 }
624}
625
626impl From<f64> for Operand {
627 fn from(source: f64) -> Self {
628 Operand::Float(source)
629 }
630}
631
632impl From<f32> for Operand {
633 fn from(source: f32) -> Self {
634 Operand::Float(source as f64)
635 }
636}
637
638impl From<Date> for Operand {
639 fn from(source: Date) -> Self {
640 Operand::Date(source)
641 }
642}
643
644impl From<OffsetDateTime> for Operand {
645 fn from(source: OffsetDateTime) -> Self {
646 Operand::DateTime(source.to_offset(offset!(UTC)))
647 }
648}
649
650fn escape(s: &str) -> String {
652 let mut result = String::new();
653 for ch in s.chars() {
654 if ch == '\\' || ch == '\'' {
655 result.push('\\');
656 }
657 result.push(ch);
658 }
659 format!("'{result}'")
660}
661
662#[cfg(test)]
663mod test {
664 use time::macros::{date, datetime};
665
666 use super::*;
667
668 #[test]
669 fn empty_query() {
670 let query = Query::default();
671 assert_eq!("", query.to_string());
672 }
673
674 #[test]
675 fn simple_event_type() {
676 let query = Query::from(EventType::NewBlock);
677 assert_eq!("tm.event = 'NewBlock'", query.to_string());
678
679 let query = Query::from(EventType::Tx);
680 assert_eq!("tm.event = 'Tx'", query.to_string());
681 }
682
683 #[test]
684 fn simple_condition() {
685 let query = Query::eq("key", "value");
686 assert_eq!("key = 'value'", query.to_string());
687
688 let query = Query::eq("key", 'v');
689 assert_eq!("key = 'v'", query.to_string());
690
691 let query = Query::eq("key", "'value'");
692 assert_eq!("key = '\\'value\\''", query.to_string());
693
694 let query = Query::eq("key", "\\'value'");
695 assert_eq!("key = '\\\\\\'value\\''", query.to_string());
696
697 let query = Query::lt("key", 42_i64);
698 assert_eq!("key < 42", query.to_string());
699
700 let query = Query::lt("key", 42_u64);
701 assert_eq!("key < 42", query.to_string());
702
703 let query = Query::lte("key", 42_i64);
704 assert_eq!("key <= 42", query.to_string());
705
706 let query = Query::gt("key", 42_i64);
707 assert_eq!("key > 42", query.to_string());
708
709 let query = Query::gte("key", 42_i64);
710 assert_eq!("key >= 42", query.to_string());
711
712 let query = Query::eq("key", 42_u8);
713 assert_eq!("key = 42", query.to_string());
714
715 let query = Query::contains("key", "some-substring");
716 assert_eq!("key CONTAINS 'some-substring'", query.to_string());
717
718 let query = Query::exists("key");
719 assert_eq!("key EXISTS", query.to_string());
720 }
721
722 #[test]
723 fn date_condition() {
724 let query = Query::eq("some_date", date!(2020 - 09 - 24));
725 assert_eq!("some_date = DATE 2020-09-24", query.to_string());
726 }
727
728 #[test]
729 fn date_time_condition() {
730 let query = Query::eq("some_date_time", datetime!(2020-09-24 10:17:23 -04:00));
731 assert_eq!(
732 "some_date_time = TIME 2020-09-24T14:17:23Z",
733 query.to_string()
734 );
735 }
736
737 #[test]
738 fn complex_query() {
739 let query = Query::from(EventType::Tx).and_eq("tx.height", 3_i64);
740 assert_eq!("tm.event = 'Tx' AND tx.height = 3", query.to_string());
741
742 let query = Query::from(EventType::Tx)
743 .and_lte("tx.height", 100_i64)
744 .and_eq("transfer.sender", "AddrA");
745 assert_eq!(
746 "tm.event = 'Tx' AND tx.height <= 100 AND transfer.sender = 'AddrA'",
747 query.to_string()
748 );
749
750 let query = Query::from(EventType::Tx)
751 .and_lte("tx.height", 100_i64)
752 .and_contains("meta.attr", "some-substring");
753 assert_eq!(
754 "tm.event = 'Tx' AND tx.height <= 100 AND meta.attr CONTAINS 'some-substring'",
755 query.to_string()
756 );
757 }
758
759 #[test]
760 fn query_event_type_parsing() {
761 let query = Query::from_str("").unwrap();
763 assert_eq!(query, Query::default());
764
765 let query = Query::from_str("tm.event='Tx'").unwrap();
767 assert_eq!(query.event_type, Some(EventType::Tx));
768 assert!(query.conditions.is_empty());
769 let query = Query::from_str("tm.event='NewBlock'").unwrap();
770 assert_eq!(query.event_type, Some(EventType::NewBlock));
771 assert!(query.conditions.is_empty());
772
773 let query = Query::from_str("tm.event = 'NewBlock'").unwrap();
775 assert_eq!(query.event_type, Some(EventType::NewBlock));
776 assert!(query.conditions.is_empty());
777
778 assert!(Query::from_str("tm.event='Tx' AND tm.event='NewBlock'").is_err());
780 }
781
782 #[test]
783 fn query_string_term_parsing() {
784 let query = Query::from_str("tm.event='Tx' AND transfer.sender='AddrA'").unwrap();
786 assert_eq!(query.event_type, Some(EventType::Tx));
787 assert_eq!(
788 query.conditions,
789 vec![Condition::eq(
790 "transfer.sender".to_owned(),
791 Operand::String("AddrA".to_owned()),
792 )]
793 );
794 let query = Query::from_str("tm.event = 'Tx' AND transfer.sender = 'AddrA'").unwrap();
796 assert_eq!(query.event_type, Some(EventType::Tx));
797 assert_eq!(
798 query.conditions,
799 vec![Condition::eq(
800 "transfer.sender".to_owned(),
801 Operand::String("AddrA".to_owned()),
802 )]
803 );
804 }
805
806 #[test]
807 fn query_unsigned_term_parsing() {
808 let query = Query::from_str("tm.event = 'Tx' AND tx.height = 10").unwrap();
809 assert_eq!(query.event_type, Some(EventType::Tx));
810 assert_eq!(
811 query.conditions,
812 vec![Condition::eq("tx.height".to_owned(), Operand::Unsigned(10))]
813 );
814
815 let query = Query::from_str("tm.event = 'Tx' AND tx.height <= 100").unwrap();
816 assert_eq!(query.event_type, Some(EventType::Tx));
817 assert_eq!(
818 query.conditions,
819 vec![Condition::lte(
820 "tx.height".to_owned(),
821 Operand::Unsigned(100)
822 )]
823 );
824 }
825
826 #[test]
827 fn query_signed_term_parsing() {
828 let query = Query::from_str("tm.event = 'Tx' AND some.value = -1").unwrap();
829 assert_eq!(query.event_type, Some(EventType::Tx));
830 assert_eq!(
831 query.conditions,
832 vec![Condition::eq("some.value".to_owned(), Operand::Signed(-1))]
833 );
834
835 let query = Query::from_str("tm.event = 'Tx' AND some.value <= -100").unwrap();
836 assert_eq!(query.event_type, Some(EventType::Tx));
837 assert_eq!(
838 query.conditions,
839 vec![Condition::lte(
840 "some.value".to_owned(),
841 Operand::Signed(-100)
842 )]
843 );
844 }
845
846 #[test]
847 fn query_date_parsing() {
848 let query = Query::from_str("tm.event = 'Tx' AND some.date <= DATE 2022-02-03").unwrap();
849 assert_eq!(query.event_type, Some(EventType::Tx));
850 assert_eq!(
851 query.conditions,
852 vec![Condition::lte(
853 "some.date".to_owned(),
854 Operand::Date(date!(2022 - 2 - 3))
855 )]
856 );
857 }
858
859 #[test]
860 fn query_datetime_parsing() {
861 let query =
862 Query::from_str("tm.event = 'Tx' AND some.datetime = TIME 2021-02-26T17:05:02.1495Z")
863 .unwrap();
864 assert_eq!(query.event_type, Some(EventType::Tx));
865 assert_eq!(
866 query.conditions,
867 vec![Condition::eq(
868 "some.datetime".to_owned(),
869 Operand::DateTime(datetime!(2021-2-26 17:05:02.149500000 UTC))
870 )]
871 )
872 }
873
874 #[test]
875 fn query_float_parsing() {
876 let query = Query::from_str("short.pi = 3.14159").unwrap();
878 assert_eq!(query.conditions.len(), 1);
879 match &query.conditions[0] {
880 Condition {
881 key: tag,
882 operation: Operation::Eq(op),
883 } => {
884 assert_eq!(tag, "short.pi");
885 match op {
886 Operand::Float(f) => {
887 assert!(floats_eq(*f, core::f64::consts::PI, 5));
888 },
889 _ => panic!("unexpected operand: {:?}", op),
890 }
891 },
892 c => panic!("unexpected condition: {:?}", c),
893 }
894
895 let query = Query::from_str("short.pi = -3.14159").unwrap();
897 assert_eq!(query.conditions.len(), 1);
898 match &query.conditions[0] {
899 Condition {
900 key: tag,
901 operation: Operation::Eq(op),
902 } => {
903 assert_eq!(tag, "short.pi");
904 match op {
905 Operand::Float(f) => {
906 assert!(floats_eq(*f, -core::f64::consts::PI, 5));
907 },
908 _ => panic!("unexpected operand: {:?}", op),
909 }
910 },
911 c => panic!("unexpected condition: {:?}", c),
912 }
913 }
914
915 fn floats_eq(a: f64, b: f64, precision: u8) -> bool {
917 let factor = 10.0f64.powi(precision as i32);
918 let a = (a * factor).trunc();
919 let b = (b * factor).trunc();
920 a == b
921 }
922
923 #[test]
924 fn query_conditions() {
925 let query = Query::from_str("some.field = 'string'").unwrap();
926 assert_eq!(
927 query,
928 Query {
929 event_type: None,
930 conditions: vec![Condition::eq(
931 "some.field".to_owned(),
932 Operand::String("string".to_owned())
933 )]
934 }
935 );
936
937 let query = Query::from_str("some.field < 5").unwrap();
938 assert_eq!(
939 query,
940 Query {
941 event_type: None,
942 conditions: vec![Condition::lt("some.field".to_owned(), Operand::Unsigned(5),)]
943 }
944 );
945
946 let query = Query::from_str("some.field <= 5").unwrap();
947 assert_eq!(
948 query,
949 Query {
950 event_type: None,
951 conditions: vec![Condition::lte(
952 "some.field".to_owned(),
953 Operand::Unsigned(5),
954 )]
955 }
956 );
957
958 let query = Query::from_str("some.field > 5").unwrap();
959 assert_eq!(
960 query,
961 Query {
962 event_type: None,
963 conditions: vec![Condition::gt("some.field".to_owned(), Operand::Unsigned(5),)]
964 }
965 );
966
967 let query = Query::from_str("some.field >= 5").unwrap();
968 assert_eq!(
969 query,
970 Query {
971 event_type: None,
972 conditions: vec![Condition::gte(
973 "some.field".to_owned(),
974 Operand::Unsigned(5),
975 )]
976 }
977 );
978
979 let query = Query::from_str("some.field CONTAINS 'inner'").unwrap();
980 assert_eq!(
981 query,
982 Query {
983 event_type: None,
984 conditions: vec![Condition::contains(
985 "some.field".to_owned(),
986 "inner".to_owned()
987 )]
988 }
989 );
990
991 let query = Query::from_str("some.field EXISTS").unwrap();
992 assert_eq!(
993 query,
994 Query {
995 event_type: None,
996 conditions: vec![Condition::exists("some.field".to_owned())]
997 }
998 );
999 }
1000}