zino_orm/
accessor.rs

1use super::{IntoSqlValue, ModelHelper, Schema};
2use std::fmt::Display;
3use zino_core::{
4    JsonValue, Map, bail,
5    datetime::DateTime,
6    error::Error,
7    extension::{JsonObjectExt, JsonValueExt},
8    model::{ModelHooks, Mutation, Query},
9    validation::Validation,
10    warn,
11};
12
13/// Access model fields.
14///
15/// This trait can be derived by `zino_derive::ModelAccessor`.
16pub trait ModelAccessor<K>: Schema<PrimaryKey = K>
17where
18    K: Default + Display + PartialEq,
19{
20    /// Returns the `id` field, i.e. the primary key.
21    fn id(&self) -> &K;
22
23    /// Returns the `name` field.
24    #[inline]
25    fn name(&self) -> &str {
26        ""
27    }
28
29    /// Returns the `namespace` field.
30    #[inline]
31    fn namespace(&self) -> &str {
32        ""
33    }
34
35    /// Returns the `visibility` field.
36    #[inline]
37    fn visibility(&self) -> &str {
38        "Private"
39    }
40
41    /// Returns the `status` field.
42    #[inline]
43    fn status(&self) -> &str {
44        "Active"
45    }
46
47    /// Returns the `description` field.
48    #[inline]
49    fn description(&self) -> &str {
50        ""
51    }
52
53    /// Returns the `extra` field.
54    #[inline]
55    fn extra(&self) -> Option<&Map> {
56        None
57    }
58
59    /// Returns the `created_at` field.
60    #[inline]
61    fn created_at(&self) -> DateTime {
62        DateTime::default()
63    }
64
65    /// Returns the `updated_at` field.
66    #[inline]
67    fn updated_at(&self) -> DateTime {
68        DateTime::default()
69    }
70
71    /// Returns the `deleted_at` field.
72    #[inline]
73    fn deleted_at(&self) -> Option<DateTime> {
74        None
75    }
76
77    /// Returns the `version` field.
78    #[inline]
79    fn version(&self) -> u64 {
80        0
81    }
82
83    /// Returns the `edition` field.
84    #[inline]
85    fn edition(&self) -> u32 {
86        0
87    }
88
89    /// Returns a snapshot of the model.
90    fn snapshot(&self) -> Map {
91        let mut snapshot = Map::new();
92        snapshot.upsert(Self::PRIMARY_KEY_NAME, self.primary_key_value());
93        snapshot.upsert("name", self.name());
94        snapshot.upsert("status", self.status());
95        snapshot.upsert("updated_at", self.updated_at());
96        snapshot.upsert("version", self.version());
97        snapshot
98    }
99
100    /// Returns `true` if the `name` is nonempty.
101    #[inline]
102    fn has_name(&self) -> bool {
103        !self.name().is_empty()
104    }
105
106    /// Returns `true` if `self` has the namespace prefix.
107    #[inline]
108    fn has_namespace_prefix(&self, namespace: &str) -> bool {
109        self.namespace()
110            .strip_prefix(namespace)
111            .is_some_and(|s| s.is_empty() || s.starts_with(':'))
112    }
113
114    /// Returns `true` if `self` has the namespace suffix.
115    #[inline]
116    fn has_namespace_suffix(&self, namespace: &str) -> bool {
117        self.namespace()
118            .strip_suffix(namespace)
119            .is_some_and(|s| s.is_empty() || s.ends_with(':'))
120    }
121
122    /// Returns `true` if the model has the specific visibility.
123    #[inline]
124    fn has_visibility(&self, visibility: &str) -> bool {
125        self.visibility().eq_ignore_ascii_case(visibility)
126    }
127
128    /// Returns `true` if the `visibility` is `Public`.
129    #[inline]
130    fn is_public(&self) -> bool {
131        self.visibility().eq_ignore_ascii_case("Public")
132    }
133
134    /// Returns `true` if the `visibility` is `Internal`.
135    #[inline]
136    fn is_internal(&self) -> bool {
137        self.visibility().eq_ignore_ascii_case("Internal")
138    }
139
140    /// Returns `true` if the `visibility` is `Protected`.
141    #[inline]
142    fn is_protected(&self) -> bool {
143        self.visibility().eq_ignore_ascii_case("Protected")
144    }
145
146    /// Returns `true` if the `visibility` is `Private`.
147    #[inline]
148    fn is_private(&self) -> bool {
149        self.visibility().eq_ignore_ascii_case("Private")
150    }
151
152    /// Returns `true` if the model has the specific status.
153    #[inline]
154    fn has_status(&self, status: &str) -> bool {
155        self.status().eq_ignore_ascii_case(status)
156    }
157
158    /// Returns `true` if the `status` is `Active`.
159    #[inline]
160    fn is_active(&self) -> bool {
161        self.status().eq_ignore_ascii_case("Active")
162    }
163
164    /// Returns `true` if the `status` is `Inactive`.
165    #[inline]
166    fn is_inactive(&self) -> bool {
167        self.status().eq_ignore_ascii_case("Inactive")
168    }
169
170    /// Returns `true` if the `status` is `Locked`.
171    #[inline]
172    fn is_locked(&self) -> bool {
173        self.status().eq_ignore_ascii_case("Locked")
174    }
175
176    /// Returns `true` if the `status` is `Deleted`.
177    #[inline]
178    fn is_deleted(&self) -> bool {
179        self.status().eq_ignore_ascii_case("Deleted")
180    }
181
182    /// Returns `true` if the `status` is `Archived`.
183    #[inline]
184    fn is_archived(&self) -> bool {
185        self.status().eq_ignore_ascii_case("Archived")
186    }
187
188    /// Returns `true` if the `description` is nonempty.
189    #[inline]
190    fn has_description(&self) -> bool {
191        !self.description().is_empty()
192    }
193
194    /// Returns a reference to the value corresponding to the key in `extra`.
195    #[inline]
196    fn get_extra_value(&self, key: &str) -> Option<&JsonValue> {
197        self.extra()?.get(key)
198    }
199
200    /// Returns the next version for the model.
201    #[inline]
202    fn next_version(&self) -> u64 {
203        self.version() + 1
204    }
205
206    /// Constructs the query filters for the model of the current version.
207    fn current_version_filters(&self) -> Map {
208        let mut filters = Map::new();
209        filters.upsert(Self::PRIMARY_KEY_NAME, self.id().to_string());
210        filters.upsert("version", self.version());
211        filters
212    }
213
214    /// Constructs the `Query` for the model of the current version.
215    fn current_version_query(&self) -> Query {
216        let mut query = Self::default_query();
217        query.append_filters(&mut self.current_version_filters());
218        query
219    }
220
221    /// Constructs the query filters for the model of the next version.
222    fn next_version_filters(&self) -> Map {
223        let mut filters = Map::new();
224        filters.upsert(Self::PRIMARY_KEY_NAME, self.id().to_string());
225        filters.upsert("version", self.next_version());
226        filters
227    }
228
229    /// Constructs the mutation updates for the model of the next version.
230    fn next_version_updates(&self) -> Map {
231        let mut updates = Map::new();
232        updates.upsert("updated_at", DateTime::now().into_sql_value());
233        updates.upsert("version", self.next_version());
234        updates
235    }
236
237    /// Constructs the `Mutation` for the model of the next version.
238    fn next_version_mutation(&self, updates: &mut Map) -> Mutation {
239        let mut mutation = Self::default_mutation();
240        mutation.append_updates(updates);
241        mutation.append_updates(&mut self.next_version_updates());
242        mutation
243    }
244
245    /// Returns the next edition for the model.
246    #[inline]
247    fn next_edition(&self) -> u32 {
248        self.edition() + 1
249    }
250
251    /// Constructs the query filters for the model of the current edition.
252    fn current_edition_filters(&self) -> Map {
253        let mut filters = Map::new();
254        filters.upsert(Self::PRIMARY_KEY_NAME, self.id().to_string());
255        filters.upsert("edition", self.edition());
256        filters
257    }
258
259    /// Constructs the `Query` for the model of the current edition.
260    fn current_edition_query(&self) -> Query {
261        let mut query = Self::default_query();
262        query.append_filters(&mut self.current_edition_filters());
263        query
264    }
265
266    /// Constructs the query filters for the model of the next edition.
267    fn next_edition_filters(&self) -> Map {
268        let mut filters = Map::new();
269        filters.upsert(Self::PRIMARY_KEY_NAME, self.id().to_string());
270        filters.upsert("edition", self.next_edition());
271        filters
272    }
273
274    /// Constructs the mutation updates for the model of the next edition.
275    fn next_edition_updates(&self) -> Map {
276        let mut updates = Map::new();
277        updates.upsert("updated_at", DateTime::now().into_sql_value());
278        updates.upsert("version", self.next_version());
279        updates.upsert("edition", self.next_edition());
280        updates
281    }
282
283    /// Constructs the `Mutation` for the model of the next edition.
284    fn next_edition_mutation(&self, updates: &mut Map) -> Mutation {
285        let mut mutation = Self::default_mutation();
286        mutation.append_updates(updates);
287        mutation.append_updates(&mut self.next_edition_updates());
288        mutation
289    }
290
291    /// Constructs a `Mutation` for logically deleting the model.
292    fn soft_delete_mutation(&self) -> Mutation {
293        let mut mutation = Self::default_mutation();
294        let mut updates = self.next_edition_updates();
295        updates.upsert("status", "Deleted");
296        mutation.append_updates(&mut updates);
297        mutation
298    }
299
300    /// Constructs a `Mutation` for locking the model.
301    fn lock_mutation(&self) -> Mutation {
302        let mut mutation = Self::default_mutation();
303        let mut updates = self.next_edition_updates();
304        updates.upsert("status", "Locked");
305        mutation.append_updates(&mut updates);
306        mutation
307    }
308
309    /// Constructs a `Mutation` for archiving the model.
310    fn archive_mutation(&self) -> Mutation {
311        let mut mutation = Self::default_mutation();
312        let mut updates = self.next_edition_updates();
313        updates.upsert("status", "Archived");
314        mutation.append_updates(&mut updates);
315        mutation
316    }
317
318    /// Constructs a default snapshot `Query` for the model.
319    fn default_snapshot_query() -> Query {
320        let mut query = Query::default();
321        let fields = [
322            Self::PRIMARY_KEY_NAME,
323            "name",
324            "status",
325            "updated_at",
326            "version",
327        ];
328        query.allow_fields(&fields);
329        query.deny_fields(Self::write_only_fields());
330        query
331    }
332
333    /// Constructs a default list `Query` for the model.
334    fn default_list_query() -> Query {
335        let mut query = Query::default();
336        let ignored_fields = [Self::write_only_fields(), &["extra"]].concat();
337        query.allow_fields(Self::fields());
338        query.deny_fields(&ignored_fields);
339        query.add_filter("status", Map::from_entry("$ne", "Deleted"));
340        query.order_desc("updated_at");
341        query
342    }
343
344    /// Checks the constraints for the model.
345    async fn check_constraints(&self) -> Result<Validation, Error> {
346        let mut validation = Validation::new();
347        if self.id() == &K::default() {
348            validation.record(Self::PRIMARY_KEY_NAME, "should not be a default value");
349        }
350        Ok(validation)
351    }
352
353    /// Fetches the data of models seleted by the `Query`.
354    async fn fetch(query: &Query) -> Result<Vec<Map>, Error> {
355        let mut models = Self::find(query).await?;
356        let translate_enabled = query.translate_enabled();
357        for model in models.iter_mut() {
358            Self::after_decode(model).await?;
359            translate_enabled.then(|| Self::translate_model(model));
360        }
361        Ok(models)
362    }
363
364    /// Fetches the data of a model seleted by the primary key.
365    async fn fetch_by_id(id: &K) -> Result<Map, Error> {
366        let mut model = Self::find_by_id::<Map>(id)
367            .await?
368            .ok_or_else(|| warn!("404 Not Found: cannot find the model `{}`", id))?;
369        Self::translate_model(&mut model);
370        Self::after_decode(&mut model).await?;
371        Ok(model)
372    }
373
374    /// Deletes a model of the primary key by setting the status as `Deleted`.
375    async fn soft_delete_by_id(id: &K) -> Result<(), Error> {
376        let mut model = Self::try_get_model(id).await?;
377        let model_data = model.before_soft_delete().await?;
378
379        let query = model.current_version_query();
380        let mut mutation = model.soft_delete_mutation();
381        let ctx = Self::update_one(&query, &mut mutation).await?;
382        Self::after_soft_delete(&ctx, model_data).await?;
383        Ok(())
384    }
385
386    /// Locks a model of the primary key by setting the status as `Locked`.
387    async fn lock_by_id(id: &K) -> Result<(), Error> {
388        let mut model = Self::try_get_model(id).await?;
389        let model_data = model.before_lock().await?;
390
391        let query = model.current_version_query();
392        let mut mutation = model.lock_mutation();
393        let ctx = Self::update_one(&query, &mut mutation).await?;
394        Self::after_lock(&ctx, model_data).await?;
395        Ok(())
396    }
397
398    /// Archives a model of the primary key by setting the status as `Archived`.
399    async fn archive_by_id(id: &K) -> Result<(), Error> {
400        let mut model = Self::try_get_model(id).await?;
401        let model_data = model.before_archive().await?;
402
403        let query = model.current_version_query();
404        let mut mutation = model.archive_mutation();
405        let ctx = Self::update_one(&query, &mut mutation).await?;
406        Self::after_archive(&ctx, model_data).await?;
407        Ok(())
408    }
409
410    /// Mutates a model of the primary key with the JSON data and the optional extension.
411    async fn mutate_by_id(
412        id: &K,
413        data: &mut Map,
414        extension: Option<<Self as ModelHooks>::Extension>,
415    ) -> Result<(Validation, Self), Error> {
416        Self::before_extract().await?;
417
418        let mut model = Self::try_get_model(id).await?;
419        let version = model.version();
420        if data.get_u64("version").is_some_and(|v| version != v) {
421            bail!(
422                "409 Conflict: there is a version conflict for the model `{}`",
423                id
424            );
425        }
426        Self::before_validation(data, extension.as_ref()).await?;
427
428        let validation = model.read_map(data);
429        if !validation.is_success() {
430            return Ok((validation, model));
431        }
432        if let Some(extension) = extension {
433            model.after_extract(extension).await?;
434        }
435
436        let validation = model.check_constraints().await?;
437        if !validation.is_success() {
438            return Ok((validation, model));
439        }
440        if model.is_deleted() {
441            data.retain(|key, _value| key == "status");
442        } else if model.is_locked() {
443            data.retain(|key, _value| key == "visibility" || key == "status");
444        } else if model.is_archived() {
445            bail!("403 Forbidden: archived model `{}` can not be modified", id);
446        }
447        model.after_validation(data).await?;
448
449        let query = model.current_version_query();
450        let mut mutation = model.next_version_mutation(data);
451
452        let model_data = model.before_update().await?;
453        let ctx = Self::update_one(&query, &mut mutation).await?;
454        if ctx.rows_affected() != Some(1) {
455            bail!(
456                "404 Not Found: there is no version `{}` for the model `{}`",
457                version,
458                id,
459            );
460        }
461        Self::after_update(&ctx, model_data).await?;
462        Ok((validation, model))
463    }
464
465    /// Generates random associations for the model.
466    async fn random_associations() -> Result<Map, Error> {
467        let mut associations = Map::new();
468        let table_name = Self::table_name();
469        for col in Self::columns() {
470            if col.reference().is_some_and(|r| r.name() == table_name) {
471                let col_name = col.name();
472                let size = col.random_size();
473                let values = Self::sample(size).await?;
474                if col.is_array_type() {
475                    associations.upsert(col_name, values);
476                } else {
477                    associations.upsert(col_name, values.first().cloned());
478                }
479            }
480        }
481        Ok(associations)
482    }
483
484    /// Attempts to generate a mocked model.
485    async fn mock() -> Result<(Validation, Self), Error> {
486        let mut data = Self::before_mock().await?;
487        let mut associations = Self::random_associations().await?;
488        data.append(&mut associations);
489        for col in Self::columns() {
490            if !col.has_attribute("constructor") {
491                let value = col.mock_value();
492                if !value.is_ignorable() {
493                    data.upsert(col.name(), value);
494                }
495            }
496        }
497        Self::before_validation(&mut data, None).await?;
498
499        let mut model = Self::new();
500        let validation = model.read_map(&data);
501        if !validation.is_success() {
502            return Ok((validation, model));
503        }
504
505        let validation = model.check_constraints().await?;
506        if !validation.is_success() {
507            return Ok((validation, model));
508        }
509        model.after_validation(&mut data).await?;
510        model.after_mock().await?;
511        Ok((validation, model))
512    }
513}