Files
calctext/_bmad-output/implementation-artifacts/1-5-c-ffi-layer-for-swift.md
2026-03-16 19:54:53 -04:00

48 lines
2.0 KiB
Markdown

---
epic: 1
story: 1.5
title: "C FFI Layer (for Swift)"
status: draft
---
## Epic 1 — Core Calculation Engine (Rust Crate)
**Goal:** Build `calcpad-engine` as a standalone Rust crate that powers all platforms. This is the foundation.
### Story 1.5: C FFI Layer (for Swift)
As a macOS/iOS developer integrating CalcPad,
I want a stable C ABI with explicit memory ownership and no panics crossing the FFI boundary,
So that Swift can safely call into the Rust engine without undefined behavior.
**Acceptance Criteria:**
**Given** a Swift caller invoking `calcpad_eval_line` with a C string input
**When** the function executes
**Then** it returns a pointer to a `CalcResult` struct (or JSON-serialized string) allocated on the Rust heap
**And** the caller is responsible for freeing the result via `calcpad_free_result`
**Given** a Swift caller invoking `calcpad_eval_sheet` with multiple lines
**When** the function executes
**Then** it returns an array of results corresponding to each line
**And** variable assignments on earlier lines are visible to later lines
**Given** an expression that would cause a Rust panic (e.g., internal bug)
**When** the FFI function is called
**Then** `catch_unwind` intercepts the panic
**And** an error result is returned instead of unwinding into Swift
**Given** the FFI result is serialized as JSON
**When** the result crosses the FFI boundary
**Then** the JSON schema is versioned so Swift can handle backward-compatible changes
**And** the JSON includes result type, display value, raw value, and any error information
**Given** a `CalcResult` pointer returned from `calcpad_eval_line`
**When** the Swift caller calls `calcpad_free_result` with that pointer
**Then** the Rust allocator deallocates the memory
**And** no double-free or use-after-free is possible from correct usage
**Given** a null or invalid pointer passed to `calcpad_free_result`
**When** the function is called
**Then** it safely handles the null/invalid input without crashing
**And** no undefined behavior occurs