Skip to content

feat: add the C ABI lifecycle - #26

Merged
0xgleb merged 4 commits into
masterfrom
feat/0.4-c-api-lifecycle
Jul 16, 2026
Merged

0xgleb merged 4 commits into
masterfrom
feat/0.4-c-api-lifecycle

Conversation

@0xgleb

@0xgleb 0xgleb commented Jul 15, 2026 •

Copy link
Copy Markdown
Member

Add the event-sorcery-ffi static library with a cbindgen-generated C header, versioned deterministic-CBOR open options and errors, an opaque store owning the captive Tokio runtime and extracted engine, migration through the canonical sqlite-es MIGRATOR, explicit buffer ownership, and panic barriers.

The crate links the existing engine facade; it does not contain storage or job decisions. Workspace tests and clippy with warnings denied are green.

Closes #62.


This is part 8 of 32 in a stack made with GitButler:

Summary by CodeRabbit

  • New Features

    • Added a C-compatible interface for opening, migrating, using, and closing event stores.
    • Added automatic generation of a C header for native integrations.
    • Exposed the event engine, commit requests, snapshots, and related errors through the public API.
    • Added a public database migration operation.
    • Added validation to prevent empty snapshot updates.
  • Bug Fixes

    • Improved store shutdown safety and error handling, including concurrent close scenarios.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@0xgleb, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 6 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d2eb20f4-5a50-4bdf-942f-14a50c8c2afc

📥 Commits

Reviewing files that changed from the base of the PR and between cb1a992 and b3ef1e8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml
  • crates/event-sorcery-ffi/Cargo.toml
  • crates/event-sorcery-ffi/build.rs
  • crates/event-sorcery-ffi/src/lib.rs

Walkthrough

Changes

The workspace now includes the FFI crate. The engine exposes public persistence APIs and migration, while the new FFI layer provides C-compatible store lifecycle functions, CBOR validation, error handling, buffer cleanup, concurrency coordination, and tests.

Engine API and FFI lifecycle

