nim-ffi/ffi/codegen/meta.nim
Ivan FB f08cb7971d
feat(codegen): C native event payloads + -d:ffiMode (native/cbor/both) + tasks
- Add the `-d:ffiMode=native|cbor|both` strdefine (default both) with
  `ffiEmitNative`/`ffiEmitCbor` helpers; the C generator now emits only the
  selected header(s) (`<lib>.h` and/or `<lib>_cbor.h`).
- Native C events: the native header documents each event's payload type
  (`"on_echo_fired" -> const EchoEvent *`) so consumers cast the callback's msg
  to the typed struct — the bare native listener already delivers it.
- nimble tasks: `genbindings_c` (both), `genbindings_c_native`,
  `genbindings_c_cbor`.

Verified: native mode emits only my_timer.h, cbor only my_timer_cbor.h, both
emits both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:37:27 +02:00

68 lines
2.3 KiB
Nim

## Compile-time metadata types for FFI binding generation.
## Populated by the {.ffiCtor.} and {.ffi.} macros and consumed by codegen.
type
FFIParamMeta* = object
name*: string # Nim param name, e.g. "req"
typeName*: string # Nim type name, e.g. "EchoRequest"
isPtr*: bool # true if the type is `ptr T`
FFIKind* {.pure.} = enum
FFI
CTOR
DTOR
FFIProcMeta* = object
procName*: string # e.g. "timer_echo"
libName*: string # library name, e.g. "timer"
kind*: FFIKind
libTypeName*: string # e.g. "Timer"
extraParams*: seq[FFIParamMeta] # all params except the lib param
returnTypeName*: string # e.g. "EchoResponse", "string", "pointer"
returnIsPtr*: bool # true if return type is ptr T
FFIFieldMeta* = object
name*: string # e.g. "delayMs"
typeName*: string # e.g. "int"
FFITypeMeta* = object
name*: string
fields*: seq[FFIFieldMeta]
FFIEventMeta* = object
## Library-initiated event declared with `{.ffiEvent: "wire_name".}`.
## `wireName` is the literal string the foreign side dispatches on
## (it appears in the CBOR `eventType` field, verbatim — no case
## conversion). `payloadTypeName` is the Nim type of the single
## payload parameter.
wireName*: string
nimProcName*: string
libName*: string
payloadTypeName*: string
# Compile-time registries populated by the macros
var ffiProcRegistry* {.compileTime.}: seq[FFIProcMeta]
var ffiTypeRegistry* {.compileTime.}: seq[FFITypeMeta]
var ffiEventRegistry* {.compileTime.}: seq[FFIEventMeta]
var currentLibName* {.compileTime.}: string
# Target language for binding generation; override with -d:targetLang=cpp
const targetLang* {.strdefine.} = "rust"
# Which ABI(s) to emit: "native" (zero-serialization C structs), "cbor"
# (inter-process), or "both" (default). Override with -d:ffiMode=native.
const ffiMode* {.strdefine.} = "both"
func ffiEmitNative*(): bool =
ffiMode == "native" or ffiMode == "both"
func ffiEmitCbor*(): bool =
ffiMode == "cbor" or ffiMode == "both"
# Output directory for generated bindings; set with -d:ffiOutputDir=path/to/dir
const ffiOutputDir* {.strdefine.} = ""
# Nim source path (relative to outputDir) embedded in generated build files;
# set with -d:ffiSrcPath=../relative/path.nim
const ffiSrcPath* {.strdefine.} = ""