Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4db081d
refactor(core)!: fuse inbound deserialize+produce at registration (03…
lxsaah Jun 11, 2026
4db78b2
refactor(core)!: fuse outbound subscribe+serialize+topic at registrat…
lxsaah Jun 11, 2026
569b870
refactor(core)!: drop unrepresentable error surface; document join er…
lxsaah Jun 11, 2026
915dc7b
refactor(tests): clean up use statements and formatting in tests
lxsaah Jun 11, 2026
89e9eee
docs(design): mark 036 W1 implemented in PR #141
lxsaah Jun 11, 2026
0b94d21
refactor(core)!: split AnyRecord into capability traits (036 W2)
lxsaah Jun 12, 2026
8569548
docs(design): mark 036 W2 implemented in PR #142
lxsaah Jun 12, 2026
03fd09b
refactor(core): dedup StringKey::intern; document the leak contract (…
lxsaah Jun 12, 2026
205db9e
refactor(tests): host_test_stubs! macro for the defmt logger triplica…
lxsaah Jun 12, 2026
46ad119
docs(design): mark 036 W5+W6 implemented in PR #143
lxsaah Jun 12, 2026
7f4781c
docs(design): 036 status round — W3 prep done, W4 deferred on W3 data…
lxsaah Jun 12, 2026
b778564
feat(knx): ACK-retransmit knob — retransmit once, disconnect on secon…
lxsaah Jun 12, 2026
d9e53c5
docs(design): mark 036 W4 implemented in PR #144; record W3 bench fin…
lxsaah Jun 12, 2026
e94311e
docs(design): record W4 hardware validation (partial) and pre-release…
lxsaah Jun 12, 2026
b33e47f
docs: fix AimX v1/v2 rot, dead spec links, and removed-API references
lxsaah Jun 12, 2026
b626a25
docs: purge never-compiled doc examples — compile, delete, or justify…
lxsaah Jun 12, 2026
904f5a0
Merge remote-tracking branch 'origin/main' into docs/146-doctest-conv…
lxsaah Jun 13, 2026
4f83395
docs: remove deprecated settings.json file
lxsaah Jun 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions aimdb-core/src/buffer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,21 @@
//!
//! # Example
//!
//! Illustrative (not compiled: `.buffer()` comes from your runtime adapter's
//! registrar extension trait, which `aimdb-core` cannot depend on):
//!
//! ```rust,ignore
//! use aimdb_core::buffer::BufferCfg;
//!
//! // High-frequency sensor data
//! reg.buffer(BufferCfg::SpmcRing { capacity: 2048 })
//! .source(|em, data| async { ... })
//! .tap(|em, data| async { ... });
//! .source(|ctx, producer| async move { /* … */ })
//! .tap(|ctx, consumer| async move { /* … */ });
//!
//! // Configuration updates
//! reg.buffer(BufferCfg::SingleLatest)
//! .source(|em, cfg| async { ... })
//! .tap(|em, cfg| async { ... });
//! .source(|ctx, producer| async move { /* … */ })
//! .tap(|ctx, consumer| async move { /* … */ });
//! ```

// Module structure
Expand Down
24 changes: 0 additions & 24 deletions aimdb-core/src/buffer/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,6 @@ pub trait BufferReader<T: Clone + Send>: Send {
/// # Requirements
/// - Record must be configured with `.with_remote_access()`
/// - Only available with the `remote-access` feature (requires serde_json)
///
/// # Example
/// ```rust,ignore
/// // Internal use in remote access handler
/// let json_reader: Box<dyn JsonBufferReader> = record.subscribe_json()?;
/// while let Ok(json_val) = json_reader.recv_json().await {
/// // Forward JSON value to remote client...
/// }
/// ```
#[cfg(feature = "remote-access")]
pub trait JsonBufferReader: Send {
/// Receive the next value as JSON (async)
Expand Down Expand Up @@ -225,21 +216,6 @@ pub struct BufferMetricsSnapshot {
///
/// Implemented by buffer types when the `metrics` feature is enabled.
/// Provides counters for diagnosing producer-consumer imbalances.
///
/// # Example
/// ```rust,ignore
/// use aimdb_core::buffer::BufferMetrics;
///
/// // After enabling `metrics` feature
/// let metrics = buffer.metrics();
/// if metrics.produced_count > metrics.consumed_count + 1000 {
/// println!("Warning: consumer is {} items behind",
/// metrics.produced_count - metrics.consumed_count);
/// }
/// if metrics.dropped_count > 0 {
/// println!("Warning: {} items dropped due to overflow", metrics.dropped_count);
/// }
/// ```
#[cfg(feature = "metrics")]
pub trait BufferMetrics {
/// Get a snapshot of current buffer metrics
Expand Down
151 changes: 16 additions & 135 deletions aimdb-core/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,16 +355,6 @@ impl AimDbBuilder {
/// must return a future that runs for as long as needed (e.g. an infinite
/// cleanup loop). Tasks are spawned in registration order, after all
/// record tasks and connectors have been started.
///
/// # Example
/// ```rust,ignore
/// builder.on_start(|ctx| async move {
/// loop {
/// do_cleanup().await;
/// ctx.time().sleep_secs(3600).await;
/// }
/// });
/// ```
pub fn on_start<F, Fut>(&mut self, f: F) -> &mut Self
where
F: FnOnce(crate::RuntimeContext) -> Fut + Send + 'static,
Expand Down Expand Up @@ -396,23 +386,28 @@ impl AimDbBuilder {
///
/// # Examples
///
/// Illustrative (not compiled: the connector types live in downstream
/// crates `aimdb-core` cannot depend on):
///
/// ```rust,ignore
/// // (1) data-plane link to an MQTT topic
/// AimDbBuilder::new().runtime(rt)
/// .with_connector(MqttConnector::new("mqtt://broker.local:1883"))
/// .configure::<Temperature>(|r| { r.link_from("mqtt://commands/temp"); })
/// .build().await?;
/// let mut b = AimDbBuilder::new().runtime(rt)
/// .with_connector(MqttConnector::new("mqtt://broker.local:1883"));
/// b.configure::<Temperature>("commands.temp", |r| {
/// r.link_from("mqtt://commands/temp").with_deserializer_raw(parse).finish();
/// });
/// b.build().await?;
///
/// // (2a) remote-access SERVER — no links, just expose this db over UDS
/// AimDbBuilder::new().runtime(rt)
/// .with_connector(UdsServer::from_config(remote_config))
/// .build().await?;
///
/// // (2b) remote-access CLIENT — mirror a record to a peer over UDS
/// AimDbBuilder::new().runtime(rt)
/// .with_connector(UdsClient::new("/run/aimdb.sock"))
/// .configure::<Temp>(|r| { r.with_remote_access().link_to("uds://temp"); })
/// .build().await?;
/// let mut b = AimDbBuilder::new().runtime(rt)
/// .with_connector(UdsClient::new("/run/aimdb.sock"));
/// b.configure::<Temp>("temp", |r| { r.with_remote_access().link_to("uds://temp"); });
/// b.build().await?;
/// ```
pub fn with_connector(
mut self,
Expand Down Expand Up @@ -441,15 +436,6 @@ impl AimDbBuilder {
/// * `key` - A unique identifier for this record. Can be a string literal, `StringKey`,
/// or any type implementing `RecordKey` (including user-defined enum keys).
/// * `f` - Configuration closure
///
/// # Example
/// ```rust,ignore
/// // Using string literal
/// builder.configure::<Temperature>("sensor.temp.room1", |reg| { ... });
///
/// // Using compile-time safe enum key
/// builder.configure::<Temperature>(SensorKey::TempRoom1, |reg| { ... });
/// ```
pub fn configure<T>(
&mut self,
key: impl RecordKey,
Expand Down Expand Up @@ -569,22 +555,6 @@ impl AimDbBuilder {
/// # Returns
/// `DbResult<()>` — Ok once the database starts; the call then blocks until
/// every future the runner is driving has completed (typically forever).
///
/// # Example
///
/// ```rust,ignore
/// #[tokio::main]
/// async fn main() -> DbResult<()> {
/// AimDbBuilder::new()
/// .runtime(Arc::new(TokioAdapter::new()?))
/// .configure::<MyData>(|reg| {
/// reg.with_buffer(BufferCfg::SpmcRing { capacity: 100 })
/// .with_source(my_producer)
/// .with_tap(my_consumer);
/// })
/// .run().await // Runs forever
/// }
/// ```
pub async fn run(self) -> DbResult<()> {
log_info!("Building database and spawning background tasks...");

Expand Down Expand Up @@ -626,19 +596,6 @@ impl AimDbBuilder {
/// buffer, duplicate keys, dependency-graph cycles) is collected and
/// returned as one [`DbError::InvalidConfiguration`] carrying **all**
/// findings — one run surfaces every mistake.
///
/// # Example
///
/// ```rust,ignore
/// let (db, runner) = AimDbBuilder::new()
/// .runtime(runtime)
/// .configure::<Temp>("temp", |reg| { /* … */ })
/// .with_connector(mqtt_builder)
/// .build().await?;
///
/// let handle = db.clone(); // clone freely before runner.run()
/// runner.run().await; // drives everything to completion
/// ```
pub async fn build(mut self) -> DbResult<(AimDb, AimDbRunner)> {
use crate::error::ConfigError;

Expand Down Expand Up @@ -868,6 +825,9 @@ impl Default for AimDbBuilder {
///
/// # Examples
///
/// Illustrative (not compiled: the runtime adapter lives in a downstream
/// crate `aimdb-core` cannot depend on):
///
/// ```rust,ignore
/// use aimdb_tokio_adapter::TokioAdapter;
///
Expand Down Expand Up @@ -952,12 +912,6 @@ impl AimDb {
///
/// External crates (e.g. `aimdb-persistence`) retrieve their typed state here
/// to service query calls. The extensions are read-only on the live handle.
///
/// # Example
/// ```rust,ignore
/// use aimdb_persistence::PersistenceState;
/// let state = db.extensions().get::<PersistenceState>().unwrap();
/// ```
pub fn extensions(&self) -> &Extensions {
&self.inner.extensions
}
Expand Down Expand Up @@ -989,13 +943,6 @@ impl AimDb {
/// # Arguments
/// * `key` - The record key (e.g., "sensor.temperature")
/// * `value` - The value to produce
///
/// # Example
///
/// ```rust,ignore
/// db.produce::<Temperature>("sensors.indoor", indoor_temp)?;
/// db.produce::<Temperature>("sensors.outdoor", outdoor_temp)?;
/// ```
pub fn produce<T>(&self, key: impl AsRef<str>, value: T) -> DbResult<()>
where
T: Send + 'static + Debug + Clone,
Expand All @@ -1013,15 +960,6 @@ impl AimDb {
///
/// # Arguments
/// * `key` - The record key (e.g., "sensor.temperature")
///
/// # Example
///
/// ```rust,ignore
/// let mut reader = db.subscribe::<Temperature>("sensors.indoor")?;
/// while let Ok(temp) = reader.recv().await {
/// println!("Indoor: {:.1}°C", temp.celsius);
/// }
/// ```
pub fn subscribe<T>(
&self,
key: impl AsRef<str>,
Expand All @@ -1039,17 +977,6 @@ impl AimDb {
///
/// # Arguments
/// * `key` - The record key (e.g., "sensor.temperature")
///
/// # Example
///
/// ```rust,ignore
/// let indoor_producer = db.producer::<Temperature>("sensors.indoor");
/// let outdoor_producer = db.producer::<Temperature>("sensors.outdoor");
///
/// // Each producer writes to its own record
/// indoor_producer.produce(indoor_temp);
/// outdoor_producer.produce(outdoor_temp);
/// ```
pub fn producer<T>(
&self,
key: impl Into<alloc::string::String>,
Expand All @@ -1070,16 +997,6 @@ impl AimDb {
///
/// # Arguments
/// * `key` - The record key (e.g., "sensor.temperature")
///
/// # Example
///
/// ```rust,ignore
/// let indoor_consumer = db.consumer::<Temperature>("sensors.indoor");
/// let outdoor_consumer = db.consumer::<Temperature>("sensors.outdoor");
///
/// // Each consumer reads from its own record
/// let mut rx = indoor_consumer.subscribe();
/// ```
pub fn consumer<T>(
&self,
key: impl Into<alloc::string::String>,
Expand All @@ -1102,14 +1019,6 @@ impl AimDb {
/// Resolve a record key to its RecordId
///
/// Useful for checking if a record exists before operations.
///
/// # Example
///
/// ```rust,ignore
/// if let Some(id) = db.resolve_key("sensors.temperature") {
/// println!("Record exists with ID: {}", id);
/// }
/// ```
pub fn resolve_key(&self, key: &str) -> Option<crate::record_id::RecordId> {
self.inner.resolve_str(key)
}
Expand All @@ -1118,13 +1027,6 @@ impl AimDb {
///
/// Returns a slice of RecordIds for all records of type T.
/// Useful for introspection when multiple records of the same type exist.
///
/// # Example
///
/// ```rust,ignore
/// let temp_ids = db.records_of_type::<Temperature>();
/// println!("Found {} temperature records", temp_ids.len());
/// ```
pub fn records_of_type<T: 'static>(&self) -> &[crate::record_id::RecordId] {
self.inner.records_of_type::<T>()
}
Expand All @@ -1149,14 +1051,6 @@ impl AimDb {
///
/// Returns metadata for all registered records, useful for remote access introspection.
/// Available only when the `std` feature is enabled.
///
/// # Example
/// ```rust,ignore
/// let records = db.list_records();
/// for record in records {
/// println!("Record: {} ({})", record.name, record.type_id);
/// }
/// ```
#[cfg(feature = "remote-access")]
pub fn list_records(&self) -> Vec<crate::remote::RecordMetadata> {
self.inner.list_records()
Expand Down Expand Up @@ -1210,11 +1104,6 @@ impl AimDb {
///
/// # Returns
/// `Ok(())` on success, error if record not found, has producers, or deserialization fails
///
/// # Example (internal use)
/// ```rust,ignore
/// db.set_record_from_json("AppConfig", json!({"debug": true}))?;
/// ```
#[cfg(feature = "remote-access")]
pub fn set_record_from_json(
&self,
Expand All @@ -1238,14 +1127,6 @@ impl AimDb {
///
/// The topic is resolved dynamically if a `TopicResolverFn` is configured,
/// otherwise the static topic from the URL is used.
///
/// # Example
/// ```rust,ignore
/// // In MqttConnector after db.build()
/// let routes = db.collect_inbound_routes("mqtt");
/// let router = RouterBuilder::from_routes(routes).build();
/// connector.set_router(router).await?;
/// ```
pub fn collect_inbound_routes(
&self,
scheme: &str,
Expand Down
20 changes: 13 additions & 7 deletions aimdb-core/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@
//!
//! # Example
//!
//! ```rust,ignore
//! use aimdb_core::BufferCfg;
//!
//! ```no_run
//! # use aimdb_core::AimDbBuilder;
//! # #[derive(Clone, Debug)] struct WeatherAlert { level: u8 }
//! # fn wire(builder: &mut AimDbBuilder) {
//! builder.configure::<WeatherAlert>("weather.alert", |reg| {
//! reg.buffer(BufferCfg::SingleLatest)
//! .link_to("mqtt://alerts/weather")
//! .with_serializer_raw(|alert: &WeatherAlert| Ok(alert.to_json_vec()))
//! // .buffer(BufferCfg::SingleLatest) — via your runtime adapter's ext trait
//! reg.link_to("mqtt://alerts/weather")
//! .with_serializer_raw(|alert: &WeatherAlert| Ok(vec![alert.level]))
//! .finish();
//! });
//! # }
//! ```

use core::fmt::{self, Debug};
Expand Down Expand Up @@ -158,8 +160,9 @@ pub type SourceFactoryFn = Arc<dyn Fn(&AimDb) -> Box<dyn SerializedSource> + Sen
///
/// # Example
///
/// ```rust,ignore
/// ```rust
/// use aimdb_core::connector::TopicProvider;
/// # #[derive(Clone, Debug)] struct Temperature { sensor_id: u32 }
///
/// struct SensorTopicProvider;
///
Expand Down Expand Up @@ -614,6 +617,9 @@ fn parse_connector_url(url: &str) -> DbResult<ConnectorUrl> {
///
/// # Example
///
/// Illustrative sketch of a connector author's `build()` (not compiled: the
/// client types are fictional — see `aimdb-mqtt-connector` for a real one):
///
/// ```rust,ignore
/// pub struct MqttConnectorBuilder {
/// broker_url: String,
Expand Down
Loading