zino_core/model/
mutation.rs

1use crate::{
2    JsonValue, Map,
3    extension::{JsonObjectExt, JsonValueExt},
4    validation::Validation,
5};
6
7/// A mutation type for models.
8#[derive(Debug, Clone, Default)]
9pub struct Mutation {
10    /// Editable fields.
11    fields: Vec<String>,
12    /// Updates.
13    updates: Map,
14}
15
16impl Mutation {
17    /// Creates a new instance.
18    #[inline]
19    pub fn new(updates: impl Into<JsonValue>) -> Self {
20        Self {
21            fields: Vec::new(),
22            updates: updates.into().into_map_opt().unwrap_or_default(),
23        }
24    }
25
26    /// Creates a new instance with the entry.
27    #[inline]
28    pub fn from_entry(key: impl Into<String>, value: impl Into<JsonValue>) -> Self {
29        Self::new(Map::from_entry(key, value))
30    }
31
32    /// Updates the mutation using the json object and returns the validation result.
33    #[must_use]
34    pub fn read_map(&mut self, data: &Map) -> Validation {
35        let mut validation = Validation::new();
36        let updates = &mut self.updates;
37        for (key, value) in data {
38            match key.as_str() {
39                "fields" => {
40                    if let Some(fields) = value.parse_str_array() {
41                        if fields.is_empty() {
42                            validation.record("fields", "must be nonempty");
43                        } else {
44                            self.fields.clear();
45                            self.fields.extend(fields.into_iter().map(|s| s.to_owned()));
46                        }
47                    }
48                }
49                _ => {
50                    if !key.starts_with('$') && value != "" {
51                        updates.insert(key.to_owned(), value.to_owned());
52                    }
53                }
54            }
55        }
56        validation
57    }
58
59    /// Retains the editable fields in the allow list.
60    /// If the editable fields are empty, it will be set to the list.
61    #[inline]
62    pub fn allow_fields(&mut self, fields: &[&str]) {
63        if self.fields.is_empty() {
64            self.fields.extend(fields.iter().map(|&key| key.to_owned()));
65        } else {
66            self.fields
67                .retain(|field| fields.iter().any(|key| field == key))
68        }
69    }
70
71    /// Removes the editable fields in the deny list.
72    #[inline]
73    pub fn deny_fields(&mut self, fields: &[&str]) {
74        self.fields
75            .retain(|field| !fields.iter().any(|key| field == key))
76    }
77
78    /// Adds a key-value pair to the mutation updates.
79    #[inline]
80    pub fn add_update(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) {
81        self.updates.upsert(key, value);
82    }
83
84    /// Moves all elements from the `updates` into `self`.
85    #[inline]
86    pub fn append_updates(&mut self, updates: &mut Map) {
87        self.updates.append(updates);
88    }
89
90    /// Returns a reference to the editable fields.
91    #[inline]
92    pub fn fields(&self) -> &[String] {
93        self.fields.as_slice()
94    }
95
96    /// Returns a reference to the mutation updates.
97    #[inline]
98    pub fn updates(&self) -> &Map {
99        &self.updates
100    }
101}