mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-05-07 02:39:38 +00:00
166 lines
4.3 KiB
Nim
166 lines
4.3 KiB
Nim
import std/[json, macros, options]
|
|
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 ffiSerialize*[T](x: seq[T]): string =
|
|
var arr = newJArray()
|
|
for item in x:
|
|
arr.add(parseJson(ffiSerialize(item)))
|
|
result = $arr
|
|
|
|
proc ffiSerialize*[T](x: Option[T]): string =
|
|
if x.isSome:
|
|
ffiSerialize(x.get)
|
|
else:
|
|
"null"
|
|
|
|
proc ffiSerialize*[T: object](x: T): string =
|
|
$(%*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)
|
|
|
|
proc ffiDeserialize*[T](s: cstring, _: typedesc[seq[T]]): Result[seq[T], string] =
|
|
try:
|
|
let node = parseJson($s)
|
|
if node.kind != JArray:
|
|
return err("expected JSON array")
|
|
var resultSeq: seq[T] = @[]
|
|
for item in node:
|
|
let itemJson = $item
|
|
let parsed = ffiDeserialize(itemJson.cstring, typedesc[T])
|
|
if parsed.isOk:
|
|
resultSeq.add(parsed.get)
|
|
else:
|
|
return err(parsed.error)
|
|
ok(resultSeq)
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
proc ffiDeserialize*[T](s: cstring, _: typedesc[Option[T]]): Result[Option[T], string] =
|
|
try:
|
|
let node = parseJson($s)
|
|
if node.kind == JNull:
|
|
ok(none(T))
|
|
else:
|
|
let itemJson = $node
|
|
let parsed = ffiDeserialize(itemJson.cstring, typedesc[T])
|
|
if parsed.isOk:
|
|
ok(some(parsed.get))
|
|
else:
|
|
err(parsed.error)
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
proc ffiDeserialize*[T: object](s: cstring, _: typedesc[T]): Result[T, string] =
|
|
try:
|
|
ok(parseJson($s).to(T))
|
|
except Exception as e:
|
|
err(e.msg)
|
|
|
|
macro ffiType*(body: untyped): untyped =
|
|
## Statement macro applied to a type declaration block.
|
|
## Registers the type in ffiTypeRegistry for binding generation.
|
|
## Serialization is handled by the generic ffiSerialize/ffiDeserialize overloads.
|
|
## 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]
|
|
|
|
let typeNameStr = $typeName
|
|
var fieldMetas: seq[FFIFieldMeta] = @[]
|
|
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:
|
|
let fieldType = identDef[^2]
|
|
let fieldTypeName =
|
|
if fieldType.kind == nnkIdent: $fieldType
|
|
elif fieldType.kind == nnkPtrTy: "ptr " & $fieldType[0]
|
|
else: fieldType.repr
|
|
for i in 0 ..< identDef.len - 2:
|
|
fieldMetas.add(FFIFieldMeta(name: $identDef[i], typeName: fieldTypeName))
|
|
|
|
ffiTypeRegistry.add(FFITypeMeta(name: typeNameStr, fields: fieldMetas))
|
|
result = body
|