icydb_schema/node/
field.rs1use crate::prelude::*;
2use canic_core::utils::case::{Case, Casing};
3
4#[derive(Clone, Debug, Serialize)]
9pub struct FieldList {
10 pub fields: &'static [Field],
11}
12
13impl FieldList {
14 #[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#[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 errs.add_result(validate_ident(self.ident));
50
51 if !self.ident.is_case(Case::Snake) {
53 err!(errs, "field ident '{}' must be snake_case", self.ident);
54 }
55
56 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}