Skip to main content

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}