prax_schema/ast/
attribute.rs

1//! Attribute definitions for the Prax schema AST.
2
3use serde::{Deserialize, Serialize};
4use smol_str::SmolStr;
5
6use super::{Ident, Span};
7
8/// An attribute argument value.
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub enum AttributeValue {
11    /// A string literal.
12    String(String),
13    /// An integer literal.
14    Int(i64),
15    /// A float literal.
16    Float(f64),
17    /// A boolean literal.
18    Boolean(bool),
19    /// An identifier/constant reference (e.g., enum value).
20    Ident(SmolStr),
21    /// A function call (e.g., `now()`, `uuid()`).
22    Function(SmolStr, Vec<AttributeValue>),
23    /// An array of values.
24    Array(Vec<AttributeValue>),
25    /// A field reference (e.g., `[field_name]`).
26    FieldRef(SmolStr),
27    /// A list of field references (e.g., `[field1, field2]`).
28    FieldRefList(Vec<SmolStr>),
29}
30
31impl AttributeValue {
32    /// Try to get the value as a string.
33    pub fn as_string(&self) -> Option<&str> {
34        match self {
35            Self::String(s) => Some(s),
36            _ => None,
37        }
38    }
39
40    /// Try to get the value as an integer.
41    pub fn as_int(&self) -> Option<i64> {
42        match self {
43            Self::Int(i) => Some(*i),
44            _ => None,
45        }
46    }
47
48    /// Try to get the value as a float.
49    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    /// Try to get the value as a boolean.
58    pub fn as_bool(&self) -> Option<bool> {
59        match self {
60            Self::Boolean(b) => Some(*b),
61            _ => None,
62        }
63    }
64
65    /// Try to get the value as an identifier.
66    pub fn as_ident(&self) -> Option<&str> {
67        match self {
68            Self::Ident(s) => Some(s),
69            _ => None,
70        }
71    }
72}
73
74/// An attribute argument (named or positional).
75#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
76pub struct AttributeArg {
77    /// Argument name (None for positional arguments).
78    pub name: Option<Ident>,
79    /// Argument value.
80    pub value: AttributeValue,
81    /// Source location.
82    pub span: Span,
83}
84
85impl AttributeArg {
86    /// Create a positional argument.
87    pub fn positional(value: AttributeValue, span: Span) -> Self {
88        Self {
89            name: None,
90            value,
91            span,
92        }
93    }
94
95    /// Create a named argument.
96    pub fn named(name: Ident, value: AttributeValue, span: Span) -> Self {
97        Self {
98            name: Some(name),
99            value,
100            span,
101        }
102    }
103
104    /// Check if this is a positional argument.
105    pub fn is_positional(&self) -> bool {
106        self.name.is_none()
107    }
108}
109
110/// An attribute applied to a field, model, or enum.
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub struct Attribute {
113    /// Attribute name (without `@` prefix).
114    pub name: Ident,
115    /// Attribute arguments.
116    pub args: Vec<AttributeArg>,
117    /// Source location (including `@`).
118    pub span: Span,
119}
120
121impl Attribute {
122    /// Create a new attribute.
123    pub fn new(name: Ident, args: Vec<AttributeArg>, span: Span) -> Self {
124        Self { name, args, span }
125    }
126
127    /// Create an attribute with no arguments.
128    pub fn simple(name: Ident, span: Span) -> Self {
129        Self {
130            name,
131            args: vec![],
132            span,
133        }
134    }
135
136    /// Get the attribute name as a string.
137    pub fn name(&self) -> &str {
138        self.name.as_str()
139    }
140
141    /// Check if this attribute has the given name.
142    pub fn is(&self, name: &str) -> bool {
143        self.name.as_str() == name
144    }
145
146    /// Get the first positional argument.
147    pub fn first_arg(&self) -> Option<&AttributeValue> {
148        self.args.first().map(|a| &a.value)
149    }
150
151    /// Get a named argument by name.
152    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    /// Check if this is a field-level attribute.
160    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    /// Check if this is a model-level attribute (prefixed with `@@`).
176    pub fn is_model_attribute(&self) -> bool {
177        matches!(
178            self.name(),
179            "map" | "index" | "unique" | "id" | "search" | "sql"
180        )
181    }
182}
183
184/// Common field attributes.
185#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
186pub struct FieldAttributes {
187    /// This field is the primary key.
188    pub is_id: bool,
189    /// This field auto-increments (for integer IDs).
190    pub is_auto: bool,
191    /// This field has a unique constraint.
192    pub is_unique: bool,
193    /// This field is indexed.
194    pub is_indexed: bool,
195    /// This field is updated automatically on record update.
196    pub is_updated_at: bool,
197    /// This field should be omitted from default selections.
198    pub is_omit: bool,
199    /// Default value expression.
200    pub default: Option<AttributeValue>,
201    /// Database column name mapping.
202    pub map: Option<String>,
203    /// Native database type (e.g., `@db.VarChar(255)`).
204    pub native_type: Option<NativeType>,
205    /// Relation attributes.
206    pub relation: Option<RelationAttribute>,
207}
208
209/// Native database type specification.
210#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
211pub struct NativeType {
212    /// Type name (e.g., "VarChar", "Text", "Decimal").
213    pub name: SmolStr,
214    /// Type arguments (e.g., length, precision, scale).
215    pub args: Vec<AttributeValue>,
216}
217
218impl NativeType {
219    /// Create a new native type.
220    pub fn new(name: impl Into<SmolStr>, args: Vec<AttributeValue>) -> Self {
221        Self {
222            name: name.into(),
223            args,
224        }
225    }
226}
227
228/// Relation attribute details.
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230pub struct RelationAttribute {
231    /// Relation name (for disambiguation).
232    pub name: Option<String>,
233    /// Fields on this model that reference the other model.
234    pub fields: Vec<SmolStr>,
235    /// Fields on the other model being referenced.
236    pub references: Vec<SmolStr>,
237    /// On delete action.
238    pub on_delete: Option<ReferentialAction>,
239    /// On update action.
240    pub on_update: Option<ReferentialAction>,
241}
242
243/// Referential actions for relations.
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
245pub enum ReferentialAction {
246    /// Cascade the operation.
247    Cascade,
248    /// Restrict the operation (error if references exist).
249    Restrict,
250    /// No action (deferred check).
251    NoAction,
252    /// Set to null.
253    SetNull,
254    /// Set to default value.
255    SetDefault,
256}
257
258impl ReferentialAction {
259    /// Parse from string.
260    #[allow(clippy::should_implement_trait)]
261    pub fn from_str(s: &str) -> Option<Self> {
262        match s {
263            "Cascade" => Some(Self::Cascade),
264            "Restrict" => Some(Self::Restrict),
265            "NoAction" => Some(Self::NoAction),
266            "SetNull" => Some(Self::SetNull),
267            "SetDefault" => Some(Self::SetDefault),
268            _ => None,
269        }
270    }
271
272    /// Get the action name.
273    pub fn as_str(&self) -> &'static str {
274        match self {
275            Self::Cascade => "CASCADE",
276            Self::Restrict => "RESTRICT",
277            Self::NoAction => "NO ACTION",
278            Self::SetNull => "SET NULL",
279            Self::SetDefault => "SET DEFAULT",
280        }
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287
288    // ==================== AttributeValue Tests ====================
289
290    #[test]
291    fn test_attribute_value_string() {
292        let val = AttributeValue::String("hello".into());
293        assert_eq!(val.as_string(), Some("hello"));
294        assert_eq!(val.as_int(), None);
295        assert_eq!(val.as_bool(), None);
296        assert_eq!(val.as_ident(), None);
297    }
298
299    #[test]
300    fn test_attribute_value_int() {
301        let val = AttributeValue::Int(42);
302        assert_eq!(val.as_int(), Some(42));
303        assert_eq!(val.as_string(), None);
304        assert_eq!(val.as_bool(), None);
305    }
306
307    #[test]
308    fn test_attribute_value_int_negative() {
309        let val = AttributeValue::Int(-100);
310        assert_eq!(val.as_int(), Some(-100));
311    }
312
313    #[test]
314    fn test_attribute_value_float() {
315        let val = AttributeValue::Float(3.14);
316        assert_eq!(val.as_int(), None);
317        assert_eq!(val.as_string(), None);
318    }
319
320    #[test]
321    fn test_attribute_value_boolean_true() {
322        let val = AttributeValue::Boolean(true);
323        assert_eq!(val.as_bool(), Some(true));
324        assert_eq!(val.as_int(), None);
325    }
326
327    #[test]
328    fn test_attribute_value_boolean_false() {
329        let val = AttributeValue::Boolean(false);
330        assert_eq!(val.as_bool(), Some(false));
331    }
332
333    #[test]
334    fn test_attribute_value_ident() {
335        let val = AttributeValue::Ident("MyEnum".into());
336        assert_eq!(val.as_ident(), Some("MyEnum"));
337        assert_eq!(val.as_string(), None);
338    }
339
340    #[test]
341    fn test_attribute_value_function() {
342        let val = AttributeValue::Function("now".into(), vec![]);
343        assert_eq!(val.as_string(), None);
344        assert_eq!(val.as_int(), None);
345
346        // Check it's the right variant
347        if let AttributeValue::Function(name, args) = val {
348            assert_eq!(name.as_str(), "now");
349            assert!(args.is_empty());
350        } else {
351            panic!("Expected Function variant");
352        }
353    }
354
355    #[test]
356    fn test_attribute_value_function_with_args() {
357        let val = AttributeValue::Function("uuid".into(), vec![AttributeValue::Int(4)]);
358
359        if let AttributeValue::Function(name, args) = val {
360            assert_eq!(name.as_str(), "uuid");
361            assert_eq!(args.len(), 1);
362        } else {
363            panic!("Expected Function variant");
364        }
365    }
366
367    #[test]
368    fn test_attribute_value_array() {
369        let val = AttributeValue::Array(vec![
370            AttributeValue::String("a".into()),
371            AttributeValue::String("b".into()),
372        ]);
373
374        if let AttributeValue::Array(items) = val {
375            assert_eq!(items.len(), 2);
376        } else {
377            panic!("Expected Array variant");
378        }
379    }
380
381    #[test]
382    fn test_attribute_value_field_ref() {
383        let val = AttributeValue::FieldRef("user_id".into());
384        assert_eq!(val.as_ident(), None);
385
386        if let AttributeValue::FieldRef(name) = val {
387            assert_eq!(name.as_str(), "user_id");
388        } else {
389            panic!("Expected FieldRef variant");
390        }
391    }
392
393    #[test]
394    fn test_attribute_value_field_ref_list() {
395        let val = AttributeValue::FieldRefList(vec!["id".into(), "name".into()]);
396
397        if let AttributeValue::FieldRefList(fields) = val {
398            assert_eq!(fields.len(), 2);
399            assert_eq!(fields[0].as_str(), "id");
400            assert_eq!(fields[1].as_str(), "name");
401        } else {
402            panic!("Expected FieldRefList variant");
403        }
404    }
405
406    #[test]
407    fn test_attribute_value_equality() {
408        let val1 = AttributeValue::Int(42);
409        let val2 = AttributeValue::Int(42);
410        let val3 = AttributeValue::Int(43);
411
412        assert_eq!(val1, val2);
413        assert_ne!(val1, val3);
414    }
415
416    // ==================== AttributeArg Tests ====================
417
418    #[test]
419    fn test_attribute_arg_positional() {
420        let arg = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
421
422        assert!(arg.is_positional());
423        assert!(arg.name.is_none());
424        assert_eq!(arg.value.as_int(), Some(42));
425    }
426
427    #[test]
428    fn test_attribute_arg_named() {
429        let arg = AttributeArg::named(
430            Ident::new("length", Span::new(0, 6)),
431            AttributeValue::Int(255),
432            Span::new(0, 10),
433        );
434
435        assert!(!arg.is_positional());
436        assert!(arg.name.is_some());
437        assert_eq!(arg.name.as_ref().unwrap().as_str(), "length");
438        assert_eq!(arg.value.as_int(), Some(255));
439    }
440
441    #[test]
442    fn test_attribute_arg_equality() {
443        let arg1 = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
444        let arg2 = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
445        let arg3 = AttributeArg::positional(AttributeValue::Int(43), Span::new(0, 2));
446
447        assert_eq!(arg1, arg2);
448        assert_ne!(arg1, arg3);
449    }
450
451    // ==================== Attribute Tests ====================
452
453    #[test]
454    fn test_attribute_new() {
455        let attr = Attribute::new(
456            Ident::new("default", Span::new(0, 7)),
457            vec![AttributeArg::positional(
458                AttributeValue::Int(0),
459                Span::new(8, 9),
460            )],
461            Span::new(0, 10),
462        );
463
464        assert_eq!(attr.name(), "default");
465        assert_eq!(attr.args.len(), 1);
466    }
467
468    #[test]
469    fn test_attribute_simple() {
470        let attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
471
472        assert_eq!(attr.name(), "id");
473        assert!(attr.args.is_empty());
474    }
475
476    #[test]
477    fn test_attribute_is() {
478        let attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
479
480        assert!(attr.is("unique"));
481        assert!(!attr.is("id"));
482        assert!(!attr.is("UNIQUE")); // case sensitive
483    }
484
485    #[test]
486    fn test_attribute_first_arg() {
487        let attr = Attribute::new(
488            Ident::new("default", Span::new(0, 7)),
489            vec![
490                AttributeArg::positional(AttributeValue::Int(42), Span::new(8, 10)),
491                AttributeArg::positional(AttributeValue::String("extra".into()), Span::new(12, 19)),
492            ],
493            Span::new(0, 20),
494        );
495
496        assert_eq!(attr.first_arg().unwrap().as_int(), Some(42));
497    }
498
499    #[test]
500    fn test_attribute_first_arg_none() {
501        let attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
502        assert!(attr.first_arg().is_none());
503    }
504
505    #[test]
506    fn test_attribute_get_arg() {
507        let attr = Attribute::new(
508            Ident::new("relation", Span::new(0, 8)),
509            vec![
510                AttributeArg::named(
511                    Ident::new("fields", Span::new(9, 15)),
512                    AttributeValue::FieldRefList(vec!["user_id".into()]),
513                    Span::new(9, 30),
514                ),
515                AttributeArg::named(
516                    Ident::new("references", Span::new(32, 42)),
517                    AttributeValue::FieldRefList(vec!["id".into()]),
518                    Span::new(32, 50),
519                ),
520            ],
521            Span::new(0, 51),
522        );
523
524        let fields = attr.get_arg("fields").unwrap();
525        if let AttributeValue::FieldRefList(f) = fields {
526            assert_eq!(f[0].as_str(), "user_id");
527        } else {
528            panic!("Expected FieldRefList");
529        }
530
531        assert!(attr.get_arg("onDelete").is_none());
532    }
533
534    #[test]
535    fn test_attribute_is_field_attribute() {
536        let id_attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
537        let auto_attr = Attribute::simple(Ident::new("auto", Span::new(0, 4)), Span::new(0, 5));
538        let unique_attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
539        let index_attr = Attribute::simple(Ident::new("index", Span::new(0, 5)), Span::new(0, 6));
540        let default_attr =
541            Attribute::simple(Ident::new("default", Span::new(0, 7)), Span::new(0, 8));
542        let updated_at_attr =
543            Attribute::simple(Ident::new("updated_at", Span::new(0, 10)), Span::new(0, 11));
544        let omit_attr = Attribute::simple(Ident::new("omit", Span::new(0, 4)), Span::new(0, 5));
545        let map_attr = Attribute::simple(Ident::new("map", Span::new(0, 3)), Span::new(0, 4));
546        let db_attr = Attribute::simple(Ident::new("db", Span::new(0, 2)), Span::new(0, 3));
547        let relation_attr =
548            Attribute::simple(Ident::new("relation", Span::new(0, 8)), Span::new(0, 9));
549        let unknown_attr =
550            Attribute::simple(Ident::new("unknown", Span::new(0, 7)), Span::new(0, 8));
551
552        assert!(id_attr.is_field_attribute());
553        assert!(auto_attr.is_field_attribute());
554        assert!(unique_attr.is_field_attribute());
555        assert!(index_attr.is_field_attribute());
556        assert!(default_attr.is_field_attribute());
557        assert!(updated_at_attr.is_field_attribute());
558        assert!(omit_attr.is_field_attribute());
559        assert!(map_attr.is_field_attribute());
560        assert!(db_attr.is_field_attribute());
561        assert!(relation_attr.is_field_attribute());
562        assert!(!unknown_attr.is_field_attribute());
563    }
564
565    #[test]
566    fn test_attribute_is_model_attribute() {
567        let map_attr = Attribute::simple(Ident::new("map", Span::new(0, 3)), Span::new(0, 4));
568        let index_attr = Attribute::simple(Ident::new("index", Span::new(0, 5)), Span::new(0, 6));
569        let unique_attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
570        let id_attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
571        let search_attr = Attribute::simple(Ident::new("search", Span::new(0, 6)), Span::new(0, 7));
572        let sql_attr = Attribute::simple(Ident::new("sql", Span::new(0, 3)), Span::new(0, 4));
573        let unknown_attr =
574            Attribute::simple(Ident::new("unknown", Span::new(0, 7)), Span::new(0, 8));
575
576        assert!(map_attr.is_model_attribute());
577        assert!(index_attr.is_model_attribute());
578        assert!(unique_attr.is_model_attribute());
579        assert!(id_attr.is_model_attribute());
580        assert!(search_attr.is_model_attribute());
581        assert!(sql_attr.is_model_attribute());
582        assert!(!unknown_attr.is_model_attribute());
583    }
584
585    // ==================== FieldAttributes Tests ====================
586
587    #[test]
588    fn test_field_attributes_default() {
589        let attrs = FieldAttributes::default();
590
591        assert!(!attrs.is_id);
592        assert!(!attrs.is_auto);
593        assert!(!attrs.is_unique);
594        assert!(!attrs.is_indexed);
595        assert!(!attrs.is_updated_at);
596        assert!(!attrs.is_omit);
597        assert!(attrs.default.is_none());
598        assert!(attrs.map.is_none());
599        assert!(attrs.native_type.is_none());
600        assert!(attrs.relation.is_none());
601    }
602
603    #[test]
604    fn test_field_attributes_with_values() {
605        let attrs = FieldAttributes {
606            is_id: true,
607            is_auto: true,
608            is_unique: false,
609            is_indexed: false,
610            is_updated_at: false,
611            is_omit: false,
612            default: Some(AttributeValue::Function("auto".into(), vec![])),
613            map: Some("user_id".to_string()),
614            native_type: None,
615            relation: None,
616        };
617
618        assert!(attrs.is_id);
619        assert!(attrs.is_auto);
620        assert!(attrs.default.is_some());
621        assert_eq!(attrs.map, Some("user_id".to_string()));
622    }
623
624    // ==================== NativeType Tests ====================
625
626    #[test]
627    fn test_native_type_new() {
628        let nt = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
629
630        assert_eq!(nt.name.as_str(), "VarChar");
631        assert_eq!(nt.args.len(), 1);
632        assert_eq!(nt.args[0].as_int(), Some(255));
633    }
634
635    #[test]
636    fn test_native_type_no_args() {
637        let nt = NativeType::new("Text", vec![]);
638
639        assert_eq!(nt.name.as_str(), "Text");
640        assert!(nt.args.is_empty());
641    }
642
643    #[test]
644    fn test_native_type_multiple_args() {
645        let nt = NativeType::new(
646            "Decimal",
647            vec![AttributeValue::Int(10), AttributeValue::Int(2)],
648        );
649
650        assert_eq!(nt.name.as_str(), "Decimal");
651        assert_eq!(nt.args.len(), 2);
652    }
653
654    #[test]
655    fn test_native_type_equality() {
656        let nt1 = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
657        let nt2 = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
658        let nt3 = NativeType::new("VarChar", vec![AttributeValue::Int(100)]);
659
660        assert_eq!(nt1, nt2);
661        assert_ne!(nt1, nt3);
662    }
663
664    // ==================== RelationAttribute Tests ====================
665
666    #[test]
667    fn test_relation_attribute() {
668        let rel = RelationAttribute {
669            name: Some("UserPosts".to_string()),
670            fields: vec!["author_id".into()],
671            references: vec!["id".into()],
672            on_delete: Some(ReferentialAction::Cascade),
673            on_update: Some(ReferentialAction::Cascade),
674        };
675
676        assert_eq!(rel.name, Some("UserPosts".to_string()));
677        assert_eq!(rel.fields[0].as_str(), "author_id");
678        assert_eq!(rel.references[0].as_str(), "id");
679        assert_eq!(rel.on_delete, Some(ReferentialAction::Cascade));
680    }
681
682    #[test]
683    fn test_relation_attribute_minimal() {
684        let rel = RelationAttribute {
685            name: None,
686            fields: vec![],
687            references: vec![],
688            on_delete: None,
689            on_update: None,
690        };
691
692        assert!(rel.name.is_none());
693        assert!(rel.fields.is_empty());
694    }
695
696    // ==================== ReferentialAction Tests ====================
697
698    #[test]
699    fn test_referential_action_from_str_cascade() {
700        assert_eq!(
701            ReferentialAction::from_str("Cascade"),
702            Some(ReferentialAction::Cascade)
703        );
704    }
705
706    #[test]
707    fn test_referential_action_from_str_restrict() {
708        assert_eq!(
709            ReferentialAction::from_str("Restrict"),
710            Some(ReferentialAction::Restrict)
711        );
712    }
713
714    #[test]
715    fn test_referential_action_from_str_no_action() {
716        assert_eq!(
717            ReferentialAction::from_str("NoAction"),
718            Some(ReferentialAction::NoAction)
719        );
720    }
721
722    #[test]
723    fn test_referential_action_from_str_set_null() {
724        assert_eq!(
725            ReferentialAction::from_str("SetNull"),
726            Some(ReferentialAction::SetNull)
727        );
728    }
729
730    #[test]
731    fn test_referential_action_from_str_set_default() {
732        assert_eq!(
733            ReferentialAction::from_str("SetDefault"),
734            Some(ReferentialAction::SetDefault)
735        );
736    }
737
738    #[test]
739    fn test_referential_action_from_str_unknown() {
740        assert_eq!(ReferentialAction::from_str("Unknown"), None);
741        assert_eq!(ReferentialAction::from_str("cascade"), None); // case sensitive
742        assert_eq!(ReferentialAction::from_str(""), None);
743    }
744
745    #[test]
746    fn test_referential_action_as_str() {
747        assert_eq!(ReferentialAction::Cascade.as_str(), "CASCADE");
748        assert_eq!(ReferentialAction::Restrict.as_str(), "RESTRICT");
749        assert_eq!(ReferentialAction::NoAction.as_str(), "NO ACTION");
750        assert_eq!(ReferentialAction::SetNull.as_str(), "SET NULL");
751        assert_eq!(ReferentialAction::SetDefault.as_str(), "SET DEFAULT");
752    }
753
754    #[test]
755    fn test_referential_action_equality() {
756        assert_eq!(ReferentialAction::Cascade, ReferentialAction::Cascade);
757        assert_ne!(ReferentialAction::Cascade, ReferentialAction::Restrict);
758    }
759
760    #[test]
761    fn test_referential_action_copy() {
762        let action = ReferentialAction::Cascade;
763        let copy = action;
764        assert_eq!(action, copy);
765    }
766}