icydb_schema/node/
field.rs

1use crate::prelude::*;
2use canic_utils::case::{Case, Casing};
3
4///
5/// FieldList
6///
7
8#[derive(Clone, Debug, Serialize)]
9pub struct FieldList {
10    pub fields: &'static [Field],
11}
12
13impl FieldList {
14    // get
15    #[must_use]
16    pub fn get(&self, ident: &str) -> Option<&Field> {
17        self.fields.iter().find(|f| f.ident == ident)
18    }
19}
20
21impl ValidateNode for FieldList {}
22
23impl VisitableNode for FieldList {
24    fn drive<V: Visitor>(&self, v: &mut V) {
25        for node in self.fields {
26            node.accept(v);
27        }
28    }
29}
30
31///
32/// Field
33///
34
35#[derive(Clone, Debug, Serialize)]
36pub struct Field {
37    pub ident: &'static str,
38    pub value: Value,
39
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub default: Option<Arg>,
42}
43
44impl ValidateNode for Field {
45    fn validate(&self) -> Result<(), ErrorTree> {
46        let mut errs = ErrorTree::new();
47
48        // idents
49        errs.add_result(validate_ident(self.ident));
50
51        // snake case
52        if !self.ident.is_case(Case::Snake) {
53            err!(errs, "field ident '{}' must be snake_case", self.ident);
54        }
55
56        // relation naming
57        if self.value.item.is_relation() {
58            let ident = self.ident;
59            match self.value.cardinality {
60                Cardinality::One | Cardinality::Opt if !ident.ends_with("id") => {
61                    err!(
62                        errs,
63                        "one or optional relationship '{ident}' should end with 'id'"
64                    );
65                }
66                Cardinality::Many if !ident.ends_with("ids") => {
67                    err!(errs, "many relationship '{ident}' should end with 'ids'");
68                }
69
70                _ => {}
71            }
72        }
73
74        errs.result()
75    }
76}
77
78impl VisitableNode for Field {
79    fn route_key(&self) -> String {
80        self.ident.to_string()
81    }
82
83    fn drive<V: Visitor>(&self, v: &mut V) {
84        self.value.accept(v);
85        if let Some(node) = &self.default {
86            node.accept(v);
87        }
88    }
89}