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 is_field_attribute(&self) -> bool {
161 matches!(
162 self.name(),
163 "id" | "auto"
164 | "unique"
165 | "index"
166 | "default"
167 | "updated_at"
168 | "omit"
169 | "map"
170 | "db"
171 | "relation"
172 )
173 }
174
175 pub fn is_model_attribute(&self) -> bool {
177 matches!(
178 self.name(),
179 "map" | "index" | "unique" | "id" | "search" | "sql"
180 )
181 }
182}
183
184#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
186pub struct FieldAttributes {
187 pub is_id: bool,
189 pub is_auto: bool,
191 pub is_unique: bool,
193 pub is_indexed: bool,
195 pub is_updated_at: bool,
197 pub is_omit: bool,
199 pub default: Option<AttributeValue>,
201 pub map: Option<String>,
203 pub native_type: Option<NativeType>,
205 pub relation: Option<RelationAttribute>,
207}
208
209#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
211pub struct NativeType {
212 pub name: SmolStr,
214 pub args: Vec<AttributeValue>,
216}
217
218impl NativeType {
219 pub fn new(name: impl Into<SmolStr>, args: Vec<AttributeValue>) -> Self {
221 Self {
222 name: name.into(),
223 args,
224 }
225 }
226}
227
228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230pub struct RelationAttribute {
231 pub name: Option<String>,
233 pub fields: Vec<SmolStr>,
235 pub references: Vec<SmolStr>,
237 pub on_delete: Option<ReferentialAction>,
239 pub on_update: Option<ReferentialAction>,
241 pub map: Option<String>,
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
247pub enum ReferentialAction {
248 Cascade,
250 Restrict,
252 NoAction,
254 SetNull,
256 SetDefault,
258}
259
260impl ReferentialAction {
261 #[allow(clippy::should_implement_trait)]
263 pub fn from_str(s: &str) -> Option<Self> {
264 match s {
265 "Cascade" => Some(Self::Cascade),
266 "Restrict" => Some(Self::Restrict),
267 "NoAction" => Some(Self::NoAction),
268 "SetNull" => Some(Self::SetNull),
269 "SetDefault" => Some(Self::SetDefault),
270 _ => None,
271 }
272 }
273
274 pub fn as_str(&self) -> &'static str {
276 match self {
277 Self::Cascade => "CASCADE",
278 Self::Restrict => "RESTRICT",
279 Self::NoAction => "NO ACTION",
280 Self::SetNull => "SET NULL",
281 Self::SetDefault => "SET DEFAULT",
282 }
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
293 fn test_attribute_value_string() {
294 let val = AttributeValue::String("hello".into());
295 assert_eq!(val.as_string(), Some("hello"));
296 assert_eq!(val.as_int(), None);
297 assert_eq!(val.as_bool(), None);
298 assert_eq!(val.as_ident(), None);
299 }
300
301 #[test]
302 fn test_attribute_value_int() {
303 let val = AttributeValue::Int(42);
304 assert_eq!(val.as_int(), Some(42));
305 assert_eq!(val.as_string(), None);
306 assert_eq!(val.as_bool(), None);
307 }
308
309 #[test]
310 fn test_attribute_value_int_negative() {
311 let val = AttributeValue::Int(-100);
312 assert_eq!(val.as_int(), Some(-100));
313 }
314
315 #[test]
316 #[allow(clippy::approx_constant)]
317 fn test_attribute_value_float() {
318 let val = AttributeValue::Float(3.14);
319 assert_eq!(val.as_int(), None);
320 assert_eq!(val.as_string(), None);
321 }
322
323 #[test]
324 fn test_attribute_value_boolean_true() {
325 let val = AttributeValue::Boolean(true);
326 assert_eq!(val.as_bool(), Some(true));
327 assert_eq!(val.as_int(), None);
328 }
329
330 #[test]
331 fn test_attribute_value_boolean_false() {
332 let val = AttributeValue::Boolean(false);
333 assert_eq!(val.as_bool(), Some(false));
334 }
335
336 #[test]
337 fn test_attribute_value_ident() {
338 let val = AttributeValue::Ident("MyEnum".into());
339 assert_eq!(val.as_ident(), Some("MyEnum"));
340 assert_eq!(val.as_string(), None);
341 }
342
343 #[test]
344 fn test_attribute_value_function() {
345 let val = AttributeValue::Function("now".into(), vec![]);
346 assert_eq!(val.as_string(), None);
347 assert_eq!(val.as_int(), None);
348
349 if let AttributeValue::Function(name, args) = val {
351 assert_eq!(name.as_str(), "now");
352 assert!(args.is_empty());
353 } else {
354 panic!("Expected Function variant");
355 }
356 }
357
358 #[test]
359 fn test_attribute_value_function_with_args() {
360 let val = AttributeValue::Function("uuid".into(), vec![AttributeValue::Int(4)]);
361
362 if let AttributeValue::Function(name, args) = val {
363 assert_eq!(name.as_str(), "uuid");
364 assert_eq!(args.len(), 1);
365 } else {
366 panic!("Expected Function variant");
367 }
368 }
369
370 #[test]
371 fn test_attribute_value_array() {
372 let val = AttributeValue::Array(vec![
373 AttributeValue::String("a".into()),
374 AttributeValue::String("b".into()),
375 ]);
376
377 if let AttributeValue::Array(items) = val {
378 assert_eq!(items.len(), 2);
379 } else {
380 panic!("Expected Array variant");
381 }
382 }
383
384 #[test]
385 fn test_attribute_value_field_ref() {
386 let val = AttributeValue::FieldRef("user_id".into());
387 assert_eq!(val.as_ident(), None);
388
389 if let AttributeValue::FieldRef(name) = val {
390 assert_eq!(name.as_str(), "user_id");
391 } else {
392 panic!("Expected FieldRef variant");
393 }
394 }
395
396 #[test]
397 fn test_attribute_value_field_ref_list() {
398 let val = AttributeValue::FieldRefList(vec!["id".into(), "name".into()]);
399
400 if let AttributeValue::FieldRefList(fields) = val {
401 assert_eq!(fields.len(), 2);
402 assert_eq!(fields[0].as_str(), "id");
403 assert_eq!(fields[1].as_str(), "name");
404 } else {
405 panic!("Expected FieldRefList variant");
406 }
407 }
408
409 #[test]
410 fn test_attribute_value_equality() {
411 let val1 = AttributeValue::Int(42);
412 let val2 = AttributeValue::Int(42);
413 let val3 = AttributeValue::Int(43);
414
415 assert_eq!(val1, val2);
416 assert_ne!(val1, val3);
417 }
418
419 #[test]
422 fn test_attribute_arg_positional() {
423 let arg = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
424
425 assert!(arg.is_positional());
426 assert!(arg.name.is_none());
427 assert_eq!(arg.value.as_int(), Some(42));
428 }
429
430 #[test]
431 fn test_attribute_arg_named() {
432 let arg = AttributeArg::named(
433 Ident::new("length", Span::new(0, 6)),
434 AttributeValue::Int(255),
435 Span::new(0, 10),
436 );
437
438 assert!(!arg.is_positional());
439 assert!(arg.name.is_some());
440 assert_eq!(arg.name.as_ref().unwrap().as_str(), "length");
441 assert_eq!(arg.value.as_int(), Some(255));
442 }
443
444 #[test]
445 fn test_attribute_arg_equality() {
446 let arg1 = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
447 let arg2 = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
448 let arg3 = AttributeArg::positional(AttributeValue::Int(43), Span::new(0, 2));
449
450 assert_eq!(arg1, arg2);
451 assert_ne!(arg1, arg3);
452 }
453
454 #[test]
457 fn test_attribute_new() {
458 let attr = Attribute::new(
459 Ident::new("default", Span::new(0, 7)),
460 vec![AttributeArg::positional(
461 AttributeValue::Int(0),
462 Span::new(8, 9),
463 )],
464 Span::new(0, 10),
465 );
466
467 assert_eq!(attr.name(), "default");
468 assert_eq!(attr.args.len(), 1);
469 }
470
471 #[test]
472 fn test_attribute_simple() {
473 let attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
474
475 assert_eq!(attr.name(), "id");
476 assert!(attr.args.is_empty());
477 }
478
479 #[test]
480 fn test_attribute_is() {
481 let attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
482
483 assert!(attr.is("unique"));
484 assert!(!attr.is("id"));
485 assert!(!attr.is("UNIQUE")); }
487
488 #[test]
489 fn test_attribute_first_arg() {
490 let attr = Attribute::new(
491 Ident::new("default", Span::new(0, 7)),
492 vec![
493 AttributeArg::positional(AttributeValue::Int(42), Span::new(8, 10)),
494 AttributeArg::positional(AttributeValue::String("extra".into()), Span::new(12, 19)),
495 ],
496 Span::new(0, 20),
497 );
498
499 assert_eq!(attr.first_arg().unwrap().as_int(), Some(42));
500 }
501
502 #[test]
503 fn test_attribute_first_arg_none() {
504 let attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
505 assert!(attr.first_arg().is_none());
506 }
507
508 #[test]
509 fn test_attribute_get_arg() {
510 let attr = Attribute::new(
511 Ident::new("relation", Span::new(0, 8)),
512 vec![
513 AttributeArg::named(
514 Ident::new("fields", Span::new(9, 15)),
515 AttributeValue::FieldRefList(vec!["user_id".into()]),
516 Span::new(9, 30),
517 ),
518 AttributeArg::named(
519 Ident::new("references", Span::new(32, 42)),
520 AttributeValue::FieldRefList(vec!["id".into()]),
521 Span::new(32, 50),
522 ),
523 ],
524 Span::new(0, 51),
525 );
526
527 let fields = attr.get_arg("fields").unwrap();
528 if let AttributeValue::FieldRefList(f) = fields {
529 assert_eq!(f[0].as_str(), "user_id");
530 } else {
531 panic!("Expected FieldRefList");
532 }
533
534 assert!(attr.get_arg("onDelete").is_none());
535 }
536
537 #[test]
538 fn test_attribute_is_field_attribute() {
539 let id_attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
540 let auto_attr = Attribute::simple(Ident::new("auto", Span::new(0, 4)), Span::new(0, 5));
541 let unique_attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
542 let index_attr = Attribute::simple(Ident::new("index", Span::new(0, 5)), Span::new(0, 6));
543 let default_attr =
544 Attribute::simple(Ident::new("default", Span::new(0, 7)), Span::new(0, 8));
545 let updated_at_attr =
546 Attribute::simple(Ident::new("updated_at", Span::new(0, 10)), Span::new(0, 11));
547 let omit_attr = Attribute::simple(Ident::new("omit", Span::new(0, 4)), Span::new(0, 5));
548 let map_attr = Attribute::simple(Ident::new("map", Span::new(0, 3)), Span::new(0, 4));
549 let db_attr = Attribute::simple(Ident::new("db", Span::new(0, 2)), Span::new(0, 3));
550 let relation_attr =
551 Attribute::simple(Ident::new("relation", Span::new(0, 8)), Span::new(0, 9));
552 let unknown_attr =
553 Attribute::simple(Ident::new("unknown", Span::new(0, 7)), Span::new(0, 8));
554
555 assert!(id_attr.is_field_attribute());
556 assert!(auto_attr.is_field_attribute());
557 assert!(unique_attr.is_field_attribute());
558 assert!(index_attr.is_field_attribute());
559 assert!(default_attr.is_field_attribute());
560 assert!(updated_at_attr.is_field_attribute());
561 assert!(omit_attr.is_field_attribute());
562 assert!(map_attr.is_field_attribute());
563 assert!(db_attr.is_field_attribute());
564 assert!(relation_attr.is_field_attribute());
565 assert!(!unknown_attr.is_field_attribute());
566 }
567
568 #[test]
569 fn test_attribute_is_model_attribute() {
570 let map_attr = Attribute::simple(Ident::new("map", Span::new(0, 3)), Span::new(0, 4));
571 let index_attr = Attribute::simple(Ident::new("index", Span::new(0, 5)), Span::new(0, 6));
572 let unique_attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
573 let id_attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
574 let search_attr = Attribute::simple(Ident::new("search", Span::new(0, 6)), Span::new(0, 7));
575 let sql_attr = Attribute::simple(Ident::new("sql", Span::new(0, 3)), Span::new(0, 4));
576 let unknown_attr =
577 Attribute::simple(Ident::new("unknown", Span::new(0, 7)), Span::new(0, 8));
578
579 assert!(map_attr.is_model_attribute());
580 assert!(index_attr.is_model_attribute());
581 assert!(unique_attr.is_model_attribute());
582 assert!(id_attr.is_model_attribute());
583 assert!(search_attr.is_model_attribute());
584 assert!(sql_attr.is_model_attribute());
585 assert!(!unknown_attr.is_model_attribute());
586 }
587
588 #[test]
591 fn test_field_attributes_default() {
592 let attrs = FieldAttributes::default();
593
594 assert!(!attrs.is_id);
595 assert!(!attrs.is_auto);
596 assert!(!attrs.is_unique);
597 assert!(!attrs.is_indexed);
598 assert!(!attrs.is_updated_at);
599 assert!(!attrs.is_omit);
600 assert!(attrs.default.is_none());
601 assert!(attrs.map.is_none());
602 assert!(attrs.native_type.is_none());
603 assert!(attrs.relation.is_none());
604 }
605
606 #[test]
607 fn test_field_attributes_with_values() {
608 let attrs = FieldAttributes {
609 is_id: true,
610 is_auto: true,
611 is_unique: false,
612 is_indexed: false,
613 is_updated_at: false,
614 is_omit: false,
615 default: Some(AttributeValue::Function("auto".into(), vec![])),
616 map: Some("user_id".to_string()),
617 native_type: None,
618 relation: None,
619 };
620
621 assert!(attrs.is_id);
622 assert!(attrs.is_auto);
623 assert!(attrs.default.is_some());
624 assert_eq!(attrs.map, Some("user_id".to_string()));
625 }
626
627 #[test]
630 fn test_native_type_new() {
631 let nt = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
632
633 assert_eq!(nt.name.as_str(), "VarChar");
634 assert_eq!(nt.args.len(), 1);
635 assert_eq!(nt.args[0].as_int(), Some(255));
636 }
637
638 #[test]
639 fn test_native_type_no_args() {
640 let nt = NativeType::new("Text", vec![]);
641
642 assert_eq!(nt.name.as_str(), "Text");
643 assert!(nt.args.is_empty());
644 }
645
646 #[test]
647 fn test_native_type_multiple_args() {
648 let nt = NativeType::new(
649 "Decimal",
650 vec![AttributeValue::Int(10), AttributeValue::Int(2)],
651 );
652
653 assert_eq!(nt.name.as_str(), "Decimal");
654 assert_eq!(nt.args.len(), 2);
655 }
656
657 #[test]
658 fn test_native_type_equality() {
659 let nt1 = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
660 let nt2 = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
661 let nt3 = NativeType::new("VarChar", vec![AttributeValue::Int(100)]);
662
663 assert_eq!(nt1, nt2);
664 assert_ne!(nt1, nt3);
665 }
666
667 #[test]
670 fn test_relation_attribute() {
671 let rel = RelationAttribute {
672 name: Some("UserPosts".to_string()),
673 fields: vec!["author_id".into()],
674 references: vec!["id".into()],
675 on_delete: Some(ReferentialAction::Cascade),
676 on_update: Some(ReferentialAction::Cascade),
677 map: Some("fk_post_author".to_string()),
678 };
679
680 assert_eq!(rel.name, Some("UserPosts".to_string()));
681 assert_eq!(rel.fields[0].as_str(), "author_id");
682 assert_eq!(rel.references[0].as_str(), "id");
683 assert_eq!(rel.on_delete, Some(ReferentialAction::Cascade));
684 assert_eq!(rel.map, Some("fk_post_author".to_string()));
685 }
686
687 #[test]
688 fn test_relation_attribute_minimal() {
689 let rel = RelationAttribute {
690 name: None,
691 fields: vec![],
692 references: vec![],
693 on_delete: None,
694 on_update: None,
695 map: None,
696 };
697
698 assert!(rel.name.is_none());
699 assert!(rel.fields.is_empty());
700 assert!(rel.map.is_none());
701 }
702
703 #[test]
706 fn test_referential_action_from_str_cascade() {
707 assert_eq!(
708 ReferentialAction::from_str("Cascade"),
709 Some(ReferentialAction::Cascade)
710 );
711 }
712
713 #[test]
714 fn test_referential_action_from_str_restrict() {
715 assert_eq!(
716 ReferentialAction::from_str("Restrict"),
717 Some(ReferentialAction::Restrict)
718 );
719 }
720
721 #[test]
722 fn test_referential_action_from_str_no_action() {
723 assert_eq!(
724 ReferentialAction::from_str("NoAction"),
725 Some(ReferentialAction::NoAction)
726 );
727 }
728
729 #[test]
730 fn test_referential_action_from_str_set_null() {
731 assert_eq!(
732 ReferentialAction::from_str("SetNull"),
733 Some(ReferentialAction::SetNull)
734 );
735 }
736
737 #[test]
738 fn test_referential_action_from_str_set_default() {
739 assert_eq!(
740 ReferentialAction::from_str("SetDefault"),
741 Some(ReferentialAction::SetDefault)
742 );
743 }
744
745 #[test]
746 fn test_referential_action_from_str_unknown() {
747 assert_eq!(ReferentialAction::from_str("Unknown"), None);
748 assert_eq!(ReferentialAction::from_str("cascade"), None); assert_eq!(ReferentialAction::from_str(""), None);
750 }
751
752 #[test]
753 fn test_referential_action_as_str() {
754 assert_eq!(ReferentialAction::Cascade.as_str(), "CASCADE");
755 assert_eq!(ReferentialAction::Restrict.as_str(), "RESTRICT");
756 assert_eq!(ReferentialAction::NoAction.as_str(), "NO ACTION");
757 assert_eq!(ReferentialAction::SetNull.as_str(), "SET NULL");
758 assert_eq!(ReferentialAction::SetDefault.as_str(), "SET DEFAULT");
759 }
760
761 #[test]
762 fn test_referential_action_equality() {
763 assert_eq!(ReferentialAction::Cascade, ReferentialAction::Cascade);
764 assert_ne!(ReferentialAction::Cascade, ReferentialAction::Restrict);
765 }
766
767 #[test]
768 fn test_referential_action_copy() {
769 let action = ReferentialAction::Cascade;
770 let copy = action;
771 assert_eq!(action, copy);
772 }
773}