refactor(executor/core): dyn-safe RuntimeOps + de-erased builder & registrar lifetimes (034 Phase 2, #130)#137
Merged
Conversation
Adds the object-safe counterpart to the RuntimeAdapter/TimeOps/Logger family: concrete time types (u64 nanos, core::time::Duration), a boxed sleep future, and LogLevel-based logging, so a runtime can travel as Arc<dyn RuntimeOps> instead of a type parameter. Implemented by all three adapters with a shared behavioral contract test; per-adapter impls are intentional (now_nanos needs an epoch anchor that the opaque TimeOps::Instant cannot provide). BoxFuture's canonical definition moves to aimdb-executor; aimdb-core re-exports it. The embassy host-test time driver now wakes immediately on schedule_wake so already-expired timers (Duration::ZERO sleeps) complete on the pinned-at-0 host clock. Groundwork only: core does not consume RuntimeOps yet (issue #131). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…130) AimDbBuilder<R> is already generic over R, and configure()/on_start() are only callable once .runtime() fixes R — the Box<dyn Any + Send> round-trip through spawn_fns/start_fns bought nothing and cost two panicking downcasts in build(). Store SpawnFnType<R>/StartFnType<R> directly and delete the downcasts. The struct-level bound on AimDb<R> moves to its impls so the field types stay well-formed for the builder's NoRuntime typestate (where both vectors are provably empty). PhantomData field dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
) RecordRegistrar methods took &'a mut self and returned &'a mut Self with 'a = the struct's own lifetime parameter — the classic fluent-API lifetime bug: the first call borrowed the registrar for its entire remaining lifetime, forcing one unbroken chain per configure closure (the old test admitted as much). - All fluent methods now take &mut self -> &mut Self (fresh borrows); separate statements like 'reg.source_raw(…); reg.tap_raw(…);' work. - configure()'s closure bound drops the HRTB: FnOnce(&mut RecordRegistrar<'_, T, R>). - Outbound/InboundConnectorBuilder split the double-'a into <'r, 'a>: 'r borrows the registrar, 'a is the registrar's record borrow; finish() returns &'r mut RecordRegistrar<'a, T, R> so chains still work. RecordT::register signature updated accordingly. - ext_macros and the hand-written extension traits (EmbassyRecordRegistrarExtCustom, RecordRegistrarPersistExt) follow. BREAKING: trait signatures changed for RecordT::register and the extension traits; the connector builders gained a lifetime parameter. Existing closure-based user code compiles unchanged (the new bounds are strictly more permissive). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #130. Part of design 034 Phase 2 (review doc §3.2/§3.5); first of three stacked PRs (#130 → #133 → #134).
What
1. Dyn-safe
RuntimeOpscapability trait (aimdb-executor) — groundwork for #131, not yet consumed by core.name()/now_nanos()(monotonic, arbitrary epoch) /unix_time()/ boxedsleep(core::time::Duration)/log(LogLevel, &str).TokioAdapter(OnceLock<Instant> anchor),EmbassyAdapter(boot-anchored uptime, µs granularity),WasmAdapter(Performance.now(), wall clock fromDate.now()).TimeOpsis impossible (now_nanosneeds an epoch anchor;TimeOps::Instantis opaque).aimdb_executor::test_support) run by all three adapters under their own executors. The embassy host time-driver stub now wakes immediately onschedule_wakesoDuration::ZEROsleeps complete (host clock is pinned at 0).BoxFuture's canonical definition moves toaimdb-executor;aimdb-corere-exports it (same type).2. De-erased builder internals (
aimdb-core/src/builder.rs) — no behavior change.spawn_fns/start_fnsstored asVec<(StringKey, SpawnFnType<R>)>/Vec<StartFnType<R>>instead ofBox<dyn Any + Send>; both panicking downcasts inbuild()deleted.AimDb<R>'s struct-levelR: RuntimeAdapter + 'staticbound moved to its impls (strictly more permissive) so the field types are well-formed on theNoRuntimetypestate, where both vectors are provably empty.3. Registrar lifetime fix (
aimdb-core/src/typed_api.rs) — breaking for code that names the old signatures; closure-based configuration compiles unchanged.&'a mut self -> &'a mut Self→&mut self -> &mut Self. A configure closure can now writereg.source_raw(…); reg.tap_raw(…);as separate statements and reuse the registrar after a chain.configure's bound drops the HRTB:FnOnce(&mut RecordRegistrar<'_, T, R>).OutboundConnectorBuilder/InboundConnectorBuilder: double-'asplit into<'r, 'a, T, R>;finish()returns&'r mut RecordRegistrar<'a, T, R>(chains still work).RecordT::register,impl_record_registrar_ext!,EmbassyRecordRegistrarExtCustom, andRecordRegistrarPersistExtfollow.Acceptance criteria (from #130)
Arc<dyn RuntimeOps>constructible from all three adapters; shared behavioral testBox<dyn Any>fields inAimDbBuilder; no downcasts inbuild()reg.source_raw(…); reg.tap_raw(…);as separate statements (newregistrar_allows_separate_statementstest;multiple_link_from_allowedextended)make checkgreen (fmt, clippy matrix, full test matrix, embedded thumbv7em, wasm, deny)Notes for reviewers
ext_macros.rsdissolution into plain generic impls stays deferred to refactor(core)!: remove runtime type parameter R from the object graph — non-generic AimDb/TypedRecord/RuntimeContext (R-removal part 2) #131 (part 2), per the issue.aimdb-wasm-adapter/tests/transform_join_integration_tests.rsdoes not compile for native targets (calls.subscribe()/.produce()on the(AimDb, AimDbRunner)tuple frombuild()); it predates this branch and is outsidemake check's legs.🤖 Generated with Claude Code