Layer / File(s) Summary
Public engine API and migration
crates/event-sorcery/src/engine.rs, crates/event-sorcery/src/lib.rs
Engine types and operations are exposed publicly, Engine::migrate runs SQLite migrations, and snapshot attachment rejects empty event requests.
Engine call-site alignment
crates/event-sorcery/src/job_sqlite.rs, crates/event-sorcery/src/sqlite_event_repository.rs
SQLite job migration delegates to Engine::migrate, while repository persistence propagates snapshot attachment errors.
C ABI store lifecycle
Cargo.toml, crates/event-sorcery-ffi/*
The FFI crate is added to the workspace, generates a C header, and implements validated open, close, buffer-free, error, panic, and concurrent lifecycle behavior with tests.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR adds open, version-check, close, and buffer-freeing C ABI functions for foreign runtimes.
Out of Scope Changes check ✅ Passed The visibility and re-export changes support the new FFI lifecycle and engine integration, with no clear unrelated additions.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a C ABI lifecycle for the FFI crate.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/0.4-c-api-lifecycle

Comment @coderabbitai help to get the list of available commands.

@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch from 13bb559 to 5d46691 Compare July 15, 2026 05:11
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from 0f4c623 to b5885b0 Compare July 15, 2026 05:11
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from b5885b0 to a9cca28 Compare July 15, 2026 06:11
@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch from 5d46691 to 7e3232b Compare July 15, 2026 06:24
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from a9cca28 to b93c3a5 Compare July 15, 2026 06:24
@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch from 7e3232b to 277ac60 Compare July 15, 2026 07:41
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from b93c3a5 to 5697838 Compare July 15, 2026 07:41
@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch from 277ac60 to 350262e Compare July 15, 2026 09:21
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from 5697838 to 9cba22b Compare July 15, 2026 09:21
@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch from 350262e to 3e8cffd Compare July 15, 2026 09:40
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from 9cba22b to f3dba53 Compare July 15, 2026 09:41
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from 2087976 to 4f46410 Compare July 16, 2026 11:44
@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch from 43b1c3b to 14d2931 Compare July 16, 2026 13:56
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from 4f46410 to 626e657 Compare July 16, 2026 13:56
@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch 2 times, most recently from 108a313 to d642d59 Compare July 16, 2026 14:51
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from 626e657 to cbfbf10 Compare July 16, 2026 14:54
@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch from d642d59 to b3875b8 Compare July 16, 2026 15:45
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from cbfbf10 to 02cee38 Compare July 16, 2026 15:51
@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch from b3875b8 to 7aa769e Compare July 16, 2026 15:58
@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from 02cee38 to 611f664 Compare July 16, 2026 15:59
@0xgleb
0xgleb force-pushed the feat/0.4-engine-ffi branch 3 times, most recently from 8983b16 to ed8d725 Compare July 16, 2026 16:48
@0xgleb

0xgleb commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@0xgleb
0xgleb force-pushed the feat/0.4-c-api-lifecycle branch from cb1a992 to 4180148 Compare July 16, 2026 17:04

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/event-sorcery-ffi/build.rs`:
- Around line 10-12: Update the build script’s header generation flow around
generate().write_to_file so the generated event_sorcery.h is also published to a
stable checked-in or install/release location outside OUT_DIR, while preserving
the existing OUT_DIR output for Rust builds.
- Around line 4-11: Update the build script’s main entry point to return a
suitable Result type, replace the expect calls for CARGO_MANIFEST_DIR, OUT_DIR,
and cbindgen generation with ? propagation, and return the generated result as
required while preserving the existing builder configuration.

In `@crates/event-sorcery-ffi/Cargo.toml`:
- Around line 13-19: Move ciborium, event-sorcery, and cbindgen into the
workspace dependency table using cargo add, then update the event-sorcery-ffi
manifest to reference each with workspace = true, preserving their existing
versions, path, and build-dependency scope.

In `@crates/event-sorcery-ffi/src/lib.rs`:
- Around line 441-468: Update the decoded tuple in the options parser to
represent runtime_threads with a fixed-width wire integer instead of usize, then
reject values above a documented maximum (while preserving the existing nonzero
validation). Convert the validated value to usize using a checked conversion
before constructing OpenOptions, and add boundary tests covering the maximum
accepted value and the first rejected value.
- Line 1: The new Rust modules lack required module-level documentation. Add //!
documentation to crates/event-sorcery-ffi/src/lib.rs describing the unsafe ABI,
lifecycle, and ownership contract, and add //! documentation to
crates/event-sorcery-ffi/build.rs describing header generation and publication.
- Around line 109-114: Update es_close and linearize_close to pass the raw owner
pointer instead of creating an &mut reference via store.as_mut(). Within
linearize_close, while holding the registry mutex, use raw-pointer read and
write operations to access and clear the owner cell, preserving the existing
close linearization behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 23afe0ea-4ead-40fa-960b-70fd7d867876

📥 Commits

Reviewing files that changed from the base of the PR and between ed8d725 and cb1a992.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • Cargo.toml
  • crates/event-sorcery-ffi/Cargo.toml
  • crates/event-sorcery-ffi/build.rs
  • crates/event-sorcery-ffi/src/lib.rs
  • crates/event-sorcery/src/engine.rs
  • crates/event-sorcery/src/job_sqlite.rs
  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery/src/sqlite_event_repository.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Before work, read SPEC.md and docs/domain.md; read relevant supplemental documentation before implementation.
New features must be documented in SPEC.md before implementation and must follow the hierarchy SPEC.md -> issue -> plan -> tests -> implementation.
Fix all known problems immediately, complete all tasks, and do not allow warnings or errors to pass through.
Keep a granular task list and clear completed tasks from the active list.
All new or modified logic must have corresponding test coverage.
Understand relevant documentation and source code before implementation, keep diffs small, and review the approach critically.
When changing direction or making an important undocumented architectural decision, obtain confirmation; record significant decisions as ADRs under adrs/.
Before handover, review the diff, revert unjustified changes, and check for scope creep.
Each aggregate in a consuming application must use exactly one SqliteCqrs instance constructed at startup; per-request construction is forbidden.
Never read secret or credential files such as .env*, credentials.json, *.key, *.pem, *.p12, *.pfx, or sensitive database files without explicit permission.
Never bypass, disable, suppress, or obscure quality-control mechanisms without explicit permission; fix lint and test issues at their root.
Use cargo check, cargo nextest, and cargo clippy for verification; never use cargo build unless build artifacts are required.

Files:

  • crates/event-sorcery-ffi/Cargo.toml
  • crates/event-sorcery/src/lib.rs
  • Cargo.toml
  • crates/event-sorcery-ffi/build.rs
  • crates/event-sorcery/src/job_sqlite.rs
  • crates/event-sorcery/src/sqlite_event_repository.rs
  • crates/event-sorcery-ffi/src/lib.rs
  • crates/event-sorcery/src/engine.rs
**/Cargo.toml

📄 CodeRabbit inference engine (AGENTS.md)

