icydb_schema/node/
entity.rs1use crate::{MAX_INDEX_FIELDS, prelude::*};
2use std::{any::Any, collections::HashSet};
3
4#[derive(Clone, Debug, Serialize)]
9pub struct Entity {
10 pub def: Def,
11 pub store: &'static str,
12 pub primary_key: &'static str,
13
14 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub name: Option<&'static str>,
16
17 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
18 pub indexes: &'static [Index],
19
20 pub fields: FieldList,
21 pub ty: Type,
22}
23
24impl Entity {
25 #[must_use]
26 pub fn get_pk_field(&self) -> &Field {
27 self.fields
28 .get(self.primary_key)
29 .unwrap_or_else(|| panic!("missing primary key field '{}'", self.primary_key))
30 }
31
32 #[must_use]
33 pub fn resolved_name(&self) -> &'static str {
34 self.name.unwrap_or(self.def.ident)
35 }
36}
37
38impl MacroNode for Entity {
39 fn as_any(&self) -> &dyn Any {
40 self
41 }
42}
43
44impl TypeNode for Entity {
45 fn ty(&self) -> &Type {
46 &self.ty
47 }
48}
49
50impl ValidateNode for Entity {
51 fn validate(&self) -> Result<(), ErrorTree> {
52 let mut errs = ErrorTree::new();
53 let schema = schema_read();
54
55 match self.fields.get(self.primary_key) {
57 Some(pk) => {
58 if !matches!(pk.value.cardinality, Cardinality::One) {
59 err!(
60 errs,
61 "primary key '{0}' must have cardinality One",
62 self.primary_key
63 );
64 }
65 }
66 None => {
67 err!(errs, "missing primary key field '{0}'", self.primary_key);
68 }
69 }
70
71 if let Err(msg) = crate::build::validate::validate_entity_name(self.resolved_name()) {
73 err!(errs, "{msg}");
74 }
75
76 match schema.cast_node::<Store>(self.store) {
78 Ok(store) if !matches!(store.ty, StoreType::Data) => {
79 err!(errs, "store is not type Data");
80 }
81 Ok(_) => {}
82 Err(e) => errs.add(e),
83 }
84
85 let mut resolved_indexes = Vec::new();
87
88 for index in self.indexes {
90 match schema.cast_node::<Store>(index.store) {
92 Ok(store) if !matches!(store.ty, StoreType::Index) => {
93 err!(errs, "store is not type Index");
94 }
95 Ok(_) => {}
96 Err(e) => errs.add(e),
97 }
98
99 if index.fields.is_empty() {
101 err!(errs, "index must reference at least one field");
102 }
103 if index.fields.len() > MAX_INDEX_FIELDS {
104 err!(
105 errs,
106 "index has {} fields; maximum is {}",
107 index.fields.len(),
108 MAX_INDEX_FIELDS
109 );
110 }
111
112 let mut seen = HashSet::new();
114 for field in index.fields {
116 if !seen.insert(*field) {
117 err!(errs, "index contains duplicate field '{field}'");
118 }
119 if let Some(field) = self.fields.get(field) {
120 if field.value.cardinality == Cardinality::Many {
121 err!(errs, "cannot add an index field with many cardinality");
122 }
123 } else {
124 err!(errs, "index field '{field}' not found");
125 }
126 }
127
128 if let Err(msg) =
129 crate::build::validate::validate_index_name_len(self.resolved_name(), index.fields)
130 {
131 err!(errs, "{msg}");
132 }
133 resolved_indexes.push(index);
134 }
135
136 for (i, a) in resolved_indexes.iter().enumerate() {
138 for b in resolved_indexes.iter().skip(i + 1) {
139 if a.unique == b.unique {
140 if a.is_prefix_of(b) {
141 err!(
142 errs,
143 "index {:?} is redundant (prefix of {:?})",
144 a.fields,
145 b.fields
146 );
147 } else if b.is_prefix_of(a) {
148 err!(
149 errs,
150 "index {:?} is redundant (prefix of {:?})",
151 b.fields,
152 a.fields
153 );
154 }
155 }
156 }
157 }
158
159 errs.result()
160 }
161}
162
163impl VisitableNode for Entity {
164 fn route_key(&self) -> String {
165 self.def.path()
166 }
167
168 fn drive<V: Visitor>(&self, v: &mut V) {
169 self.def.accept(v);
170 self.fields.accept(v);
171 self.ty.accept(v);
172 }
173}