1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use super::{Commerce, CommerceBackend};
use chrono::{DateTime, Utc};
use stateset_core::OrderFilter;
use stateset_db::Database;
use stateset_observability::{Metrics, MetricsSnapshot};
#[cfg(all(feature = "sqlite", feature = "vector"))]
use stateset_core::CommerceError;
#[cfg(all(feature = "sqlite", feature = "vector"))]
use crate::Vector;
/// Runtime health status for a [`Commerce`] engine instance.
#[derive(Debug, Clone)]
pub struct CommerceHealth {
/// Whether the engine is healthy at the time of the check.
pub healthy: bool,
/// Active database backend.
pub backend: CommerceBackend,
/// Whether a basic database probe succeeded.
pub database_reachable: bool,
/// Error details from the last database probe, if any.
pub database_error: Option<String>,
/// Point-in-time metrics snapshot.
pub metrics: MetricsSnapshot,
/// Number of active event subscribers (when `events` is enabled).
#[cfg(feature = "events")]
pub event_subscribers: usize,
/// Check timestamp in UTC.
pub checked_at: DateTime<Utc>,
}
impl Commerce {
/// Get the underlying database (for advanced use cases).
pub fn database(&self) -> &dyn Database {
&*self.db
}
/// Get the active database backend kind.
pub const fn backend(&self) -> CommerceBackend {
self.backend
}
/// Access engine metrics handle.
pub const fn metrics(&self) -> &Metrics {
&self.metrics
}
/// Capture a point-in-time metrics snapshot.
pub fn metrics_snapshot(&self) -> MetricsSnapshot {
self.metrics.snapshot()
}
/// Run a lightweight engine health check.
///
/// The database probe uses `orders().count(Default::default())` and does not
/// mutate state.
pub fn health_check(&self) -> CommerceHealth {
let probe = self.db.orders().count(OrderFilter::default());
let metrics = self.metrics_snapshot();
CommerceHealth {
healthy: probe.is_ok(),
backend: self.backend,
database_reachable: probe.is_ok(),
database_error: probe.err().map(|e| e.to_string()),
metrics,
#[cfg(feature = "events")]
event_subscribers: self.event_system.subscriber_count(),
checked_at: Utc::now(),
}
}
/// Access vector search operations.
///
/// Requires the `vector` feature and an OpenAI API key for embedding generation.
///
/// # Arguments
///
/// * `api_key` - OpenAI API key for generating embeddings
///
/// # Example
///
/// ```rust,ignore
/// use stateset_embedded::Commerce;
///
/// let commerce = Commerce::new("./store.db")?;
/// let api_key = std::env::var("OPENAI_API_KEY")?;
///
/// let vector = commerce.vector(api_key)?;
///
/// // Index products for search
/// for product in commerce.products().list(Default::default())? {
/// vector.index_product(&product)?;
/// }
///
/// // Semantic search
/// let results = vector.search_products("wireless bluetooth headphones", 10)?;
/// for result in results {
/// println!("{}: {} (score: {:.2})", result.entity.name, result.entity.id, result.score);
/// }
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[cfg(all(feature = "sqlite", feature = "vector"))]
pub fn vector(&self, api_key: String) -> Result<Vector, CommerceError> {
match &self.sqlite_db {
Some(db) => Ok(Vector::new(db.vector(), api_key)),
None => Err(CommerceError::NotPermitted(
"Vector search requires SQLite database. Use Commerce::new() instead of with_database() or with_postgres().".to_string()
)),
}
}
}