mirror of
https://github.com/status-im/nim-serialization.git
synced 2025-02-17 06:27:02 +00:00
Custom serialization for object fields (initial version)
This commit is contained in:
parent
e126d48e6c
commit
99daa96284
@ -1,6 +1,9 @@
|
||||
import
|
||||
stew/shims/macros, stew/objects
|
||||
|
||||
type
|
||||
FieldTag[RecordType; fieldName: static string; FieldType] = distinct void
|
||||
|
||||
template dontSerialize* {.pragma.}
|
||||
## Specifies that a certain field should be ignored for
|
||||
## the purposes of serialization
|
||||
@ -48,10 +51,11 @@ macro enumAllSerializedFields*(T: type,
|
||||
## a different order. Fields marked with the `dontSerialize` pragma
|
||||
## are skipped.
|
||||
##
|
||||
var Timpl = getImpl(getType(T)[1])
|
||||
var typeAst = getType(T)[1]
|
||||
var typeImpl = getImpl(typeAst)
|
||||
result = newStmtList()
|
||||
|
||||
for field in recordFields(Timpl):
|
||||
for field in recordFields(typeImpl):
|
||||
if field.readPragma("dontSerialize") != nil:
|
||||
continue
|
||||
|
||||
@ -63,7 +67,7 @@ macro enumAllSerializedFields*(T: type,
|
||||
result.add quote do:
|
||||
block:
|
||||
template `fieldNameVar`: auto = `fieldName`
|
||||
# type `fieldTypeVar` = `fieldType`
|
||||
# type `fieldTypeVar` = `fieldType`
|
||||
# TODO: This is a work-around for a classic Nim issue:
|
||||
type `fieldTypeVar` = type(default(`T`).`fident`)
|
||||
`body`
|
||||
@ -73,7 +77,7 @@ type
|
||||
|
||||
FieldReader*[RecordType, Reader] = tuple[
|
||||
fieldName: string,
|
||||
reader: proc (rec: var RecordType, reader: var Reader) {.nimcall.}
|
||||
reader: proc (rec: var RecordType, reader: var Reader) {.gcsafe, nimcall.}
|
||||
]
|
||||
|
||||
FieldReadersTable*[RecordType, Reader] = openarray[FieldReader[RecordType, Reader]]
|
||||
@ -88,14 +92,20 @@ template totalSerializedFields*(T: type): int =
|
||||
macro customSerialization*(field: untyped, definition): untyped =
|
||||
discard
|
||||
|
||||
template readFieldIMPL[Reader](field: type FieldTag,
|
||||
reader: var Reader): untyped =
|
||||
mixin readValue
|
||||
reader.readValue(field.FieldType)
|
||||
|
||||
proc makeFieldReadersTable(RecordType, Reader: distinct type):
|
||||
seq[FieldReader[RecordType, Reader]] =
|
||||
mixin enumAllSerializedFields
|
||||
mixin enumAllSerializedFields, readFieldIMPL
|
||||
|
||||
enumAllSerializedFields(RecordType, fieldName, FieldType):
|
||||
proc readField(obj: var RecordType, reader: var Reader) {.nimcall.} =
|
||||
try:
|
||||
obj.field(fieldName) = reader.readValue(FieldType)
|
||||
type F = FieldTag[RecordType, fieldName, type(FieldType)]
|
||||
obj.field(fieldName) = readFieldIMPL(F, reader)
|
||||
except SerializationError:
|
||||
raise
|
||||
except CatchableError as err:
|
||||
@ -105,7 +115,7 @@ proc makeFieldReadersTable(RecordType, Reader: distinct type):
|
||||
result.add((fieldName, readField))
|
||||
|
||||
proc fieldReadersTable*(RecordType, Reader: distinct type):
|
||||
ptr seq[FieldReader[RecordType, Reader]] {.gcsafe.} =
|
||||
ptr seq[FieldReader[RecordType, Reader]] =
|
||||
mixin readValue
|
||||
var tbl {.global.} = makeFieldReadersTable(RecordType, Reader)
|
||||
{.gcsafe.}:
|
||||
@ -175,3 +185,81 @@ macro setSerializedFields*(T: typedesc, fields: varargs[untyped]): untyped =
|
||||
|
||||
return getAst(payload(T, fieldsArray))
|
||||
|
||||
proc getReaderAndWriter(customSerializationBody: NimNode): (NimNode, NimNode) =
|
||||
template fail(n) =
|
||||
error "useCustomSerialization expects a block with only `read` and `write` definitions", n
|
||||
|
||||
for n in customSerializationBody:
|
||||
if n.kind in nnkCallKinds:
|
||||
if eqIdent(n[0], "read"):
|
||||
result[0] = n[1]
|
||||
elif eqIdent(n[0], "write"):
|
||||
result[1] = n[1]
|
||||
else:
|
||||
fail n[0]
|
||||
elif n.kind == nnkCommentStmt:
|
||||
continue
|
||||
else:
|
||||
fail n
|
||||
|
||||
let
|
||||
# Identifiers affecting the public interface of the library:
|
||||
readerVar {.compileTime.} = ident "reader"
|
||||
writerVar {.compileTime.} = ident "writer"
|
||||
holderVar {.compileTime.} = ident "holder"
|
||||
valueVar {.compileTime.} = ident "value"
|
||||
|
||||
proc genCustomSerializationForField(Format, field,
|
||||
readBody, writeBody: NimNode): NimNode =
|
||||
var
|
||||
RecordType = field[0]
|
||||
fieldIdent = field[1]
|
||||
fieldName = newLit $fieldIdent
|
||||
FieldType = genSym(nskType, "FieldType")
|
||||
|
||||
result = newStmtList()
|
||||
result.add quote do:
|
||||
type `FieldType` = type default(`RecordType`).`fieldIdent`
|
||||
|
||||
if readBody != nil:
|
||||
result.add quote do:
|
||||
type Reader = ReaderType(`Format`)
|
||||
proc readFieldIMPL*(F: type FieldTag[`RecordType`, `fieldName`, auto],
|
||||
`readerVar`: var Reader): `FieldType` =
|
||||
`readBody`
|
||||
|
||||
if writeBody != nil:
|
||||
result.add quote do:
|
||||
type Writer = WriterType(`Format`)
|
||||
proc writeFieldIMPL*(F: type FieldTag[`RecordType`, `fieldName`, auto],
|
||||
`writerVar`: var Writer) =
|
||||
`writeBody`
|
||||
|
||||
proc genCustomSerializationForType(Format, typ: NimNode,
|
||||
readBody, writeBody: NimNode): NimNode =
|
||||
result = newStmtList()
|
||||
|
||||
if readBody != nil:
|
||||
result.add quote do:
|
||||
type Reader = ReaderType(`Format`)
|
||||
proc readValue*(`readerVar`: var Reader, T: type `typ`): `typ` =
|
||||
`readBody`
|
||||
|
||||
if writeBody != nil:
|
||||
result.add quote do:
|
||||
type Writer = WriterType(`Format`)
|
||||
proc writeValue*(`writerVar`: var Writer, `valueVar`: `typ`) =
|
||||
`writeBody`
|
||||
|
||||
macro useCustomSerialization*(Format: typed, field: untyped, body: untyped): untyped =
|
||||
let (readBody, writeBody) = getReaderAndWriter(body)
|
||||
if field.kind == nnkDotExpr:
|
||||
result = genCustomSerializationForField(Format, field, readBody, writeBody)
|
||||
elif field.kind in {nnkIdent, nnkAccQuoted}:
|
||||
result = genCustomSerializationForType(Format, field, readBody, writeBody)
|
||||
else:
|
||||
error "useCustomSerialization expects a type name or a field of a type (e.g. MyType.myField)"
|
||||
|
||||
when defined(debugUseCustomSerialization):
|
||||
echo result.repr
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user