aimdb-executor 0.1.0

Pure async executor trait definitions for AimDB - runtime-agnostic abstractions
Documentation

aimdb-executor

Pure async executor trait definitions for AimDB - runtime-agnostic abstractions.

Overview

aimdb-executor provides the trait abstractions that enable AimDB to work across different async runtime environments. This crate has zero dependencies and defines only traits, enabling dependency inversion where the core database depends on abstractions rather than concrete implementations.

Key Features:

  • Runtime Agnostic: No concrete runtime dependencies
  • Simple Trait Structure: 4 focused traits covering all runtime needs
  • Platform Flexible: Works across std and no_std environments
  • Zero Dependencies: Pure trait definitions with minimal coupling

Architecture

┌────────────────────────────────────────┐
│         aimdb-executor (traits)        │
│  - RuntimeAdapter                      │
│  - TimeOps                             │
│  - Logger                              │
│  - Spawn                               │
└────────────────────────────────────────┘
            △                △
            │                │
    ┌───────┴────┐      ┌────┴──────┐
    │  Tokio     │      │  Embassy  │
    │  Adapter   │      │  Adapter  │
    └────────────┘      └───────────┘

Trait Overview

1. RuntimeAdapter

Platform identification and metadata:

pub trait RuntimeAdapter: Send + Sync + 'static {
    fn runtime_name() -> &'static str
    where
        Self: Sized;
}

Usage: Identify runtime at compile-time (associated function, not method).

2. TimeOps

Time operations for async contexts:

pub trait TimeOps: RuntimeAdapter {
    type Instant: Clone + Send + Sync + core::fmt::Debug + 'static;
    type Duration: Clone + Send + Sync + core::fmt::Debug + 'static;

    fn now(&self) -> Self::Instant;
    fn duration_since(&self, later: Self::Instant, earlier: Self::Instant) 
        -> Option<Self::Duration>;
    fn millis(&self, ms: u64) -> Self::Duration;
    fn secs(&self, secs: u64) -> Self::Duration;
    fn micros(&self, micros: u64) -> Self::Duration;
    fn sleep(&self, duration: Self::Duration) -> impl Future<Output = ()> + Send;
}

Usage: Generic time operations with platform-specific Instant/Duration types.

3. Logger

Structured logging abstraction:

pub trait Logger: RuntimeAdapter {
    fn info(&self, message: &str);
    fn debug(&self, message: &str);
    fn warn(&self, message: &str);
    fn error(&self, message: &str);
}

Usage: Logging that works across tracing (std) and defmt (embedded).

4. Spawn

Task spawning with platform-specific tokens:

pub trait Spawn: RuntimeAdapter {
    type SpawnToken: Send + 'static;
    
    fn spawn<F>(&self, future: F) -> ExecutorResult<Self::SpawnToken>
    where
        F: Future<Output = ()> + Send + 'static;
}

Usage: Spawn async tasks with runtime-appropriate tokens (JoinHandle, SpawnToken, etc.).

Quick Start

Implementing an Adapter

use aimdb_executor::{RuntimeAdapter, TimeOps, Logger, Spawn, ExecutorResult};
use core::future::Future;

pub struct MyAdapter;

impl RuntimeAdapter for MyAdapter {
    fn runtime_name() -> &'static str
    where
        Self: Sized,
    {
        "my-runtime"
    }
}

impl TimeOps for MyAdapter {
    type Instant = my_runtime::Instant;
    type Duration = my_runtime::Duration;

    fn now(&self) -> Self::Instant {
        my_runtime::time::now()
    }
    
    fn duration_since(
        &self,
        later: Self::Instant,
        earlier: Self::Instant,
    ) -> Option<Self::Duration> {
        later.checked_duration_since(earlier)
    }
    
    fn millis(&self, ms: u64) -> Self::Duration {
        my_runtime::Duration::from_millis(ms)
    }
    
    fn secs(&self, secs: u64) -> Self::Duration {
        my_runtime::Duration::from_secs(secs)
    }
    
    fn micros(&self, micros: u64) -> Self::Duration {
        my_runtime::Duration::from_micros(micros)
    }
    
    fn sleep(&self, duration: Self::Duration) -> impl Future<Output = ()> + Send {
        my_runtime::time::sleep(duration)
    }
}

impl Logger for MyAdapter {
    fn info(&self, message: &str) {
        my_runtime::log::info!("{}", message);
    }
    
    fn debug(&self, message: &str) {
        my_runtime::log::debug!("{}", message);
    }
    
    fn warn(&self, message: &str) {
        my_runtime::log::warn!("{}", message);
    }
    
    fn error(&self, message: &str) {
        my_runtime::log::error!("{}", message);
    }
}

impl Spawn for MyAdapter {
    type SpawnToken = my_runtime::TaskHandle;
    
    fn spawn<F>(&self, future: F) -> ExecutorResult<Self::SpawnToken>
    where
        F: Future<Output = ()> + Send + 'static,
    {
        my_runtime::spawn(future)
            .map_err(|e| ExecutorError::SpawnFailed {
                message: e.to_string()
            })
    }
}

Error Types

Simple error enum for executor operations:

pub enum ExecutorError {
    SpawnFailed { message: String },      // or &'static str in no_std
    RuntimeUnavailable { message: String },
    TaskJoinFailed { message: String },
}

pub type ExecutorResult<T> = Result<T, ExecutorError>;

Trait Variants

All traits are directly usable - no trait_variant macros used. All traits require Send + Sync bounds where appropriate.

Usage in AimDB Core

The core database is generic over runtime traits:

pub struct Database<A: RuntimeAdapter + TimeOps + Logger + Spawn> {
    adapter: A,
    // ... fields ...
}

impl<A> Database<A>
where
    A: RuntimeAdapter + TimeOps + Logger + Spawn,
{
    pub fn new(adapter: A) -> Self {
        // Use adapter traits
        adapter.info("Database initialized");
        let now = adapter.now();
        // ...
    }
}

Runtime Trait Bundle

A convenience trait that bundles all requirements:

pub trait Runtime: RuntimeAdapter + TimeOps + Logger + Spawn {
    fn runtime_info(&self) -> RuntimeInfo {
        RuntimeInfo { name: Self::runtime_name() }
    }
}

// Auto-implemented for any type with all four traits
impl<T> Runtime for T where T: RuntimeAdapter + TimeOps + Logger + Spawn {}

Existing Implementations

Two official adapters are available:

Tokio Adapter

[dependencies]
aimdb-tokio-adapter = "0.1"
  • Standard library environments
  • Uses tokio::time, tokio::task, tracing

Embassy Adapter

[dependencies]
aimdb-embassy-adapter = "0.1"
  • Embedded no_std environments
  • Uses embassy_time, embassy_executor, defmt

Design Philosophy

  1. Minimal Surface Area: Only essential operations
  2. No Concrete Dependencies: Pure trait definitions
  3. Platform Neutral: Works across std and no_std
  4. Future Proof: Easy to extend without breaking changes

Features

[features]
std = []  # Enable standard library support

Testing

# Run tests
cargo test -p aimdb-executor

# Check no_std compatibility
cargo check -p aimdb-executor --no-default-features

Documentation

Generate API docs:

cargo doc -p aimdb-executor --open

Examples

See adapter implementations:

  • aimdb-tokio-adapter/src/lib.rs - Full Tokio implementation
  • aimdb-embassy-adapter/src/lib.rs - Full Embassy implementation

License

See LICENSE file.