icydb_schema/node/
item.rs1use crate::prelude::*;
2use std::ops::Not;
3
4#[derive(Clone, Debug, Serialize)]
12pub struct Item {
13 target: ItemTarget,
14
15 #[serde(skip_serializing_if = "Option::is_none")]
16 relation: Option<&'static str>,
17
18 #[serde(skip_serializing_if = "Option::is_none")]
19 scale: Option<u32>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
22 max_len: Option<u32>,
23
24 #[serde(skip_serializing_if = "<[_]>::is_empty")]
25 validators: &'static [TypeValidator],
26
27 #[serde(skip_serializing_if = "<[_]>::is_empty")]
28 sanitizers: &'static [TypeSanitizer],
29
30 #[serde(skip_serializing_if = "Not::not")]
31 indirect: bool,
32}
33
34impl Item {
35 #[must_use]
36 pub const fn new(
37 target: ItemTarget,
38 relation: Option<&'static str>,
39 scale: Option<u32>,
40 max_len: Option<u32>,
41 validators: &'static [TypeValidator],
42 sanitizers: &'static [TypeSanitizer],
43 indirect: bool,
44 ) -> Self {
45 Self {
46 target,
47 relation,
48 scale,
49 max_len,
50 validators,
51 sanitizers,
52 indirect,
53 }
54 }
55
56 #[must_use]
57 pub const fn target(&self) -> &ItemTarget {
58 &self.target
59 }
60
61 #[must_use]
62 pub const fn relation(&self) -> Option<&'static str> {
63 self.relation
64 }
65
66 #[must_use]
67 pub const fn scale(&self) -> Option<u32> {
68 self.scale
69 }
70
71 #[must_use]
72 pub const fn max_len(&self) -> Option<u32> {
73 self.max_len
74 }
75
76 #[must_use]
77 pub const fn validators(&self) -> &'static [TypeValidator] {
78 self.validators
79 }
80
81 #[must_use]
82 pub const fn sanitizers(&self) -> &'static [TypeSanitizer] {
83 self.sanitizers
84 }
85
86 #[must_use]
87 pub const fn indirect(&self) -> bool {
88 self.indirect
89 }
90
91 #[must_use]
92 pub const fn is_relation(&self) -> bool {
93 self.relation().is_some()
94 }
95}
96
97impl ValidateNode for Item {
98 fn validate(&self) -> Result<(), ErrorTree> {
99 let mut errs = ErrorTree::new();
100 let schema = schema_read();
101
102 match self.target() {
104 ItemTarget::Is(path) => {
105 if schema.check_node_as::<Entity>(path).is_ok() {
107 err!(errs, "a non-relation Item cannot reference an Entity");
108 }
109 }
110
111 ItemTarget::Primitive(_) => {}
112 }
113
114 if let Some(relation) = self.relation() {
116 match schema.cast_node::<Entity>(relation) {
118 Ok(entity) => {
119 if let Some(primary_field) = entity.get_pk_field() {
121 let relation_target = primary_field.value().item().target();
122
123 let relation_scale = primary_field.value().item().scale();
125 let relation_max_len = primary_field.value().item().max_len();
126 if self.target() != relation_target
127 || self.scale() != relation_scale
128 || self.max_len() != relation_max_len
129 {
130 err!(
131 errs,
132 "relation target type mismatch: expected ({:?}, scale={:?}, max_len={:?}), found ({:?}, scale={:?}, max_len={:?})",
133 relation_target,
134 relation_scale,
135 relation_max_len,
136 self.target(),
137 self.scale(),
138 self.max_len()
139 );
140 }
141 } else {
142 err!(
143 errs,
144 "relation entity '{relation}' missing primary key field '{0}'",
145 entity.primary_key().field()
146 );
147 }
148 }
149 Err(_) => {
150 err!(errs, "relation entity '{relation}' not found");
151 }
152 }
153 }
154
155 errs.result()
156 }
157}
158
159impl VisitableNode for Item {
160 fn drive<V: Visitor>(&self, v: &mut V) {
161 for node in self.validators() {
162 node.accept(v);
163 }
164 }
165}
166
167#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
175pub enum ItemTarget {
176 Is(&'static str),
177 Primitive(Primitive),
178}