Never manually edit Cargo.toml to add dependencies; use cargo add, with workspace dependencies declared centrally and referenced by workspace=true.

Files:

  • crates/event-sorcery-ffi/Cargo.toml
  • Cargo.toml
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Organize code by business feature rather than technical layer; avoid catch-all modules such as types.rs, error.rs, models.rs, utils.rs, helpers.rs, and services.rs.
Never write directly to the events table; emit events through CqrsFramework::execute() or execute_with_metadata().
Use cqrs-es Services for side effects in handle() and follow the {Action}er -> {Domain}Service -> {Domain}Manager naming pattern.
Place command-execution logging in aggregate handle() methods rather than callers.
Model invalid states with enums, ADTs, newtypes, and typestate rather than relying on runtime validation.
Use domain newtypes at APIs and convert to SDK primitives inside the callee, except at cross-crate boundaries where conversion at the call site is necessary.
Keep visibility as restrictive as possible: private over pub(crate) over pub.
Use a three-group import order: external crates, workspace crates, then crate-internal imports; do not use function-level imports except enum variants.
Do not use unwrap() or expect() in production Rust code; they are permitted in #[cfg(test)] code.
Never create error variants containing opaque String values; prefer #[from], ?, #[source], and preserve error chains.
Log a warning or error before silent early returns such as let-else failures.
Never silently mask numeric failures with caps, fallback defaults, precision truncation, unwrap_or(), or unwrap_or_default(); use explicit checked conversions and errors.
Prefer functional patterns, pattern matching, combinators, type-driven design, and iterators over imperative loops unless complexity increases.
Use ASCII in identifiers, comments, log messages, and configuration keys; Unicode is preferred only in user-facing rendered output.
Do not use single-letter variables, arguments, closure parameters, or generic type parameters except an unambiguous lone type parameter or short unambiguous closure.
Every module must have a //! docstring and should order public API, private implementati...

Files:

  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery-ffi/build.rs
  • crates/event-sorcery/src/job_sqlite.rs
  • crates/event-sorcery/src/sqlite_event_repository.rs
  • crates/event-sorcery-ffi/src/lib.rs
  • crates/event-sorcery/src/engine.rs
*

⚙️ CodeRabbit configuration file

Focus on providing constructive criticism. Whenever you see a suboptimal approach, suggest more idiomatic or robust alternative(s). Flag potential footguns. Suggest FP alternatives to mutable/imperative code. Point out architectural flaws like leaky abstractions, tight coupling, wrong level of abstraction, poor type modeling, over-abstraction, unclear domain boundaries. Code should generally be organized based on business concerns rather than technical aspects - suggest improvements if you find violations. Point out gaps in test coverage but suggest tests that are not too coupled to the implementation and actually test domain invariants and business logic

Files:

  • Cargo.toml
🔇 Additional comments (7)
crates/event-sorcery/src/engine.rs (1)

17-58: LGTM!

Also applies to: 67-74, 94-116, 129-146, 167-167, 195-195, 223-223, 281-281, 385-385, 705-727, 800-808, 820-821

crates/event-sorcery/src/lib.rs (1)

128-128: LGTM!

crates/event-sorcery/src/job_sqlite.rs (1)

77-79: LGTM!

crates/event-sorcery/src/sqlite_event_repository.rs (1)

108-126: LGTM!

Cargo.toml (1)

2-6: LGTM!

crates/event-sorcery-ffi/Cargo.toml (1)

1-10: LGTM!

Also applies to: 21-31

crates/event-sorcery-ffi/src/lib.rs (1)

2-108: LGTM!

Also applies to: 115-440, 469-798

Comment thread crates/event-sorcery-ffi/build.rs Outdated
Comment thread crates/event-sorcery-ffi/build.rs Outdated
Comment thread crates/event-sorcery-ffi/Cargo.toml Outdated
Comment thread crates/event-sorcery-ffi/src/lib.rs
Comment thread crates/event-sorcery-ffi/src/lib.rs Outdated
Comment thread crates/event-sorcery-ffi/src/lib.rs Outdated
Base automatically changed from feat/0.4-engine-ffi to master July 16, 2026 17:11
@0xgleb

0xgleb commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@0xgleb
0xgleb added this pull request to the merge queue Jul 16, 2026
Merged via the queue into master with commit 3ed3ff1 Jul 16, 2026
7 checks passed
@0xgleb
0xgleb deleted the feat/0.4-c-api-lifecycle branch July 16, 2026 17:30
@github-project-automation github-project-automation Bot moved this from In Progress to Done in event-sorcery Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

The engine has no C ABI lifecycle

1 participant