Skip to main content

teaql_core/
meta.rs

1use crate::{DataType, default_table_name};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct PropertyDescriptor {
5    pub name: String,
6    pub data_type: DataType,
7    pub nullable: bool,
8    pub column_name: String,
9    pub is_id: bool,
10    pub is_version: bool,
11    pub max_length: Option<u32>,
12    pub numeric_precision: Option<u32>,
13    pub numeric_scale: Option<u32>,
14}
15
16impl PropertyDescriptor {
17    pub fn new(name: impl Into<String>, data_type: DataType) -> Self {
18        let name = name.into();
19        Self {
20            column_name: name.clone(),
21            name,
22            data_type,
23            nullable: true,
24            is_id: false,
25            is_version: false,
26            max_length: None,
27            numeric_precision: None,
28            numeric_scale: None,
29        }
30    }
31
32    pub fn column_name(mut self, column_name: impl Into<String>) -> Self {
33        self.column_name = column_name.into();
34        self
35    }
36
37    pub fn not_null(mut self) -> Self {
38        self.nullable = false;
39        self
40    }
41
42    pub fn id(mut self) -> Self {
43        self.is_id = true;
44        self
45    }
46
47    pub fn version(mut self) -> Self {
48        self.is_version = true;
49        self
50    }
51
52    pub fn max_length(mut self, max_length: u32) -> Self {
53        self.max_length = Some(max_length);
54        self
55    }
56
57    pub fn numeric_precision(mut self, precision: u32) -> Self {
58        self.numeric_precision = Some(precision);
59        self
60    }
61
62    pub fn numeric_scale(mut self, scale: u32) -> Self {
63        self.numeric_scale = Some(scale);
64        self
65    }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct RelationDescriptor {
70    pub name: String,
71    pub target_entity: String,
72    pub local_key: String,
73    pub foreign_key: String,
74    pub many: bool,
75    pub attach: bool,
76    pub delete_missing: bool,
77}
78
79impl RelationDescriptor {
80    pub fn new(name: impl Into<String>, target_entity: impl Into<String>) -> Self {
81        Self {
82            name: name.into(),
83            target_entity: target_entity.into(),
84            local_key: "id".to_owned(),
85            foreign_key: "id".to_owned(),
86            many: false,
87            attach: true,
88            delete_missing: true,
89        }
90    }
91
92    pub fn local_key(mut self, key: impl Into<String>) -> Self {
93        self.local_key = key.into();
94        self
95    }
96
97    pub fn foreign_key(mut self, key: impl Into<String>) -> Self {
98        self.foreign_key = key.into();
99        self
100    }
101
102    pub fn many(mut self) -> Self {
103        self.many = true;
104        self
105    }
106
107    pub fn attach(mut self) -> Self {
108        self.attach = true;
109        self
110    }
111
112    pub fn detached(mut self) -> Self {
113        self.attach = false;
114        self
115    }
116
117    pub fn delete_missing(mut self) -> Self {
118        self.delete_missing = true;
119        self
120    }
121
122    pub fn keep_missing(mut self) -> Self {
123        self.delete_missing = false;
124        self
125    }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct EntityDescriptor {
130    pub name: String,
131    pub table_name: String,
132    pub data_service: Option<String>,
133    pub properties: Vec<PropertyDescriptor>,
134    pub relations: Vec<RelationDescriptor>,
135    pub audit_mask_fields: Vec<String>,
136    /// False for legacy metadata that never declared a complete field log policy.
137    pub audit_mask_fields_declared: bool,
138    pub audit_value_max_len: Option<usize>,
139}
140
141impl EntityDescriptor {
142    pub fn new(name: impl Into<String>) -> Self {
143        let name = name.into();
144        Self {
145            table_name: default_table_name(&name),
146            name,
147            data_service: None,
148            properties: Vec::new(),
149            relations: Vec::new(),
150            audit_mask_fields: Vec::new(),
151            audit_mask_fields_declared: false,
152            audit_value_max_len: None,
153        }
154    }
155
156    pub fn table_name(mut self, table_name: impl Into<String>) -> Self {
157        self.table_name = table_name.into();
158        self
159    }
160
161    pub fn data_service(mut self, data_service: impl Into<String>) -> Self {
162        self.data_service = Some(data_service.into());
163        self
164    }
165
166    pub fn property(mut self, property: PropertyDescriptor) -> Self {
167        self.properties.push(property);
168        self
169    }
170
171    pub fn relation(mut self, relation: RelationDescriptor) -> Self {
172        self.relations.push(relation);
173        self
174    }
175
176    pub fn audit_mask_fields(mut self, fields: Vec<String>) -> Self {
177        self.audit_mask_fields = fields;
178        self.audit_mask_fields_declared = true;
179        self
180    }
181
182    pub fn audit_value_max_len(mut self, max_len: Option<usize>) -> Self {
183        self.audit_value_max_len = max_len;
184        self
185    }
186
187    pub fn property_by_name(&self, name: &str) -> Option<&PropertyDescriptor> {
188        self.properties
189            .iter()
190            .find(|property| property.name == name)
191    }
192
193    pub fn relation_by_name(&self, name: &str) -> Option<&RelationDescriptor> {
194        self.relations.iter().find(|relation| relation.name == name)
195    }
196
197    pub fn id_property(&self) -> Option<&PropertyDescriptor> {
198        self.properties.iter().find(|property| property.is_id)
199    }
200
201    pub fn version_property(&self) -> Option<&PropertyDescriptor> {
202        self.properties.iter().find(|property| property.is_version)
203    }
204
205    pub fn writable_properties(&self) -> impl Iterator<Item = &PropertyDescriptor> {
206        self.properties.iter().filter(|property| !property.is_id)
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn test_property_descriptor_builder() {
216        let prop = PropertyDescriptor::new("username", DataType::Text)
217            .column_name("user_name")
218            .not_null()
219            .id()
220            .version()
221            .max_length(120)
222            .numeric_precision(19)
223            .numeric_scale(7);
224
225        assert_eq!(prop.name, "username");
226        assert_eq!(prop.column_name, "user_name");
227        assert_eq!(prop.data_type, DataType::Text);
228        assert!(!prop.nullable);
229        assert!(prop.is_id);
230        assert!(prop.is_version);
231        assert_eq!(prop.max_length, Some(120));
232        assert_eq!(prop.numeric_precision, Some(19));
233        assert_eq!(prop.numeric_scale, Some(7));
234    }
235
236    #[test]
237    fn test_relation_descriptor_builder() {
238        let rel = RelationDescriptor::new("orders", "Order")
239            .local_key("user_id")
240            .foreign_key("customer_id")
241            .many()
242            .detached()
243            .keep_missing();
244
245        assert_eq!(rel.name, "orders");
246        assert_eq!(rel.target_entity, "Order");
247        assert_eq!(rel.local_key, "user_id");
248        assert_eq!(rel.foreign_key, "customer_id");
249        assert!(rel.many);
250        assert!(!rel.attach);
251        assert!(!rel.delete_missing);
252    }
253
254    #[test]
255    fn test_entity_descriptor_builder_and_lookups() {
256        let mut entity = EntityDescriptor::new("User")
257            .table_name("users")
258            .data_service("auth_db")
259            .audit_mask_fields(vec!["password".to_string()])
260            .audit_value_max_len(Some(255));
261
262        let id_prop = PropertyDescriptor::new("id", DataType::I64).id();
263        let name_prop = PropertyDescriptor::new("name", DataType::Text);
264        let version_prop = PropertyDescriptor::new("version", DataType::I64).version();
265
266        let orders_rel = RelationDescriptor::new("orders", "Order");
267
268        entity = entity
269            .property(id_prop.clone())
270            .property(name_prop.clone())
271            .property(version_prop.clone())
272            .relation(orders_rel.clone());
273
274        assert_eq!(entity.name, "User");
275        assert_eq!(entity.table_name, "users");
276        assert_eq!(entity.data_service, Some("auth_db".to_string()));
277        assert_eq!(entity.audit_mask_fields, vec!["password".to_string()]);
278        assert!(entity.audit_mask_fields_declared);
279        assert_eq!(entity.audit_value_max_len, Some(255));
280
281        // Lookups
282        assert_eq!(entity.property_by_name("name"), Some(&name_prop));
283        assert_eq!(entity.property_by_name("missing"), None);
284
285        assert_eq!(entity.relation_by_name("orders"), Some(&orders_rel));
286        assert_eq!(entity.relation_by_name("missing"), None);
287
288        assert_eq!(entity.id_property(), Some(&id_prop));
289        assert_eq!(entity.version_property(), Some(&version_prop));
290
291        let writable: Vec<_> = entity.writable_properties().collect();
292        assert_eq!(writable.len(), 2);
293        assert!(writable.contains(&&name_prop));
294        assert!(writable.contains(&&version_prop));
295    }
296}