diff --git a/src/x/query.rs b/src/x/query.rs index 53d98539..c7cc6d17 100644 --- a/src/x/query.rs +++ b/src/x/query.rs @@ -10,6 +10,51 @@ use std::fmt; pub trait Query { /// Run this query for a given window ID. fn run(&self, id: Xid, x: &X) -> Result; + + /// Combine this query with another query using a logical AND. + /// + /// This follows typical short-circuiting behavior, i.e. if the first query + /// returns false, the second query will not be run. + fn and(self, other: Other) -> AndQuery + where + Self: Sized + 'static, + Other: Query + 'static, + { + AndQuery { + first: Box::new(self), + second: Box::new(other), + _phantom: std::marker::PhantomData, + } + } + + /// Combine this query with another query using a logical OR. + /// + /// This follows typical short-circuiting behavior, i.e. if the first query + /// returns true, the second query will not be run. + fn or(self, other: Other) -> OrQuery + where + Self: Sized + 'static, + Other: Query + 'static, + { + OrQuery { + first: Box::new(self), + second: Box::new(other), + _phantom: std::marker::PhantomData, + } + } + + /// Apply a logical NOT to this query. + /// + /// This will invert the result of the query. + fn not(self) -> NotQuery + where + Self: Sized + 'static, + { + NotQuery { + inner: Box::new(self), + _phantom: std::marker::PhantomData, + } + } } impl fmt::Debug for Box> { @@ -98,3 +143,44 @@ where } } } + +/// A meta [Query] for combining two queries with a logical AND. +#[derive(Debug)] +pub struct AndQuery { + first: Box>, + second: Box>, + _phantom: std::marker::PhantomData, +} + +impl Query for AndQuery { + fn run(&self, id: Xid, x: &X) -> Result { + Ok(self.first.run(id, x)? && self.second.run(id, x)?) + } +} + +/// A meta [Query] for combining two queries with a logical OR. +#[derive(Debug)] +pub struct OrQuery { + first: Box>, + second: Box>, + _phantom: std::marker::PhantomData, +} + +impl Query for OrQuery { + fn run(&self, id: Xid, x: &X) -> Result { + Ok(self.first.run(id, x)? || self.second.run(id, x)?) + } +} + +/// A meta [Query] for applying a logical NOT to a query. +#[derive(Debug)] +pub struct NotQuery { + inner: Box>, + _phantom: std::marker::PhantomData, +} + +impl Query for NotQuery { + fn run(&self, id: Xid, x: &X) -> Result { + Ok(!self.inner.run(id, x)?) + } +}