tendermint_rpc/
query.rs

1//! Structured querying for the Tendermint RPC event subscription system.
2//!
3//! See [`Query`] for details as to how to construct queries.
4//!
5//! [`Query`]: struct.Query.html
6
7use 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/// A structured query for use in interacting with the Tendermint RPC event
18/// subscription system.
19///
20/// Allows for compile-time validation of queries.
21///
22/// See the [subscribe endpoint documentation] for more details.
23///
24/// ## Examples
25///
26/// ### Direct construction of queries
27///
28/// ```rust
29/// use tendermint_rpc::query::{Query, EventType};
30///
31/// let query = Query::from(EventType::NewBlock);
32/// assert_eq!("tm.event = 'NewBlock'", query.to_string());
33///
34/// let query = Query::from(EventType::Tx).and_eq("tx.hash", "XYZ");
35/// assert_eq!("tm.event = 'Tx' AND tx.hash = 'XYZ'", query.to_string());
36///
37/// let query = Query::from(EventType::Tx).and_gte("tx.height", 100_u64);
38/// assert_eq!("tm.event = 'Tx' AND tx.height >= 100", query.to_string());
39/// ```
40///
41/// ### Query parsing
42///
43/// ```rust
44/// use tendermint_rpc::query::{Query, EventType};
45///
46/// let query: Query = "tm.event = 'NewBlock'".parse().unwrap();
47/// assert_eq!(query, Query::from(EventType::NewBlock));
48///
49/// let query: Query = "tm.event = 'Tx' AND tx.hash = 'XYZ'".parse().unwrap();
50/// assert_eq!(query, Query::from(EventType::Tx).and_eq("tx.hash", "XYZ"));
51///
52/// let query: Query = "tm.event = 'Tx' AND tx.height >= 100".parse().unwrap();
53/// assert_eq!(query, Query::from(EventType::Tx).and_gte("tx.height", 100_u64));
54/// ```
55///
56/// [subscribe endpoint documentation]: https://docs.tendermint.com/v0.34/rpc/#/Websocket/subscribe
57#[derive(Debug, Clone, PartialEq)]
58pub struct Query {
59    // We can only have at most one event type at present in a query.
60    pub event_type: Option<EventType>,
61    // We can have zero or more additional conditions associated with a query.
62    // Conditions are currently exclusively joined by logical ANDs.
63    pub conditions: Vec<Condition>,
64}
65
66impl Query {
67    /// Query constructor testing whether `<key> = <value>`
68    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    /// Query constructor testing whether `<key> < <value>`
76    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    /// Query constructor testing whether `<key> <= <value>`
84    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    /// Query constructor testing whether `<key> > <value>`
92    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    /// Query constructor testing whether `<key> >= <value>`
100    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    /// Query constructor testing whether `<key> CONTAINS <value>` (assuming
108    /// `key` contains a string, this tests whether `value` is a sub-string
109    /// within it).
110    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    /// Query constructor testing whether `<key> EXISTS`.
118    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    /// Add the condition `<key> = <value>` to the query.
126    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    /// Add the condition `<key> < <value>` to the query.
133    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    /// Add the condition `<key> <= <value>` to the query.
140    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    /// Add the condition `<key> > <value>` to the query.
147    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    /// Add the condition `<key> >= <value>` to the query.
154    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    /// Add the condition `<key> CONTAINS <value>` to the query.
161    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    /// Add the condition `<key> EXISTS` to the query.
168    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    /// An empty query matches any set of events. See [these docs].
176    ///
177    /// [these docs]: https://godoc.org/github.com/tendermint/tendermint/libs/pubsub/query#Empty
178    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        // Some or no whitespace.
214        rule _() = quiet!{[' ']*}
215
216        // At least some whitespace.
217        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/// A term in a query is either an event type or a general condition.
337/// Exclusively used for query parsing.
338#[derive(Debug)]
339pub enum Term {
340    EventType(EventType),
341    Condition(Condition),
342}
343
344// Separate a list of terms into lists of each type of term.
345fn 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/// The types of Tendermint events for which we can query at present.
396#[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/// A condition which is part of a [`Query`].
424#[derive(Debug, Clone, PartialEq)]
425pub struct Condition {
426    /// The key this condition applies to.
427    pub key: String,
428    /// The operation to apply to the key or its value,
429    /// depending on the type of operation.
430    pub operation: Operation,
431}
432
433impl Condition {
434    /// Create a new condition that applies the given `operation` to the `key`
435    pub fn new(key: String, operation: Operation) -> Self {
436        Self { key, operation }
437    }
438
439    /// Check if the value for the key is equal to this operand
440    pub fn eq(key: String, op: Operand) -> Self {
441        Self::new(key, Operation::Eq(op))
442    }
443
444    /// Check if the value for the key is less than this operand
445    pub fn lt(key: String, op: Operand) -> Self {
446        Self::new(key, Operation::Lt(op))
447    }
448
449    /// Check if the value for the key is less than or equal to this operand
450    pub fn lte(key: String, op: Operand) -> Self {
451        Self::new(key, Operation::Lte(op))
452    }
453
454    /// Check if the value for the key is greater than this operand
455    pub fn gt(key: String, op: Operand) -> Self {
456        Self::new(key, Operation::Gt(op))
457    }
458
459    /// Check if the value for the key is greater than or equal to this operand
460    pub fn gte(key: String, op: Operand) -> Self {
461        Self::new(key, Operation::Gte(op))
462    }
463
464    /// Check if the value for the key contains a certain sub-string
465    pub fn contains(key: String, op: String) -> Self {
466        Self::new(key, Operation::Contains(op))
467    }
468
469    /// Check if the key exists
470    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/// The different types of operations supported by a [`Query`].
490///
491/// Those operations apply to a given `key`, which is part of
492/// the enclosing [`Condition`].
493#[derive(Debug, Clone, PartialEq)]
494pub enum Operation {
495    /// Check if the value for the key is equal to this operand
496    Eq(Operand),
497    /// Check if the value for the key is less than this operand
498    Lt(Operand),
499    /// Check if the value for the key is less than or equal to this operand
500    Lte(Operand),
501    /// Check if the value for the key is greater than this operand
502    Gt(Operand),
503    /// Check if the value for the key is greater than or equal to this operand
504    Gte(Operand),
505    /// Check if the value for the key contains a certain sub-string
506    Contains(String),
507    /// Check if the key exists
508    Exists,
509}
510
511/// A typed operand for use in an [`Condition`].
512///
513/// According to the [Tendermint RPC subscribe docs][tm-subscribe],
514/// an operand can be a string, number, date or time. We differentiate here
515/// between integer and floating point numbers.
516///
517/// [`Condition`]: enum.Condition.html
518/// [tm-subscribe]: https://docs.tendermint.com/v0.34/rpc/#/Websocket/subscribe
519#[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
650/// Escape backslashes and single quotes within the given string with a backslash.
651fn 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        // Test the empty query (that matches all possible events)
762        let query = Query::from_str("").unwrap();
763        assert_eq!(query, Query::default());
764
765        // With just one event type
766        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        // One event type, with whitespace
774        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        // Two event types are not allowed
779        assert!(Query::from_str("tm.event='Tx' AND tm.event='NewBlock'").is_err());
780    }
781
782    #[test]
783    fn query_string_term_parsing() {
784        // Query with string term
785        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        // Query with string term, with extra whitespace
795        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        // Positive floating point number
877        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        // Negative floating point number
896        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    // From https://stackoverflow.com/a/41447964/1156132
916    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}