1use serde::{Deserialize, Serialize};
4use smol_str::SmolStr;
5
6use super::{Ident, Span};
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub enum AttributeValue {
11 String(String),
13 Int(i64),
15 Float(f64),
17 Boolean(bool),
19 Ident(SmolStr),
21 Function(SmolStr, Vec<AttributeValue>),
23 Array(Vec<AttributeValue>),
25 FieldRef(SmolStr),
27 FieldRefList(Vec<SmolStr>),
29}
30
31impl AttributeValue {
32 pub fn as_string(&self) -> Option<&str> {
34 match self {
35 Self::String(s) => Some(s),
36 _ => None,
37 }
38 }
39
40 pub fn as_int(&self) -> Option<i64> {
42 match self {
43 Self::Int(i) => Some(*i),
44 _ => None,
45 }
46 }
47
48 pub fn as_float(&self) -> Option<f64> {
50 match self {
51 Self::Float(f) => Some(*f),
52 Self::Int(i) => Some(*i as f64),
53 _ => None,
54 }
55 }
56
57 pub fn as_bool(&self) -> Option<bool> {
59 match self {
60 Self::Boolean(b) => Some(*b),
61 _ => None,
62 }
63 }
64
65 pub fn as_ident(&self) -> Option<&str> {
67 match self {
68 Self::Ident(s) => Some(s),
69 _ => None,
70 }
71 }
72}
73
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
76pub struct AttributeArg {
77 pub name: Option<Ident>,
79 pub value: AttributeValue,
81 pub span: Span,
83}
84
85impl AttributeArg {
86 pub fn positional(value: AttributeValue, span: Span) -> Self {
88 Self {
89 name: None,
90 value,
91 span,
92 }
93 }
94
95 pub fn named(name: Ident, value: AttributeValue, span: Span) -> Self {
97 Self {
98 name: Some(name),
99 value,
100 span,
101 }
102 }
103
104 pub fn is_positional(&self) -> bool {
106 self.name.is_none()
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub struct Attribute {
113 pub name: Ident,
115 pub args: Vec<AttributeArg>,
117 pub span: Span,
119}
120
121impl Attribute {
122 pub fn new(name: Ident, args: Vec<AttributeArg>, span: Span) -> Self {
124 Self { name, args, span }
125 }
126
127 pub fn simple(name: Ident, span: Span) -> Self {
129 Self {
130 name,
131 args: vec![],
132 span,
133 }
134 }
135
136 pub fn name(&self) -> &str {
138 self.name.as_str()
139 }
140
141 pub fn is(&self, name: &str) -> bool {
143 self.name.as_str() == name
144 }
145
146 pub fn first_arg(&self) -> Option<&AttributeValue> {
148 self.args.first().map(|a| &a.value)
149 }
150
151 pub fn get_arg(&self, name: &str) -> Option<&AttributeValue> {
153 self.args
154 .iter()
155 .find(|a| a.name.as_ref().map(|n| n.as_str()) == Some(name))
156 .map(|a| &a.value)
157 }
158
159 pub fn first_string_arg(&self) -> Option<&str> {
161 self.args.first().and_then(|a| a.value.as_string())
162 }
163
164 pub fn first_ident_arg(&self) -> Option<&str> {
168 self.args.first().and_then(|a| match &a.value {
169 AttributeValue::Ident(s) => Some(s.as_str()),
170 AttributeValue::String(s) if !s.contains('.') => Some(s.as_str()),
171 _ => None,
172 })
173 }
174
175 pub fn first_path_arg(&self) -> Option<String> {
178 self.args.first().and_then(|a| match &a.value {
179 AttributeValue::Ident(s) => Some(s.as_str().to_string()),
180 AttributeValue::String(s) => Some(s.clone()),
181 _ => None,
182 })
183 }
184
185 pub fn is_field_attribute(&self) -> bool {
187 matches!(
188 self.name(),
189 "id" | "auto"
190 | "unique"
191 | "index"
192 | "default"
193 | "updated_at"
194 | "omit"
195 | "map"
196 | "db"
197 | "relation"
198 | "generated"
199 | "stored"
200 | "virtual"
201 | "count"
202 | "sum"
203 | "avg"
204 | "min"
205 | "max"
206 )
207 }
208
209 pub fn is_model_attribute(&self) -> bool {
211 matches!(
212 self.name(),
213 "map" | "index" | "unique" | "id" | "search" | "sql"
214 )
215 }
216}
217
218#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
220pub struct FieldAttributes {
221 pub is_id: bool,
223 pub is_auto: bool,
225 pub is_unique: bool,
227 pub is_indexed: bool,
229 pub is_updated_at: bool,
231 pub is_omit: bool,
233 pub default: Option<AttributeValue>,
235 pub map: Option<String>,
237 pub native_type: Option<NativeType>,
239 pub relation: Option<RelationAttribute>,
241 pub generated: Option<GeneratedAttribute>,
243 pub aggregate: Option<AggregateAttribute>,
245}
246
247#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
249pub struct NativeType {
250 pub name: SmolStr,
252 pub args: Vec<AttributeValue>,
254}
255
256impl NativeType {
257 pub fn new(name: impl Into<SmolStr>, args: Vec<AttributeValue>) -> Self {
259 Self {
260 name: name.into(),
261 args,
262 }
263 }
264}
265
266#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
268pub struct RelationAttribute {
269 pub name: Option<String>,
271 pub fields: Vec<SmolStr>,
273 pub references: Vec<SmolStr>,
275 pub on_delete: Option<ReferentialAction>,
277 pub on_update: Option<ReferentialAction>,
279 pub map: Option<String>,
281}
282
283#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
285pub enum AggregateKind {
286 Count,
287 Sum,
288 Avg,
289 Min,
290 Max,
291}
292
293#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
295pub struct GeneratedAttribute {
296 pub expression: String,
298 pub stored: bool,
300}
301
302#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
304pub struct AggregateAttribute {
305 pub kind: AggregateKind,
306 pub relation: SmolStr,
308 pub field: Option<SmolStr>,
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
314pub enum ReferentialAction {
315 Cascade,
317 Restrict,
319 NoAction,
321 SetNull,
323 SetDefault,
325}
326
327impl ReferentialAction {
328 #[allow(clippy::should_implement_trait)]
330 pub fn from_str(s: &str) -> Option<Self> {
331 match s {
332 "Cascade" => Some(Self::Cascade),
333 "Restrict" => Some(Self::Restrict),
334 "NoAction" => Some(Self::NoAction),
335 "SetNull" => Some(Self::SetNull),
336 "SetDefault" => Some(Self::SetDefault),
337 _ => None,
338 }
339 }
340
341 pub fn as_str(&self) -> &'static str {
343 match self {
344 Self::Cascade => "CASCADE",
345 Self::Restrict => "RESTRICT",
346 Self::NoAction => "NO ACTION",
347 Self::SetNull => "SET NULL",
348 Self::SetDefault => "SET DEFAULT",
349 }
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[test]
360 fn test_attribute_value_string() {
361 let val = AttributeValue::String("hello".into());
362 assert_eq!(val.as_string(), Some("hello"));
363 assert_eq!(val.as_int(), None);
364 assert_eq!(val.as_bool(), None);
365 assert_eq!(val.as_ident(), None);
366 }
367
368 #[test]
369 fn test_attribute_value_int() {
370 let val = AttributeValue::Int(42);
371 assert_eq!(val.as_int(), Some(42));
372 assert_eq!(val.as_string(), None);
373 assert_eq!(val.as_bool(), None);
374 }
375
376 #[test]
377 fn test_attribute_value_int_negative() {
378 let val = AttributeValue::Int(-100);
379 assert_eq!(val.as_int(), Some(-100));
380 }
381
382 #[test]
383 #[allow(clippy::approx_constant)]
384 fn test_attribute_value_float() {
385 let val = AttributeValue::Float(3.14);
386 assert_eq!(val.as_int(), None);
387 assert_eq!(val.as_string(), None);
388 }
389
390 #[test]
391 fn test_attribute_value_boolean_true() {
392 let val = AttributeValue::Boolean(true);
393 assert_eq!(val.as_bool(), Some(true));
394 assert_eq!(val.as_int(), None);
395 }
396
397 #[test]
398 fn test_attribute_value_boolean_false() {
399 let val = AttributeValue::Boolean(false);
400 assert_eq!(val.as_bool(), Some(false));
401 }
402
403 #[test]
404 fn test_attribute_value_ident() {
405 let val = AttributeValue::Ident("MyEnum".into());
406 assert_eq!(val.as_ident(), Some("MyEnum"));
407 assert_eq!(val.as_string(), None);
408 }
409
410 #[test]
411 fn test_attribute_value_function() {
412 let val = AttributeValue::Function("now".into(), vec![]);
413 assert_eq!(val.as_string(), None);
414 assert_eq!(val.as_int(), None);
415
416 if let AttributeValue::Function(name, args) = val {
418 assert_eq!(name.as_str(), "now");
419 assert!(args.is_empty());
420 } else {
421 panic!("Expected Function variant");
422 }
423 }
424
425 #[test]
426 fn test_attribute_value_function_with_args() {
427 let val = AttributeValue::Function("uuid".into(), vec![AttributeValue::Int(4)]);
428
429 if let AttributeValue::Function(name, args) = val {
430 assert_eq!(name.as_str(), "uuid");
431 assert_eq!(args.len(), 1);
432 } else {
433 panic!("Expected Function variant");
434 }
435 }
436
437 #[test]
438 fn test_attribute_value_array() {
439 let val = AttributeValue::Array(vec![
440 AttributeValue::String("a".into()),
441 AttributeValue::String("b".into()),
442 ]);
443
444 if let AttributeValue::Array(items) = val {
445 assert_eq!(items.len(), 2);
446 } else {
447 panic!("Expected Array variant");
448 }
449 }
450
451 #[test]
452 fn test_attribute_value_field_ref() {
453 let val = AttributeValue::FieldRef("user_id".into());
454 assert_eq!(val.as_ident(), None);
455
456 if let AttributeValue::FieldRef(name) = val {
457 assert_eq!(name.as_str(), "user_id");
458 } else {
459 panic!("Expected FieldRef variant");
460 }
461 }
462
463 #[test]
464 fn test_attribute_value_field_ref_list() {
465 let val = AttributeValue::FieldRefList(vec!["id".into(), "name".into()]);
466
467 if let AttributeValue::FieldRefList(fields) = val {
468 assert_eq!(fields.len(), 2);
469 assert_eq!(fields[0].as_str(), "id");
470 assert_eq!(fields[1].as_str(), "name");
471 } else {
472 panic!("Expected FieldRefList variant");
473 }
474 }
475
476 #[test]
477 fn test_attribute_value_equality() {
478 let val1 = AttributeValue::Int(42);
479 let val2 = AttributeValue::Int(42);
480 let val3 = AttributeValue::Int(43);
481
482 assert_eq!(val1, val2);
483 assert_ne!(val1, val3);
484 }
485
486 #[test]
489 fn test_attribute_arg_positional() {
490 let arg = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
491
492 assert!(arg.is_positional());
493 assert!(arg.name.is_none());
494 assert_eq!(arg.value.as_int(), Some(42));
495 }
496
497 #[test]
498 fn test_attribute_arg_named() {
499 let arg = AttributeArg::named(
500 Ident::new("length", Span::new(0, 6)),
501 AttributeValue::Int(255),
502 Span::new(0, 10),
503 );
504
505 assert!(!arg.is_positional());
506 assert!(arg.name.is_some());
507 assert_eq!(arg.name.as_ref().unwrap().as_str(), "length");
508 assert_eq!(arg.value.as_int(), Some(255));
509 }
510
511 #[test]
512 fn test_attribute_arg_equality() {
513 let arg1 = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
514 let arg2 = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
515 let arg3 = AttributeArg::positional(AttributeValue::Int(43), Span::new(0, 2));
516
517 assert_eq!(arg1, arg2);
518 assert_ne!(arg1, arg3);
519 }
520
521 #[test]
524 fn test_attribute_new() {
525 let attr = Attribute::new(
526 Ident::new("default", Span::new(0, 7)),
527 vec![AttributeArg::positional(
528 AttributeValue::Int(0),
529 Span::new(8, 9),
530 )],
531 Span::new(0, 10),
532 );
533
534 assert_eq!(attr.name(), "default");
535 assert_eq!(attr.args.len(), 1);
536 }
537
538 #[test]
539 fn test_attribute_simple() {
540 let attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
541
542 assert_eq!(attr.name(), "id");
543 assert!(attr.args.is_empty());
544 }
545
546 #[test]
547 fn test_attribute_is() {
548 let attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
549
550 assert!(attr.is("unique"));
551 assert!(!attr.is("id"));
552 assert!(!attr.is("UNIQUE")); }
554
555 #[test]
556 fn test_attribute_first_arg() {
557 let attr = Attribute::new(
558 Ident::new("default", Span::new(0, 7)),
559 vec![
560 AttributeArg::positional(AttributeValue::Int(42), Span::new(8, 10)),
561 AttributeArg::positional(AttributeValue::String("extra".into()), Span::new(12, 19)),
562 ],
563 Span::new(0, 20),
564 );
565
566 assert_eq!(attr.first_arg().unwrap().as_int(), Some(42));
567 }
568
569 #[test]
570 fn test_attribute_first_arg_none() {
571 let attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
572 assert!(attr.first_arg().is_none());
573 }
574
575 #[test]
576 fn test_attribute_get_arg() {
577 let attr = Attribute::new(
578 Ident::new("relation", Span::new(0, 8)),
579 vec![
580 AttributeArg::named(
581 Ident::new("fields", Span::new(9, 15)),
582 AttributeValue::FieldRefList(vec!["user_id".into()]),
583 Span::new(9, 30),
584 ),
585 AttributeArg::named(
586 Ident::new("references", Span::new(32, 42)),
587 AttributeValue::FieldRefList(vec!["id".into()]),
588 Span::new(32, 50),
589 ),
590 ],
591 Span::new(0, 51),
592 );
593
594 let fields = attr.get_arg("fields").unwrap();
595 if let AttributeValue::FieldRefList(f) = fields {
596 assert_eq!(f[0].as_str(), "user_id");
597 } else {
598 panic!("Expected FieldRefList");
599 }
600
601 assert!(attr.get_arg("onDelete").is_none());
602 }
603
604 #[test]
605 fn test_attribute_is_field_attribute() {
606 let id_attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
607 let auto_attr = Attribute::simple(Ident::new("auto", Span::new(0, 4)), Span::new(0, 5));
608 let unique_attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
609 let index_attr = Attribute::simple(Ident::new("index", Span::new(0, 5)), Span::new(0, 6));
610 let default_attr =
611 Attribute::simple(Ident::new("default", Span::new(0, 7)), Span::new(0, 8));
612 let updated_at_attr =
613 Attribute::simple(Ident::new("updated_at", Span::new(0, 10)), Span::new(0, 11));
614 let omit_attr = Attribute::simple(Ident::new("omit", Span::new(0, 4)), Span::new(0, 5));
615 let map_attr = Attribute::simple(Ident::new("map", Span::new(0, 3)), Span::new(0, 4));
616 let db_attr = Attribute::simple(Ident::new("db", Span::new(0, 2)), Span::new(0, 3));
617 let relation_attr =
618 Attribute::simple(Ident::new("relation", Span::new(0, 8)), Span::new(0, 9));
619 let unknown_attr =
620 Attribute::simple(Ident::new("unknown", Span::new(0, 7)), Span::new(0, 8));
621
622 assert!(id_attr.is_field_attribute());
623 assert!(auto_attr.is_field_attribute());
624 assert!(unique_attr.is_field_attribute());
625 assert!(index_attr.is_field_attribute());
626 assert!(default_attr.is_field_attribute());
627 assert!(updated_at_attr.is_field_attribute());
628 assert!(omit_attr.is_field_attribute());
629 assert!(map_attr.is_field_attribute());
630 assert!(db_attr.is_field_attribute());
631 assert!(relation_attr.is_field_attribute());
632 assert!(!unknown_attr.is_field_attribute());
633 }
634
635 #[test]
636 fn test_attribute_is_model_attribute() {
637 let map_attr = Attribute::simple(Ident::new("map", Span::new(0, 3)), Span::new(0, 4));
638 let index_attr = Attribute::simple(Ident::new("index", Span::new(0, 5)), Span::new(0, 6));
639 let unique_attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
640 let id_attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
641 let search_attr = Attribute::simple(Ident::new("search", Span::new(0, 6)), Span::new(0, 7));
642 let sql_attr = Attribute::simple(Ident::new("sql", Span::new(0, 3)), Span::new(0, 4));
643 let unknown_attr =
644 Attribute::simple(Ident::new("unknown", Span::new(0, 7)), Span::new(0, 8));
645
646 assert!(map_attr.is_model_attribute());
647 assert!(index_attr.is_model_attribute());
648 assert!(unique_attr.is_model_attribute());
649 assert!(id_attr.is_model_attribute());
650 assert!(search_attr.is_model_attribute());
651 assert!(sql_attr.is_model_attribute());
652 assert!(!unknown_attr.is_model_attribute());
653 }
654
655 #[test]
658 fn test_field_attributes_default() {
659 let attrs = FieldAttributes::default();
660
661 assert!(!attrs.is_id);
662 assert!(!attrs.is_auto);
663 assert!(!attrs.is_unique);
664 assert!(!attrs.is_indexed);
665 assert!(!attrs.is_updated_at);
666 assert!(!attrs.is_omit);
667 assert!(attrs.default.is_none());
668 assert!(attrs.map.is_none());
669 assert!(attrs.native_type.is_none());
670 assert!(attrs.relation.is_none());
671 }
672
673 #[test]
674 fn test_field_attributes_with_values() {
675 let attrs = FieldAttributes {
676 is_id: true,
677 is_auto: true,
678 is_unique: false,
679 is_indexed: false,
680 is_updated_at: false,
681 is_omit: false,
682 default: Some(AttributeValue::Function("auto".into(), vec![])),
683 map: Some("user_id".to_string()),
684 native_type: None,
685 relation: None,
686 generated: None,
687 aggregate: None,
688 };
689
690 assert!(attrs.is_id);
691 assert!(attrs.is_auto);
692 assert!(attrs.default.is_some());
693 assert_eq!(attrs.map, Some("user_id".to_string()));
694 }
695
696 #[test]
699 fn test_native_type_new() {
700 let nt = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
701
702 assert_eq!(nt.name.as_str(), "VarChar");
703 assert_eq!(nt.args.len(), 1);
704 assert_eq!(nt.args[0].as_int(), Some(255));
705 }
706
707 #[test]
708 fn test_native_type_no_args() {
709 let nt = NativeType::new("Text", vec![]);
710
711 assert_eq!(nt.name.as_str(), "Text");
712 assert!(nt.args.is_empty());
713 }
714
715 #[test]
716 fn test_native_type_multiple_args() {
717 let nt = NativeType::new(
718 "Decimal",
719 vec![AttributeValue::Int(10), AttributeValue::Int(2)],
720 );
721
722 assert_eq!(nt.name.as_str(), "Decimal");
723 assert_eq!(nt.args.len(), 2);
724 }
725
726 #[test]
727 fn test_native_type_equality() {
728 let nt1 = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
729 let nt2 = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
730 let nt3 = NativeType::new("VarChar", vec![AttributeValue::Int(100)]);
731
732 assert_eq!(nt1, nt2);
733 assert_ne!(nt1, nt3);
734 }
735
736 #[test]
739 fn test_relation_attribute() {
740 let rel = RelationAttribute {
741 name: Some("UserPosts".to_string()),
742 fields: vec!["author_id".into()],
743 references: vec!["id".into()],
744 on_delete: Some(ReferentialAction::Cascade),
745 on_update: Some(ReferentialAction::Cascade),
746 map: Some("fk_post_author".to_string()),
747 };
748
749 assert_eq!(rel.name, Some("UserPosts".to_string()));
750 assert_eq!(rel.fields[0].as_str(), "author_id");
751 assert_eq!(rel.references[0].as_str(), "id");
752 assert_eq!(rel.on_delete, Some(ReferentialAction::Cascade));
753 assert_eq!(rel.map, Some("fk_post_author".to_string()));
754 }
755
756 #[test]
757 fn test_relation_attribute_minimal() {
758 let rel = RelationAttribute {
759 name: None,
760 fields: vec![],
761 references: vec![],
762 on_delete: None,
763 on_update: None,
764 map: None,
765 };
766
767 assert!(rel.name.is_none());
768 assert!(rel.fields.is_empty());
769 assert!(rel.map.is_none());
770 }
771
772 #[test]
775 fn test_referential_action_from_str_cascade() {
776 assert_eq!(
777 ReferentialAction::from_str("Cascade"),
778 Some(ReferentialAction::Cascade)
779 );
780 }
781
782 #[test]
783 fn test_referential_action_from_str_restrict() {
784 assert_eq!(
785 ReferentialAction::from_str("Restrict"),
786 Some(ReferentialAction::Restrict)
787 );
788 }
789
790 #[test]
791 fn test_referential_action_from_str_no_action() {
792 assert_eq!(
793 ReferentialAction::from_str("NoAction"),
794 Some(ReferentialAction::NoAction)
795 );
796 }
797
798 #[test]
799 fn test_referential_action_from_str_set_null() {
800 assert_eq!(
801 ReferentialAction::from_str("SetNull"),
802 Some(ReferentialAction::SetNull)
803 );
804 }
805
806 #[test]
807 fn test_referential_action_from_str_set_default() {
808 assert_eq!(
809 ReferentialAction::from_str("SetDefault"),
810 Some(ReferentialAction::SetDefault)
811 );
812 }
813
814 #[test]
815 fn test_referential_action_from_str_unknown() {
816 assert_eq!(ReferentialAction::from_str("Unknown"), None);
817 assert_eq!(ReferentialAction::from_str("cascade"), None); assert_eq!(ReferentialAction::from_str(""), None);
819 }
820
821 #[test]
822 fn test_referential_action_as_str() {
823 assert_eq!(ReferentialAction::Cascade.as_str(), "CASCADE");
824 assert_eq!(ReferentialAction::Restrict.as_str(), "RESTRICT");
825 assert_eq!(ReferentialAction::NoAction.as_str(), "NO ACTION");
826 assert_eq!(ReferentialAction::SetNull.as_str(), "SET NULL");
827 assert_eq!(ReferentialAction::SetDefault.as_str(), "SET DEFAULT");
828 }
829
830 #[test]
831 fn test_referential_action_equality() {
832 assert_eq!(ReferentialAction::Cascade, ReferentialAction::Cascade);
833 assert_ne!(ReferentialAction::Cascade, ReferentialAction::Restrict);
834 }
835
836 #[test]
837 fn test_referential_action_copy() {
838 let action = ReferentialAction::Cascade;
839 let copy = action;
840 assert_eq!(action, copy);
841 }
842}