diff --git a/src/query/mod.rs b/src/query/mod.rs index 75a8cbf8a2abf3cf19929a2a691e0e6757941e0e..ee8d74e2fe3cde4b59b701347fc168952febf496 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -273,7 +273,7 @@ impl ops::Operable for Scalar {} /// /// This also allows combining complex queries, especially when a function is generic over a single type /// `T` that implements `Operable`, you can convert the existing type into this to make it work. -/// This can be done easily with the [`Expression`] trait which has a blanket implementation for all types +/// This can be done easily with the [`QueryExt`] trait which has a blanket implementation for all types /// which implement [`Operable`] (all query builder API types). /// /// Note: This is generally not recommended unless it is necessary to use. This is because the query builder @@ -320,30 +320,6 @@ impl From<String> for RawExpr { } } -/// Helper trait to convert a query builder API type into a [`RawExpr`] type -/// -/// This is useful when a function takes a sequence (array/vector/iterator) of a generic type `T: Operable` -/// but you want to combine different types (produced from query fns or other complex types). -/// -/// This trait has a blanket implementation for all `T` that implement `Display` which include types -/// that implement `Operable` -/// -/// While the `String` type could have been allowed to be part of the builder API directly, -/// it is by design that a user must explicitly turn a string into a [`RawExpr`] saying that -/// they're opting in to use a raw string in the query builder API. -pub trait Expression { - /// Build the query early and produce a [`RawExpr`] - /// - /// See the type's documentation for additional details. - fn to_expr(&self) -> RawExpr; -} - -impl<T: Display> Expression for T { - fn to_expr(&self) -> RawExpr { - RawExpr(self.to_string()) - } -} - impl IntoQuery for RawExpr { type Target = String; fn into_query(self) -> Self::Target { @@ -360,6 +336,54 @@ impl Display for RawExpr { } } +/// Additional/extended query builder API functionality +/// +/// This trait has a blanket implementation for all types that implement [`Operable`] +pub trait QueryExt { + /// Convert a query builder API type into a [`RawExpr`] type + /// + /// This is useful when a function takes a sequence (array/vector/iterator) of a generic type `T: Operable` + /// but you want to combine different types (produced from query fns or other complex types). + /// + /// While the `String` type could have been allowed to be part of the builder API directly, + /// it is by design that a user must explicitly turn a string into a [`RawExpr`] saying that + /// they're opting in to use a raw string in the query builder API. + fn to_expr(&self) -> RawExpr; + /// Call a function or closure on `Self` to transform it into another type + /// + /// This is similar to the `Iterator's` `map` method, except this is eagerly evaluated and operates on a single item. + /// This is useful for building complex queries that have many nested functions. + /// + /// For example: + /// ``` + /// # use mquery::query::{Metric, QueryExt, fns::aggregate::sum}; + /// // the usual way + /// let nest = sum(Metric::new("http_requests")); + /// // with `then` + /// let chain = Metric::new("http_requests").then(sum); + /// assert_eq!(nest.to_string(), chain.to_string()); + /// ``` + /// + /// > Note: the reason this function is called `then` instead of `pipe` or `map` is because `then` semantically provides a clearer + /// > idea of what's happening when writing/reading the complex queries. + fn then<R>(self, func: impl FnOnce(Self) -> R) -> R + where + Self: Sized; +} + +impl<T: Operable> QueryExt for T { + fn to_expr(&self) -> RawExpr { + RawExpr(self.to_string()) + } + + fn then<R>(self, func: impl FnOnce(Self) -> R) -> R + where + Self: Sized, + { + func(self) + } +} + #[cfg(test)] mod tests { use super::*;