mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-05-07 10:49:28 +00:00
131 lines
3.7 KiB
Nim
131 lines
3.7 KiB
Nim
import std/[json, macros]
|
|
import results
|
|
import ./codegen/meta
|
|
|
|
proc ffiSerialize*(x: string): string =
|
|
$(%* x)
|
|
|
|
proc ffiSerialize*(x: cstring): string =
|
|
if x.isNil: "null"
|
|
else: ffiSerialize($x)
|
|
|
|
proc ffiSerialize*(x: int): string =
|
|
$x
|
|
|
|
proc ffiSerialize*(x: int32): string =
|
|
$x
|
|
|
|
proc ffiSerialize*(x: bool): string =
|
|
if x: "true" else: "false"
|
|
|
|
proc ffiSerialize*(x: float): string =
|
|
$(%* x)
|
|
|
|
proc ffiSerialize*(x: pointer): string =
|
|
$cast[uint](x)
|
|
|
|
proc ffiDeserialize*(s: cstring, _: typedesc[string]): Result[string, string] =
|
|
try:
|
|
let node = parseJson($s)
|
|
if node.kind != JString:
|
|
return err("expected JSON string")
|
|
ok(node.getStr())
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
proc ffiDeserialize*(s: cstring, _: typedesc[int]): Result[int, string] =
|
|
try:
|
|
ok(int(parseJson($s).getBiggestInt()))
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
proc ffiDeserialize*(s: cstring, _: typedesc[int32]): Result[int32, string] =
|
|
try:
|
|
ok(int32(parseJson($s).getBiggestInt()))
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
proc ffiDeserialize*(s: cstring, _: typedesc[bool]): Result[bool, string] =
|
|
try:
|
|
ok(parseJson($s).getBool())
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
proc ffiDeserialize*(s: cstring, _: typedesc[float]): Result[float, string] =
|
|
try:
|
|
ok(parseJson($s).getFloat())
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
proc ffiDeserialize*(s: cstring, _: typedesc[pointer]): Result[pointer, string] =
|
|
try:
|
|
let address = cast[pointer](uint(parseJson($s).getBiggestInt()))
|
|
ok(address)
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
proc ffiSerialize*[T](x: ptr T): string =
|
|
$cast[uint](x)
|
|
|
|
proc ffiDeserialize*[T](s: cstring, _: typedesc[ptr T]): Result[ptr T, string] =
|
|
try:
|
|
let address = cast[ptr T](uint(parseJson($s).getBiggestInt()))
|
|
ok(address)
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
macro ffiType*(body: untyped): untyped =
|
|
## Statement macro applied to a type declaration block.
|
|
## Generates ffiSerialize and ffiDeserialize overloads for each type,
|
|
## and registers the type in ffiTypeRegistry for binding generation.
|
|
## Usage:
|
|
## ffiType:
|
|
## type Foo = object
|
|
## field: int
|
|
let typeSection = body[0]
|
|
let typeDef = typeSection[0]
|
|
let typeName =
|
|
if typeDef[0].kind == nnkPostfix:
|
|
typeDef[0][1]
|
|
else:
|
|
typeDef[0]
|
|
|
|
# Collect field metadata for the codegen registry
|
|
let typeNameStr = $typeName
|
|
var fieldMetas: seq[FFIFieldMeta] = @[]
|
|
# typeDef layout: TypDef[name, genericParams, objectTy]
|
|
# objectTy layout: ObjectTy[empty, empty, recList]
|
|
let objTy = typeDef[2]
|
|
if objTy.kind == nnkObjectTy and objTy.len >= 3:
|
|
let recList = objTy[2]
|
|
if recList.kind == nnkRecList:
|
|
for identDef in recList:
|
|
if identDef.kind == nnkIdentDefs:
|
|
# identDef: [name1, ..., type, default]
|
|
let fieldType = identDef[^2]
|
|
var fieldTypeName: string
|
|
if fieldType.kind == nnkIdent:
|
|
fieldTypeName = $fieldType
|
|
elif fieldType.kind == nnkPtrTy:
|
|
fieldTypeName = "ptr " & $fieldType[0]
|
|
else:
|
|
fieldTypeName = fieldType.repr
|
|
for i in 0 ..< identDef.len - 2:
|
|
let fname = $identDef[i]
|
|
fieldMetas.add(FFIFieldMeta(name: fname, typeName: fieldTypeName))
|
|
|
|
ffiTypeRegistry.add(FFITypeMeta(name: typeNameStr, fields: fieldMetas))
|
|
|
|
let serializeProc = quote do:
|
|
proc ffiSerialize*(x: `typeName`): string =
|
|
$(%* x)
|
|
|
|
let deserializeProc = quote do:
|
|
proc ffiDeserialize*(s: cstring, _: typedesc[`typeName`]): Result[`typeName`, string] =
|
|
try:
|
|
ok(parseJson($s).to(`typeName`))
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
result = newStmtList(body, serializeProc, deserializeProc)
|