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    #[allow(clippy::approx_constant)]
315    fn test_attribute_value_float() {
316        let val = AttributeValue::Float(3.14);
317        assert_eq!(val.as_int(), None);
318        assert_eq!(val.as_string(), None);
319    }
320
321    #[test]
322    fn test_attribute_value_boolean_true() {
323        let val = AttributeValue::Boolean(true);
324        assert_eq!(val.as_bool(), Some(true));
325        assert_eq!(val.as_int(), None);
326    }
327
328    #[test]
329    fn test_attribute_value_boolean_false() {
330        let val = AttributeValue::Boolean(false);
331        assert_eq!(val.as_bool(), Some(false));
332    }
333
334    #[test]
335    fn test_attribute_value_ident() {
336        let val = AttributeValue::Ident("MyEnum".into());
337        assert_eq!(val.as_ident(), Some("MyEnum"));
338        assert_eq!(val.as_string(), None);
339    }
340
341    #[test]
342    fn test_attribute_value_function() {
343        let val = AttributeValue::Function("now".into(), vec![]);
344        assert_eq!(val.as_string(), None);
345        assert_eq!(val.as_int(), None);
346
347        // Check it's the right variant
348        if let AttributeValue::Function(name, args) = val {
349            assert_eq!(name.as_str(), "now");
350            assert!(args.is_empty());
351        } else {
352            panic!("Expected Function variant");
353        }
354    }
355
356    #[test]
357    fn test_attribute_value_function_with_args() {
358        let val = AttributeValue::Function("uuid".into(), vec![AttributeValue::Int(4)]);
359
360        if let AttributeValue::Function(name, args) = val {
361            assert_eq!(name.as_str(), "uuid");
362            assert_eq!(args.len(), 1);
363        } else {
364            panic!("Expected Function variant");
365        }
366    }
367
368    #[test]
369    fn test_attribute_value_array() {
370        let val = AttributeValue::Array(vec![
371            AttributeValue::String("a".into()),
372            AttributeValue::String("b".into()),
373        ]);
374
375        if let AttributeValue::Array(items) = val {
376            assert_eq!(items.len(), 2);
377        } else {
378            panic!("Expected Array variant");
379        }
380    }
381
382    #[test]
383    fn test_attribute_value_field_ref() {
384        let val = AttributeValue::FieldRef("user_id".into());
385        assert_eq!(val.as_ident(), None);
386
387        if let AttributeValue::FieldRef(name) = val {
388            assert_eq!(name.as_str(), "user_id");
389        } else {
390            panic!("Expected FieldRef variant");
391        }
392    }
393
394    #[test]
395    fn test_attribute_value_field_ref_list() {
396        let val = AttributeValue::FieldRefList(vec!["id".into(), "name".into()]);
397
398        if let AttributeValue::FieldRefList(fields) = val {
399            assert_eq!(fields.len(), 2);
400            assert_eq!(fields[0].as_str(), "id");
401            assert_eq!(fields[1].as_str(), "name");
402        } else {
403            panic!("Expected FieldRefList variant");
404        }
405    }
406
407    #[test]
408    fn test_attribute_value_equality() {
409        let val1 = AttributeValue::Int(42);
410        let val2 = AttributeValue::Int(42);
411        let val3 = AttributeValue::Int(43);
412
413        assert_eq!(val1, val2);
414        assert_ne!(val1, val3);
415    }
416
417    // ==================== AttributeArg Tests ====================
418
419    #[test]
420    fn test_attribute_arg_positional() {
421        let arg = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
422
423        assert!(arg.is_positional());
424        assert!(arg.name.is_none());
425        assert_eq!(arg.value.as_int(), Some(42));
426    }
427
428    #[test]
429    fn test_attribute_arg_named() {
430        let arg = AttributeArg::named(
431            Ident::new("length", Span::new(0, 6)),
432            AttributeValue::Int(255),
433            Span::new(0, 10),
434        );
435
436        assert!(!arg.is_positional());
437        assert!(arg.name.is_some());
438        assert_eq!(arg.name.as_ref().unwrap().as_str(), "length");
439        assert_eq!(arg.value.as_int(), Some(255));
440    }
441
442    #[test]
443    fn test_attribute_arg_equality() {
444        let arg1 = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
445        let arg2 = AttributeArg::positional(AttributeValue::Int(42), Span::new(0, 2));
446        let arg3 = AttributeArg::positional(AttributeValue::Int(43), Span::new(0, 2));
447
448        assert_eq!(arg1, arg2);
449        assert_ne!(arg1, arg3);
450    }
451
452    // ==================== Attribute Tests ====================
453
454    #[test]
455    fn test_attribute_new() {
456        let attr = Attribute::new(
457            Ident::new("default", Span::new(0, 7)),
458            vec![AttributeArg::positional(
459                AttributeValue::Int(0),
460                Span::new(8, 9),
461            )],
462            Span::new(0, 10),
463        );
464
465        assert_eq!(attr.name(), "default");
466        assert_eq!(attr.args.len(), 1);
467    }
468
469    #[test]
470    fn test_attribute_simple() {
471        let attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
472
473        assert_eq!(attr.name(), "id");
474        assert!(attr.args.is_empty());
475    }
476
477    #[test]
478    fn test_attribute_is() {
479        let attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
480
481        assert!(attr.is("unique"));
482        assert!(!attr.is("id"));
483        assert!(!attr.is("UNIQUE")); // case sensitive
484    }
485
486    #[test]
487    fn test_attribute_first_arg() {
488        let attr = Attribute::new(
489            Ident::new("default", Span::new(0, 7)),
490            vec![
491                AttributeArg::positional(AttributeValue::Int(42), Span::new(8, 10)),
492                AttributeArg::positional(AttributeValue::String("extra".into()), Span::new(12, 19)),
493            ],
494            Span::new(0, 20),
495        );
496
497        assert_eq!(attr.first_arg().unwrap().as_int(), Some(42));
498    }
499
500    #[test]
501    fn test_attribute_first_arg_none() {
502        let attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
503        assert!(attr.first_arg().is_none());
504    }
505
506    #[test]
507    fn test_attribute_get_arg() {
508        let attr = Attribute::new(
509            Ident::new("relation", Span::new(0, 8)),
510            vec![
511                AttributeArg::named(
512                    Ident::new("fields", Span::new(9, 15)),
513                    AttributeValue::FieldRefList(vec!["user_id".into()]),
514                    Span::new(9, 30),
515                ),
516                AttributeArg::named(
517                    Ident::new("references", Span::new(32, 42)),
518                    AttributeValue::FieldRefList(vec!["id".into()]),
519                    Span::new(32, 50),
520                ),
521            ],
522            Span::new(0, 51),
523        );
524
525        let fields = attr.get_arg("fields").unwrap();
526        if let AttributeValue::FieldRefList(f) = fields {
527            assert_eq!(f[0].as_str(), "user_id");
528        } else {
529            panic!("Expected FieldRefList");
530        }
531
532        assert!(attr.get_arg("onDelete").is_none());
533    }
534
535    #[test]
536    fn test_attribute_is_field_attribute() {
537        let id_attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
538        let auto_attr = Attribute::simple(Ident::new("auto", Span::new(0, 4)), Span::new(0, 5));
539        let unique_attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
540        let index_attr = Attribute::simple(Ident::new("index", Span::new(0, 5)), Span::new(0, 6));
541        let default_attr =
542            Attribute::simple(Ident::new("default", Span::new(0, 7)), Span::new(0, 8));
543        let updated_at_attr =
544            Attribute::simple(Ident::new("updated_at", Span::new(0, 10)), Span::new(0, 11));
545        let omit_attr = Attribute::simple(Ident::new("omit", Span::new(0, 4)), Span::new(0, 5));
546        let map_attr = Attribute::simple(Ident::new("map", Span::new(0, 3)), Span::new(0, 4));
547        let db_attr = Attribute::simple(Ident::new("db", Span::new(0, 2)), Span::new(0, 3));
548        let relation_attr =
549            Attribute::simple(Ident::new("relation", Span::new(0, 8)), Span::new(0, 9));
550        let unknown_attr =
551            Attribute::simple(Ident::new("unknown", Span::new(0, 7)), Span::new(0, 8));
552
553        assert!(id_attr.is_field_attribute());
554        assert!(auto_attr.is_field_attribute());
555        assert!(unique_attr.is_field_attribute());
556        assert!(index_attr.is_field_attribute());
557        assert!(default_attr.is_field_attribute());
558        assert!(updated_at_attr.is_field_attribute());
559        assert!(omit_attr.is_field_attribute());
560        assert!(map_attr.is_field_attribute());
561        assert!(db_attr.is_field_attribute());
562        assert!(relation_attr.is_field_attribute());
563        assert!(!unknown_attr.is_field_attribute());
564    }
565
566    #[test]
567    fn test_attribute_is_model_attribute() {
568        let map_attr = Attribute::simple(Ident::new("map", Span::new(0, 3)), Span::new(0, 4));
569        let index_attr = Attribute::simple(Ident::new("index", Span::new(0, 5)), Span::new(0, 6));
570        let unique_attr = Attribute::simple(Ident::new("unique", Span::new(0, 6)), Span::new(0, 7));
571        let id_attr = Attribute::simple(Ident::new("id", Span::new(0, 2)), Span::new(0, 3));
572        let search_attr = Attribute::simple(Ident::new("search", Span::new(0, 6)), Span::new(0, 7));
573        let sql_attr = Attribute::simple(Ident::new("sql", Span::new(0, 3)), Span::new(0, 4));
574        let unknown_attr =
575            Attribute::simple(Ident::new("unknown", Span::new(0, 7)), Span::new(0, 8));
576
577        assert!(map_attr.is_model_attribute());
578        assert!(index_attr.is_model_attribute());
579        assert!(unique_attr.is_model_attribute());
580        assert!(id_attr.is_model_attribute());
581        assert!(search_attr.is_model_attribute());
582        assert!(sql_attr.is_model_attribute());
583        assert!(!unknown_attr.is_model_attribute());
584    }
585
586    // ==================== FieldAttributes Tests ====================
587
588    #[test]
589    fn test_field_attributes_default() {
590        let attrs = FieldAttributes::default();
591
592        assert!(!attrs.is_id);
593        assert!(!attrs.is_auto);
594        assert!(!attrs.is_unique);
595        assert!(!attrs.is_indexed);
596        assert!(!attrs.is_updated_at);
597        assert!(!attrs.is_omit);
598        assert!(attrs.default.is_none());
599        assert!(attrs.map.is_none());
600        assert!(attrs.native_type.is_none());
601        assert!(attrs.relation.is_none());
602    }
603
604    #[test]
605    fn test_field_attributes_with_values() {
606        let attrs = FieldAttributes {
607            is_id: true,
608            is_auto: true,
609            is_unique: false,
610            is_indexed: false,
611            is_updated_at: false,
612            is_omit: false,
613            default: Some(AttributeValue::Function("auto".into(), vec![])),
614            map: Some("user_id".to_string()),
615            native_type: None,
616            relation: None,
617        };
618
619        assert!(attrs.is_id);
620        assert!(attrs.is_auto);
621        assert!(attrs.default.is_some());
622        assert_eq!(attrs.map, Some("user_id".to_string()));
623    }
624
625    // ==================== NativeType Tests ====================
626
627    #[test]
628    fn test_native_type_new() {
629        let nt = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
630
631        assert_eq!(nt.name.as_str(), "VarChar");
632        assert_eq!(nt.args.len(), 1);
633        assert_eq!(nt.args[0].as_int(), Some(255));
634    }
635
636    #[test]
637    fn test_native_type_no_args() {
638        let nt = NativeType::new("Text", vec![]);
639
640        assert_eq!(nt.name.as_str(), "Text");
641        assert!(nt.args.is_empty());
642    }
643
644    #[test]
645    fn test_native_type_multiple_args() {
646        let nt = NativeType::new(
647            "Decimal",
648            vec![AttributeValue::Int(10), AttributeValue::Int(2)],
649        );
650
651        assert_eq!(nt.name.as_str(), "Decimal");
652        assert_eq!(nt.args.len(), 2);
653    }
654
655    #[test]
656    fn test_native_type_equality() {
657        let nt1 = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
658        let nt2 = NativeType::new("VarChar", vec![AttributeValue::Int(255)]);
659        let nt3 = NativeType::new("VarChar", vec![AttributeValue::Int(100)]);
660
661        assert_eq!(nt1, nt2);
662        assert_ne!(nt1, nt3);
663    }
664
665    // ==================== RelationAttribute Tests ====================
666
667    #[test]
668    fn test_relation_attribute() {
669        let rel = RelationAttribute {
670            name: Some("UserPosts".to_string()),
671            fields: vec!["author_id".into()],
672            references: vec!["id".into()],
673            on_delete: Some(ReferentialAction::Cascade),
674            on_update: Some(ReferentialAction::Cascade),
675        };
676
677        assert_eq!(rel.name, Some("UserPosts".to_string()));
678        assert_eq!(rel.fields[0].as_str(), "author_id");
679        assert_eq!(rel.references[0].as_str(), "id");
680        assert_eq!(rel.on_delete, Some(ReferentialAction::Cascade));
681    }
682
683    #[test]
684    fn test_relation_attribute_minimal() {
685        let rel = RelationAttribute {
686            name: None,
687            fields: vec![],
688            references: vec![],
689            on_delete: None,
690            on_update: None,
691        };
692
693        assert!(rel.name.is_none());
694        assert!(rel.fields.is_empty());
695    }
696
697    // ==================== ReferentialAction Tests ====================
698
699    #[test]
700    fn test_referential_action_from_str_cascade() {
701        assert_eq!(
702            ReferentialAction::from_str("Cascade"),
703            Some(ReferentialAction::Cascade)
704        );
705    }
706
707    #[test]
708    fn test_referential_action_from_str_restrict() {
709        assert_eq!(
710            ReferentialAction::from_str("Restrict"),
711            Some(ReferentialAction::Restrict)
712        );
713    }
714
715    #[test]
716    fn test_referential_action_from_str_no_action() {
717        assert_eq!(
718            ReferentialAction::from_str("NoAction"),
719            Some(ReferentialAction::NoAction)
720        );
721    }
722
723    #[test]
724    fn test_referential_action_from_str_set_null() {
725        assert_eq!(
726            ReferentialAction::from_str("SetNull"),
727            Some(ReferentialAction::SetNull)
728        );
729    }
730
731    #[test]
732    fn test_referential_action_from_str_set_default() {
733        assert_eq!(
734            ReferentialAction::from_str("SetDefault"),
735            Some(ReferentialAction::SetDefault)
736        );
737    }
738
739    #[test]
740    fn test_referential_action_from_str_unknown() {
741        assert_eq!(ReferentialAction::from_str("Unknown"), None);
742        assert_eq!(ReferentialAction::from_str("cascade"), None); // case sensitive
743        assert_eq!(ReferentialAction::from_str(""), None);
744    }
745
746    #[test]
747    fn test_referential_action_as_str() {
748        assert_eq!(ReferentialAction::Cascade.as_str(), "CASCADE");
749        assert_eq!(ReferentialAction::Restrict.as_str(), "RESTRICT");
750        assert_eq!(ReferentialAction::NoAction.as_str(), "NO ACTION");
751        assert_eq!(ReferentialAction::SetNull.as_str(), "SET NULL");
752        assert_eq!(ReferentialAction::SetDefault.as_str(), "SET DEFAULT");
753    }
754
755    #[test]
756    fn test_referential_action_equality() {
757        assert_eq!(ReferentialAction::Cascade, ReferentialAction::Cascade);
758        assert_ne!(ReferentialAction::Cascade, ReferentialAction::Restrict);
759    }
760
761    #[test]
762    fn test_referential_action_copy() {
763        let action = ReferentialAction::Cascade;
764        let copy = action;
765        assert_eq!(action, copy);
766    }
767}