Skip to content

Commit 2f67c78

Browse files
authored
Adding meta queries to support combinations of queries as queries. (#291)
* 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
1 parent 2e67f2a commit 2f67c78

File tree

1 file changed

+86
-0
lines changed

1 file changed

+86
-0
lines changed

src/x/query.rs

+86
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,51 @@ use std::fmt;
1010
pub trait Query<X: XConn> {
1111
/// Run this query for a given window ID.
1212
fn run(&self, id: Xid, x: &X) -> Result<bool>;
13+
14+
/// Combine this query with another query using a logical AND.
15+
///
16+
/// This follows typical short-circuiting behavior, i.e. if the first query
17+
/// returns false, the second query will not be run.
18+
fn and<Other>(self, other: Other) -> AndQuery<X>
19+
where
20+
Self: Sized + 'static,
21+
Other: Query<X> + 'static,
22+
{
23+
AndQuery {
24+
first: Box::new(self),
25+
second: Box::new(other),
26+
_phantom: std::marker::PhantomData,
27+
}
28+
}
29+
30+
/// Combine this query with another query using a logical OR.
31+
///
32+
/// This follows typical short-circuiting behavior, i.e. if the first query
33+
/// returns true, the second query will not be run.
34+
fn or<Other>(self, other: Other) -> OrQuery<X>
35+
where
36+
Self: Sized + 'static,
37+
Other: Query<X> + 'static,
38+
{
39+
OrQuery {
40+
first: Box::new(self),
41+
second: Box::new(other),
42+
_phantom: std::marker::PhantomData,
43+
}
44+
}
45+
46+
/// Apply a logical NOT to this query.
47+
///
48+
/// This will invert the result of the query.
49+
fn not(self) -> NotQuery<X>
50+
where
51+
Self: Sized + 'static,
52+
{
53+
NotQuery {
54+
inner: Box::new(self),
55+
_phantom: std::marker::PhantomData,
56+
}
57+
}
1358
}
1459

1560
impl<X: XConn> fmt::Debug for Box<dyn Query<X>> {
@@ -98,3 +143,44 @@ where
98143
}
99144
}
100145
}
146+
147+
/// A meta [Query] for combining two queries with a logical AND.
148+
#[derive(Debug)]
149+
pub struct AndQuery<X: XConn> {
150+
first: Box<dyn Query<X>>,
151+
second: Box<dyn Query<X>>,
152+
_phantom: std::marker::PhantomData<X>,
153+
}
154+
155+
impl<X: XConn> Query<X> for AndQuery<X> {
156+
fn run(&self, id: Xid, x: &X) -> Result<bool> {
157+
Ok(self.first.run(id, x)? && self.second.run(id, x)?)
158+
}
159+
}
160+
161+
/// A meta [Query] for combining two queries with a logical OR.
162+
#[derive(Debug)]
163+
pub struct OrQuery<X: XConn> {
164+
first: Box<dyn Query<X>>,
165+
second: Box<dyn Query<X>>,
166+
_phantom: std::marker::PhantomData<X>,
167+
}
168+
169+
impl<X: XConn> Query<X> for OrQuery<X> {
170+
fn run(&self, id: Xid, x: &X) -> Result<bool> {
171+
Ok(self.first.run(id, x)? || self.second.run(id, x)?)
172+
}
173+
}
174+
175+
/// A meta [Query] for applying a logical NOT to a query.
176+
#[derive(Debug)]
177+
pub struct NotQuery<X: XConn> {
178+
inner: Box<dyn Query<X>>,
179+
_phantom: std::marker::PhantomData<X>,
180+
}
181+
182+
impl<X: XConn> Query<X> for NotQuery<X> {
183+
fn run(&self, id: Xid, x: &X) -> Result<bool> {
184+
Ok(!self.inner.run(id, x)?)
185+
}
186+
}

0 commit comments

Comments
 (0)