cognite/dto/
patch_item.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use serde_with::skip_serializing_none;
5
6use crate::{EqIdentity, Identity};
7
8/// Options for performing automatic upserts.
9#[derive(Clone, Copy, Debug, Default)]
10#[non_exhaustive]
11pub struct UpsertOptions {
12    /// Ignore null values of fields when updating. If this is `false` (the default),
13    /// missing values will be set to `null`.
14    pub ignore_nulls: bool,
15    /// Merge maps when updating. If this is `false` (the default),
16    /// maps (like metadata) will be overwritten.
17    pub merge_maps: bool,
18    /// Merge lists when updating. If this is `false` (the default),
19    /// lists will be overwritten.
20    pub merge_lists: bool,
21}
22
23impl UpsertOptions {
24    /// Ignore null values of fields when updating. If this is `false` (the default),
25    /// missing values will be set to `null`.
26    pub fn ignore_nulls(mut self, ignore_nulls: bool) -> Self {
27        self.ignore_nulls = ignore_nulls;
28        self
29    }
30
31    /// Merge metadata maps when updating. If this is `false` (the default),
32    /// metadata will be overwritten.
33    pub fn merge_metadata(mut self, merge_metadata: bool) -> Self {
34        self.merge_maps = merge_metadata;
35        self
36    }
37
38    /// Merge lists when updating. If this is `false` (the default),
39    /// lists will be overwritten.
40    pub fn merge_lists(mut self, merge_lists: bool) -> Self {
41        self.merge_lists = merge_lists;
42        self
43    }
44}
45
46/// Trait for converting a value into a patch item, used for upsert.
47pub trait IntoPatchItem<TPatch> {
48    /// Convert self into a patch, optionally ignoring null values.
49    fn patch(self, options: &UpsertOptions) -> Option<TPatch>;
50}
51
52/// Trait for converting a value into a patch, used for upsert.
53pub trait IntoPatch<TPatch> {
54    /// Convert self into a patch, optionally ignoring null values.
55    fn patch(self, options: &UpsertOptions) -> TPatch;
56}
57
58#[skip_serializing_none]
59#[derive(Serialize, Deserialize, Debug, Clone)]
60#[serde(untagged, rename_all_fields = "camelCase")]
61/// Update the value of an item, or set it to null.
62pub enum UpdateSetNull<T> {
63    /// Set a new value.
64    Set {
65        /// New value to set.
66        set: T,
67    },
68    /// Set the value to null.
69    SetNull {
70        /// Whether to set the value to null, or leave it unmodified.
71        set_null: bool,
72    },
73}
74
75impl<T> Default for UpdateSetNull<T> {
76    fn default() -> Self {
77        Self::SetNull { set_null: false }
78    }
79}
80
81impl<T> IntoPatchItem<UpdateSetNull<T>> for Option<T> {
82    fn patch(self, options: &UpsertOptions) -> Option<UpdateSetNull<T>> {
83        match (self, options.ignore_nulls) {
84            (None, true) => None,
85            (None, false) => Some(UpdateSetNull::SetNull { set_null: true }),
86            (Some(x), _) => Some(UpdateSetNull::Set { set: x }),
87        }
88    }
89}
90
91impl<T> UpdateSetNull<T> {
92    /// Set a new value
93    ///
94    /// # Arguments
95    ///
96    /// * `value` - Value to set.
97    pub fn set(value: T) -> Self {
98        Self::Set { set: value }
99    }
100
101    /// Set the value to null.
102    ///
103    /// # Arguments
104    ///
105    /// * `set_null` - Whether to set the value to null, or leave it unmodified.
106    pub fn set_null(set_null: bool) -> Self {
107        Self::SetNull { set_null }
108    }
109}
110
111#[skip_serializing_none]
112#[derive(Serialize, Deserialize, Debug, Clone)]
113#[serde(rename_all = "camelCase")]
114/// Update the value of an item.
115pub struct UpdateSet<T> {
116    /// New value of item.
117    pub set: T,
118}
119
120impl<T> UpdateSet<T> {
121    /// Set a new value
122    ///
123    /// # Arguments
124    ///
125    /// * `value` - Value to set.
126    pub fn set(value: T) -> Self {
127        Self { set: value }
128    }
129}
130
131impl<T> IntoPatchItem<UpdateSet<T>> for T {
132    fn patch(self, _options: &UpsertOptions) -> Option<UpdateSet<T>> {
133        Some(UpdateSet { set: self })
134    }
135}
136
137#[skip_serializing_none]
138#[derive(Serialize, Deserialize, Debug, Clone)]
139#[serde(untagged, rename_all_fields = "camelCase")]
140/// Update the value of a list item, adding and removing or setting the values.
141pub enum UpdateList<TAdd, TRemove> {
142    /// Add new values and remove old values.
143    AddRemove {
144        /// New values to add.
145        add: Option<Vec<TAdd>>,
146        /// Old values to remove.
147        remove: Option<Vec<TRemove>>,
148    },
149    /// Set the list to a new value.
150    Set {
151        /// New value for list.
152        set: Vec<TAdd>,
153    },
154}
155
156impl<TAdd, TRemove> UpdateList<TAdd, TRemove> {
157    /// Add items given by `add` and remove any given by `remove`
158    ///
159    /// # Arguments
160    ///
161    /// * `add` - New values to add.
162    /// * `remove` - Old values to remove.
163    pub fn add_remove(add: Vec<TAdd>, remove: Vec<TRemove>) -> Self {
164        Self::AddRemove {
165            add: Some(add),
166            remove: Some(remove),
167        }
168    }
169
170    /// Add items given by `add`, overwriting any that already exist.
171    ///
172    /// # Arguments
173    ///
174    /// * `add` - New values to add.
175    pub fn add(add: Vec<TAdd>) -> Self {
176        Self::AddRemove {
177            add: Some(add),
178            remove: None,
179        }
180    }
181
182    /// Remove items given by `remove`, if they exist.
183    ///
184    /// # Arguments
185    ///
186    /// * `remove` - Old values to remove.
187    pub fn remove(remove: Vec<TRemove>) -> Self {
188        Self::AddRemove {
189            add: None,
190            remove: Some(remove),
191        }
192    }
193
194    /// Set the list to `set`.
195    ///
196    /// # Arguments
197    ///
198    /// * `set` - New value for list.
199    pub fn set(set: Vec<TAdd>) -> Self {
200        Self::Set { set }
201    }
202}
203
204impl<TAdd, TRemove> IntoPatchItem<UpdateList<TAdd, TRemove>> for Vec<TAdd> {
205    fn patch(self, options: &UpsertOptions) -> Option<UpdateList<TAdd, TRemove>> {
206        if options.merge_lists {
207            Some(UpdateList::add(self))
208        } else {
209            Some(UpdateList::set(self))
210        }
211    }
212}
213
214impl<TAdd, TRemove> IntoPatchItem<UpdateList<TAdd, TRemove>> for Option<Vec<TAdd>> {
215    fn patch(self, options: &UpsertOptions) -> Option<UpdateList<TAdd, TRemove>> {
216        match (self, options.ignore_nulls, options.merge_lists) {
217            (Some(x), _, true) => Some(UpdateList::add(x)),
218            (Some(x), _, false) => Some(UpdateList::set(x)),
219            (None, false, false) => Some(UpdateList::set(vec![])),
220            (None, _, _) => None,
221        }
222    }
223}
224
225#[skip_serializing_none]
226#[derive(Serialize, Deserialize, Debug, Clone)]
227#[serde(untagged, rename_all_fields = "camelCase")]
228/// Update a map from `TKey` to `TValue`, adding and removing or setting the values.
229pub enum UpdateMap<TKey, TValue>
230where
231    TKey: std::hash::Hash + std::cmp::Eq,
232{
233    /// Add new values and remove existing keys.
234    AddRemove {
235        /// New values to add, any existing will be overwritten.
236        add: Option<HashMap<TKey, TValue>>,
237        /// Old keys to remove.
238        remove: Option<Vec<TKey>>,
239    },
240    /// Set the map to a new value.
241    Set {
242        /// New value for map.
243        set: HashMap<TKey, TValue>,
244    },
245}
246
247impl<TKey, TValue> UpdateMap<TKey, TValue>
248where
249    TKey: std::hash::Hash + std::cmp::Eq,
250{
251    /// Add items given by `add` and remove any given by `remove`
252    ///
253    /// # Arguments
254    ///
255    /// * `add` - New values to add, any existing will be overwritten.
256    /// * `remove` - Old keys to remove.
257    pub fn add_remove(add: HashMap<TKey, TValue>, remove: Vec<TKey>) -> Self {
258        Self::AddRemove {
259            add: Some(add),
260            remove: Some(remove),
261        }
262    }
263
264    /// Add items given by `add`, overwriting any that already exist.
265    ///
266    /// # Arguments
267    ///
268    /// * `add` - New values to add, any existing will be overwritten.
269    pub fn add(add: HashMap<TKey, TValue>) -> Self {
270        Self::AddRemove {
271            add: Some(add),
272            remove: None,
273        }
274    }
275
276    /// Remove items given by `remove`, if they exist.
277    ///
278    /// # Arguments
279    ///
280    /// * `remove` - Old keys to remove.
281    pub fn remove(remove: Vec<TKey>) -> Self {
282        Self::AddRemove {
283            add: None,
284            remove: Some(remove),
285        }
286    }
287
288    /// Set the map to `set`.
289    ///
290    /// # Arguments
291    ///
292    /// * `set` - New value for map.
293    pub fn set(set: HashMap<TKey, TValue>) -> Self {
294        Self::Set { set }
295    }
296}
297
298impl<TKey: std::hash::Hash + std::cmp::Eq, TValue> IntoPatchItem<UpdateMap<TKey, TValue>>
299    for HashMap<TKey, TValue>
300{
301    fn patch(self, options: &UpsertOptions) -> Option<UpdateMap<TKey, TValue>> {
302        if options.merge_maps {
303            Some(UpdateMap::add(self))
304        } else {
305            Some(UpdateMap::set(self))
306        }
307    }
308}
309
310impl<TKey: std::hash::Hash + std::cmp::Eq, TValue> IntoPatchItem<UpdateMap<TKey, TValue>>
311    for Option<HashMap<TKey, TValue>>
312{
313    fn patch(self, options: &UpsertOptions) -> Option<UpdateMap<TKey, TValue>> {
314        match (self, options.ignore_nulls, options.merge_lists) {
315            (Some(x), _, true) => Some(UpdateMap::add(x)),
316            (Some(x), _, false) => Some(UpdateMap::set(x)),
317            (None, false, false) => Some(UpdateMap::set(HashMap::new())),
318            (None, _, _) => None,
319        }
320    }
321}
322
323#[derive(Serialize, Deserialize, Debug, Default, Clone)]
324#[serde(rename_all = "camelCase")]
325/// Wrapper around a patch update.
326pub struct Patch<T>
327where
328    T: Default,
329{
330    #[serde(flatten)]
331    /// Identity of resource to update.
332    pub id: Identity,
333    /// Resource patch.
334    pub update: T,
335}
336
337impl<T> Patch<T>
338where
339    T: Default,
340{
341    /// Create a new patch
342    ///
343    /// # Arguments
344    ///
345    /// * `id` - Identity of resource to update.
346    pub fn new(id: Identity) -> Self {
347        Patch::<T> {
348            id,
349            update: T::default(),
350        }
351    }
352}
353
354impl<T> EqIdentity for Patch<T>
355where
356    T: Default,
357{
358    fn eq(&self, id: &Identity) -> bool {
359        &self.id == id
360    }
361}
362
363/// Macro to extract the identity from a resource.
364macro_rules! to_idt {
365    ($it:ident) => {
366        if $it.id > 0 {
367            Identity::Id { id: $it.id }
368        } else {
369            $it.external_id
370                .as_ref()
371                .map(|e| Identity::ExternalId {
372                    external_id: e.clone(),
373                })
374                .unwrap_or_else(|| Identity::Id { id: $it.id })
375        }
376    };
377}