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