Skip to main content

stateset_embedded/
customers.rs

1//! Customer operations
2
3use stateset_core::{
4    AddressType, CreateCustomer, CreateCustomerAddress, Customer, CustomerAddress, CustomerFilter,
5    CustomerId, Result, UpdateCustomer, Validate,
6};
7use stateset_db::Database;
8use stateset_observability::Metrics;
9use std::sync::Arc;
10use uuid::Uuid;
11
12#[cfg(feature = "events")]
13use crate::events::EventSystem;
14#[cfg(feature = "events")]
15use stateset_core::CommerceEvent;
16
17/// Customer operations interface.
18pub struct Customers {
19    db: Arc<dyn Database>,
20    metrics: Metrics,
21    #[cfg(feature = "events")]
22    event_system: Arc<EventSystem>,
23}
24
25impl std::fmt::Debug for Customers {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        f.debug_struct("Customers").finish_non_exhaustive()
28    }
29}
30
31impl Customers {
32    #[cfg(feature = "events")]
33    pub(crate) fn new(
34        db: Arc<dyn Database>,
35        event_system: Arc<EventSystem>,
36        metrics: Metrics,
37    ) -> Self {
38        Self { db, metrics, event_system }
39    }
40
41    #[cfg(not(feature = "events"))]
42    pub(crate) fn new(db: Arc<dyn Database>, metrics: Metrics) -> Self {
43        Self { db, metrics }
44    }
45
46    #[cfg(feature = "events")]
47    fn emit(&self, event: CommerceEvent) {
48        self.event_system.emit(event);
49    }
50
51    #[cfg(feature = "events")]
52    fn fields_changed(input: &UpdateCustomer) -> Vec<String> {
53        let mut fields = Vec::new();
54        if input.email.is_some() {
55            fields.push("email".to_string());
56        }
57        if input.first_name.is_some() {
58            fields.push("first_name".to_string());
59        }
60        if input.last_name.is_some() {
61            fields.push("last_name".to_string());
62        }
63        if input.phone.is_some() {
64            fields.push("phone".to_string());
65        }
66        if input.status.is_some() {
67            fields.push("status".to_string());
68        }
69        if input.accepts_marketing.is_some() {
70            fields.push("accepts_marketing".to_string());
71        }
72        if input.tags.is_some() {
73            fields.push("tags".to_string());
74        }
75        if input.metadata.is_some() {
76            fields.push("metadata".to_string());
77        }
78        fields
79    }
80
81    /// Create a new customer.
82    ///
83    /// # Example
84    ///
85    /// ```rust,no_run
86    /// # use stateset_embedded::*;
87    /// # let commerce = Commerce::new(":memory:")?;
88    /// let customer = commerce.customers().create(CreateCustomer {
89    ///     email: "alice@example.com".into(),
90    ///     first_name: "Alice".into(),
91    ///     last_name: "Smith".into(),
92    ///     phone: Some("+1-555-0123".into()),
93    ///     ..Default::default()
94    /// })?;
95    /// # Ok::<(), CommerceError>(())
96    /// ```
97    pub fn create(&self, input: CreateCustomer) -> Result<Customer> {
98        // Reject empty/malformed email or empty names before persisting.
99        input.validate()?;
100        let customer = self.db.customers().create(input)?;
101        self.metrics.record_customer_created(&customer.id.to_string());
102        #[cfg(feature = "events")]
103        {
104            self.emit(CommerceEvent::CustomerCreated {
105                customer_id: customer.id,
106                email: customer.email.clone(),
107                timestamp: customer.created_at,
108            });
109        }
110        Ok(customer)
111    }
112
113    /// Get a customer by ID.
114    pub fn get(&self, id: CustomerId) -> Result<Option<Customer>> {
115        self.db.customers().get(id)
116    }
117
118    /// Get a customer by email.
119    pub fn get_by_email(&self, email: &str) -> Result<Option<Customer>> {
120        self.db.customers().get_by_email(email)
121    }
122
123    /// Update a customer.
124    pub fn update(&self, id: CustomerId, input: UpdateCustomer) -> Result<Customer> {
125        #[cfg(feature = "events")]
126        let previous = self.db.customers().get(id)?;
127        #[cfg(feature = "events")]
128        let fields_changed = Self::fields_changed(&input);
129
130        let updated = self.db.customers().update(id, input)?;
131
132        #[cfg(feature = "events")]
133        {
134            if !fields_changed.is_empty() {
135                self.emit(CommerceEvent::CustomerUpdated {
136                    customer_id: updated.id,
137                    fields_changed,
138                    timestamp: updated.updated_at,
139                });
140            }
141
142            if let Some(previous) = previous {
143                if previous.status != updated.status {
144                    self.emit(CommerceEvent::CustomerStatusChanged {
145                        customer_id: updated.id,
146                        from_status: previous.status,
147                        to_status: updated.status,
148                        timestamp: updated.updated_at,
149                    });
150                }
151            }
152        }
153
154        Ok(updated)
155    }
156
157    /// List customers with optional filtering.
158    pub fn list(&self, filter: CustomerFilter) -> Result<Vec<Customer>> {
159        self.db.customers().list(filter)
160    }
161
162    /// Delete a customer (soft delete).
163    pub fn delete(&self, id: CustomerId) -> Result<()> {
164        self.db.customers().delete(id)
165    }
166
167    /// Add an address for a customer.
168    pub fn add_address(&self, input: CreateCustomerAddress) -> Result<CustomerAddress> {
169        // Reject empty required address fields before persisting.
170        input.validate()?;
171        let address = self.db.customers().add_address(input)?;
172        #[cfg(feature = "events")]
173        {
174            self.emit(CommerceEvent::CustomerAddressAdded {
175                customer_id: address.customer_id,
176                address_id: address.id,
177                timestamp: address.created_at,
178            });
179        }
180        Ok(address)
181    }
182
183    /// Get all addresses for a customer.
184    pub fn get_addresses(&self, customer_id: CustomerId) -> Result<Vec<CustomerAddress>> {
185        self.db.customers().get_addresses(customer_id)
186    }
187
188    /// Update an address.
189    pub fn update_address(
190        &self,
191        address_id: Uuid,
192        input: CreateCustomerAddress,
193    ) -> Result<CustomerAddress> {
194        self.db.customers().update_address(address_id, input)
195    }
196
197    /// Delete an address.
198    pub fn delete_address(&self, address_id: Uuid) -> Result<()> {
199        self.db.customers().delete_address(address_id)
200    }
201
202    /// Set a default address for a customer.
203    pub fn set_default_address(
204        &self,
205        customer_id: CustomerId,
206        address_id: Uuid,
207        address_type: AddressType,
208    ) -> Result<()> {
209        self.db.customers().set_default_address(customer_id, address_id, address_type)
210    }
211
212    /// Count customers matching a filter.
213    pub fn count(&self, filter: CustomerFilter) -> Result<u64> {
214        self.db.customers().count(filter)
215    }
216
217    /// Find or create a customer by email.
218    pub fn find_or_create(&self, input: CreateCustomer) -> Result<Customer> {
219        if let Some(customer) = self.get_by_email(&input.email)? {
220            Ok(customer)
221        } else {
222            self.create(input)
223        }
224    }
225}