dittolive_ditto/dql/
query_result.rs1use std::{
2 num::NonZeroU64,
3 sync::{Arc, Mutex},
4};
5
6use ffi_sdk::{self, ffi_utils::repr_c};
7use serde::de::DeserializeOwned;
8
9use crate::{
10 error::DittoError, store::DocumentId, utils::extension_traits::FfiResultIntoRustResult,
11};
12
13type CborObject = ::std::collections::HashMap<Box<str>, ::serde_cbor::Value>;
14
15pub struct QueryResult {
21 raw: repr_c::Box<ffi_sdk::FfiQueryResult>,
22 count: usize,
23}
24
25impl From<repr_c::Box_<ffi_sdk::FfiQueryResult>> for QueryResult {
26 fn from(raw: repr_c::Box<ffi_sdk::FfiQueryResult>) -> QueryResult {
27 let count = ffi_sdk::dittoffi_query_result_item_count(&raw);
28 QueryResult { raw, count }
29 }
30}
31
32impl QueryResult {
33 pub fn get_item(&self, index: usize) -> Option<QueryResultItem> {
36 if index >= self.count {
37 return None;
38 }
39 Some(QueryResultItem::from(
40 ffi_sdk::dittoffi_query_result_item_at(&self.raw, index),
41 ))
42 }
43
44 pub fn item_count(&self) -> usize {
46 self.count
47 }
48
49 pub fn mutated_document_ids(&self) -> Vec<DocumentId> {
55 let mutated_document_number =
56 ffi_sdk::dittoffi_query_result_mutated_document_id_count(&self.raw);
57
58 (0..mutated_document_number)
59 .map(|idx| ffi_sdk::dittoffi_query_result_mutated_document_id_at(&self.raw, idx))
60 .map(|raw_slice| DocumentId::from(Box::<[u8]>::from(raw_slice)))
61 .collect()
62 }
63
64 pub fn commit_id(&self) -> Option<NonZeroU64> {
75 NonZeroU64::new(ffi_sdk::dittoffi_query_result_commit_id(&self.raw))
76 }
77}
78
79impl QueryResult {
80 pub fn iter(&self) -> impl '_ + Iterator<Item = QueryResultItem> {
82 self.into_iter()
83 }
84}
85
86mod sealed {
87 pub struct QueryResultIterator<'iter> {
88 pub(super) query_result: &'iter super::QueryResult,
89 pub(super) idx: usize,
90 }
91}
92use self::sealed::QueryResultIterator;
93
94impl<'iter> IntoIterator for &'iter QueryResult {
95 type IntoIter = QueryResultIterator<'iter>;
96 type Item = QueryResultItem;
97
98 fn into_iter(self) -> QueryResultIterator<'iter> {
99 QueryResultIterator {
100 query_result: self,
101 idx: 0,
102 }
103 }
104}
105
106impl Iterator for QueryResultIterator<'_> {
107 type Item = QueryResultItem;
108
109 fn next(&mut self) -> Option<Self::Item> {
110 let return_value = self.query_result.get_item(self.idx);
111 if return_value.is_some() {
112 self.idx += 1;
113 }
114 return_value
115 }
116}
117
118pub struct QueryResultItem {
138 pub(super) raw: repr_c::Arc_<ffi_sdk::FfiQueryResultItem>,
140 materialized_value: Mutex<Option<Arc<CborObject>>>,
141}
142
143impl From<repr_c::Arc_<ffi_sdk::FfiQueryResultItem>> for QueryResultItem {
144 fn from(raw: repr_c::Arc<ffi_sdk::FfiQueryResultItem>) -> Self {
145 Self {
146 raw,
147 materialized_value: <_>::default(),
148 }
149 }
150}
151
152impl QueryResultItem {
153 pub fn value(&self) -> Arc<CborObject> {
163 let cache = &mut *self.materialized_value.lock().unwrap();
164 Self::materialize_(&self.raw, cache).clone()
165 }
166
167 pub fn is_materialized(&self) -> bool {
175 self.materialized_value.lock().unwrap().is_some()
176 }
177
178 fn materialize_<'cache>(
180 raw: &ffi_sdk::FfiQueryResultItem,
181 cache: &'cache mut Option<Arc<CborObject>>,
182 ) -> &'cache Arc<CborObject> {
183 cache.get_or_insert_with(|| {
184 let cbor_data = ffi_sdk::dittoffi_query_result_item_cbor(raw);
185 Arc::new(::serde_cbor::from_slice(&cbor_data[..]).expect(
186 "internal inconsistency, couldn't materialize query result item due to CBOR \
187 decoding error",
188 ))
189 })
190 }
191
192 pub fn materialize(&mut self) {
200 Self::materialize_(&self.raw, self.materialized_value.get_mut().unwrap());
201 }
202
203 pub fn dematerialize(&mut self) {
206 *self.materialized_value.get_mut().unwrap() = None;
207 }
208
209 pub fn cbor_data(&self) -> Vec<u8> {
214 let c_slice = ffi_sdk::dittoffi_query_result_item_cbor(&self.raw);
215 Box::<[u8]>::from(c_slice).into()
216 }
217
218 pub fn json_string(&self) -> String {
223 let raw_string = ffi_sdk::dittoffi_query_result_item_json(&self.raw);
224 raw_string.into_string()
225 }
226
227 pub fn deserialize_value<T: DeserializeOwned>(&self) -> Result<T, DittoError> {
232 ::serde_cbor::from_slice(&self.cbor_data()).map_err(Into::into)
233 }
234
235 #[doc(hidden)]
240 pub fn unstable_try_from_serde_json_value(
241 value: serde_json::Value,
242 ) -> Result<Self, DittoError> {
243 let json_data = value.to_string().into_bytes().to_vec();
244 let raw = ffi_sdk::dittoffi_query_result_item_new(json_data.as_slice().into())
245 .into_rust_result()?;
246 Ok(QueryResultItem {
247 raw,
248 materialized_value: Mutex::new(None),
249 })
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use serde_json::json;
256
257 use super::*;
258 use crate::prelude::CborValueGetters;
259
260 #[test]
261 fn create_query_result_item_from_json() {
262 let json_dict = json!({"_id": "1", "data": "A"});
263 let query_result_item =
264 QueryResultItem::unstable_try_from_serde_json_value(json_dict).unwrap();
265 let value = query_result_item.value();
266 assert_eq!(value["_id"].as_str().unwrap(), "1");
267 assert_eq!(value["data"].as_str().unwrap(), "A");
268 }
269}