Skip to content

refactor(core): unify DbError on alloc::String — remove std/no_std dual-field variants #129

@lxsaah

Description

@lxsaah

Problem

Every variant of DbError (aimdb-core/src/error.rs) carries different fields per target:

RecordKeyNotFound {
    #[cfg(feature = "std")]
    key: String,
    #[cfg(not(feature = "std"))]
    _key: (),
},

This design multiplies cost across the whole codebase:

  • Every construction site needs a dual #[cfg] branch. builder.rs alone has ~8 of these blocks; typed_record.rs, typed_api.rs, and the adapters repeat the pattern. Helper constructors (DbError::runtime_error, record_key_not_found, …) exist but only cover some paths, so both styles coexist.
  • Error context is silently discarded on embedded targets — exactly where debugging is hardest. The no_std Display impl can only print a numeric code.
  • The no_std shape cannot be tested from a std build — the enum is literally a different type per target.

The kicker: aimdb-core always requires alloc (extern crate alloc is unconditional, and every feature builds on the alloc feature). alloc::string::String is available on every supported target, and thiserror 2.x supports no_std. The dual-field design solves a problem the crate does not have.

Proposal

  1. Give every DbError variant one shape, using alloc::string::String for context fields unconditionally.
  2. Derive thiserror::Error + #[error(...)] unconditionally (thiserror 2.x is no_std-capable); delete the hand-written no_std Display impl with its numeric-code table.
  3. Delete every dual construction branch at call sites (builder.rs, typed_record.rs, typed_api.rs, connectors, adapters) — each collapses to a single expression.
  4. Keep the #[cfg(feature = "std")]-only variants that genuinely wrap std types (Io, Json, sync-API timeouts) — those gates are legitimate.
  5. Keep (and prefer) the existing helper constructors so future call sites stay one-liners.

If String formatting cost on MCU ever becomes a measured concern, the answer is still one shape (e.g. &'static str context), not two enums behind cfg.

Affected files (non-exhaustive)

  • aimdb-core/src/error.rs — variant definitions, Display impl, helpers
  • aimdb-core/src/builder.rs, typed_record.rs, typed_api.rs — dual construction sites
  • aimdb-tokio-adapter, aimdb-embassy-adapter, connectors — error mapping sites (e.g. the #[cfg] branches in aimdb-knx-connector/src/tokio_client.rs)

Acceptance criteria

  • No DbError variant has #[cfg]-gated fields; no _field: () placeholders remain
  • No #[cfg(feature = "std")] / #[cfg(not(feature = "std"))] pairs exist purely to construct a DbError
  • no_std builds produce the same error messages as std builds (modulo std-only variants)
  • cargo build -p aimdb-core --no-default-features --features alloc and the embedded targets (make test-embedded) still pass
  • Public API: variant names unchanged; field renames (_key: ()key: String) noted in the changelog as a no_std-only breaking change

Estimated effect

−400 to −600 lines of pure noise; every future error site is one branch instead of two.

See docs/design/034-technical-debt-review.md §3.1.1 and Phase 1 of the remediation roadmap for full context.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions