dittolive_ditto/dql/query_v2.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
use safer_ffi::char_p;
use crate::{error::DittoError, utils::zstr::ZString};
/// A DQL query string with its arguments
///
/// Most APIs in the Ditto SDK don't take a [`QueryV2`] directly, but instead take a generic
/// parameter that implements [`IntoQuery`], a trait implemented by types that can be turned into a
/// [`QueryV2`].
///
/// Common examples are:
/// - `String` (and string-like types)
/// - `(String, Args)` where `Args` is anything that implements `Serialize`
///
/// ```rust
/// # use dittolive_ditto::prelude::*;
/// # use serde_json::json;
/// let select_query = "SELECT * FROM cars".into_query();
///
/// let insert_query = (
/// "INSERT INTO cars DOCUMENTS (:doc)",
/// json!({"doc": {"foo": "bar"}}),
/// ).into_query();
/// ```
///
/// ## The `Args` type
///
/// This type is generic over its arguments, with the requirement that the arguments must:
/// - implement `Serialize`
/// - serialize to a map-like type (e.g. [`serde_json::json!({"some":
/// "object"})`][serde_json::json] or [`HashMap`][std::collections::HashMap], not a `String`)
///
/// If the arguments are mutated using interior mutability, the result is not specified and may
/// cause logic errors. Note that this is **not** [undefined behaviour][ub], since any errors will
/// be confined to this instance of [`QueryV2`].
///
/// When no arguments are provided, the `Args` type defaults to `()`
///
/// [ub]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
#[derive(Debug, Clone, PartialEq)]
pub struct QueryV2<Args> {
pub(crate) string: ZString,
pub(crate) args: Args,
pub(crate) args_cbor: Option<Vec<u8>>,
}
/// Types which can be used to construct a [`QueryV2`].
///
/// The main implementors are:
/// - String types (e.g. `String`, `&str`, etc.)
/// - Tuples `(S, A)`, where `S` is a string type, and `A` implements [`Serialize`]
///
/// This conversion may be fallible.
///
/// Note that, due to historical naming reasons, this trait is not used to create a [`Query`].
///
/// [`Query`]: super::query::Query
/// [`Serialize`]: serde::Serialize
pub trait IntoQuery {
/// The type of the arguments provided with this query
type Args;
/// Convert this object into a [`QueryV2`]
fn into_query(self) -> Result<QueryV2<Self::Args>, DittoError>;
}
/// A query implements [`IntoQuery`] :)
impl<A> IntoQuery for QueryV2<A> {
type Args = A;
fn into_query(self) -> Result<QueryV2<Self::Args>, DittoError> {
Ok(self)
}
}
impl<Q: ?Sized> IntoQuery for &Q
where
Q: ToOwned,
Q::Owned: IntoQuery,
{
type Args = <Q::Owned as IntoQuery>::Args;
fn into_query(self) -> Result<QueryV2<Self::Args>, DittoError> {
let owned = self.to_owned();
owned.into_query()
}
}
impl<Q, A> IntoQuery for (Q, A)
where
Q: IntoQuery<Args = ()>,
A: serde::Serialize,
{
type Args = A;
fn into_query(self) -> Result<QueryV2<Self::Args>, DittoError> {
let (string, args) = self;
let string = string.into_query()?.string;
let args_cbor = serde_cbor::to_vec(&args).unwrap();
Ok(QueryV2 {
string,
args,
args_cbor: Some(args_cbor),
})
}
}
impl IntoQuery for String {
type Args = ();
fn into_query(self) -> Result<QueryV2<Self::Args>, DittoError> {
Ok(QueryV2 {
string: char_p::new(self).into(),
args: (),
args_cbor: None,
})
}
}