Skip to content
Snippets Groups Projects
Commit 14d6f12f authored by Maaz Ahmed's avatar Maaz Ahmed
Browse files

feat(qol): raw expression type to allow strings in the builder API

This improves the API for query fns that take a sequence of generic arguments, as any type can be converted into a `RawExpr`.
parent 1f9b5519
No related branches found
No related tags found
1 merge request!18Resolve "QoL: improve query fns that take a sequence of generic arguments"
......@@ -50,6 +50,7 @@ commit_preprocessors = [
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^[f,F]eat", group = "Features" },
{ message = "^feat(qol)|qol", group = "QoL Enhancements"},
{ message = "^fix", group = "Bug Fixes" },
{ message = "^doc", group = "Documentation" },
{ message = "^perf", group = "Performance" },
......
......@@ -7,6 +7,8 @@ use std::fmt::Display;
use crate::seal::Sealed;
use self::ops::Operable;
pub mod fns;
pub mod ops;
......@@ -267,6 +269,97 @@ impl Display for Scalar {
impl Sealed for Scalar {}
impl ops::Operable for Scalar {}
/// Raw Expression type that allows raw strings to be used in the builder API where necessary
///
/// 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
/// 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
/// API is lazy. It doesn't build the query until `to_string` or `into_query` methods are called. This avoids
/// many intermediate string allocations. The [`RawExpr`] type, however, is built with a `String`, which means
/// that when you turn `T: Operable` into [`RawExpr`], the `to_string` method is called early.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RawExpr(String);
impl RawExpr {
/// Construct a raw expression type from a string
///
/// See type level docs for additional details
fn new<T: ToOwned<Owned = String>>(query: T) -> Self {
RawExpr(query.to_owned())
}
/// Construct a raw expression type from a type that that implements `Operable`
///
/// See type level docs for additional details
fn from_expr<E: Operable>(expr: E) -> Self {
RawExpr(expr.to_string())
}
/// Get the inner string
fn into_inner(self) -> String {
self.0
}
}
impl AsRef<str> for RawExpr {
fn as_ref(&self) -> &str {
self.0.as_str()
}
}
impl From<&str> for RawExpr {
fn from(value: &str) -> Self {
RawExpr(value.to_owned())
}
}
impl From<String> for RawExpr {
fn from(value: String) -> Self {
RawExpr(value)
}
}
/// 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 {
self.0
}
}
impl Operable for RawExpr {}
impl Sealed for RawExpr {}
impl Display for RawExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment