reinhardt_urls/proxy/collection.rs
1//! Collection association proxies for one-to-many and many-to-many relationships
2
3pub mod aggregations;
4pub mod operations;
5
6pub use aggregations::CollectionAggregations;
7pub use operations::CollectionOperations;
8
9use crate::proxy::{ProxyError, ProxyResult, ScalarValue};
10use serde::{Deserialize, Serialize};
11
12/// Collection proxy for accessing multiple related objects' attributes
13///
14/// Used for one-to-many and many-to-many relationships where the proxy
15/// returns a collection of scalar values.
16///
17/// ## Example
18///
19/// ```rust,no_run
20/// # use reinhardt_urls::proxy::CollectionProxy;
21/// # #[tokio::main]
22/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
23/// # struct User;
24/// # let user = User;
25/// // User has many posts, access all post titles directly
26/// let titles_proxy: CollectionProxy = CollectionProxy::new("posts", "title");
27/// // let titles: Vec<String> = titles_proxy.get_values(&user).await?;
28/// # Ok(())
29/// # }
30/// ```
31#[derive(Clone)]
32pub struct CollectionProxy {
33 /// Name of the relationship
34 pub relationship: String,
35
36 /// Name of the attribute on the related objects
37 pub attribute: String,
38
39 /// Whether to remove duplicates
40 pub unique: bool,
41
42 /// Whether to deduplicate values
43 pub deduplicate: bool,
44
45 /// Loading strategy for the relationship
46 pub loading_strategy: Option<crate::proxy::LoadingStrategy>,
47
48 /// Factory for creating new instances from scalar values
49 pub factory: Option<std::sync::Arc<dyn super::reflection::ReflectableFactory>>,
50
51 /// Whether caching is enabled
52 caching: bool,
53
54 /// Cache time-to-live in seconds
55 cache_ttl: Option<u64>,
56
57 /// Whether streaming is enabled
58 streaming: bool,
59
60 /// Whether triggers are enabled
61 triggers: bool,
62
63 /// Trigger events to monitor
64 trigger_events: Vec<String>,
65
66 /// Stored procedure name
67 stored_procedure: Option<String>,
68
69 /// Stored procedure parameters
70 procedure_params: Vec<(String, String)>,
71
72 /// Target database name
73 database: Option<String>,
74
75 /// Fallback database name
76 fallback_database: Option<String>,
77
78 /// Whether async loading is enabled
79 async_loading: bool,
80
81 /// Whether concurrent access is supported
82 concurrent_access: bool,
83
84 /// Whether this proxy targets a database view
85 is_view: bool,
86
87 /// Batch size for bulk operations
88 batch_size: Option<usize>,
89
90 /// Chunk size for processing batches
91 chunk_size: Option<usize>,
92
93 /// Whether cascade delete/update is enabled
94 cascade: bool,
95
96 /// Whether version tracking is enabled for items
97 version_tracking: bool,
98
99 /// Memory limit in bytes for collection operations
100 memory_limit: Option<usize>,
101}
102
103impl CollectionProxy {
104 /// Create a new collection proxy
105 ///
106 /// # Examples
107 ///
108 /// ```
109 /// use reinhardt_urls::proxy::CollectionProxy;
110 ///
111 /// let proxy = CollectionProxy::new("posts", "title");
112 /// assert_eq!(proxy.relationship, "posts");
113 /// assert_eq!(proxy.attribute, "title");
114 /// assert!(!proxy.unique);
115 /// ```
116 pub fn new(relationship: &str, attribute: &str) -> Self {
117 Self {
118 relationship: relationship.to_string(),
119 attribute: attribute.to_string(),
120 unique: false,
121 deduplicate: false,
122 loading_strategy: None,
123 factory: None,
124 caching: false,
125 cache_ttl: None,
126 streaming: false,
127 triggers: false,
128 trigger_events: Vec::new(),
129 stored_procedure: None,
130 procedure_params: Vec::new(),
131 database: None,
132 fallback_database: None,
133 async_loading: false,
134 concurrent_access: false,
135 is_view: false,
136 batch_size: None,
137 chunk_size: None,
138 cascade: false,
139 version_tracking: false,
140 memory_limit: None,
141 }
142 }
143 /// Create a collection proxy that removes duplicates
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use reinhardt_urls::proxy::CollectionProxy;
149 ///
150 /// let proxy = CollectionProxy::unique("tags", "name");
151 /// assert_eq!(proxy.relationship, "tags");
152 /// assert_eq!(proxy.attribute, "name");
153 /// assert!(proxy.unique);
154 /// ```
155 pub fn unique(relationship: &str, attribute: &str) -> Self {
156 Self {
157 relationship: relationship.to_string(),
158 attribute: attribute.to_string(),
159 unique: true,
160 deduplicate: false,
161 loading_strategy: None,
162 factory: None,
163 caching: false,
164 cache_ttl: None,
165 streaming: false,
166 triggers: false,
167 trigger_events: Vec::new(),
168 stored_procedure: None,
169 procedure_params: Vec::new(),
170 database: None,
171 fallback_database: None,
172 async_loading: false,
173 concurrent_access: false,
174 is_view: false,
175 batch_size: None,
176 chunk_size: None,
177 cascade: false,
178 version_tracking: false,
179 memory_limit: None,
180 }
181 }
182
183 /// Create a collection proxy with a factory for creating instances
184 pub fn with_factory(
185 relationship: &str,
186 attribute: &str,
187 factory: std::sync::Arc<dyn super::reflection::ReflectableFactory>,
188 ) -> Self {
189 Self {
190 relationship: relationship.to_string(),
191 attribute: attribute.to_string(),
192 unique: false,
193 deduplicate: false,
194 loading_strategy: None,
195 factory: Some(factory),
196 caching: false,
197 cache_ttl: None,
198 streaming: false,
199 triggers: false,
200 trigger_events: Vec::new(),
201 stored_procedure: None,
202 procedure_params: Vec::new(),
203 database: None,
204 fallback_database: None,
205 async_loading: false,
206 concurrent_access: false,
207 is_view: false,
208 batch_size: None,
209 chunk_size: None,
210 cascade: false,
211 version_tracking: false,
212 memory_limit: None,
213 }
214 }
215
216 /// Set the factory for this proxy
217 pub fn set_factory(
218 &mut self,
219 factory: std::sync::Arc<dyn super::reflection::ReflectableFactory>,
220 ) {
221 self.factory = Some(factory);
222 }
223 /// Get collection of values from related objects
224 ///
225 /// # Examples
226 ///
227 /// ```no_run
228 /// use reinhardt_urls::proxy::CollectionProxy;
229 ///
230 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
231 /// let proxy = CollectionProxy::new("posts", "title");
232 // Assuming \`user\` implements Reflectable
233 // let titles = proxy.get_values(&user).await?;
234 /// # Ok(())
235 /// # }
236 /// ```
237 pub async fn get_values<T>(&self, source: &T) -> ProxyResult<Vec<ScalarValue>>
238 where
239 T: super::reflection::Reflectable,
240 {
241 // 1. Access the relationship on source
242 let relationship = source
243 .get_relationship(&self.relationship)
244 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
245
246 // 2. Downcast to Vec<Box<dyn Reflectable>>
247 let collection = super::reflection::downcast_relationship::<
248 Vec<Box<dyn super::reflection::Reflectable>>,
249 >(relationship)?;
250
251 // 3. Extract the attribute from each item
252 let mut values = Vec::new();
253 for item in collection.iter() {
254 let value = item
255 .get_attribute(&self.attribute)
256 .ok_or_else(|| ProxyError::AttributeNotFound(self.attribute.clone()))?;
257 values.push(value);
258 }
259
260 // 4. Optionally remove duplicates
261 if self.unique {
262 values.sort_by(|a, b| format!("{:?}", a).cmp(&format!("{:?}", b)));
263 values.dedup_by(|a, b| format!("{:?}", a) == format!("{:?}", b));
264 }
265
266 Ok(values)
267 }
268 /// Set collection of values by creating/updating related objects
269 ///
270 /// # Examples
271 ///
272 /// ```no_run
273 /// use reinhardt_urls::proxy::{CollectionProxy, ScalarValue};
274 ///
275 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
276 /// let proxy = CollectionProxy::new("tags", "name");
277 /// let values = vec![ScalarValue::String("rust".to_string())];
278 // let mut user = ...;
279 // proxy.set_values(&mut user, values).await?;
280 /// # Ok(())
281 /// # }
282 /// ```
283 pub async fn set_values<T>(&self, source: &mut T, values: Vec<ScalarValue>) -> ProxyResult<()>
284 where
285 T: super::reflection::Reflectable,
286 {
287 // 1. Check if factory is configured
288 let factory = self
289 .factory
290 .as_ref()
291 .ok_or(ProxyError::FactoryNotConfigured)?;
292
293 // 2. Access the relationship
294 let relationship = source
295 .get_relationship_mut(&self.relationship)
296 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
297
298 // 3. Downcast to Vec<Box<dyn Reflectable>>
299 let collection = relationship
300 .downcast_mut::<Vec<Box<dyn super::reflection::Reflectable>>>()
301 .ok_or_else(|| ProxyError::TypeMismatch {
302 expected: "Vec<Box<dyn Reflectable>>".to_string(),
303 actual: "unknown".to_string(),
304 })?;
305
306 // 4. Clear existing collection
307 collection.clear();
308
309 // 5. Create new objects from scalar values and add to collection
310 for value in values {
311 let new_object = factory.create_from_scalar(&self.attribute, value)?;
312 collection.push(new_object);
313 }
314
315 Ok(())
316 }
317 /// Append a value to the collection
318 ///
319 /// # Examples
320 ///
321 /// ```no_run
322 /// use reinhardt_urls::proxy::{CollectionProxy, ScalarValue};
323 ///
324 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
325 /// let proxy = CollectionProxy::new("tags", "name");
326 // let mut user = ...;
327 // proxy.append(&mut user, ScalarValue::String("new_tag".to_string())).await?;
328 /// # Ok(())
329 /// # }
330 /// ```
331 pub async fn append<T>(&self, source: &mut T, value: ScalarValue) -> ProxyResult<()>
332 where
333 T: super::reflection::Reflectable,
334 {
335 // 1. Check if factory is configured
336 let factory = self
337 .factory
338 .as_ref()
339 .ok_or(ProxyError::FactoryNotConfigured)?;
340
341 // 2. Access the relationship
342 let relationship = source
343 .get_relationship_mut(&self.relationship)
344 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
345
346 // 3. Downcast to Vec<Box<dyn Reflectable>>
347 let collection = relationship
348 .downcast_mut::<Vec<Box<dyn super::reflection::Reflectable>>>()
349 .ok_or_else(|| ProxyError::TypeMismatch {
350 expected: "Vec<Box<dyn Reflectable>>".to_string(),
351 actual: "unknown".to_string(),
352 })?;
353
354 // 4. Create new object from scalar value
355 let new_object = factory.create_from_scalar(&self.attribute, value)?;
356
357 // 5. Append to collection
358 collection.push(new_object);
359
360 Ok(())
361 }
362 /// Remove a value from the collection
363 ///
364 /// # Examples
365 ///
366 /// ```no_run
367 /// use reinhardt_urls::proxy::{CollectionProxy, ScalarValue};
368 ///
369 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
370 /// let proxy = CollectionProxy::new("tags", "name");
371 // let mut user = ...;
372 // proxy.remove(&mut user, ScalarValue::String("old_tag".to_string())).await?;
373 /// # Ok(())
374 /// # }
375 /// ```
376 pub async fn remove<T>(&self, source: &mut T, value: ScalarValue) -> ProxyResult<()>
377 where
378 T: super::reflection::Reflectable,
379 {
380 // 1. Access the relationship
381 let relationship = source
382 .get_relationship_mut(&self.relationship)
383 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
384
385 // 2. Downcast to Vec<Box<dyn Reflectable>>
386 let collection = relationship
387 .downcast_mut::<Vec<Box<dyn super::reflection::Reflectable>>>()
388 .ok_or_else(|| ProxyError::TypeMismatch {
389 expected: "Vec<Box<dyn Reflectable>>".to_string(),
390 actual: "unknown".to_string(),
391 })?;
392
393 // 3. Find and remove items with matching attribute value
394 collection.retain(|item| {
395 item.get_attribute(&self.attribute)
396 .map(|v| v != value)
397 .unwrap_or(true)
398 });
399
400 Ok(())
401 }
402 /// Check if the collection contains a value
403 ///
404 /// # Examples
405 ///
406 /// ```no_run
407 /// use reinhardt_urls::proxy::{CollectionProxy, ScalarValue};
408 ///
409 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
410 /// let proxy = CollectionProxy::new("tags", "name");
411 // let user = ...;
412 // let has_tag = proxy.contains(&user, ScalarValue::String("rust".to_string())).await?;
413 /// # Ok(())
414 /// # }
415 /// ```
416 pub async fn contains<T>(&self, source: &T, value: ScalarValue) -> ProxyResult<bool>
417 where
418 T: super::reflection::Reflectable,
419 {
420 let values = self.get_values(source).await?;
421 Ok(values.contains(&value))
422 }
423 /// Get the count of items in the collection
424 ///
425 /// # Examples
426 ///
427 /// ```no_run
428 /// use reinhardt_urls::proxy::CollectionProxy;
429 ///
430 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
431 /// let proxy = CollectionProxy::new("posts", "title");
432 // let user = ...;
433 // let count = proxy.count(&user).await?;
434 /// # Ok(())
435 /// # }
436 /// ```
437 pub async fn count<T>(&self, source: &T) -> ProxyResult<usize>
438 where
439 T: super::reflection::Reflectable,
440 {
441 // 1. Access the relationship
442 let relationship = source
443 .get_relationship(&self.relationship)
444 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
445
446 // 2. Downcast to Vec<Box<dyn Reflectable>>
447 let collection = super::reflection::downcast_relationship::<
448 Vec<Box<dyn super::reflection::Reflectable>>,
449 >(relationship)?;
450
451 // 3. Return the count
452 Ok(collection.len())
453 }
454 /// Filter collection by a condition on the proxy attribute
455 ///
456 /// # Examples
457 ///
458 /// ```ignore
459 /// use reinhardt_urls::proxy::{CollectionProxy, query::{FilterCondition, FilterOp}};
460 ///
461 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
462 /// let proxy = CollectionProxy::new("posts", "status");
463 /// let condition = FilterCondition::new("status", FilterOp::eq("published"));
464 // let user = ...;
465 // let filtered = proxy.filter(&user, condition).await?;
466 /// # Ok(())
467 /// # }
468 /// ```
469 pub async fn filter<T>(
470 &self,
471 source: &T,
472 condition: crate::proxy::query::FilterCondition,
473 ) -> ProxyResult<Vec<ScalarValue>>
474 where
475 T: super::reflection::Reflectable,
476 {
477 // Get all values first
478 let values = self.get_values(source).await?;
479
480 // Filter using the condition
481 let filtered: Vec<ScalarValue> = values
482 .into_iter()
483 .filter(|v| condition.matches(v))
484 .collect();
485
486 Ok(filtered)
487 }
488 /// Filter collection using a custom predicate
489 ///
490 /// # Examples
491 ///
492 /// ```no_run
493 /// use reinhardt_urls::proxy::{CollectionProxy, ScalarValue};
494 ///
495 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
496 /// let proxy = CollectionProxy::new("posts", "views");
497 // let user = ...;
498 // let popular = proxy.filter_by(&user, |v| {
499 // matches!(v, ScalarValue::Integer(n) if *n > 1000)
500 // }).await?;
501 /// # Ok(())
502 /// # }
503 /// ```
504 pub async fn filter_by<T, F>(&self, source: &T, predicate: F) -> ProxyResult<Vec<ScalarValue>>
505 where
506 T: super::reflection::Reflectable,
507 F: Fn(&ScalarValue) -> bool,
508 {
509 // Get all values first
510 let values = self.get_values(source).await?;
511
512 // Filter using the predicate
513 let filtered: Vec<ScalarValue> = values.into_iter().filter(|v| predicate(v)).collect();
514
515 Ok(filtered)
516 }
517
518 // ========== Accessor methods ==========
519
520 /// Get the relationship name
521 ///
522 /// # Examples
523 ///
524 /// ```
525 /// use reinhardt_urls::proxy::CollectionProxy;
526 ///
527 /// let proxy = CollectionProxy::new("posts", "title");
528 /// assert_eq!(proxy.relationship(), "posts");
529 /// ```
530 pub fn relationship(&self) -> &str {
531 &self.relationship
532 }
533
534 /// Get the attribute name
535 ///
536 /// # Examples
537 ///
538 /// ```
539 /// use reinhardt_urls::proxy::CollectionProxy;
540 ///
541 /// let proxy = CollectionProxy::new("posts", "title");
542 /// assert_eq!(proxy.attribute(), "title");
543 /// ```
544 pub fn attribute(&self) -> &str {
545 &self.attribute
546 }
547
548 /// Check if this proxy removes duplicates
549 ///
550 /// # Examples
551 ///
552 /// ```
553 /// use reinhardt_urls::proxy::CollectionProxy;
554 ///
555 /// let proxy = CollectionProxy::unique("tags", "name");
556 /// assert!(proxy.is_unique());
557 ///
558 /// let proxy2 = CollectionProxy::new("posts", "title");
559 /// assert!(!proxy2.is_unique());
560 /// ```
561 pub fn is_unique(&self) -> bool {
562 self.unique
563 }
564
565 /// Check if this proxy deduplicates values
566 ///
567 /// # Examples
568 ///
569 /// ```
570 /// use reinhardt_urls::proxy::CollectionProxy;
571 ///
572 /// let proxy = CollectionProxy::new("tags", "name").with_deduplication(true);
573 /// assert!(proxy.deduplicates());
574 /// ```
575 pub fn deduplicates(&self) -> bool {
576 self.deduplicate
577 }
578
579 // ========== Builder pattern methods ==========
580
581 /// Set whether to remove duplicates (builder pattern)
582 ///
583 /// # Examples
584 ///
585 /// ```
586 /// use reinhardt_urls::proxy::CollectionProxy;
587 ///
588 /// let proxy = CollectionProxy::new("tags", "name").with_unique(true);
589 /// assert!(proxy.is_unique());
590 /// ```
591 pub fn with_unique(mut self, unique: bool) -> Self {
592 self.unique = unique;
593 self
594 }
595
596 /// Set whether to deduplicate values (builder pattern)
597 ///
598 /// # Examples
599 ///
600 /// ```
601 /// use reinhardt_urls::proxy::CollectionProxy;
602 ///
603 /// let proxy = CollectionProxy::new("tags", "name").with_deduplication(true);
604 /// assert!(proxy.deduplicates());
605 /// ```
606 pub fn with_deduplication(mut self, deduplicate: bool) -> Self {
607 self.deduplicate = deduplicate;
608 self
609 }
610
611 /// Set the loading strategy (builder pattern)
612 ///
613 /// # Examples
614 ///
615 /// ```
616 /// use reinhardt_urls::proxy::{CollectionProxy, LoadingStrategy};
617 ///
618 /// let proxy = CollectionProxy::new("posts", "title")
619 /// .with_loading_strategy(LoadingStrategy::Joined);
620 /// ```
621 pub fn with_loading_strategy(mut self, strategy: crate::proxy::LoadingStrategy) -> Self {
622 self.loading_strategy = Some(strategy);
623 self
624 }
625
626 /// Filter collection where any item matches the condition
627 ///
628 /// Returns a new proxy with filtered values where at least one item
629 /// in the collection matches the specified field and value.
630 ///
631 /// # Examples
632 ///
633 /// ```no_run
634 /// use reinhardt_urls::proxy::CollectionProxy;
635 ///
636 /// // Example: Filter posts where any tag matches "rust"
637 /// // let proxy = CollectionProxy::new("posts", "tags");
638 /// // let filtered = proxy.filter_with_any("name", "rust").await?;
639 /// ```
640 pub async fn filter_with_any<T>(
641 &self,
642 source: &T,
643 field: &str,
644 value: &str,
645 ) -> ProxyResult<Vec<ScalarValue>>
646 where
647 T: super::reflection::Reflectable,
648 {
649 // Get the collection
650 let rel = source
651 .get_relationship(&self.relationship)
652 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
653
654 let collection = super::reflection::downcast_relationship::<
655 Vec<Box<dyn super::reflection::Reflectable>>,
656 >(rel)?;
657
658 // Filter values where any item in the collection matches the field and value
659 let mut filtered_values = Vec::new();
660 for item in collection.iter() {
661 // Check if any attribute of the item matches
662 if let Some(attr_value) = item.get_attribute(field) {
663 let matches = match &attr_value {
664 ScalarValue::String(s) => s.contains(value),
665 _ => format!("{:?}", attr_value).contains(value),
666 };
667
668 if matches {
669 // Add the proxy's target attribute to filtered values
670 if let Some(value) = item.get_attribute(&self.attribute) {
671 filtered_values.push(value);
672 }
673 }
674 }
675 }
676
677 Ok(filtered_values)
678 }
679
680 /// Filter collection where item has specific relationship
681 ///
682 /// Filters the collection to include only items that have a specific
683 /// relationship with the given value.
684 ///
685 /// # Examples
686 ///
687 /// ```no_run
688 /// use reinhardt_urls::proxy::CollectionProxy;
689 ///
690 /// // Example: Filter posts that have comments from "Alice"
691 /// // let proxy = CollectionProxy::new("posts", "title");
692 /// // let source = ...;
693 /// // let filtered = proxy.filter_with_has(&source, "comments", "Alice").await?;
694 /// ```
695 pub async fn filter_with_has<T>(
696 &self,
697 source: &T,
698 relationship: &str,
699 value: &str,
700 ) -> ProxyResult<Vec<ScalarValue>>
701 where
702 T: super::reflection::Reflectable,
703 {
704 // Get the collection
705 let rel = source
706 .get_relationship(&self.relationship)
707 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
708
709 let collection = super::reflection::downcast_relationship::<
710 Vec<Box<dyn super::reflection::Reflectable>>,
711 >(rel)?;
712
713 // Filter items that have the specified relationship
714 let mut filtered_values = Vec::new();
715 for item in collection.iter() {
716 // Check if item has the specified relationship
717 if let Some(item_rel) = item.get_relationship(relationship) {
718 // Check if the relationship contains the value
719 if let Ok(rel_collection) = super::reflection::downcast_relationship::<
720 Vec<Box<dyn super::reflection::Reflectable>>,
721 >(item_rel)
722 {
723 let has_value = rel_collection.iter().any(|rel_item| {
724 if let Some(ScalarValue::String(s)) =
725 rel_item.get_attribute(&self.attribute).as_ref()
726 {
727 s == value
728 } else {
729 false
730 }
731 });
732
733 if has_value {
734 // Add the item's attribute to filtered values
735 if let Some(value) = item.get_attribute(&self.attribute) {
736 filtered_values.push(value);
737 }
738 }
739 }
740 }
741 }
742
743 Ok(filtered_values)
744 }
745
746 /// Configure cascade behavior for related operations
747 ///
748 /// # Examples
749 ///
750 /// ```
751 /// use reinhardt_urls::proxy::CollectionProxy;
752 ///
753 /// let proxy = CollectionProxy::new("posts", "title")
754 /// .with_cascade(true);
755 /// assert!(proxy.is_cascade());
756 /// ```
757 pub fn with_cascade(mut self, cascade: bool) -> Self {
758 self.cascade = cascade;
759 self
760 }
761
762 /// Merge with another collection proxy
763 ///
764 /// Merges two collection proxies by combining their values.
765 /// Note: This operation requires both proxies to work on the same source object.
766 ///
767 /// # Examples
768 ///
769 /// ```no_run
770 /// use reinhardt_urls::proxy::CollectionProxy;
771 ///
772 /// // Merge values from two different relationships
773 /// // let proxy1 = CollectionProxy::new("posts", "title");
774 /// // let proxy2 = CollectionProxy::new("drafts", "title");
775 /// // let source = ...;
776 /// // let merged = proxy1.merge(&source, proxy2, &source).await?;
777 /// ```
778 pub async fn merge<T>(
779 &self,
780 source1: &T,
781 other: &Self,
782 source2: &T,
783 ) -> ProxyResult<Vec<ScalarValue>>
784 where
785 T: super::reflection::Reflectable,
786 {
787 // Get values from both proxies
788 let mut values1 = self.get_values(source1).await?;
789 let values2 = other.get_values(source2).await?;
790
791 // Merge the values
792 values1.extend(values2);
793
794 // Optionally deduplicate if unique is set
795 if self.unique || other.unique {
796 values1.sort_by(|a, b| format!("{:?}", a).cmp(&format!("{:?}", b)));
797 values1.dedup_by(|a, b| format!("{:?}", a) == format!("{:?}", b));
798 }
799
800 Ok(values1)
801 }
802
803 /// Set batch size for collection operations
804 ///
805 /// # Examples
806 ///
807 /// ```
808 /// use reinhardt_urls::proxy::CollectionProxy;
809 ///
810 /// let proxy = CollectionProxy::new("posts", "title")
811 /// .with_batch_size(100);
812 /// assert_eq!(proxy.batch_size(), Some(100));
813 /// ```
814 pub fn with_batch_size(mut self, batch_size: usize) -> Self {
815 self.batch_size = Some(batch_size);
816 self
817 }
818
819 /// Enable async loading for collection
820 ///
821 /// # Examples
822 ///
823 /// ```no_run
824 /// use reinhardt_urls::proxy::CollectionProxy;
825 ///
826 /// let proxy = CollectionProxy::new("posts", "title")
827 /// .with_async_loading(true);
828 /// ```
829 pub fn with_async_loading(mut self, async_loading: bool) -> Self {
830 self.async_loading = async_loading;
831 self
832 }
833
834 /// Enable streaming for large collections
835 ///
836 /// # Examples
837 ///
838 /// ```no_run
839 /// use reinhardt_urls::proxy::CollectionProxy;
840 ///
841 /// let proxy = CollectionProxy::new("logs", "message")
842 /// .with_streaming(true);
843 /// ```
844 pub fn with_streaming(mut self, streaming: bool) -> Self {
845 self.streaming = streaming;
846 self
847 }
848
849 /// Enable caching for collection queries
850 ///
851 /// # Examples
852 ///
853 /// ```no_run
854 /// use reinhardt_urls::proxy::CollectionProxy;
855 ///
856 /// let proxy = CollectionProxy::new("posts", "title")
857 /// .with_caching(true);
858 /// ```
859 pub fn with_caching(mut self, caching: bool) -> Self {
860 self.caching = caching;
861 self
862 }
863
864 /// Configure database for collection operations
865 ///
866 /// # Examples
867 ///
868 /// ```no_run
869 /// use reinhardt_urls::proxy::CollectionProxy;
870 ///
871 /// let proxy = CollectionProxy::new("posts", "title")
872 /// .with_database("analytics");
873 /// ```
874 pub fn with_database(mut self, database: &str) -> Self {
875 self.database = Some(database.to_string());
876 self
877 }
878
879 /// Set memory limit for collection operations
880 ///
881 /// # Examples
882 ///
883 /// ```
884 /// use reinhardt_urls::proxy::CollectionProxy;
885 ///
886 /// let proxy = CollectionProxy::new("posts", "title")
887 /// .with_memory_limit(1024 * 1024);
888 /// assert_eq!(proxy.memory_limit(), Some(1024 * 1024));
889 /// ```
890 pub fn with_memory_limit(mut self, memory_limit: usize) -> Self {
891 self.memory_limit = Some(memory_limit);
892 self
893 }
894
895 /// Configure stored procedure for collection operations
896 ///
897 /// # Examples
898 ///
899 /// ```no_run
900 /// use reinhardt_urls::proxy::CollectionProxy;
901 ///
902 /// let proxy = CollectionProxy::new("posts", "title")
903 /// .with_stored_procedure("get_posts");
904 /// ```
905 pub fn with_stored_procedure(mut self, procedure: &str) -> Self {
906 self.stored_procedure = Some(procedure.to_string());
907 self
908 }
909
910 /// Configure triggers for collection operations
911 ///
912 /// # Examples
913 ///
914 /// ```no_run
915 /// use reinhardt_urls::proxy::CollectionProxy;
916 ///
917 /// let proxy = CollectionProxy::new("posts", "title")
918 /// .with_triggers(true);
919 /// ```
920 pub fn with_triggers(mut self, triggers: bool) -> Self {
921 self.triggers = triggers;
922 self
923 }
924
925 /// Enable version tracking for collection items
926 ///
927 /// # Examples
928 ///
929 /// ```
930 /// use reinhardt_urls::proxy::CollectionProxy;
931 ///
932 /// let proxy = CollectionProxy::new("posts", "title")
933 /// .with_version_tracking(true);
934 /// assert!(proxy.is_version_tracking());
935 /// ```
936 pub fn with_version_tracking(mut self, version_tracking: bool) -> Self {
937 self.version_tracking = version_tracking;
938 self
939 }
940
941 /// Bulk insert multiple items into the collection
942 ///
943 /// Inserts multiple scalar values into the collection by creating
944 /// Reflectable objects using the configured factory.
945 ///
946 /// # Examples
947 ///
948 /// ```no_run
949 /// use reinhardt_urls::proxy::{CollectionProxy, ScalarValue};
950 ///
951 /// // Requires factory to be configured
952 /// // let proxy = CollectionProxy::with_factory("tags", "name", factory)
953 /// // .with_batch_size(100);
954 /// // let mut source = ...;
955 /// // let values = vec![
956 /// // ScalarValue::String("rust".to_string()),
957 /// // ScalarValue::String("python".to_string()),
958 /// // ];
959 /// // proxy.bulk_insert(&mut source, values).await?;
960 /// ```
961 pub async fn bulk_insert<T>(&self, source: &mut T, items: Vec<ScalarValue>) -> ProxyResult<()>
962 where
963 T: super::reflection::Reflectable,
964 {
965 // Check if factory is configured
966 let factory = self
967 .factory
968 .as_ref()
969 .ok_or(ProxyError::FactoryNotConfigured)?;
970
971 // Access the relationship
972 let relationship = source
973 .get_relationship_mut(&self.relationship)
974 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
975
976 // Downcast to Vec<Box<dyn Reflectable>>
977 let collection = relationship
978 .downcast_mut::<Vec<Box<dyn super::reflection::Reflectable>>>()
979 .ok_or_else(|| ProxyError::TypeMismatch {
980 expected: "Vec<Box<dyn Reflectable>>".to_string(),
981 actual: "unknown".to_string(),
982 })?;
983
984 // Determine batch size (default to all items if not set)
985 let batch_size = self.batch_size.unwrap_or(items.len());
986
987 // Process items in batches
988 for chunk in items.chunks(batch_size) {
989 for value in chunk {
990 let new_object = factory.create_from_scalar(&self.attribute, value.clone())?;
991 collection.push(new_object);
992 }
993 }
994
995 Ok(())
996 }
997
998 /// Clear all items from the collection
999 ///
1000 /// # Examples
1001 ///
1002 /// ```no_run
1003 /// use reinhardt_urls::proxy::CollectionProxy;
1004 ///
1005 /// // Requires a mutable source object with Reflectable trait
1006 /// // let mut source = ...;
1007 /// // let proxy = CollectionProxy::new("tags", "name");
1008 /// // proxy.clear_on(&mut source).await?;
1009 /// ```
1010 pub async fn clear_on<T>(&self, source: &mut T) -> ProxyResult<()>
1011 where
1012 T: super::reflection::Reflectable,
1013 {
1014 // Access the relationship
1015 let relationship = source
1016 .get_relationship_mut(&self.relationship)
1017 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
1018
1019 // Downcast to Vec<Box<dyn Reflectable>>
1020 let collection = relationship
1021 .downcast_mut::<Vec<Box<dyn super::reflection::Reflectable>>>()
1022 .ok_or_else(|| ProxyError::TypeMismatch {
1023 expected: "Vec<Box<dyn Reflectable>>".to_string(),
1024 actual: "unknown".to_string(),
1025 })?;
1026
1027 // Clear the collection
1028 collection.clear();
1029
1030 Ok(())
1031 }
1032
1033 /// Check if this proxy targets a database view
1034 ///
1035 /// # Examples
1036 ///
1037 /// ```
1038 /// use reinhardt_urls::proxy::CollectionProxy;
1039 ///
1040 /// let proxy = CollectionProxy::new("user_view", "name")
1041 /// .with_view(true);
1042 /// assert!(proxy.is_view());
1043 /// ```
1044 pub fn is_view(&self) -> bool {
1045 self.is_view
1046 }
1047
1048 /// Configure this proxy to target a database view
1049 ///
1050 /// # Examples
1051 ///
1052 /// ```
1053 /// use reinhardt_urls::proxy::CollectionProxy;
1054 ///
1055 /// let proxy = CollectionProxy::new("user_summary", "name")
1056 /// .with_view(true);
1057 /// assert!(proxy.is_view());
1058 /// ```
1059 pub fn with_view(mut self, is_view: bool) -> Self {
1060 self.is_view = is_view;
1061 self
1062 }
1063
1064 /// Update items with version tracking
1065 ///
1066 /// Updates collection items with optimistic locking based on version numbers.
1067 /// This method ensures that updates only succeed if the version matches,
1068 /// preventing concurrent modification conflicts.
1069 ///
1070 /// # Examples
1071 ///
1072 /// ```no_run
1073 /// use reinhardt_urls::proxy::{CollectionProxy, ScalarValue};
1074 ///
1075 /// // Requires version tracking to be enabled
1076 /// // let proxy = CollectionProxy::new("documents", "content")
1077 /// // .with_version_tracking(true);
1078 /// // let mut source = ...;
1079 /// // let new_value = ScalarValue::String("updated content".to_string());
1080 /// // proxy.update_with_version(&mut source, new_value, 1).await?;
1081 /// ```
1082 pub async fn update_with_version<T>(
1083 &self,
1084 source: &mut T,
1085 value: ScalarValue,
1086 expected_version: i64,
1087 ) -> ProxyResult<()>
1088 where
1089 T: super::reflection::Reflectable,
1090 {
1091 // Check if version tracking is enabled
1092 if !self.version_tracking {
1093 return Err(ProxyError::VersionTrackingNotEnabled);
1094 }
1095
1096 // Access the relationship
1097 let relationship = source
1098 .get_relationship_mut(&self.relationship)
1099 .ok_or_else(|| ProxyError::RelationshipNotFound(self.relationship.clone()))?;
1100
1101 // Downcast to Vec<Box<dyn Reflectable>>
1102 let collection = relationship
1103 .downcast_mut::<Vec<Box<dyn super::reflection::Reflectable>>>()
1104 .ok_or_else(|| ProxyError::TypeMismatch {
1105 expected: "Vec<Box<dyn Reflectable>>".to_string(),
1106 actual: "unknown".to_string(),
1107 })?;
1108
1109 // Update items with version check
1110 for item in collection.iter_mut() {
1111 // Check if item has version field
1112 if let Some(current_version) = item.get_attribute("version")
1113 && let ScalarValue::Integer(ver) = current_version
1114 {
1115 // Check version match
1116 if ver == expected_version {
1117 // Update the value using set_attribute
1118 item.set_attribute(&self.attribute, value.clone())?;
1119 // Increment version
1120 item.set_attribute("version", ScalarValue::Integer(ver + 1))?;
1121 } else {
1122 return Err(ProxyError::VersionMismatch {
1123 expected: expected_version,
1124 actual: ver,
1125 });
1126 }
1127 }
1128 }
1129
1130 Ok(())
1131 }
1132
1133 /// Configure cache time-to-live in seconds
1134 ///
1135 /// # Examples
1136 ///
1137 /// ```
1138 /// use reinhardt_urls::proxy::CollectionProxy;
1139 ///
1140 /// let proxy = CollectionProxy::new("products", "price")
1141 /// .with_caching(true)
1142 /// .with_cache_ttl(3600); // 1 hour
1143 /// ```
1144 pub fn with_cache_ttl(mut self, ttl: u64) -> Self {
1145 self.cache_ttl = Some(ttl);
1146 self
1147 }
1148
1149 /// Configure chunk size for batch operations
1150 ///
1151 /// # Examples
1152 ///
1153 /// ```
1154 /// use reinhardt_urls::proxy::CollectionProxy;
1155 ///
1156 /// let proxy = CollectionProxy::new("orders", "total")
1157 /// .with_batch_size(100)
1158 /// .with_chunk_size(10); // Process 10 items at a time
1159 /// assert_eq!(proxy.chunk_size(), Some(10));
1160 /// ```
1161 pub fn with_chunk_size(mut self, chunk_size: usize) -> Self {
1162 self.chunk_size = Some(chunk_size);
1163 self
1164 }
1165
1166 /// Configure concurrent access settings
1167 ///
1168 /// # Examples
1169 ///
1170 /// ```
1171 /// use reinhardt_urls::proxy::CollectionProxy;
1172 ///
1173 /// let proxy = CollectionProxy::new("sessions", "data")
1174 /// .with_concurrent_access(true);
1175 /// ```
1176 pub fn with_concurrent_access(mut self, concurrent: bool) -> Self {
1177 self.concurrent_access = concurrent;
1178 self
1179 }
1180
1181 /// Configure fallback database for read operations
1182 ///
1183 /// # Examples
1184 ///
1185 /// ```
1186 /// use reinhardt_urls::proxy::CollectionProxy;
1187 ///
1188 /// let proxy = CollectionProxy::new("users", "name")
1189 /// .with_database("primary")
1190 /// .with_fallback_database("replica");
1191 /// ```
1192 pub fn with_fallback_database(mut self, database: &str) -> Self {
1193 self.fallback_database = Some(database.to_string());
1194 self
1195 }
1196
1197 /// Configure parameters for stored procedures
1198 ///
1199 /// # Examples
1200 ///
1201 /// ```
1202 /// use reinhardt_urls::proxy::CollectionProxy;
1203 ///
1204 /// let proxy = CollectionProxy::new("reports", "data")
1205 /// .with_stored_procedure("generate_report")
1206 /// .with_procedure_params(&[("start_date", "2024-01-01"), ("end_date", "2024-12-31")]);
1207 /// ```
1208 pub fn with_procedure_params(mut self, params: &[(&str, &str)]) -> Self {
1209 self.procedure_params = params
1210 .iter()
1211 .map(|(k, v)| (k.to_string(), v.to_string()))
1212 .collect();
1213 self
1214 }
1215
1216 /// Configure trigger events to monitor
1217 ///
1218 /// # Examples
1219 ///
1220 /// ```
1221 /// use reinhardt_urls::proxy::CollectionProxy;
1222 ///
1223 /// let proxy = CollectionProxy::new("audit_logs", "action")
1224 /// .with_triggers(true)
1225 /// .with_trigger_events(&["INSERT", "UPDATE", "DELETE"]);
1226 /// ```
1227 pub fn with_trigger_events(mut self, events: &[&str]) -> Self {
1228 self.trigger_events = events.iter().map(|e| e.to_string()).collect();
1229 self
1230 }
1231
1232 // Getter methods
1233
1234 /// Check if caching is enabled
1235 ///
1236 /// # Examples
1237 ///
1238 /// ```
1239 /// use reinhardt_urls::proxy::CollectionProxy;
1240 ///
1241 /// let proxy = CollectionProxy::new("posts", "title")
1242 /// .with_caching(true);
1243 /// assert!(proxy.is_cached());
1244 /// ```
1245 pub fn is_cached(&self) -> bool {
1246 self.caching
1247 }
1248
1249 /// Get cache time-to-live
1250 ///
1251 /// # Examples
1252 ///
1253 /// ```
1254 /// use reinhardt_urls::proxy::CollectionProxy;
1255 ///
1256 /// let proxy = CollectionProxy::new("posts", "title")
1257 /// .with_cache_ttl(300);
1258 /// assert_eq!(proxy.cache_ttl(), Some(300));
1259 /// ```
1260 pub fn cache_ttl(&self) -> Option<u64> {
1261 self.cache_ttl
1262 }
1263
1264 /// Check if triggers are enabled
1265 ///
1266 /// # Examples
1267 ///
1268 /// ```
1269 /// use reinhardt_urls::proxy::CollectionProxy;
1270 ///
1271 /// let proxy = CollectionProxy::new("users", "name")
1272 /// .with_triggers(true);
1273 /// assert!(proxy.has_triggers());
1274 /// ```
1275 pub fn has_triggers(&self) -> bool {
1276 self.triggers
1277 }
1278
1279 /// Get trigger events
1280 ///
1281 /// # Examples
1282 ///
1283 /// ```
1284 /// use reinhardt_urls::proxy::CollectionProxy;
1285 ///
1286 /// let proxy = CollectionProxy::new("logs", "action")
1287 /// .with_trigger_events(&["INSERT", "UPDATE"]);
1288 /// assert_eq!(proxy.trigger_events().len(), 2);
1289 /// ```
1290 pub fn trigger_events(&self) -> &[String] {
1291 &self.trigger_events
1292 }
1293
1294 /// Get stored procedure name
1295 ///
1296 /// # Examples
1297 ///
1298 /// ```
1299 /// use reinhardt_urls::proxy::CollectionProxy;
1300 ///
1301 /// let proxy = CollectionProxy::new("reports", "data")
1302 /// .with_stored_procedure("generate_report");
1303 /// assert_eq!(proxy.stored_procedure(), Some("generate_report"));
1304 /// ```
1305 pub fn stored_procedure(&self) -> Option<&str> {
1306 self.stored_procedure.as_deref()
1307 }
1308
1309 /// Get stored procedure parameters
1310 ///
1311 /// # Examples
1312 ///
1313 /// ```
1314 /// use reinhardt_urls::proxy::CollectionProxy;
1315 ///
1316 /// let proxy = CollectionProxy::new("reports", "data")
1317 /// .with_procedure_params(&[("year", "2024")]);
1318 /// assert_eq!(proxy.procedure_params().len(), 1);
1319 /// ```
1320 pub fn procedure_params(&self) -> &[(String, String)] {
1321 &self.procedure_params
1322 }
1323
1324 /// Get target database name
1325 ///
1326 /// # Examples
1327 ///
1328 /// ```
1329 /// use reinhardt_urls::proxy::CollectionProxy;
1330 ///
1331 /// let proxy = CollectionProxy::new("users", "name")
1332 /// .with_database("primary");
1333 /// assert_eq!(proxy.database(), Some("primary"));
1334 /// ```
1335 pub fn database(&self) -> Option<&str> {
1336 self.database.as_deref()
1337 }
1338
1339 /// Get fallback database name
1340 ///
1341 /// # Examples
1342 ///
1343 /// ```
1344 /// use reinhardt_urls::proxy::CollectionProxy;
1345 ///
1346 /// let proxy = CollectionProxy::new("users", "name")
1347 /// .with_fallback_database("replica");
1348 /// assert_eq!(proxy.fallback_database(), Some("replica"));
1349 /// ```
1350 pub fn fallback_database(&self) -> Option<&str> {
1351 self.fallback_database.as_deref()
1352 }
1353
1354 /// Check if async loading is enabled
1355 ///
1356 /// # Examples
1357 ///
1358 /// ```
1359 /// use reinhardt_urls::proxy::CollectionProxy;
1360 ///
1361 /// let proxy = CollectionProxy::new("posts", "title")
1362 /// .with_async_loading(true);
1363 /// assert!(proxy.is_async_loading());
1364 /// ```
1365 pub fn is_async_loading(&self) -> bool {
1366 self.async_loading
1367 }
1368
1369 /// Check if concurrent access is supported
1370 ///
1371 /// # Examples
1372 ///
1373 /// ```
1374 /// use reinhardt_urls::proxy::CollectionProxy;
1375 ///
1376 /// let proxy = CollectionProxy::new("sessions", "data")
1377 /// .with_concurrent_access(true);
1378 /// assert!(proxy.supports_concurrent_access());
1379 /// ```
1380 pub fn supports_concurrent_access(&self) -> bool {
1381 self.concurrent_access
1382 }
1383
1384 /// Get batch size
1385 ///
1386 /// # Examples
1387 ///
1388 /// ```
1389 /// use reinhardt_urls::proxy::CollectionProxy;
1390 ///
1391 /// let proxy = CollectionProxy::new("orders", "total")
1392 /// .with_batch_size(100);
1393 /// assert_eq!(proxy.batch_size(), Some(100));
1394 /// ```
1395 pub fn batch_size(&self) -> Option<usize> {
1396 self.batch_size
1397 }
1398
1399 /// Get chunk size
1400 ///
1401 /// # Examples
1402 ///
1403 /// ```
1404 /// use reinhardt_urls::proxy::CollectionProxy;
1405 ///
1406 /// let proxy = CollectionProxy::new("orders", "total")
1407 /// .with_chunk_size(10);
1408 /// assert_eq!(proxy.chunk_size(), Some(10));
1409 /// ```
1410 pub fn chunk_size(&self) -> Option<usize> {
1411 self.chunk_size
1412 }
1413
1414 /// Check if cascade is enabled
1415 ///
1416 /// # Examples
1417 ///
1418 /// ```
1419 /// use reinhardt_urls::proxy::CollectionProxy;
1420 ///
1421 /// let proxy = CollectionProxy::new("posts", "title")
1422 /// .with_cascade(true);
1423 /// assert!(proxy.is_cascade());
1424 /// ```
1425 pub fn is_cascade(&self) -> bool {
1426 self.cascade
1427 }
1428
1429 /// Check if version tracking is enabled
1430 ///
1431 /// # Examples
1432 ///
1433 /// ```
1434 /// use reinhardt_urls::proxy::CollectionProxy;
1435 ///
1436 /// let proxy = CollectionProxy::new("documents", "content")
1437 /// .with_version_tracking(true);
1438 /// assert!(proxy.is_version_tracking());
1439 /// ```
1440 pub fn is_version_tracking(&self) -> bool {
1441 self.version_tracking
1442 }
1443
1444 /// Get memory limit
1445 ///
1446 /// # Examples
1447 ///
1448 /// ```
1449 /// use reinhardt_urls::proxy::CollectionProxy;
1450 ///
1451 /// let proxy = CollectionProxy::new("large_data", "content")
1452 /// .with_memory_limit(1024 * 1024);
1453 /// assert_eq!(proxy.memory_limit(), Some(1024 * 1024));
1454 /// ```
1455 pub fn memory_limit(&self) -> Option<usize> {
1456 self.memory_limit
1457 }
1458}
1459
1460// Manual Debug implementation for CollectionProxy
1461impl std::fmt::Debug for CollectionProxy {
1462 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1463 f.debug_struct("CollectionProxy")
1464 .field("relationship", &self.relationship)
1465 .field("attribute", &self.attribute)
1466 .field("unique", &self.unique)
1467 .field("deduplicate", &self.deduplicate)
1468 .field("loading_strategy", &self.loading_strategy)
1469 .field("factory", &self.factory.as_ref().map(|_| "Some(<factory>)"))
1470 .finish()
1471 }
1472}
1473
1474// Manual Serialize implementation (factory and loading_strategy are not serializable)
1475impl Serialize for CollectionProxy {
1476 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1477 where
1478 S: serde::Serializer,
1479 {
1480 use serde::ser::SerializeStruct;
1481 let mut state = serializer.serialize_struct("CollectionProxy", 8)?;
1482 state.serialize_field("relationship", &self.relationship)?;
1483 state.serialize_field("attribute", &self.attribute)?;
1484 state.serialize_field("unique", &self.unique)?;
1485 state.serialize_field("batch_size", &self.batch_size)?;
1486 state.serialize_field("chunk_size", &self.chunk_size)?;
1487 state.serialize_field("cascade", &self.cascade)?;
1488 state.serialize_field("version_tracking", &self.version_tracking)?;
1489 state.serialize_field("memory_limit", &self.memory_limit)?;
1490 // loading_strategy and factory are not serialized (not Serialize)
1491 state.end()
1492 }
1493}
1494
1495// Manual Deserialize implementation (factory and loading_strategy are restored as None)
1496impl<'de> Deserialize<'de> for CollectionProxy {
1497 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1498 where
1499 D: serde::Deserializer<'de>,
1500 {
1501 struct CollectionProxyVisitor;
1502
1503 impl<'de> serde::de::Visitor<'de> for CollectionProxyVisitor {
1504 type Value = CollectionProxy;
1505
1506 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1507 formatter.write_str("struct CollectionProxy")
1508 }
1509
1510 fn visit_map<V>(self, mut map: V) -> Result<CollectionProxy, V::Error>
1511 where
1512 V: serde::de::MapAccess<'de>,
1513 {
1514 let mut relationship = None;
1515 let mut attribute = None;
1516 let mut unique = None;
1517 let mut caching = None;
1518 let mut cache_ttl = None;
1519 let mut batch_size = None;
1520 let mut chunk_size = None;
1521 let mut cascade = None;
1522 let mut version_tracking = None;
1523 let mut memory_limit = None;
1524
1525 while let Some(key) = map.next_key::<String>()? {
1526 match key.as_str() {
1527 "relationship" => {
1528 if relationship.is_some() {
1529 return Err(serde::de::Error::duplicate_field("relationship"));
1530 }
1531 relationship = Some(map.next_value()?);
1532 }
1533 "attribute" => {
1534 if attribute.is_some() {
1535 return Err(serde::de::Error::duplicate_field("attribute"));
1536 }
1537 attribute = Some(map.next_value()?);
1538 }
1539 "unique" => {
1540 if unique.is_some() {
1541 return Err(serde::de::Error::duplicate_field("unique"));
1542 }
1543 unique = Some(map.next_value()?);
1544 }
1545 "caching" => {
1546 if caching.is_some() {
1547 return Err(serde::de::Error::duplicate_field("caching"));
1548 }
1549 caching = Some(map.next_value()?);
1550 }
1551 "cache_ttl" => {
1552 if cache_ttl.is_some() {
1553 return Err(serde::de::Error::duplicate_field("cache_ttl"));
1554 }
1555 cache_ttl = Some(map.next_value()?);
1556 }
1557 "batch_size" => {
1558 if batch_size.is_some() {
1559 return Err(serde::de::Error::duplicate_field("batch_size"));
1560 }
1561 batch_size = Some(map.next_value()?);
1562 }
1563 "chunk_size" => {
1564 if chunk_size.is_some() {
1565 return Err(serde::de::Error::duplicate_field("chunk_size"));
1566 }
1567 chunk_size = Some(map.next_value()?);
1568 }
1569 "cascade" => {
1570 if cascade.is_some() {
1571 return Err(serde::de::Error::duplicate_field("cascade"));
1572 }
1573 cascade = Some(map.next_value()?);
1574 }
1575 "version_tracking" => {
1576 if version_tracking.is_some() {
1577 return Err(serde::de::Error::duplicate_field("version_tracking"));
1578 }
1579 version_tracking = Some(map.next_value()?);
1580 }
1581 "memory_limit" => {
1582 if memory_limit.is_some() {
1583 return Err(serde::de::Error::duplicate_field("memory_limit"));
1584 }
1585 memory_limit = Some(map.next_value()?);
1586 }
1587 _ => {
1588 // Ignore unknown fields
1589 let _ = map.next_value::<serde::de::IgnoredAny>()?;
1590 }
1591 }
1592 }
1593
1594 let relationship =
1595 relationship.ok_or_else(|| serde::de::Error::missing_field("relationship"))?;
1596 let attribute =
1597 attribute.ok_or_else(|| serde::de::Error::missing_field("attribute"))?;
1598 let unique = unique.ok_or_else(|| serde::de::Error::missing_field("unique"))?;
1599
1600 Ok(CollectionProxy {
1601 relationship,
1602 attribute,
1603 unique,
1604 deduplicate: false, // Default value
1605 loading_strategy: None, // Cannot be deserialized
1606 factory: None, // Cannot be deserialized
1607 caching: caching.unwrap_or(false),
1608 cache_ttl,
1609 streaming: false,
1610 triggers: false,
1611 trigger_events: Vec::new(),
1612 stored_procedure: None,
1613 procedure_params: Vec::new(),
1614 database: None,
1615 fallback_database: None,
1616 async_loading: false,
1617 concurrent_access: false,
1618 is_view: false,
1619 batch_size,
1620 chunk_size,
1621 cascade: cascade.unwrap_or(false),
1622 version_tracking: version_tracking.unwrap_or(false),
1623 memory_limit,
1624 })
1625 }
1626 }
1627
1628 const FIELDS: &[&str] = &[
1629 "relationship",
1630 "attribute",
1631 "unique",
1632 "batch_size",
1633 "chunk_size",
1634 "cascade",
1635 "version_tracking",
1636 "memory_limit",
1637 ];
1638 deserializer.deserialize_struct("CollectionProxy", FIELDS, CollectionProxyVisitor)
1639 }
1640}