1use crate::collection::{Document, FindPlan, NitriteId};
2use crate::common::{
3 Convertible, DocumentCursor, JoinedDocumentCursor, Lookup, ProjectedDocumentCursor, Value,
4};
5use crate::errors::{ErrorKind, NitriteError, NitriteResult};
6use crate::repository::NitriteEntity;
7use std::marker::PhantomData;
8
9pub struct ObjectCursor<T> {
10 cursor: DocumentCursor,
11 _phantom: PhantomData<T>,
12}
13
14impl<T> ObjectCursor<T>
15where
16 T: Convertible<Output = T> + NitriteEntity,
17{
18 pub fn new(cursor: DocumentCursor) -> Self {
19 ObjectCursor {
20 cursor,
21 _phantom: PhantomData,
22 }
23 }
24
25 pub fn reset(&mut self) {
26 self.cursor.reset();
27 }
28
29 pub fn size(&mut self) -> usize {
30 self.reset();
32 let count = self.cursor.size();
33 self.reset();
35 count
36 }
37
38 pub fn first(&mut self) -> Option<NitriteResult<T>> {
39 let doc_result = self.cursor.first();
40 match doc_result {
41 Some(Ok(doc)) => {
42 let result = T::from_value(&Value::Document(doc));
43 match result {
44 Ok(obj) => Some(Ok(obj)),
45 Err(e) => Some(Err(e)),
46 }
47 }
48 Some(Err(e)) => Some(Err(e)),
49 None => None,
50 }
51 }
52
53 pub fn find_plan(&self) -> Option<&FindPlan> {
54 self.cursor.find_plan()
55 }
56
57 pub(crate) fn set_find_plan(mut self, find_plan: FindPlan) -> Self {
58 self.cursor = self.cursor.set_find_plan(find_plan);
59 self
60 }
61
62 pub fn join<'a, J: Convertible<Output = J> + NitriteEntity>(
63 &'a mut self,
64 foreign_cursor: &'a mut ObjectCursor<J>,
65 lookup: &'a Lookup,
66 ) -> NitriteResult<JoinedObjectCursor<'a, J>> {
67 let joined_doc_cursor = self.cursor.join(&mut foreign_cursor.cursor, lookup)?;
68 Ok(JoinedObjectCursor::new(joined_doc_cursor))
69 }
70
71 pub fn project<P>(&'_ mut self) -> NitriteResult<ProjectedObjectCursor<'_, P>>
72 where
73 P: Convertible<Output = P> + NitriteEntity + Default,
74 {
75 ProjectedObjectCursor::new(&mut self.cursor)
76 }
77
78 pub fn iter_with_id(&mut self) -> ObjectCursorWithId<'_, T> {
83 ObjectCursorWithId {
84 cursor: &mut self.cursor,
85 _phantom: PhantomData,
86 }
87 }
88}
89
90pub struct ObjectCursorWithId<'a, T> {
93 cursor: &'a mut DocumentCursor,
94 _phantom: PhantomData<T>,
95}
96
97impl<'a, T> Iterator for ObjectCursorWithId<'a, T>
98where
99 T: Convertible<Output = T> + NitriteEntity,
100{
101 type Item = NitriteResult<(NitriteId, T)>;
102
103 fn next(&mut self) -> Option<Self::Item> {
104 let doc_result = self.cursor.next();
105 match doc_result {
106 Some(Ok(mut doc)) => {
107 let id = match doc.id() {
109 Ok(id) => id,
110 Err(e) => return Some(Err(e)),
111 };
112
113 let result = T::from_value(&Value::Document(doc));
115 match result {
116 Ok(obj) => Some(Ok((id, obj))),
117 Err(e) => Some(Err(e)),
118 }
119 }
120 Some(Err(e)) => Some(Err(e)),
121 None => None,
122 }
123 }
124}
125
126impl<T> Iterator for ObjectCursor<T>
127where
128 T: Convertible<Output = T> + NitriteEntity,
129{
130 type Item = NitriteResult<T>;
131
132 fn next(&mut self) -> Option<Self::Item> {
133 let doc_result = self.cursor.next();
134 match doc_result {
135 Some(Ok(doc)) => {
136 let result = T::from_value(&Value::Document(doc));
137 match result {
138 Ok(obj) => Some(Ok(obj)),
139 Err(e) => Some(Err(e)),
140 }
141 }
142 Some(Err(e)) => Some(Err(e)),
143 None => None,
144 }
145 }
146}
147
148pub struct JoinedObjectCursor<'a, T>
149where
150 T: Convertible<Output = T> + NitriteEntity,
151{
152 cursor: JoinedDocumentCursor<'a>,
153 _phantom: PhantomData<T>,
154}
155
156impl<'a, T> JoinedObjectCursor<'a, T>
157where
158 T: Convertible<Output = T> + NitriteEntity,
159{
160 pub fn new(cursor: JoinedDocumentCursor<'a>) -> Self {
161 JoinedObjectCursor {
162 cursor,
163 _phantom: PhantomData,
164 }
165 }
166
167 pub fn size(&mut self) -> usize {
168 let mut count = 0;
169 while self.next().is_some() {
171 count += 1;
172 }
173 self.cursor.reset();
175 count
176 }
177}
178
179impl<'a, T> Iterator for JoinedObjectCursor<'a, T>
180where
181 T: Convertible<Output = T> + NitriteEntity,
182{
183 type Item = NitriteResult<T>;
184
185 fn next(&mut self) -> Option<Self::Item> {
186 let doc_result = self.cursor.next();
187 match doc_result {
188 Some(Ok(doc)) => {
189 let result = T::from_value(&Value::Document(doc));
190 match result {
191 Ok(obj) => Some(Ok(obj)),
192 Err(e) => Some(Err(e)),
193 }
194 }
195 Some(Err(e)) => Some(Err(e)),
196 None => None,
197 }
198 }
199}
200
201pub struct ProjectedObjectCursor<'a, P>
202where
203 P: Convertible<Output = P> + NitriteEntity + Default,
204{
205 cursor: ProjectedDocumentCursor<'a>,
206 _phantom: PhantomData<P>,
207}
208
209impl<'a, P> ProjectedObjectCursor<'a, P>
210where
211 P: Convertible<Output = P> + NitriteEntity + Default,
212{
213 pub fn new(cursor: &'a mut DocumentCursor) -> NitriteResult<Self> {
214 let projection = P::default().to_value()?;
215 let projection = projection.as_document().cloned().ok_or_else(|| {
216 log::error!("Projection type is not convertible to document");
217 NitriteError::new(
218 "Projection type is not convertible to document",
219 ErrorKind::ObjectMappingError,
220 )
221 })?;
222
223
224 Ok(ProjectedObjectCursor {
225 cursor: cursor.project(projection)?,
226 _phantom: PhantomData,
227 })
228 }
229
230 pub fn size(&mut self) -> usize {
231 let count = self.cursor.size();
232 self.cursor.reset();
234 count
235 }
236}
237
238impl<'a, P> Iterator for ProjectedObjectCursor<'a, P>
239where
240 P: Convertible<Output = P> + NitriteEntity + Default,
241{
242 type Item = NitriteResult<P>;
243
244 fn next(&mut self) -> Option<Self::Item> {
245 let doc_result = self.cursor.next();
246 match doc_result {
247 Some(Ok(doc)) => {
248 let result = P::from_value(&Value::Document(doc));
249 match result {
250 Ok(obj) => Some(Ok(obj)),
251 Err(e) => Some(Err(e)),
252 }
253 }
254 Some(Err(e)) => Some(Err(e)),
255 None => None,
256 }
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263 use crate::collection::Document;
264 use crate::common::{ProcessorChain, NON_UNIQUE_INDEX};
265 use crate::doc;
266 use crate::errors::{ErrorKind, NitriteError};
267 use crate::repository::{EntityId, EntityIndex};
268
269 struct TestEntity {
270 first: String,
271 last: String,
272 }
273
274 impl Convertible for TestEntity {
275 type Output = TestEntity;
276
277 fn to_value(&self) -> NitriteResult<Value> {
278 let doc = doc!{
279 "first": (self.first.to_string()),
280 "last": (self.last.to_string()),
281 };
282 Ok(Value::Document(doc))
283 }
284
285 fn from_value(value: &Value) -> NitriteResult<Self::Output> {
286 match value {
287 Value::Document(doc) => {
288 let first = doc.get("first")?;
289 let first = match first.as_string() {
290 Some(s) => s.to_string(),
291 None => {
292 log::error!("TestEntity field 'first' should be string, got: {:?}", first);
293 return Err(NitriteError::new(
294 "TestEntity field 'first' must be a string",
295 ErrorKind::ObjectMappingError,
296 ));
297 }
298 };
299
300 let last = doc.get("last")?;
301 let last = match last.as_string() {
302 Some(s) => s.to_string(),
303 None => {
304 log::error!("TestEntity field 'last' should be string, got: {:?}", last);
305 return Err(NitriteError::new(
306 "TestEntity field 'last' must be a string",
307 ErrorKind::ObjectMappingError,
308 ));
309 }
310 };
311
312 Ok(TestEntity { first, last })
313 }
314 _ => {
315 log::error!("Expected Document for TestEntity, got: {:?}", value);
316 Err(NitriteError::new(
317 "Object cursor deserialization error: expected document but found another value type for TestEntity",
318 ErrorKind::ObjectMappingError
319 ))
320 }
321 }
322 }
323 }
324
325 impl NitriteEntity for TestEntity {
326 type Id = ();
327
328 fn entity_name(&self) -> String {
329 "TestEntity".to_string()
330 }
331
332 fn entity_indexes(&self) -> Option<Vec<EntityIndex>> {
333 Some(vec![
334 EntityIndex::new(vec!["first", "last"], Some(NON_UNIQUE_INDEX)),
335 ])
336 }
337
338 fn entity_id(&self) -> Option<EntityId> {
339 Some(EntityId::new("first", None, None))
340 }
341 }
342
343 impl Default for TestEntity {
344 fn default() -> Self {
345 TestEntity {
346 first: "".to_string(),
347 last: "".to_string(),
348 }
349 }
350 }
351
352 fn create_document(first: &str, last: &str) -> Document {
353 let doc = doc!{
354 "first": first,
355 "last": last,
356 };
357 doc
358 }
359
360 #[test]
361 fn test_new_object_cursor() {
362 let docs = vec![
363 Ok(create_document("John", "Doe")),
364 Ok(create_document("Jane", "Doe")),
365 ];
366 let iter = Box::new(docs.into_iter());
367 let cursor = DocumentCursor::new(iter, ProcessorChain::new());
368 let object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
369 assert_eq!(object_cursor.cursor.count(), 2);
370 }
371
372 #[test]
373 fn test_object_cursor_first() {
374 let docs = vec![
375 Ok(create_document("John", "Doe")),
376 Ok(create_document("Jane", "Doe")),
377 ];
378 let iter = Box::new(docs.into_iter());
379 let cursor = DocumentCursor::new(iter, ProcessorChain::new());
380 let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
381 let first = object_cursor.first().unwrap().unwrap();
382 assert_eq!(first.first, "John");
383 }
384
385 #[test]
386 fn test_object_cursor_first_with_error() {
387 let docs = vec![
388 Err(NitriteError::new("Test Error", ErrorKind::IOError)),
389 ];
390 let iter = Box::new(docs.into_iter());
391 let cursor = DocumentCursor::new(iter, ProcessorChain::new());
392 let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
393 let object_cursor = &mut object_cursor;
394 assert!(object_cursor.next().unwrap().is_err());
395 }
396
397 #[test]
398 fn test_object_cursor_next() {
399 let docs = vec![
400 Ok(create_document("John", "Doe")),
401 Ok(create_document("Jane", "Doe")),
402 ];
403 let iter = Box::new(docs.into_iter());
404 let cursor = DocumentCursor::new(iter, ProcessorChain::new());
405 let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
406 let iter = &mut object_cursor;
407 let first = iter.next().unwrap().unwrap();
408 assert_eq!(first.first, "John");
409 let second = iter.next().unwrap().unwrap();
410 assert_eq!(second.first, "Jane");
411 assert!(iter.next().is_none());
412 }
413
414 #[test]
415 fn test_object_cursor_next_with_error() {
416 let docs = vec![
417 Ok(create_document("John", "Doe")),
418 Err(NitriteError::new("Test Error", ErrorKind::IOError)),
419 ];
420 let iter = Box::new(docs.into_iter());
421 let cursor = DocumentCursor::new(iter, ProcessorChain::new());
422 let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
423 let iter = &mut object_cursor;
424 let first = iter.next().unwrap().unwrap();
425 assert_eq!(first.first, "John");
426 assert!(iter.next().unwrap().is_err());
427 }
428
429 #[test]
430 fn test_object_cursor_find_plan() {
431 let docs = vec![Ok(create_document("John", "Doe"))];
432 let iter = Box::new(docs.into_iter());
433 let cursor = DocumentCursor::new(iter, ProcessorChain::new());
434 let object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
435 assert!(object_cursor.find_plan().is_none());
436 }
437
438 #[test]
439 fn test_object_cursor_set_find_plan() {
440 let docs = vec![Ok(create_document("John", "Doe"))];
441 let iter = Box::new(docs.into_iter());
442 let find_plan = FindPlan::new(); let cursor = DocumentCursor::new(iter, ProcessorChain::new()).set_find_plan(find_plan.clone());
444 let object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor).set_find_plan(find_plan.clone());
445 assert!(object_cursor.find_plan().is_some());
446 assert_eq!(object_cursor.find_plan().unwrap().index_descriptor(), find_plan.index_descriptor());
447 }
448
449 #[test]
450 fn test_object_cursor_join() {
451 let docs1 = vec![Ok(create_document("John", "Doe"))];
452 let docs2 = vec![Ok(create_document("Jane", "Doe"))];
453 let iter1 = Box::new(docs1.into_iter());
454 let iter2 = Box::new(docs2.into_iter());
455 let cursor1 = DocumentCursor::new(iter1, ProcessorChain::new());
456 let cursor2 = DocumentCursor::new(iter2, ProcessorChain::new());
457 let mut object_cursor1: ObjectCursor<TestEntity> = ObjectCursor::new(cursor1);
458 let mut object_cursor2: ObjectCursor<TestEntity> = ObjectCursor::new(cursor2);
459 let lookup = Lookup {
460 local_field: "last".to_string(),
461 foreign_field: "last".to_string(),
462 target_field: "surname".to_string(),
463 };
464 let joined_cursor = object_cursor1.join(&mut object_cursor2, &lookup).expect("Failed to join");
465 assert_eq!(joined_cursor.count(), 1);
466 }
467
468 #[test]
469 fn test_object_cursor_project() {
470 let docs = vec![Ok(create_document("John", "Doe"))];
471 let iter = Box::new(docs.into_iter());
472 let cursor = DocumentCursor::new(iter, ProcessorChain::new());
473 let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
474 let mut projected_cursor = object_cursor.project::<TestEntity>().expect("Failed to project");
475 assert_eq!(projected_cursor.size(), 1);
476 }
477
478 #[test]
479 fn test_test_entity_from_value_with_valid_document() {
480 let doc = create_document("John", "Doe");
482 let result = TestEntity::from_value(&Value::Document(doc));
483 assert!(result.is_ok());
484
485 let entity = result.unwrap();
486 assert_eq!(entity.first, "John");
487 assert_eq!(entity.last, "Doe");
488 }
489
490 #[test]
491 fn test_test_entity_from_value_with_invalid_type() {
492 let result = TestEntity::from_value(&Value::I32(42));
494 assert!(result.is_err());
495 }
496
497 #[test]
498 fn test_test_entity_from_value_with_wrong_first_type() {
499 let mut doc = Document::new();
501 doc.put("first", Value::I32(123)).unwrap(); doc.put("last", Value::String("Doe".to_string())).unwrap();
503
504 let result = TestEntity::from_value(&Value::Document(doc));
505 assert!(result.is_err());
506 if let Err(e) = result {
507 assert_eq!(e.kind(), &ErrorKind::ObjectMappingError);
508 }
509 }
510
511 #[test]
512 fn test_test_entity_from_value_with_wrong_last_type() {
513 let mut doc = Document::new();
515 doc.put("first", Value::String("John".to_string())).unwrap();
516 doc.put("last", Value::Bool(true)).unwrap(); let result = TestEntity::from_value(&Value::Document(doc));
519 assert!(result.is_err());
520 if let Err(e) = result {
521 assert_eq!(e.kind(), &ErrorKind::ObjectMappingError);
522 }
523 }
524
525 #[test]
526 fn test_object_cursor_iteration_with_type_validation() {
527 let docs = vec![
529 Ok(create_document("John", "Doe")),
530 Ok(create_document("Jane", "Smith")),
531 ];
532 let iter = Box::new(docs.into_iter());
533 let cursor = DocumentCursor::new(iter, ProcessorChain::new());
534 let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
535 let iter = &mut object_cursor;
536
537 let mut count = 0;
538 for result in iter {
539 assert!(result.is_ok());
540 let entity = result.unwrap();
541 assert!(!entity.first.is_empty());
542 assert!(!entity.last.is_empty());
543 count += 1;
544 }
545 assert_eq!(count, 2);
546 }
547}