Skip to main content

dittolive_ditto/dql/
query_v2.rs

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