AimDbBuilder

Struct AimDbBuilder 

Source
pub struct AimDbBuilder<R = NoRuntime> { /* private fields */ }
Expand description

Database builder for producer-consumer pattern

Provides a fluent API for constructing databases with type-safe record registration. Use .runtime() to set the runtime and transition to a typed builder.

Implementations§

Source§

impl AimDbBuilder<NoRuntime>

Source

pub fn new() -> Self

Creates a new database builder without a runtime

Call .runtime() to set the runtime adapter.

Source

pub fn runtime<R>(self, rt: Arc<R>) -> AimDbBuilder<R>
where R: Spawn + 'static,

Sets the runtime adapter

This transitions the builder from untyped to typed with concrete runtime R.

§Type Safety Note

The connector_builders field is intentionally reset to Vec::new() during this transition because connectors are parameterized by the runtime type:

  • Before: Vec<Box<dyn ConnectorBuilder<NoRuntime>>>
  • After: Vec<Box<dyn ConnectorBuilder<R>>>

These types are incompatible and cannot be transferred. However, this is not a bug because .with_connector() is only available AFTER calling .runtime() (it’s defined in the impl<R> where R: Spawn block, not in impl AimDbBuilder<NoRuntime>).

This means the type system enforces the correct call order:

AimDbBuilder::new()
    .runtime(runtime)           // ← Must be called first
    .with_connector(connector)  // ← Now available

The records and remote_config are preserved across the transition since they are not parameterized by the runtime type.

Source§

impl<R> AimDbBuilder<R>
where R: Spawn + 'static,

Source

pub fn with_connector(self, builder: impl ConnectorBuilder<R> + 'static) -> Self

Registers a connector builder that will be invoked during build()

The connector builder will be called after the database is constructed, allowing it to collect routes and initialize the connector properly.

§Arguments
  • builder - A connector builder that implements ConnectorBuilder<R>
§Example
use aimdb_mqtt_connector::MqttConnector;

let db = AimDbBuilder::new()
    .runtime(runtime)
    .with_connector(MqttConnector::new("mqtt://broker.local:1883"))
    .configure::<Temperature>(|reg| {
        reg.link_from("mqtt://commands/temp")...
    })
    .build().await?;
Source

pub fn with_remote_access(self, config: AimxConfig) -> Self

Enables remote access via AimX protocol (std only)

Configures the database to accept remote connections over a Unix domain socket, allowing external clients to introspect records, subscribe to updates, and (optionally) write data.

The remote access supervisor will be spawned automatically during build().

§Arguments
  • config - Remote access configuration (socket path, security policy, etc.)
§Example
use aimdb_core::remote::{AimxConfig, SecurityPolicy};

let config = AimxConfig::new("/tmp/aimdb.sock")
    .with_security(SecurityPolicy::read_only());

let db = AimDbBuilder::new()
    .runtime(runtime)
    .with_remote_access(config)
    .build()?;
Source

pub fn configure<T>( &mut self, key: impl RecordKey, f: impl for<'a> FnOnce(&'a mut RecordRegistrar<'a, T, R>), ) -> &mut Self
where T: Send + Sync + 'static + Debug + Clone,

Configures a record type manually with a unique key

The key uniquely identifies this record instance. Multiple records of the same type can exist with different keys (e.g., “sensor.temperature.room1” and “sensor.temperature.room2”).

§Arguments
  • 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
// Using string literal
builder.configure::<Temperature>("sensor.temp.room1", |reg| { ... });

// Using compile-time safe enum key
builder.configure::<Temperature>(SensorKey::TempRoom1, |reg| { ... });
Source

pub fn register_record<T>(&mut self, cfg: &T::Config) -> &mut Self
where T: RecordT<R>,

Registers a self-registering record type

The record type must implement RecordT<R>.

Uses the type name as the default key. For custom keys, use configure() directly.

Source

pub fn register_record_with_key<T>( &mut self, key: impl RecordKey, cfg: &T::Config, ) -> &mut Self
where T: RecordT<R>,

Registers a self-registering record type with a custom key

The record type must implement RecordT<R>.

Source

pub async fn run(self) -> DbResult<()>

Runs the database indefinitely (never returns)

This method builds the database, spawns all producer and consumer tasks, and then parks the current task indefinitely. This is the primary way to run AimDB services.

All logic runs in background tasks via producers, consumers, and connectors. The application continues until interrupted (e.g., Ctrl+C).

§Returns

DbResult<()> - Ok when database starts successfully, then parks forever

§Example
#[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
}
Source

pub async fn build(self) -> DbResult<AimDb<R>>

Builds the database and returns the handle (async)

Use this when you need programmatic access to the database handle for manual subscriptions or production. For typical services, use .run().await instead.

Automatic Task Spawning: This method spawns all producer services and .tap() observer tasks that were registered during configuration.

Connector Setup: Connectors must be created manually before calling build():

use aimdb_mqtt_connector::{MqttConnector, router::RouterBuilder};

// Configure records with connector links
let builder = AimDbBuilder::new()
    .runtime(runtime)
    .configure::<Temp>("temp", |reg| {
        reg.link_from("mqtt://commands/temp")
           .with_buffer(BufferCfg::SingleLatest)
           .with_serialization();
    });

// Create MQTT connector with router
let router = RouterBuilder::new()
    .route("commands/temp", /* deserializer */)
    .build();
let connector = MqttConnector::new("mqtt://localhost:1883", router).await?;

// Register connector and build
let db = builder
    .with_connector("mqtt", Arc::new(connector))
    .build().await?;
§Returns

DbResult<AimDb<R>> - The database instance

Trait Implementations§

Source§

impl Default for AimDbBuilder<NoRuntime>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<R> Freeze for AimDbBuilder<R>

§

impl<R = NoRuntime> !RefUnwindSafe for AimDbBuilder<R>

§

impl<R> Send for AimDbBuilder<R>
where R: Send + Sync,

§

impl<R = NoRuntime> !Sync for AimDbBuilder<R>

§

impl<R> Unpin for AimDbBuilder<R>
where R: Unpin,

§

impl<R = NoRuntime> !UnwindSafe for AimDbBuilder<R>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.