aimdb_core/
database.rs

1//! Core database implementation
2//!
3//! `AimDb` is the central coordination point for AimDB, managing type-safe
4//! in-memory storage with type-safe records and data synchronization across
5//! MCU → edge → cloud environments.
6
7use crate::{AimDb, DbError, DbResult, RuntimeAdapter, RuntimeContext};
8use aimdb_executor::Spawn;
9use core::fmt::Debug;
10
11#[cfg(not(feature = "std"))]
12extern crate alloc;
13
14#[cfg(not(feature = "std"))]
15use alloc::boxed::Box;
16
17#[cfg(feature = "std")]
18use std::boxed::Box;
19
20/// AimDB Database implementation
21///
22/// Unified database combining runtime adapter management with type-safe record
23/// registration and producer-consumer patterns. See `examples/` for usage patterns.
24///
25/// This is a thin wrapper around `AimDb<R>` that adds adapter-specific functionality.
26/// Most users should use `AimDbBuilder` directly to create databases.
27pub struct Database<A: RuntimeAdapter + aimdb_executor::Spawn + 'static> {
28    adapter: A,
29    aimdb: AimDb<A>,
30}
31
32impl<A: RuntimeAdapter + aimdb_executor::Spawn + 'static> Database<A> {
33    /// Internal accessor for the AimDb instance
34    ///
35    /// Used by adapter crates. Should not be used by application code.
36    #[doc(hidden)]
37    pub fn inner_aimdb(&self) -> &AimDb<A> {
38        &self.aimdb
39    }
40
41    /// Creates a new database from adapter and AimDb
42    ///
43    /// # Arguments
44    /// * `adapter` - The runtime adapter
45    /// * `aimdb` - The configured AimDb instance
46    ///
47    /// Most users should use `AimDbBuilder` directly instead of this constructor.
48    pub fn new(adapter: A, aimdb: AimDb<A>) -> DbResult<Self> {
49        #[cfg(feature = "tracing")]
50        tracing::info!("Initializing unified database with typed records");
51
52        Ok(Self { adapter, aimdb })
53    }
54
55    /// Gets a reference to the runtime adapter
56    ///
57    /// # Example
58    /// ```rust,ignore
59    /// # use aimdb_core::Database;
60    /// # #[cfg(feature = "tokio-runtime")]
61    /// # {
62    /// # async fn example<A: aimdb_core::RuntimeAdapter>(db: Database<A>) {
63    /// let adapter = db.adapter();
64    /// // Use adapter directly
65    /// # }
66    /// # }
67    /// ```
68    pub fn adapter(&self) -> &A {
69        &self.adapter
70    }
71
72    /// Produces typed data to a specific record by key
73    ///
74    /// # Example
75    /// ```rust,ignore
76    /// # async fn example<A: aimdb_core::RuntimeAdapter>(db: aimdb_core::Database<A>) -> aimdb_core::DbResult<()> {
77    /// db.produce("sensor.temp", SensorData { temp: 23.5 }).await?;
78    /// # Ok(())
79    /// # }
80    /// ```
81    pub async fn produce<T>(&self, key: impl AsRef<str>, data: T) -> DbResult<()>
82    where
83        T: Send + 'static + Clone + core::fmt::Debug,
84    {
85        self.aimdb.produce(key, data).await
86    }
87
88    /// Subscribes to a record by key
89    ///
90    /// Creates a subscription to the configured buffer for the given record key.
91    /// Returns a boxed reader for receiving values asynchronously.
92    ///
93    /// # Example
94    /// ```rust,ignore
95    /// # async fn example<A: aimdb_core::RuntimeAdapter>(db: aimdb_core::Database<A>) -> aimdb_core::DbResult<()> {
96    /// let mut reader = db.subscribe::<SensorData>("sensor.temp")?;
97    ///
98    /// loop {
99    ///     match reader.recv().await {
100    ///         Ok(data) => println!("Received: {:?}", data),
101    ///         Err(e) => {
102    ///             eprintln!("Error: {:?}", e);
103    ///             break;
104    ///         }
105    ///     }
106    /// }
107    /// # Ok(())
108    /// # }
109    /// ```
110    pub fn subscribe<T>(
111        &self,
112        key: impl AsRef<str>,
113    ) -> DbResult<Box<dyn crate::buffer::BufferReader<T> + Send>>
114    where
115        T: Send + Sync + 'static + Debug + Clone,
116    {
117        self.aimdb.subscribe(key)
118    }
119
120    /// Creates a RuntimeContext for this database
121    ///
122    /// Provides services with access to runtime capabilities (timing, logging) plus the emitter.
123    ///
124    /// # Example
125    /// ```rust,ignore
126    /// # use aimdb_core::Database;
127    /// # #[cfg(feature = "tokio-runtime")]
128    /// # {
129    /// # async fn example<A: aimdb_executor::Runtime + Clone>(db: Database<A>) {
130    /// let ctx = db.context();
131    /// // Pass ctx to services
132    /// # }
133    /// # }
134    /// ```
135    pub fn context(&self) -> RuntimeContext<A>
136    where
137        A: aimdb_executor::Runtime + Clone,
138    {
139        #[cfg(feature = "std")]
140        {
141            RuntimeContext::from_arc(std::sync::Arc::new(self.adapter.clone()))
142        }
143        #[cfg(not(feature = "std"))]
144        {
145            // For no_std, we need a static reference - this would typically be handled
146            // by the caller storing the adapter in a static cell first
147            // For now, we'll document this limitation
148            panic!("context() not supported in no_std without a static reference. To use context(), store your adapter in a static cell (e.g., StaticCell from portable-atomic or embassy-sync), or use adapter() directly.")
149        }
150    }
151}
152
153// Spawn implementation for databases with spawn-capable adapters
154impl<A> Database<A>
155where
156    A: RuntimeAdapter + Spawn,
157{
158    /// Spawns a service on the database's runtime
159    ///
160    /// # Example
161    /// ```rust,ignore
162    /// # use aimdb_core::Database;
163    /// # use aimdb_executor::{Runtime, Spawn};
164    /// # #[cfg(feature = "tokio-runtime")]
165    /// # {
166    /// async fn my_service<R: Runtime>(ctx: aimdb_core::RuntimeContext<R>) -> aimdb_core::DbResult<()> {
167    ///     // Service implementation
168    ///     Ok(())
169    /// }
170    ///
171    /// # async fn example<A: Runtime + Spawn>(db: Database<A>) -> aimdb_core::DbResult<()> {
172    /// let ctx = db.context();
173    /// db.spawn(async move {
174    ///     if let Err(e) = my_service(ctx).await {
175    ///         eprintln!("Service error: {:?}", e);
176    ///     }
177    /// })?;
178    /// # Ok(())
179    /// # }
180    /// # }
181    /// ```
182    pub fn spawn<F>(&self, future: F) -> DbResult<()>
183    where
184        F: core::future::Future<Output = ()> + Send + 'static,
185    {
186        #[cfg(feature = "tracing")]
187        tracing::debug!("Spawning service on database runtime");
188
189        self.adapter.spawn(future).map_err(DbError::from)?;
190        Ok(())
191    }
192}