Skip to main content

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, sync::Arc};
16
17#[cfg(feature = "std")]
18use std::{boxed::Box, sync::Arc};
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        RuntimeContext::from_arc(Arc::new(self.adapter.clone()))
140    }
141}
142
143// Spawn implementation for databases with spawn-capable adapters
144impl<A> Database<A>
145where
146    A: RuntimeAdapter + Spawn,
147{
148    /// Spawns a service on the database's runtime
149    ///
150    /// # Example
151    /// ```rust,ignore
152    /// # use aimdb_core::Database;
153    /// # use aimdb_executor::{Runtime, Spawn};
154    /// # #[cfg(feature = "tokio-runtime")]
155    /// # {
156    /// async fn my_service<R: Runtime>(ctx: aimdb_core::RuntimeContext<R>) -> aimdb_core::DbResult<()> {
157    ///     // Service implementation
158    ///     Ok(())
159    /// }
160    ///
161    /// # async fn example<A: Runtime + Spawn>(db: Database<A>) -> aimdb_core::DbResult<()> {
162    /// let ctx = db.context();
163    /// db.spawn(async move {
164    ///     if let Err(e) = my_service(ctx).await {
165    ///         eprintln!("Service error: {:?}", e);
166    ///     }
167    /// })?;
168    /// # Ok(())
169    /// # }
170    /// # }
171    /// ```
172    pub fn spawn<F>(&self, future: F) -> DbResult<()>
173    where
174        F: core::future::Future<Output = ()> + Send + 'static,
175    {
176        #[cfg(feature = "tracing")]
177        tracing::debug!("Spawning service on database runtime");
178
179        self.adapter.spawn(future).map_err(DbError::from)?;
180        Ok(())
181    }
182}