1use super::{Model, RelationshipType};
11use reinhardt_query::prelude::{
12 Alias, Condition, Expr, ExprTrait, Query, QueryStatementBuilder, SimpleExpr,
13};
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::marker::PhantomData;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum InheritanceType {
22 SingleTable,
25
26 JoinedTable,
29
30 ConcreteTable,
32}
33
34#[non_exhaustive]
37#[derive(Debug, Clone)]
38pub struct PolymorphicConfig {
39 type_field: String,
43
44 id_field: String,
47
48 inheritance_type: InheritanceType,
50
51 default_type: Option<String>,
53}
54
55impl PolymorphicConfig {
56 pub fn new(type_field: impl Into<String>, id_field: impl Into<String>) -> Self {
69 Self {
70 type_field: type_field.into(),
71 id_field: id_field.into(),
72 inheritance_type: InheritanceType::SingleTable,
73 default_type: None,
74 }
75 }
76
77 pub fn with_inheritance(mut self, inheritance_type: InheritanceType) -> Self {
79 self.inheritance_type = inheritance_type;
80 self
81 }
82
83 pub fn with_default_type(mut self, default_type: impl Into<String>) -> Self {
85 self.default_type = Some(default_type.into());
86 self
87 }
88
89 pub fn type_field(&self) -> &str {
91 &self.type_field
92 }
93
94 pub fn id_field(&self) -> &str {
96 &self.id_field
97 }
98
99 pub fn inheritance_type(&self) -> InheritanceType {
101 self.inheritance_type
102 }
103
104 pub fn default_type(&self) -> Option<&str> {
106 self.default_type.as_deref()
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
113pub struct PolymorphicIdentity {
114 type_id: String,
116
117 table_name: String,
119
120 pk_field: String,
122}
123
124impl PolymorphicIdentity {
125 pub fn new(
138 type_id: impl Into<String>,
139 table_name: impl Into<String>,
140 pk_field: impl Into<String>,
141 ) -> Self {
142 Self {
143 type_id: type_id.into(),
144 table_name: table_name.into(),
145 pk_field: pk_field.into(),
146 }
147 }
148
149 pub fn type_id(&self) -> &str {
151 &self.type_id
152 }
153
154 pub fn table_name(&self) -> &str {
156 &self.table_name
157 }
158
159 pub fn pk_field(&self) -> &str {
161 &self.pk_field
162 }
163}
164
165pub struct PolymorphicRelation<P: Model> {
168 name: String,
170
171 config: PolymorphicConfig,
173
174 identities: HashMap<String, PolymorphicIdentity>,
176
177 relationship_type: RelationshipType,
179
180 _phantom: PhantomData<P>,
181}
182
183impl<P: Model> PolymorphicRelation<P> {
184 pub fn new(name: impl Into<String>, config: PolymorphicConfig) -> Self {
217 Self {
218 name: name.into(),
219 config,
220 identities: HashMap::new(),
221 relationship_type: RelationshipType::ManyToOne,
222 _phantom: PhantomData,
223 }
224 }
225
226 pub fn register_type(&mut self, identity: PolymorphicIdentity) {
262 self.identities
263 .insert(identity.type_id().to_string(), identity);
264 }
265
266 pub fn get_identity(&self, type_id: &str) -> Option<&PolymorphicIdentity> {
268 self.identities.get(type_id)
269 }
270
271 pub fn type_ids(&self) -> Vec<&str> {
273 self.identities.keys().map(|s| s.as_str()).collect()
274 }
275
276 pub fn name(&self) -> &str {
278 &self.name
279 }
280
281 pub fn config(&self) -> &PolymorphicConfig {
283 &self.config
284 }
285
286 pub fn relationship_type(&self) -> RelationshipType {
288 self.relationship_type
289 }
290
291 pub fn build_query(&self, type_id: &str, object_id: &str) -> Option<(String, Vec<String>)> {
336 let identity = self.get_identity(type_id)?;
337
338 let sql = format!(
339 "SELECT * FROM \"{}\" WHERE \"{}\" = $1",
340 identity.table_name(),
341 identity.pk_field(),
342 );
343 Some((sql, vec![object_id.to_string()]))
344 }
345
346 pub fn type_filter(&self, type_id: &str) -> SimpleExpr {
351 Expr::col(Alias::new(self.config.type_field())).eq(type_id)
352 }
353
354 pub fn join_clause(&self, type_id: &str, parent_alias: &str) -> Option<String> {
358 let identity = self.get_identity(type_id)?;
359
360 Some(format!(
361 "LEFT JOIN \"{}\" ON \"{}\".\"{}\" = \"{}\".\"{}\"",
362 identity.table_name(),
363 parent_alias,
364 self.config.id_field(),
365 identity.table_name(),
366 identity.pk_field()
367 ))
368 }
369}
370
371#[derive(Debug, Default)]
374pub struct PolymorphicRegistry {
375 identities: HashMap<String, PolymorphicIdentity>,
377}
378
379impl PolymorphicRegistry {
380 pub fn new() -> Self {
391 Self::default()
392 }
393
394 pub fn register(&mut self, identity: PolymorphicIdentity) {
408 self.identities
409 .insert(identity.type_id().to_string(), identity);
410 }
411
412 pub fn get(&self, type_id: &str) -> Option<&PolymorphicIdentity> {
414 self.identities.get(type_id)
415 }
416
417 pub fn type_ids(&self) -> Vec<&str> {
419 self.identities.keys().map(|s| s.as_str()).collect()
420 }
421
422 pub fn count(&self) -> usize {
424 self.identities.len()
425 }
426
427 pub fn clear(&mut self) {
429 self.identities.clear();
430 }
431}
432
433pub struct PolymorphicQuery<P: Model> {
436 _phantom: PhantomData<P>,
438
439 relation: PolymorphicRelation<P>,
441
442 filters: Vec<SimpleExpr>,
444
445 selected_type: Option<String>,
447}
448
449impl<P: Model> PolymorphicQuery<P> {
450 pub fn new(relation: PolymorphicRelation<P>) -> Self {
452 Self {
453 _phantom: PhantomData,
454 relation,
455 filters: Vec::new(),
456 selected_type: None,
457 }
458 }
459
460 pub fn filter_type(mut self, type_id: impl Into<String>) -> Self {
494 let type_id = type_id.into();
495 self.filters.push(self.relation.type_filter(&type_id));
496 self.selected_type = Some(type_id);
497 self
498 }
499
500 pub fn filter(mut self, condition: SimpleExpr) -> Self {
505 self.filters.push(condition);
506 self
507 }
508
509 pub fn build_sql(&self) -> (String, reinhardt_query::value::Values) {
513 let base_table = P::table_name();
514 let mut stmt = Query::select()
515 .column(Alias::new("*"))
516 .from(Alias::new(base_table))
517 .to_owned();
518
519 if !self.filters.is_empty() {
520 let mut cond = Condition::all();
521 for f in &self.filters {
522 cond = cond.add(f.clone());
523 }
524 stmt = stmt.cond_where(cond).to_owned();
525 }
526
527 stmt.build_any(&reinhardt_query::prelude::PostgresQueryBuilder)
528 }
529
530 pub fn selected_type(&self) -> Option<&str> {
532 self.selected_type.as_deref()
533 }
534
535 pub fn relation(&self) -> &PolymorphicRelation<P> {
537 &self.relation
538 }
539}
540
541static POLYMORPHIC_REGISTRY: once_cell::sync::Lazy<parking_lot::RwLock<PolymorphicRegistry>> =
543 once_cell::sync::Lazy::new(|| parking_lot::RwLock::new(PolymorphicRegistry::new()));
544
545pub fn polymorphic_registry() -> &'static parking_lot::RwLock<PolymorphicRegistry> {
547 &POLYMORPHIC_REGISTRY
548}
549
550#[cfg(test)]
551mod tests {
552 use super::*;
553 use crate::orm::Manager;
554 use reinhardt_core::validators::TableName;
555 use serde::{Deserialize, Serialize};
556
557 #[derive(Debug, Clone, Serialize, Deserialize)]
558 struct Comment {
559 id: Option<i64>,
560 content_type: String,
561 object_id: i64,
562 text: String,
563 }
564
565 const COMMENT_TABLE: TableName = TableName::new_const("comments");
566
567 #[derive(Debug, Clone)]
568 struct CommentFields;
569
570 impl crate::orm::model::FieldSelector for CommentFields {
571 fn with_alias(self, _alias: &str) -> Self {
572 self
573 }
574 }
575
576 impl Model for Comment {
577 type PrimaryKey = i64;
578 type Fields = CommentFields;
579 type Objects = Manager<Self>;
580
581 fn table_name() -> &'static str {
582 COMMENT_TABLE.as_str()
583 }
584
585 fn primary_key(&self) -> Option<Self::PrimaryKey> {
586 self.id
587 }
588
589 fn set_primary_key(&mut self, value: Self::PrimaryKey) {
590 self.id = Some(value);
591 }
592
593 fn primary_key_field() -> &'static str {
594 "id"
595 }
596
597 fn new_fields() -> Self::Fields {
598 CommentFields
599 }
600 }
601
602 #[allow(dead_code)]
604 #[derive(Debug, Clone, Serialize, Deserialize)]
605 struct Post {
606 id: Option<i64>,
607 title: String,
608 }
609
610 #[allow(dead_code)]
612 const POST_TABLE: TableName = TableName::new_const("posts");
613
614 #[derive(Debug, Clone)]
615 struct PostFields;
616
617 impl crate::orm::model::FieldSelector for PostFields {
618 fn with_alias(self, _alias: &str) -> Self {
619 self
620 }
621 }
622
623 impl Model for Post {
624 type PrimaryKey = i64;
625 type Fields = PostFields;
626 type Objects = Manager<Self>;
627
628 fn table_name() -> &'static str {
629 POST_TABLE.as_str()
630 }
631
632 fn primary_key(&self) -> Option<Self::PrimaryKey> {
633 self.id
634 }
635
636 fn set_primary_key(&mut self, value: Self::PrimaryKey) {
637 self.id = Some(value);
638 }
639
640 fn primary_key_field() -> &'static str {
641 "id"
642 }
643
644 fn new_fields() -> Self::Fields {
645 PostFields
646 }
647 }
648
649 #[test]
650 fn test_polymorphic_config_creation() {
651 let config = PolymorphicConfig::new("content_type", "object_id");
652 assert_eq!(config.type_field(), "content_type");
653 assert_eq!(config.id_field(), "object_id");
654 assert_eq!(config.inheritance_type(), InheritanceType::SingleTable);
655 }
656
657 #[test]
658 fn test_polymorphic_config_with_inheritance() {
659 let config =
660 PolymorphicConfig::new("type", "id").with_inheritance(InheritanceType::JoinedTable);
661 assert_eq!(config.inheritance_type(), InheritanceType::JoinedTable);
662 }
663
664 #[test]
665 fn test_polymorphic_config_with_default_type() {
666 let config = PolymorphicConfig::new("type", "id").with_default_type("post");
667 assert_eq!(config.default_type(), Some("post"));
668 }
669
670 #[test]
671 fn test_polymorphic_identity_creation() {
672 let identity = PolymorphicIdentity::new("user", "users", "id");
673 assert_eq!(identity.type_id(), "user");
674 assert_eq!(identity.table_name(), "users");
675 assert_eq!(identity.pk_field(), "id");
676 }
677
678 #[test]
679 fn test_polymorphic_identity_serialization() {
680 let identity = PolymorphicIdentity::new("post", "posts", "id");
681 let json = serde_json::to_string(&identity).unwrap();
682 let deserialized: PolymorphicIdentity = serde_json::from_str(&json).unwrap();
683 assert_eq!(identity, deserialized);
684 }
685
686 #[test]
687 fn test_polymorphic_relation_creation() {
688 let config = PolymorphicConfig::new("content_type", "object_id");
689 let rel = PolymorphicRelation::<Comment>::new("content_object", config);
690 assert_eq!(rel.name(), "content_object");
691 assert_eq!(rel.relationship_type(), RelationshipType::ManyToOne);
692 }
693
694 #[test]
695 fn test_polymorphic_relation_register_type() {
696 let config = PolymorphicConfig::new("content_type", "object_id");
697 let mut rel = PolymorphicRelation::<Comment>::new("content_object", config);
698
699 let post_identity = PolymorphicIdentity::new("post", "posts", "id");
700 rel.register_type(post_identity);
701
702 assert!(rel.get_identity("post").is_some());
703 assert_eq!(rel.type_ids(), vec!["post"]);
704 }
705
706 #[test]
707 fn test_polymorphic_relation_multiple_types() {
708 let config = PolymorphicConfig::new("content_type", "object_id");
709 let mut rel = PolymorphicRelation::<Comment>::new("content_object", config);
710
711 rel.register_type(PolymorphicIdentity::new("post", "posts", "id"));
712 rel.register_type(PolymorphicIdentity::new("user", "users", "id"));
713
714 assert_eq!(rel.type_ids().len(), 2);
715 assert!(rel.get_identity("post").is_some());
716 assert!(rel.get_identity("user").is_some());
717 }
718
719 #[test]
720 fn test_polymorphic_relation_build_query() {
721 let config = PolymorphicConfig::new("content_type", "object_id");
722 let mut rel = PolymorphicRelation::<Comment>::new("content_object", config);
723
724 rel.register_type(PolymorphicIdentity::new("post", "posts", "id"));
725
726 let result = rel.build_query("post", "123");
727 let (sql, params) = result.unwrap();
728 assert!(sql.contains("posts"));
729 assert!(sql.contains("$1"));
730 assert_eq!(params, vec!["123"]);
731 }
732
733 #[test]
734 fn test_polymorphic_relation_build_query_unknown_type() {
735 let config = PolymorphicConfig::new("content_type", "object_id");
736 let rel = PolymorphicRelation::<Comment>::new("content_object", config);
737
738 let sql = rel.build_query("unknown", "123");
739 assert!(sql.is_none());
740 }
741
742 #[test]
743 fn test_polymorphic_relation_type_filter() {
744 use reinhardt_query::prelude::PostgresQueryBuilder;
745
746 let config = PolymorphicConfig::new("content_type", "object_id");
747 let rel = PolymorphicRelation::<Comment>::new("content_object", config);
748
749 let filter = rel.type_filter("post");
751 let stmt = Query::select()
752 .column(Alias::new("*"))
753 .from(Alias::new("comments"))
754 .cond_where(Condition::all().add(filter))
755 .to_owned();
756 let (sql, _) = stmt.build_any(&PostgresQueryBuilder);
757 assert!(sql.contains("\"content_type\""));
758 assert!(sql.contains("$1"));
759 }
760
761 #[test]
762 fn test_polymorphic_relation_join_clause() {
763 let config = PolymorphicConfig::new("content_type", "object_id");
764 let mut rel = PolymorphicRelation::<Comment>::new("content_object", config);
765
766 rel.register_type(PolymorphicIdentity::new("post", "posts", "id"));
767
768 let join = rel.join_clause("post", "comments");
769 let join = join.unwrap();
770 assert!(join.contains("LEFT JOIN \"posts\""));
771 assert!(join.contains("\"comments\".\"object_id\" = \"posts\".\"id\""));
772 }
773
774 #[test]
775 fn test_polymorphic_registry_creation() {
776 let registry = PolymorphicRegistry::new();
777 assert_eq!(registry.count(), 0);
778 }
779
780 #[test]
781 fn test_polymorphic_registry_register() {
782 let mut registry = PolymorphicRegistry::new();
783 let identity = PolymorphicIdentity::new("user", "users", "id");
784 registry.register(identity);
785
786 assert_eq!(registry.count(), 1);
787 assert!(registry.get("user").is_some());
788 }
789
790 #[test]
791 fn test_polymorphic_registry_multiple_types() {
792 let mut registry = PolymorphicRegistry::new();
793 registry.register(PolymorphicIdentity::new("user", "users", "id"));
794 registry.register(PolymorphicIdentity::new("post", "posts", "id"));
795
796 assert_eq!(registry.count(), 2);
797 assert_eq!(registry.type_ids().len(), 2);
798 }
799
800 #[test]
801 fn test_polymorphic_registry_clear() {
802 let mut registry = PolymorphicRegistry::new();
803 registry.register(PolymorphicIdentity::new("user", "users", "id"));
804 assert_eq!(registry.count(), 1);
805
806 registry.clear();
807 assert_eq!(registry.count(), 0);
808 }
809
810 #[test]
811 fn test_polymorphic_registry_get_unknown() {
812 let registry = PolymorphicRegistry::new();
813 assert!(registry.get("unknown").is_none());
814 }
815
816 #[test]
817 fn test_polymorphic_query_creation() {
818 let config = PolymorphicConfig::new("content_type", "object_id");
819 let rel = PolymorphicRelation::<Comment>::new("content_object", config);
820 let query = PolymorphicQuery::new(rel);
821
822 assert!(query.selected_type().is_none());
823 }
824
825 #[test]
826 fn test_polymorphic_query_filter_type() {
827 let config = PolymorphicConfig::new("content_type", "object_id");
828 let rel = PolymorphicRelation::<Comment>::new("content_object", config);
829 let query = PolymorphicQuery::new(rel).filter_type("post");
830
831 assert_eq!(query.selected_type(), Some("post"));
832 }
833
834 #[test]
835 fn test_polymorphic_query_build_sql() {
836 let config = PolymorphicConfig::new("content_type", "object_id");
837 let rel = PolymorphicRelation::<Comment>::new("content_object", config);
838 let query = PolymorphicQuery::new(rel).filter_type("post");
839
840 let (sql, values) = query.build_sql();
841 assert!(sql.contains("\"comments\""));
842 assert!(sql.contains("\"content_type\""));
843 assert!(sql.contains("$1"));
844 assert_eq!(values.0.len(), 1);
845 }
846
847 #[test]
848 fn test_polymorphic_query_multiple_filters() {
849 let config = PolymorphicConfig::new("content_type", "object_id");
850 let rel = PolymorphicRelation::<Comment>::new("content_object", config);
851 let query = PolymorphicQuery::new(rel)
852 .filter_type("post")
853 .filter(Expr::col(Alias::new("object_id")).gt(100));
854
855 let (sql, values) = query.build_sql();
856 assert!(sql.contains("\"content_type\""));
857 assert!(sql.contains("\"object_id\""));
858 assert!(sql.contains("AND"));
859 assert_eq!(values.0.len(), 2);
860 }
861
862 #[test]
863 fn test_global_registry_access() {
864 let registry = polymorphic_registry();
865 let mut reg = registry.write();
866 let initial_count = reg.count();
867
868 reg.register(PolymorphicIdentity::new("test", "test_table", "id"));
869 assert_eq!(reg.count(), initial_count + 1);
870
871 reg.clear();
872 }
873
874 #[test]
875 fn test_inheritance_type_equality() {
876 assert_eq!(InheritanceType::SingleTable, InheritanceType::SingleTable);
877 assert_ne!(InheritanceType::SingleTable, InheritanceType::JoinedTable);
878 assert_ne!(InheritanceType::JoinedTable, InheritanceType::ConcreteTable);
879 }
880}