Skip to main content

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}