diff --git a/README.md b/README.md index 9d2f88a..1a7b46c 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,11 @@ No hand-written `.h` files, no manual request enums, no shared-memory plumbing. `{.ffiDtor.}`, `{.ffiEvent.}`). - You **call `genBindings()` last**, which emits the foreign bindings. -Every request/response crosses the boundary as a single CBOR blob; the ctx -handle returned by the constructor is the only pointer that crosses. Each -`{.ffi.}` proc runs on the library's own chronos event loop, so bodies can -`await` freely. +By default, every request/response crosses the boundary as a single CBOR blob +(the wire format is configurable per library or per annotation — see +[ABI format](#abi-format)); the ctx handle returned by the constructor is the +only pointer that crosses. Each `{.ffi.}` proc runs on the library's own chronos +event loop, so bodies can `await` freely. ## Minimal example @@ -31,7 +32,8 @@ type Counter = object declareLibrary("counter", Counter) # 2. Request/response shapes. Any {.ffi.} object type becomes a first-class -# struct/class in the generated bindings and rides the wire as CBOR. +# struct/class in the generated bindings and rides the wire in the library's +# ABI format (CBOR by default). type BumpRequest {.ffi.} = object by: int @@ -51,7 +53,7 @@ proc counterBump*( return ok(BumpResponse(newValue: c.value + req.by)) # 5. Destructor: exactly one param (the library value). -proc counter_destroy*(c: Counter) {.ffiDtor.} = +proc counterDestroy*(c: Counter) {.ffiDtor.} = discard # 6. genBindings() must be the LAST FFI call in the file (see below). @@ -66,7 +68,7 @@ The generated C export names are the snake_case form of the proc names, e.g. | Pragma | Applies to | Purpose | | --- | --- | --- | | `declareLibrary(name, LibType[, defaultABIFormat])` | call | Registers the library, its state type, and the default wire format. Must run before any annotation. | -| `{.ffi.}` on a `type` | `object` | Registers the type for binding generation; it serializes via the generic CBOR overloads. | +| `{.ffi.}` on a `type` | `object` | Registers the type for binding generation; it serializes via the library's ABI format (CBOR by default). | | `{.ffi.}` on a `proc` | proc | Exposes a method. First param is the library value, then typed params; returns `Future[Result[T, string]]`. | | `{.ffiCtor.}` | proc | The constructor. Returns `Future[Result[LibType, string]]`; creates the FFI context. | | `{.ffiDtor.}` | proc | The destructor. Exactly one param `(x: LibType)`; tears the context down. | diff --git a/ffi/internal/ffi_macro.nim b/ffi/internal/ffi_macro.nim index 7910ea1..3644ce4 100644 --- a/ffi/internal/ffi_macro.nim +++ b/ffi/internal/ffi_macro.nim @@ -20,6 +20,18 @@ proc requireLibraryDeclared(where: string) {.compileTime.} = ": declareLibrary(name, LibType[, defaultABIFormat]) must be called before any FFI annotation" ) +proc resolveEventWireName( + leading: seq[NimNode], userProcName: NimNode +): tuple[wireName: string, abiSpecStart: int] {.compileTime.} = + ## A leading string that doesn't parse as an `"abi = ..."` spec is the explicit + ## wire name; anything else means derive the name from the proc. Returns the + ## resolved name and the index where the trailing ABI specs begin. + if leading.len > 0 and leading[0].kind in {nnkStrLit, nnkRStrLit, nnkTripleStrLit} and + ($leading[0]).len > 0 and not parseAbiSpec($leading[0]).ok: + ($leading[0], 1) + else: + (camelToSnakeCase($userProcName), 0) + proc requireBeforeGenBindings(where: string) {.compileTime.} = ## Enforce that this annotation expands before `genBindings()`. Anything ## registered afterwards never reaches the generator, so turn what used to be @@ -1705,18 +1717,8 @@ macro ffiEvent*(args: varargs[untyped]): untyped = if procName.kind == nnkPostfix: userProcName = procName[1] - # Leading args before the proc are an optional explicit wire-name string - # followed by optional `"abi = ..."` specs. A leading string that isn't an - # ABI spec is the wire name; otherwise derive it from the proc name. let leading = args[0 ..^ 2] - var wireName = "" - var abiSpecStart = 0 - if leading.len > 0 and leading[0].kind in {nnkStrLit, nnkRStrLit, nnkTripleStrLit} and - not parseAbiSpec($leading[0]).ok: - wireName = $leading[0] - abiSpecStart = 1 - if wireName.len == 0: - wireName = camelToSnakeCase($userProcName) + let (wireName, abiSpecStart) = resolveEventWireName(leading, userProcName) let abiFormat = resolveABIFormat(leading[abiSpecStart ..^ 1]) gateABIFormat(abiFormat, "`.ffiEvent.` proc")