Skip to main content

stateset_embedded/
custom_objects.rs

1//! Custom Objects (custom states / metaobjects) operations
2
3use stateset_core::{
4    CreateCustomObject, CreateCustomObjectType, CustomObject, CustomObjectFilter, CustomObjectType,
5    CustomObjectTypeFilter, Result, UpdateCustomObject, UpdateCustomObjectType,
6};
7use stateset_db::Database;
8use std::sync::Arc;
9use uuid::Uuid;
10
11#[cfg(feature = "events")]
12use crate::events::EventSystem;
13#[cfg(feature = "events")]
14use stateset_core::CommerceEvent;
15
16/// Custom object operations interface.
17pub struct CustomObjects {
18    db: Arc<dyn Database>,
19    #[cfg(feature = "events")]
20    event_system: Arc<EventSystem>,
21}
22
23impl std::fmt::Debug for CustomObjects {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        f.debug_struct("CustomObjects").finish_non_exhaustive()
26    }
27}
28
29impl CustomObjects {
30    #[cfg(feature = "events")]
31    pub(crate) fn new(db: Arc<dyn Database>, event_system: Arc<EventSystem>) -> Self {
32        Self { db, event_system }
33    }
34
35    #[cfg(not(feature = "events"))]
36    pub(crate) fn new(db: Arc<dyn Database>) -> Self {
37        Self { db }
38    }
39
40    #[cfg(feature = "events")]
41    fn emit(&self, event: CommerceEvent) {
42        self.event_system.emit(event);
43    }
44
45    #[cfg(feature = "events")]
46    fn type_fields_changed(input: &UpdateCustomObjectType) -> Vec<String> {
47        let mut fields = Vec::new();
48        if input.display_name.is_some() {
49            fields.push("display_name".to_string());
50        }
51        if input.description.is_some() {
52            fields.push("description".to_string());
53        }
54        if input.fields.is_some() {
55            fields.push("fields".to_string());
56        }
57        fields
58    }
59
60    #[cfg(feature = "events")]
61    fn object_fields_changed(input: &UpdateCustomObject) -> Vec<String> {
62        let mut fields = Vec::new();
63        if input.handle.is_some() {
64            fields.push("handle".to_string());
65        }
66        if input.owner_type.is_some() || input.owner_id.is_some() {
67            fields.push("owner".to_string());
68        }
69        if input.values.is_some() {
70            fields.push("values".to_string());
71        }
72        fields
73    }
74
75    // ------------------------------------------------------------------------
76    // Types (schemas)
77    // ------------------------------------------------------------------------
78
79    pub fn create_type(&self, input: CreateCustomObjectType) -> Result<CustomObjectType> {
80        let ty = self.db.custom_objects().create_type(input)?;
81        #[cfg(feature = "events")]
82        {
83            self.emit(CommerceEvent::CustomObjectTypeCreated {
84                type_id: ty.id,
85                handle: ty.handle.clone(),
86                timestamp: ty.created_at,
87            });
88        }
89        Ok(ty)
90    }
91
92    pub fn get_type(&self, id: Uuid) -> Result<Option<CustomObjectType>> {
93        self.db.custom_objects().get_type(id)
94    }
95
96    pub fn get_type_by_handle(&self, handle: &str) -> Result<Option<CustomObjectType>> {
97        self.db.custom_objects().get_type_by_handle(handle)
98    }
99
100    pub fn update_type(&self, id: Uuid, input: UpdateCustomObjectType) -> Result<CustomObjectType> {
101        #[cfg(feature = "events")]
102        let fields_changed = Self::type_fields_changed(&input);
103
104        let updated = self.db.custom_objects().update_type(id, input)?;
105
106        #[cfg(feature = "events")]
107        if !fields_changed.is_empty() {
108            self.emit(CommerceEvent::CustomObjectTypeUpdated {
109                type_id: updated.id,
110                handle: updated.handle.clone(),
111                fields_changed,
112                timestamp: updated.updated_at,
113            });
114        }
115
116        Ok(updated)
117    }
118
119    pub fn list_types(&self, filter: CustomObjectTypeFilter) -> Result<Vec<CustomObjectType>> {
120        self.db.custom_objects().list_types(filter)
121    }
122
123    pub fn delete_type(&self, id: Uuid) -> Result<()> {
124        #[cfg(feature = "events")]
125        let ty = self.db.custom_objects().get_type(id)?;
126
127        self.db.custom_objects().delete_type(id)?;
128
129        #[cfg(feature = "events")]
130        if let Some(ty) = ty {
131            self.emit(CommerceEvent::CustomObjectTypeDeleted {
132                type_id: ty.id,
133                handle: ty.handle,
134                timestamp: chrono::Utc::now(),
135            });
136        }
137
138        Ok(())
139    }
140
141    // ------------------------------------------------------------------------
142    // Records
143    // ------------------------------------------------------------------------
144
145    pub fn create_object(&self, input: CreateCustomObject) -> Result<CustomObject> {
146        let obj = self.db.custom_objects().create_object(input)?;
147        #[cfg(feature = "events")]
148        {
149            self.emit(CommerceEvent::CustomObjectCreated {
150                object_id: obj.id,
151                type_handle: obj.type_handle.clone(),
152                owner_type: obj.owner_type.clone(),
153                owner_id: obj.owner_id.clone(),
154                timestamp: obj.created_at,
155            });
156        }
157        Ok(obj)
158    }
159
160    pub fn get_object(&self, id: Uuid) -> Result<Option<CustomObject>> {
161        self.db.custom_objects().get_object(id)
162    }
163
164    pub fn get_object_by_handle(
165        &self,
166        type_handle: &str,
167        object_handle: &str,
168    ) -> Result<Option<CustomObject>> {
169        self.db.custom_objects().get_object_by_handle(type_handle, object_handle)
170    }
171
172    pub fn update_object(&self, id: Uuid, input: UpdateCustomObject) -> Result<CustomObject> {
173        #[cfg(feature = "events")]
174        let fields_changed = Self::object_fields_changed(&input);
175
176        let updated = self.db.custom_objects().update_object(id, input)?;
177
178        #[cfg(feature = "events")]
179        if !fields_changed.is_empty() {
180            self.emit(CommerceEvent::CustomObjectUpdated {
181                object_id: updated.id,
182                type_handle: updated.type_handle.clone(),
183                fields_changed,
184                timestamp: updated.updated_at,
185            });
186        }
187
188        Ok(updated)
189    }
190
191    pub fn list_objects(&self, filter: CustomObjectFilter) -> Result<Vec<CustomObject>> {
192        self.db.custom_objects().list_objects(filter)
193    }
194
195    pub fn delete_object(&self, id: Uuid) -> Result<()> {
196        #[cfg(feature = "events")]
197        let obj = self.db.custom_objects().get_object(id)?;
198
199        self.db.custom_objects().delete_object(id)?;
200
201        #[cfg(feature = "events")]
202        if let Some(obj) = obj {
203            self.emit(CommerceEvent::CustomObjectDeleted {
204                object_id: obj.id,
205                type_handle: obj.type_handle,
206                timestamp: chrono::Utc::now(),
207            });
208        }
209
210        Ok(())
211    }
212}