use stateset_core::{
AddressType, CreateCustomer, CreateCustomerAddress, Customer, CustomerAddress, CustomerFilter,
CustomerId, Result, UpdateCustomer,
};
use stateset_db::Database;
use stateset_observability::Metrics;
use std::sync::Arc;
use uuid::Uuid;
#[cfg(feature = "events")]
use crate::events::EventSystem;
#[cfg(feature = "events")]
use stateset_core::CommerceEvent;
pub struct Customers {
db: Arc<dyn Database>,
metrics: Metrics,
#[cfg(feature = "events")]
event_system: Arc<EventSystem>,
}
impl std::fmt::Debug for Customers {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Customers").finish_non_exhaustive()
}
}
impl Customers {
#[cfg(feature = "events")]
pub(crate) fn new(
db: Arc<dyn Database>,
event_system: Arc<EventSystem>,
metrics: Metrics,
) -> Self {
Self { db, metrics, event_system }
}
#[cfg(not(feature = "events"))]
pub(crate) fn new(db: Arc<dyn Database>, metrics: Metrics) -> Self {
Self { db, metrics }
}
#[cfg(feature = "events")]
fn emit(&self, event: CommerceEvent) {
self.event_system.emit(event);
}
#[cfg(feature = "events")]
fn fields_changed(input: &UpdateCustomer) -> Vec<String> {
let mut fields = Vec::new();
if input.email.is_some() {
fields.push("email".to_string());
}
if input.first_name.is_some() {
fields.push("first_name".to_string());
}
if input.last_name.is_some() {
fields.push("last_name".to_string());
}
if input.phone.is_some() {
fields.push("phone".to_string());
}
if input.status.is_some() {
fields.push("status".to_string());
}
if input.accepts_marketing.is_some() {
fields.push("accepts_marketing".to_string());
}
if input.tags.is_some() {
fields.push("tags".to_string());
}
if input.metadata.is_some() {
fields.push("metadata".to_string());
}
fields
}
pub fn create(&self, input: CreateCustomer) -> Result<Customer> {
let customer = self.db.customers().create(input)?;
self.metrics.record_customer_created(&customer.id.to_string());
#[cfg(feature = "events")]
{
self.emit(CommerceEvent::CustomerCreated {
customer_id: customer.id,
email: customer.email.clone(),
timestamp: customer.created_at,
});
}
Ok(customer)
}
pub fn get(&self, id: CustomerId) -> Result<Option<Customer>> {
self.db.customers().get(id)
}
pub fn get_by_email(&self, email: &str) -> Result<Option<Customer>> {
self.db.customers().get_by_email(email)
}
pub fn update(&self, id: CustomerId, input: UpdateCustomer) -> Result<Customer> {
#[cfg(feature = "events")]
let previous = self.db.customers().get(id)?;
#[cfg(feature = "events")]
let fields_changed = Self::fields_changed(&input);
let updated = self.db.customers().update(id, input)?;
#[cfg(feature = "events")]
{
if !fields_changed.is_empty() {
self.emit(CommerceEvent::CustomerUpdated {
customer_id: updated.id,
fields_changed,
timestamp: updated.updated_at,
});
}
if let Some(previous) = previous {
if previous.status != updated.status {
self.emit(CommerceEvent::CustomerStatusChanged {
customer_id: updated.id,
from_status: previous.status,
to_status: updated.status,
timestamp: updated.updated_at,
});
}
}
}
Ok(updated)
}
pub fn list(&self, filter: CustomerFilter) -> Result<Vec<Customer>> {
self.db.customers().list(filter)
}
pub fn delete(&self, id: CustomerId) -> Result<()> {
self.db.customers().delete(id)
}
pub fn add_address(&self, input: CreateCustomerAddress) -> Result<CustomerAddress> {
let address = self.db.customers().add_address(input)?;
#[cfg(feature = "events")]
{
self.emit(CommerceEvent::CustomerAddressAdded {
customer_id: address.customer_id,
address_id: address.id,
timestamp: address.created_at,
});
}
Ok(address)
}
pub fn get_addresses(&self, customer_id: CustomerId) -> Result<Vec<CustomerAddress>> {
self.db.customers().get_addresses(customer_id)
}
pub fn update_address(
&self,
address_id: Uuid,
input: CreateCustomerAddress,
) -> Result<CustomerAddress> {
self.db.customers().update_address(address_id, input)
}
pub fn delete_address(&self, address_id: Uuid) -> Result<()> {
self.db.customers().delete_address(address_id)
}
pub fn set_default_address(
&self,
customer_id: CustomerId,
address_id: Uuid,
address_type: AddressType,
) -> Result<()> {
self.db.customers().set_default_address(customer_id, address_id, address_type)
}
pub fn count(&self, filter: CustomerFilter) -> Result<u64> {
self.db.customers().count(filter)
}
pub fn find_or_create(&self, input: CreateCustomer) -> Result<Customer> {
if let Some(customer) = self.get_by_email(&input.email)? {
Ok(customer)
} else {
self.create(input)
}
}
}