1use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5use smol_str::SmolStr;
6
7use super::{
8 CompositeType, Datasource, Enum, Generator, Model, Policy, Relation, ServerGroup, View,
9};
10
11#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
13pub struct Schema {
14 pub datasource: Option<Datasource>,
16 pub generators: IndexMap<SmolStr, Generator>,
18 pub models: IndexMap<SmolStr, Model>,
20 pub enums: IndexMap<SmolStr, Enum>,
22 pub types: IndexMap<SmolStr, CompositeType>,
24 pub views: IndexMap<SmolStr, View>,
26 pub server_groups: IndexMap<SmolStr, ServerGroup>,
28 pub policies: Vec<Policy>,
30 pub raw_sql: Vec<RawSql>,
32 pub relations: Vec<Relation>,
34}
35
36impl Schema {
37 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn set_datasource(&mut self, datasource: Datasource) {
44 self.datasource = Some(datasource);
45 }
46
47 pub fn datasource(&self) -> Option<&Datasource> {
49 self.datasource.as_ref()
50 }
51
52 pub fn has_vector_support(&self) -> bool {
54 self.datasource
55 .as_ref()
56 .is_some_and(|ds| ds.has_vector_support())
57 }
58
59 pub fn required_extensions(&self) -> Vec<&super::PostgresExtension> {
61 self.datasource
62 .as_ref()
63 .map(|ds| ds.extensions.iter().collect())
64 .unwrap_or_default()
65 }
66
67 pub fn add_generator(&mut self, generator: Generator) {
69 self.generators.insert(generator.name.clone(), generator);
70 }
71
72 pub fn get_generator(&self, name: &str) -> Option<&Generator> {
74 self.generators.get(name)
75 }
76
77 pub fn enabled_generators(&self) -> Vec<&Generator> {
79 self.generators
80 .values()
81 .filter(|g| g.is_enabled())
82 .collect()
83 }
84
85 pub fn add_model(&mut self, model: Model) {
87 self.models.insert(model.name.name.clone(), model);
88 }
89
90 pub fn add_enum(&mut self, e: Enum) {
92 self.enums.insert(e.name.name.clone(), e);
93 }
94
95 pub fn add_type(&mut self, t: CompositeType) {
97 self.types.insert(t.name.name.clone(), t);
98 }
99
100 pub fn add_view(&mut self, v: View) {
102 self.views.insert(v.name.name.clone(), v);
103 }
104
105 pub fn add_server_group(&mut self, sg: ServerGroup) {
107 self.server_groups.insert(sg.name.name.clone(), sg);
108 }
109
110 pub fn add_policy(&mut self, policy: Policy) {
112 self.policies.push(policy);
113 }
114
115 pub fn add_raw_sql(&mut self, sql: RawSql) {
117 self.raw_sql.push(sql);
118 }
119
120 pub fn get_model(&self, name: &str) -> Option<&Model> {
122 self.models.get(name)
123 }
124
125 pub fn get_model_mut(&mut self, name: &str) -> Option<&mut Model> {
127 self.models.get_mut(name)
128 }
129
130 pub fn get_enum(&self, name: &str) -> Option<&Enum> {
132 self.enums.get(name)
133 }
134
135 pub fn get_type(&self, name: &str) -> Option<&CompositeType> {
137 self.types.get(name)
138 }
139
140 pub fn get_view(&self, name: &str) -> Option<&View> {
142 self.views.get(name)
143 }
144
145 pub fn get_server_group(&self, name: &str) -> Option<&ServerGroup> {
147 self.server_groups.get(name)
148 }
149
150 pub fn server_group_names(&self) -> impl Iterator<Item = &str> {
152 self.server_groups.keys().map(|s| s.as_str())
153 }
154
155 pub fn get_policy(&self, name: &str) -> Option<&Policy> {
157 self.policies.iter().find(|p| p.name() == name)
158 }
159
160 pub fn policies_for(&self, model: &str) -> Vec<&Policy> {
162 self.policies
163 .iter()
164 .filter(|p| p.table() == model)
165 .collect()
166 }
167
168 pub fn has_policies(&self, model: &str) -> bool {
170 self.policies.iter().any(|p| p.table() == model)
171 }
172
173 pub fn policy_names(&self) -> impl Iterator<Item = &str> {
175 self.policies.iter().map(|p| p.name())
176 }
177
178 pub fn type_exists(&self, name: &str) -> bool {
180 self.models.contains_key(name)
181 || self.enums.contains_key(name)
182 || self.types.contains_key(name)
183 || self.views.contains_key(name)
184 }
185
186 pub fn model_names(&self) -> impl Iterator<Item = &str> {
188 self.models.keys().map(|s| s.as_str())
189 }
190
191 pub fn enum_names(&self) -> impl Iterator<Item = &str> {
193 self.enums.keys().map(|s| s.as_str())
194 }
195
196 pub fn relations_for(&self, model: &str) -> Vec<&Relation> {
198 self.relations
199 .iter()
200 .filter(|r| r.from_model == model || r.to_model == model)
201 .collect()
202 }
203
204 pub fn relations_from(&self, model: &str) -> Vec<&Relation> {
206 self.relations
207 .iter()
208 .filter(|r| r.from_model == model)
209 .collect()
210 }
211
212 #[deprecated(since = "0.9.8", note = "use try_merge for collision-aware merging")]
217 pub fn merge(&mut self, other: Schema) {
218 self.models.extend(other.models);
219 self.enums.extend(other.enums);
220 self.types.extend(other.types);
221 self.views.extend(other.views);
222 self.server_groups.extend(other.server_groups);
223 self.policies.extend(other.policies);
224 self.raw_sql.extend(other.raw_sql);
225 }
226
227 pub fn try_merge(&mut self, other: Schema) -> Result<(), Vec<crate::loader::MergeConflict>> {
234 use crate::loader::MergeConflict;
235 use crate::loader::merge::loc;
236
237 let mut conflicts: Vec<MergeConflict> = Vec::new();
238
239 for (name, m) in other.models {
240 if let Some(existing) = self.models.get(&name) {
241 conflicts.push(MergeConflict::DuplicateModel {
242 name: name.clone(),
243 existing: loc(existing.source_id, existing.span),
244 incoming: loc(m.source_id, m.span),
245 });
246 } else {
247 self.models.insert(name, m);
248 }
249 }
250
251 for (name, e) in other.enums {
252 if let Some(existing) = self.enums.get(&name) {
253 conflicts.push(MergeConflict::DuplicateEnum {
254 name: name.clone(),
255 existing: loc(existing.source_id, existing.span),
256 incoming: loc(e.source_id, e.span),
257 });
258 } else {
259 self.enums.insert(name, e);
260 }
261 }
262
263 for (name, t) in other.types {
264 if let Some(existing) = self.types.get(&name) {
265 conflicts.push(MergeConflict::DuplicateType {
266 name: name.clone(),
267 existing: loc(existing.source_id, existing.span),
268 incoming: loc(t.source_id, t.span),
269 });
270 } else {
271 self.types.insert(name, t);
272 }
273 }
274
275 for (name, v) in other.views {
276 if let Some(existing) = self.views.get(&name) {
277 conflicts.push(MergeConflict::DuplicateView {
278 name: name.clone(),
279 existing: loc(existing.source_id, existing.span),
280 incoming: loc(v.source_id, v.span),
281 });
282 } else {
283 self.views.insert(name, v);
284 }
285 }
286
287 for (name, sg) in other.server_groups {
288 if let Some(existing) = self.server_groups.get(&name) {
289 conflicts.push(MergeConflict::DuplicateServerGroup {
290 name: name.clone(),
291 existing: loc(existing.source_id, existing.span),
292 incoming: loc(sg.source_id, sg.span),
293 });
294 } else {
295 self.server_groups.insert(name, sg);
296 }
297 }
298
299 for (name, g) in other.generators {
300 if let Some(existing) = self.generators.get(&name) {
301 conflicts.push(MergeConflict::DuplicateGenerator {
302 name: name.clone(),
303 existing: loc(existing.source_id, existing.span),
304 incoming: loc(g.source_id, g.span),
305 });
306 } else {
307 self.generators.insert(name, g);
308 }
309 }
310
311 for p in other.policies {
312 if let Some(existing) = self.policies.iter().find(|x| x.name() == p.name()) {
313 conflicts.push(MergeConflict::DuplicatePolicy {
314 name: SmolStr::new(p.name()),
315 existing: loc(existing.source_id, existing.span),
316 incoming: loc(p.source_id, p.span),
317 });
318 } else {
319 self.policies.push(p);
320 }
321 }
322
323 for r in other.raw_sql {
324 if let Some(existing) = self.raw_sql.iter().find(|x| x.name == r.name) {
325 conflicts.push(MergeConflict::DuplicateRawSql {
326 name: r.name.clone(),
327 existing: loc(existing.source_id, super::Span::new(0, 0)),
328 incoming: loc(r.source_id, super::Span::new(0, 0)),
329 });
330 } else {
331 self.raw_sql.push(r);
332 }
333 }
334
335 match (&self.datasource, other.datasource) {
336 (Some(existing), Some(incoming)) => {
337 conflicts.push(MergeConflict::MultipleDatasource {
338 existing: loc(existing.source_id, existing.span),
339 incoming: loc(incoming.source_id, incoming.span),
340 });
341 }
342 (None, Some(incoming)) => self.datasource = Some(incoming),
343 (_, None) => {}
344 }
345
346 if conflicts.is_empty() {
347 Ok(())
348 } else {
349 Err(conflicts)
350 }
351 }
352}
353
354#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
356pub struct RawSql {
357 pub name: SmolStr,
359 pub sql: String,
361 #[serde(default, skip_serializing_if = "Option::is_none")]
363 pub source_id: Option<crate::loader::SourceId>,
364}
365
366impl RawSql {
367 pub fn new(name: impl Into<SmolStr>, sql: impl Into<String>) -> Self {
369 Self {
370 name: name.into(),
371 sql: sql.into(),
372 source_id: None,
373 }
374 }
375}
376
377#[derive(Debug, Clone, Default)]
379pub struct SchemaStats {
380 pub model_count: usize,
382 pub enum_count: usize,
384 pub type_count: usize,
386 pub view_count: usize,
388 pub server_group_count: usize,
390 pub policy_count: usize,
392 pub field_count: usize,
394 pub relation_count: usize,
396}
397
398impl Schema {
399 pub fn stats(&self) -> SchemaStats {
401 SchemaStats {
402 model_count: self.models.len(),
403 enum_count: self.enums.len(),
404 type_count: self.types.len(),
405 view_count: self.views.len(),
406 server_group_count: self.server_groups.len(),
407 policy_count: self.policies.len(),
408 field_count: self.models.values().map(|m| m.fields.len()).sum(),
409 relation_count: self.relations.len(),
410 }
411 }
412}
413
414impl std::fmt::Display for Schema {
415 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
416 let stats = self.stats();
417 write!(
418 f,
419 "Schema({} models, {} enums, {} types, {} views, {} server groups, {} policies, {} fields, {} relations)",
420 stats.model_count,
421 stats.enum_count,
422 stats.type_count,
423 stats.view_count,
424 stats.server_group_count,
425 stats.policy_count,
426 stats.field_count,
427 stats.relation_count
428 )
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use crate::ast::{
436 Attribute, EnumVariant, Field, FieldType, Ident, Policy, RelationType, ScalarType, Span,
437 TypeModifier,
438 };
439
440 fn make_span() -> Span {
441 Span::new(0, 10)
442 }
443
444 fn make_ident(name: &str) -> Ident {
445 Ident::new(name, make_span())
446 }
447
448 fn make_model(name: &str) -> Model {
449 let mut model = Model::new(make_ident(name), make_span());
450 let id_field = make_id_field();
451 model.add_field(id_field);
452 model
453 }
454
455 fn make_id_field() -> Field {
456 let mut field = Field::new(
457 make_ident("id"),
458 FieldType::Scalar(ScalarType::Int),
459 TypeModifier::Required,
460 vec![],
461 make_span(),
462 );
463 field
464 .attributes
465 .push(Attribute::simple(make_ident("id"), make_span()));
466 field
467 }
468
469 fn make_field(name: &str, field_type: FieldType) -> Field {
470 Field::new(
471 make_ident(name),
472 field_type,
473 TypeModifier::Required,
474 vec![],
475 make_span(),
476 )
477 }
478
479 fn make_enum(name: &str, variants: &[&str]) -> Enum {
480 let mut e = Enum::new(make_ident(name), make_span());
481 for v in variants {
482 e.add_variant(EnumVariant::new(make_ident(v), make_span()));
483 }
484 e
485 }
486
487 #[test]
490 fn test_schema_new() {
491 let schema = Schema::new();
492 assert!(schema.models.is_empty());
493 assert!(schema.enums.is_empty());
494 assert!(schema.types.is_empty());
495 assert!(schema.views.is_empty());
496 assert!(schema.policies.is_empty());
497 assert!(schema.raw_sql.is_empty());
498 assert!(schema.relations.is_empty());
499 }
500
501 #[test]
502 fn test_schema_default() {
503 let schema = Schema::default();
504 assert!(schema.models.is_empty());
505 }
506
507 #[test]
508 fn test_schema_add_model() {
509 let mut schema = Schema::new();
510 let model = make_model("User");
511
512 schema.add_model(model);
513
514 assert_eq!(schema.models.len(), 1);
515 assert!(schema.models.contains_key("User"));
516 }
517
518 #[test]
519 fn test_schema_add_multiple_models() {
520 let mut schema = Schema::new();
521 schema.add_model(make_model("User"));
522 schema.add_model(make_model("Post"));
523 schema.add_model(make_model("Comment"));
524
525 assert_eq!(schema.models.len(), 3);
526 }
527
528 #[test]
529 fn test_schema_add_enum() {
530 let mut schema = Schema::new();
531 let e = make_enum("Role", &["User", "Admin"]);
532
533 schema.add_enum(e);
534
535 assert_eq!(schema.enums.len(), 1);
536 assert!(schema.enums.contains_key("Role"));
537 }
538
539 #[test]
540 fn test_schema_add_type() {
541 let mut schema = Schema::new();
542 let ct = CompositeType::new(make_ident("Address"), make_span());
543
544 schema.add_type(ct);
545
546 assert_eq!(schema.types.len(), 1);
547 assert!(schema.types.contains_key("Address"));
548 }
549
550 #[test]
551 fn test_schema_add_view() {
552 let mut schema = Schema::new();
553 let view = View::new(make_ident("UserStats"), make_span());
554
555 schema.add_view(view);
556
557 assert_eq!(schema.views.len(), 1);
558 assert!(schema.views.contains_key("UserStats"));
559 }
560
561 #[test]
562 fn test_schema_add_raw_sql() {
563 let mut schema = Schema::new();
564 let sql = RawSql::new("migration_1", "CREATE TABLE test ();");
565
566 schema.add_raw_sql(sql);
567
568 assert_eq!(schema.raw_sql.len(), 1);
569 }
570
571 #[test]
572 fn test_schema_get_model() {
573 let mut schema = Schema::new();
574 schema.add_model(make_model("User"));
575
576 let model = schema.get_model("User");
577 assert!(model.is_some());
578 assert_eq!(model.unwrap().name(), "User");
579
580 assert!(schema.get_model("NonExistent").is_none());
581 }
582
583 #[test]
584 fn test_schema_get_model_mut() {
585 let mut schema = Schema::new();
586 schema.add_model(make_model("User"));
587
588 let model = schema.get_model_mut("User");
589 assert!(model.is_some());
590
591 let model = model.unwrap();
593 model.add_field(make_field("email", FieldType::Scalar(ScalarType::String)));
594
595 assert_eq!(schema.get_model("User").unwrap().fields.len(), 2);
597 }
598
599 #[test]
600 fn test_schema_get_enum() {
601 let mut schema = Schema::new();
602 schema.add_enum(make_enum("Role", &["User", "Admin"]));
603
604 let e = schema.get_enum("Role");
605 assert!(e.is_some());
606 assert_eq!(e.unwrap().name(), "Role");
607
608 assert!(schema.get_enum("NonExistent").is_none());
609 }
610
611 #[test]
612 fn test_schema_get_type() {
613 let mut schema = Schema::new();
614 schema.add_type(CompositeType::new(make_ident("Address"), make_span()));
615
616 let ct = schema.get_type("Address");
617 assert!(ct.is_some());
618
619 assert!(schema.get_type("NonExistent").is_none());
620 }
621
622 #[test]
623 fn test_schema_get_view() {
624 let mut schema = Schema::new();
625 schema.add_view(View::new(make_ident("Stats"), make_span()));
626
627 let v = schema.get_view("Stats");
628 assert!(v.is_some());
629
630 assert!(schema.get_view("NonExistent").is_none());
631 }
632
633 #[test]
634 fn test_schema_type_exists() {
635 let mut schema = Schema::new();
636 schema.add_model(make_model("User"));
637 schema.add_enum(make_enum("Role", &["User"]));
638 schema.add_type(CompositeType::new(make_ident("Address"), make_span()));
639 schema.add_view(View::new(make_ident("Stats"), make_span()));
640
641 assert!(schema.type_exists("User")); assert!(schema.type_exists("Role")); assert!(schema.type_exists("Address")); assert!(schema.type_exists("Stats")); assert!(!schema.type_exists("NonExistent"));
646 }
647
648 #[test]
649 fn test_schema_model_names() {
650 let mut schema = Schema::new();
651 schema.add_model(make_model("User"));
652 schema.add_model(make_model("Post"));
653
654 let names: Vec<_> = schema.model_names().collect();
655 assert_eq!(names.len(), 2);
656 assert!(names.contains(&"User"));
657 assert!(names.contains(&"Post"));
658 }
659
660 #[test]
661 fn test_schema_enum_names() {
662 let mut schema = Schema::new();
663 schema.add_enum(make_enum("Role", &["User"]));
664 schema.add_enum(make_enum("Status", &["Active"]));
665
666 let names: Vec<_> = schema.enum_names().collect();
667 assert_eq!(names.len(), 2);
668 assert!(names.contains(&"Role"));
669 assert!(names.contains(&"Status"));
670 }
671
672 #[test]
673 fn test_schema_relations_for() {
674 let mut schema = Schema::new();
675 schema.relations.push(Relation::new(
676 "Post",
677 "author",
678 "User",
679 RelationType::ManyToOne,
680 ));
681 schema.relations.push(Relation::new(
682 "Comment",
683 "user",
684 "User",
685 RelationType::ManyToOne,
686 ));
687 schema.relations.push(Relation::new(
688 "Post",
689 "tags",
690 "Tag",
691 RelationType::ManyToMany,
692 ));
693
694 let user_relations = schema.relations_for("User");
695 assert_eq!(user_relations.len(), 2);
696
697 let post_relations = schema.relations_for("Post");
698 assert_eq!(post_relations.len(), 2);
699
700 let tag_relations = schema.relations_for("Tag");
701 assert_eq!(tag_relations.len(), 1);
702 }
703
704 #[test]
705 fn test_schema_relations_from() {
706 let mut schema = Schema::new();
707 schema.relations.push(Relation::new(
708 "Post",
709 "author",
710 "User",
711 RelationType::ManyToOne,
712 ));
713 schema.relations.push(Relation::new(
714 "Post",
715 "tags",
716 "Tag",
717 RelationType::ManyToMany,
718 ));
719 schema.relations.push(Relation::new(
720 "User",
721 "posts",
722 "Post",
723 RelationType::OneToMany,
724 ));
725
726 let post_relations = schema.relations_from("Post");
727 assert_eq!(post_relations.len(), 2);
728
729 let user_relations = schema.relations_from("User");
730 assert_eq!(user_relations.len(), 1);
731
732 let tag_relations = schema.relations_from("Tag");
733 assert_eq!(tag_relations.len(), 0);
734 }
735
736 #[test]
737 #[allow(deprecated)]
738 fn test_schema_merge() {
739 let mut schema1 = Schema::new();
740 schema1.add_model(make_model("User"));
741 schema1.add_enum(make_enum("Role", &["User"]));
742
743 let mut schema2 = Schema::new();
744 schema2.add_model(make_model("Post"));
745 schema2.add_enum(make_enum("Status", &["Active"]));
746 schema2.add_raw_sql(RawSql::new("init", "-- init"));
747
748 schema1.merge(schema2);
749
750 assert_eq!(schema1.models.len(), 2);
751 assert_eq!(schema1.enums.len(), 2);
752 assert_eq!(schema1.raw_sql.len(), 1);
753 }
754
755 #[test]
756 fn test_schema_stats() {
757 let mut schema = Schema::new();
758
759 let mut user = make_model("User");
760 user.add_field(make_field("email", FieldType::Scalar(ScalarType::String)));
761 user.add_field(make_field("name", FieldType::Scalar(ScalarType::String)));
762 schema.add_model(user);
763
764 let mut post = make_model("Post");
765 post.add_field(make_field("title", FieldType::Scalar(ScalarType::String)));
766 schema.add_model(post);
767
768 schema.add_enum(make_enum("Role", &["User", "Admin"]));
769 schema.add_type(CompositeType::new(make_ident("Address"), make_span()));
770 schema.add_view(View::new(make_ident("Stats"), make_span()));
771 schema.relations.push(Relation::new(
772 "Post",
773 "author",
774 "User",
775 RelationType::ManyToOne,
776 ));
777
778 let stats = schema.stats();
779 assert_eq!(stats.model_count, 2);
780 assert_eq!(stats.enum_count, 1);
781 assert_eq!(stats.type_count, 1);
782 assert_eq!(stats.view_count, 1);
783 assert_eq!(stats.field_count, 5); assert_eq!(stats.relation_count, 1);
785 }
786
787 #[test]
788 fn test_schema_display() {
789 let mut schema = Schema::new();
790 schema.add_model(make_model("User"));
791 schema.add_enum(make_enum("Role", &["User"]));
792
793 let display = format!("{}", schema);
794 assert!(display.contains("1 models"));
795 assert!(display.contains("1 enums"));
796 assert!(display.contains("0 policies"));
797 }
798
799 #[test]
800 fn test_schema_equality() {
801 let schema1 = Schema::new();
802 let schema2 = Schema::new();
803 assert_eq!(schema1, schema2);
804 }
805
806 #[test]
807 fn test_schema_clone() {
808 let mut schema = Schema::new();
809 schema.add_model(make_model("User"));
810
811 let cloned = schema.clone();
812 assert_eq!(cloned.models.len(), 1);
813 }
814
815 #[test]
818 fn test_raw_sql_new() {
819 let sql = RawSql::new("create_users", "CREATE TABLE users ();");
820
821 assert_eq!(sql.name.as_str(), "create_users");
822 assert_eq!(sql.sql, "CREATE TABLE users ();");
823 }
824
825 #[test]
826 fn test_raw_sql_from_strings() {
827 let name = String::from("migration");
828 let content = String::from("ALTER TABLE users ADD COLUMN age INT;");
829 let sql = RawSql::new(name, content);
830
831 assert_eq!(sql.name.as_str(), "migration");
832 }
833
834 #[test]
835 fn test_raw_sql_equality() {
836 let sql1 = RawSql::new("test", "SELECT 1;");
837 let sql2 = RawSql::new("test", "SELECT 1;");
838 let sql3 = RawSql::new("test", "SELECT 2;");
839
840 assert_eq!(sql1, sql2);
841 assert_ne!(sql1, sql3);
842 }
843
844 #[test]
845 fn test_raw_sql_clone() {
846 let sql = RawSql::new("test", "SELECT 1;");
847 let cloned = sql.clone();
848 assert_eq!(sql, cloned);
849 }
850
851 #[test]
854 fn test_schema_stats_default() {
855 let stats = SchemaStats::default();
856 assert_eq!(stats.model_count, 0);
857 assert_eq!(stats.enum_count, 0);
858 assert_eq!(stats.type_count, 0);
859 assert_eq!(stats.view_count, 0);
860 assert_eq!(stats.policy_count, 0);
861 assert_eq!(stats.field_count, 0);
862 assert_eq!(stats.relation_count, 0);
863 }
864
865 #[test]
866 fn test_schema_stats_debug() {
867 let stats = SchemaStats::default();
868 let debug = format!("{:?}", stats);
869 assert!(debug.contains("SchemaStats"));
870 }
871
872 #[test]
873 fn test_schema_stats_clone() {
874 let stats = SchemaStats {
875 model_count: 5,
876 enum_count: 2,
877 type_count: 1,
878 view_count: 3,
879 server_group_count: 2,
880 policy_count: 4,
881 field_count: 25,
882 relation_count: 10,
883 };
884 let cloned = stats.clone();
885 assert_eq!(cloned.model_count, 5);
886 assert_eq!(cloned.field_count, 25);
887 assert_eq!(cloned.policy_count, 4);
888 }
889
890 #[test]
893 fn test_schema_add_policy() {
894 let mut schema = Schema::new();
895 let policy = Policy::new(make_ident("read_own"), make_ident("User"), make_span());
896
897 schema.add_policy(policy);
898
899 assert_eq!(schema.policies.len(), 1);
900 }
901
902 #[test]
903 fn test_schema_get_policy() {
904 let mut schema = Schema::new();
905 schema.add_policy(Policy::new(
906 make_ident("read_own"),
907 make_ident("User"),
908 make_span(),
909 ));
910
911 let policy = schema.get_policy("read_own");
912 assert!(policy.is_some());
913 assert_eq!(policy.unwrap().name(), "read_own");
914
915 assert!(schema.get_policy("nonexistent").is_none());
916 }
917
918 #[test]
919 fn test_schema_policies_for_model() {
920 let mut schema = Schema::new();
921 schema.add_policy(Policy::new(
922 make_ident("user_read"),
923 make_ident("User"),
924 make_span(),
925 ));
926 schema.add_policy(Policy::new(
927 make_ident("user_write"),
928 make_ident("User"),
929 make_span(),
930 ));
931 schema.add_policy(Policy::new(
932 make_ident("post_read"),
933 make_ident("Post"),
934 make_span(),
935 ));
936
937 let user_policies = schema.policies_for("User");
938 assert_eq!(user_policies.len(), 2);
939
940 let post_policies = schema.policies_for("Post");
941 assert_eq!(post_policies.len(), 1);
942
943 let comment_policies = schema.policies_for("Comment");
944 assert!(comment_policies.is_empty());
945 }
946
947 #[test]
948 fn test_schema_has_policies() {
949 let mut schema = Schema::new();
950 schema.add_policy(Policy::new(
951 make_ident("test"),
952 make_ident("User"),
953 make_span(),
954 ));
955
956 assert!(schema.has_policies("User"));
957 assert!(!schema.has_policies("Post"));
958 }
959
960 #[test]
961 fn test_schema_policy_names() {
962 let mut schema = Schema::new();
963 schema.add_policy(Policy::new(
964 make_ident("policy1"),
965 make_ident("User"),
966 make_span(),
967 ));
968 schema.add_policy(Policy::new(
969 make_ident("policy2"),
970 make_ident("Post"),
971 make_span(),
972 ));
973
974 let names: Vec<_> = schema.policy_names().collect();
975 assert_eq!(names.len(), 2);
976 assert!(names.contains(&"policy1"));
977 assert!(names.contains(&"policy2"));
978 }
979
980 #[test]
981 #[allow(deprecated)]
982 fn test_schema_merge_with_policies() {
983 let mut schema1 = Schema::new();
984 schema1.add_policy(Policy::new(
985 make_ident("policy1"),
986 make_ident("User"),
987 make_span(),
988 ));
989
990 let mut schema2 = Schema::new();
991 schema2.add_policy(Policy::new(
992 make_ident("policy2"),
993 make_ident("Post"),
994 make_span(),
995 ));
996
997 schema1.merge(schema2);
998
999 assert_eq!(schema1.policies.len(), 2);
1000 }
1001
1002 #[test]
1003 fn test_schema_stats_with_policies() {
1004 let mut schema = Schema::new();
1005 schema.add_model(make_model("User"));
1006 schema.add_policy(Policy::new(
1007 make_ident("policy1"),
1008 make_ident("User"),
1009 make_span(),
1010 ));
1011 schema.add_policy(Policy::new(
1012 make_ident("policy2"),
1013 make_ident("User"),
1014 make_span(),
1015 ));
1016
1017 let stats = schema.stats();
1018 assert_eq!(stats.model_count, 1);
1019 assert_eq!(stats.policy_count, 2);
1020 }
1021}