Skip to content

Commit

Permalink
Adding meta queries to support combinations of queries as queries. (#291
Browse files Browse the repository at this point in the history
)

* Adding meta queries to support combinations of queries as queries.

- `AndQuery`
- `OrQuery`
- `NotQuery`
- `AnyQuery`
- `AllQuery`

* Add ergonomic extension trait to simplify creating Meta queries

* Make return types more explicit

* Make the changes requested by @sminez

* Add dynamic dispatch to combinator internal types

This makes the public interface more simple.

The `'static` bound is not terribly onerous, since we are not passing a
reference to `self`, we are consuming self, and returning a new thing.

Since owned objects are by definition `'static`, this shouldn't matter.

* Clean up doc comments
  • Loading branch information
favilo authored Jun 19, 2024
1 parent 2e67f2a commit 2f67c78
Showing 1 changed file with 86 additions and 0 deletions.
86 changes: 86 additions & 0 deletions src/x/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,51 @@ use std::fmt;
pub trait Query<X: XConn> {
/// Run this query for a given window ID.
fn run(&self, id: Xid, x: &X) -> Result<bool>;

/// 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<Other>(self, other: Other) -> AndQuery<X>
where
Self: Sized + 'static,
Other: Query<X> + '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<Other>(self, other: Other) -> OrQuery<X>
where
Self: Sized + 'static,
Other: Query<X> + '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<X>
where
Self: Sized + 'static,
{
NotQuery {
inner: Box::new(self),
_phantom: std::marker::PhantomData,
}
}
}

impl<X: XConn> fmt::Debug for Box<dyn Query<X>> {
Expand Down Expand Up @@ -98,3 +143,44 @@ where
}
}
}

/// A meta [Query] for combining two queries with a logical AND.
#[derive(Debug)]
pub struct AndQuery<X: XConn> {
first: Box<dyn Query<X>>,
second: Box<dyn Query<X>>,
_phantom: std::marker::PhantomData<X>,
}

impl<X: XConn> Query<X> for AndQuery<X> {
fn run(&self, id: Xid, x: &X) -> Result<bool> {
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<X: XConn> {
first: Box<dyn Query<X>>,
second: Box<dyn Query<X>>,
_phantom: std::marker::PhantomData<X>,
}

impl<X: XConn> Query<X> for OrQuery<X> {
fn run(&self, id: Xid, x: &X) -> Result<bool> {
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<X: XConn> {
inner: Box<dyn Query<X>>,
_phantom: std::marker::PhantomData<X>,
}

impl<X: XConn> Query<X> for NotQuery<X> {
fn run(&self, id: Xid, x: &X) -> Result<bool> {
Ok(!self.inner.run(id, x)?)
}
}

0 comments on commit 2f67c78

Please sign in to comment.