Transfer some of the complexity in handling case objects from SSZ to the general serialization lib
This commit is contained in:
parent
e97e0b0684
commit
56ad4b18de
|
@ -4,6 +4,15 @@ import
|
||||||
type
|
type
|
||||||
FieldTag[RecordType; fieldName: static string; FieldType] = distinct void
|
FieldTag[RecordType; fieldName: static string; FieldType] = distinct void
|
||||||
|
|
||||||
|
let
|
||||||
|
# Identifiers affecting the public interface of the library:
|
||||||
|
valueVar {.compileTime.} = ident "value"
|
||||||
|
readerVar {.compileTime.} = ident "reader"
|
||||||
|
writerVar {.compileTime.} = ident "writer"
|
||||||
|
holderVar {.compileTime.} = ident "holder"
|
||||||
|
fieldNameVar {.compileTime.} = ident "fieldName"
|
||||||
|
FieldTypeSym {.compileTime.} = ident "FieldType"
|
||||||
|
|
||||||
template dontSerialize* {.pragma.}
|
template dontSerialize* {.pragma.}
|
||||||
## Specifies that a certain field should be ignored for
|
## Specifies that a certain field should be ignored for
|
||||||
## the purposes of serialization
|
## the purposes of serialization
|
||||||
|
@ -33,19 +42,31 @@ template enumInstanceSerializedFields*(obj: auto,
|
||||||
when not hasCustomPragmaFixed(ObjType, fieldNameVar, dontSerialize):
|
when not hasCustomPragmaFixed(ObjType, fieldNameVar, dontSerialize):
|
||||||
body
|
body
|
||||||
|
|
||||||
macro enumAllSerializedFields*(T: type,
|
macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
|
||||||
fieldNameVar, fieldTypeVar,
|
|
||||||
body: untyped): untyped =
|
|
||||||
## Expands a block over all fields of a type
|
## Expands a block over all fields of a type
|
||||||
##
|
##
|
||||||
## Inside the block body, the passed `fieldNameVar` identifier
|
|
||||||
## will refer to the name of each field as a string. `fieldTypeVar`
|
|
||||||
## is an identifier that will refer to the field's type.
|
|
||||||
##
|
|
||||||
## Please note that the main difference between
|
## Please note that the main difference between
|
||||||
## `enumInstanceSerializedFields` and `enumAllSerializedFields`
|
## `enumInstanceSerializedFields` and `enumAllSerializedFields`
|
||||||
## is that the later will visit all fields of case objects.
|
## is that the later will visit all fields of case objects.
|
||||||
##
|
##
|
||||||
|
## Inside the block body, the following symbols will be defined:
|
||||||
|
##
|
||||||
|
## * `fieldName`
|
||||||
|
## String literal for the field name
|
||||||
|
##
|
||||||
|
## * `FieldType`
|
||||||
|
## Type alias for the field type
|
||||||
|
##
|
||||||
|
## * `fieldCaseDisciminator`
|
||||||
|
## String literal denoting the name of the case object
|
||||||
|
## discrimator under which the visited field is nested.
|
||||||
|
## If the field is not nested in a specific case branch,
|
||||||
|
## this will be an empty string.
|
||||||
|
##
|
||||||
|
## * `fieldCaseBranches`
|
||||||
|
## A set literal node denoting the possible values of the
|
||||||
|
## case object discrimator which make this field accessible.
|
||||||
|
##
|
||||||
## The order of visited fields matches the order of the fields in
|
## The order of visited fields matches the order of the fields in
|
||||||
## the object definition unless `serialziedFields` is used to specify
|
## the object definition unless `serialziedFields` is used to specify
|
||||||
## a different order. Fields marked with the `dontSerialize` pragma
|
## a different order. Fields marked with the `dontSerialize` pragma
|
||||||
|
@ -60,18 +81,38 @@ macro enumAllSerializedFields*(T: type,
|
||||||
continue
|
continue
|
||||||
|
|
||||||
let
|
let
|
||||||
fident = field.name
|
|
||||||
fieldName = newLit($field.name)
|
|
||||||
fieldType = field.typ
|
fieldType = field.typ
|
||||||
|
fieldIdent = field.name
|
||||||
|
fieldName = newLit($fieldIdent)
|
||||||
|
discrimator = newLit(if field.caseField == nil: ""
|
||||||
|
else: $field.caseField[0])
|
||||||
|
branches = field.caseBranch
|
||||||
|
|
||||||
result.add quote do:
|
result.add quote do:
|
||||||
block:
|
block:
|
||||||
template `fieldNameVar`: auto = `fieldName`
|
template `fieldNameVar`: auto = `fieldName`
|
||||||
|
template fieldCaseDisciminator: auto = `discrimator`
|
||||||
|
template fieldCaseBranches: auto = `branches`
|
||||||
# type `fieldTypeVar` = `fieldType`
|
# type `fieldTypeVar` = `fieldType`
|
||||||
# TODO: This is a work-around for a classic Nim issue:
|
# TODO: This is a work-around for a classic Nim issue:
|
||||||
type `fieldTypeVar` = type(default(`T`).`fident`)
|
type `FieldTypeSym` = type(default(`T`).`fieldIdent`)
|
||||||
`body`
|
`body`
|
||||||
|
|
||||||
|
template enumAllSerializedFields*(T: type, body): untyped =
|
||||||
|
when T is ref|ptr:
|
||||||
|
type TT = type(default(T)[])
|
||||||
|
enumAllSerializedFieldsImpl(TT, body)
|
||||||
|
else:
|
||||||
|
enumAllSerializedFieldsImpl(T, body)
|
||||||
|
|
||||||
|
func isCaseObject*(T: type): bool {.compileTime.} =
|
||||||
|
genExpr:
|
||||||
|
enumAllSerializedFields(T):
|
||||||
|
if fieldCaseDisciminator != "":
|
||||||
|
return newLit(true)
|
||||||
|
|
||||||
|
newLit(false)
|
||||||
|
|
||||||
type
|
type
|
||||||
FieldMarkerImpl*[name: static string] = object
|
FieldMarkerImpl*[name: static string] = object
|
||||||
|
|
||||||
|
@ -84,7 +125,7 @@ type
|
||||||
|
|
||||||
proc totalSerializedFieldsImpl(T: type): int =
|
proc totalSerializedFieldsImpl(T: type): int =
|
||||||
mixin enumAllSerializedFields
|
mixin enumAllSerializedFields
|
||||||
enumAllSerializedFields(T, fieldName, fieldType): inc result
|
enumAllSerializedFields(T): inc result
|
||||||
|
|
||||||
template totalSerializedFields*(T: type): int =
|
template totalSerializedFields*(T: type): int =
|
||||||
(static(totalSerializedFieldsImpl(T)))
|
(static(totalSerializedFieldsImpl(T)))
|
||||||
|
@ -101,7 +142,7 @@ proc makeFieldReadersTable(RecordType, Reader: distinct type):
|
||||||
seq[FieldReader[RecordType, Reader]] =
|
seq[FieldReader[RecordType, Reader]] =
|
||||||
mixin enumAllSerializedFields, readFieldIMPL
|
mixin enumAllSerializedFields, readFieldIMPL
|
||||||
|
|
||||||
enumAllSerializedFields(RecordType, fieldName, FieldType):
|
enumAllSerializedFields(RecordType):
|
||||||
proc readField(obj: var RecordType, reader: var Reader) {.gcsafe, nimcall.} =
|
proc readField(obj: var RecordType, reader: var Reader) {.gcsafe, nimcall.} =
|
||||||
try:
|
try:
|
||||||
type F = FieldTag[RecordType, fieldName, type(FieldType)]
|
type F = FieldTag[RecordType, fieldName, type(FieldType)]
|
||||||
|
@ -140,7 +181,8 @@ macro setSerializedFields*(T: typedesc, fields: varargs[untyped]): untyped =
|
||||||
for f in fields: fieldsArray.add newCall(bindSym"ident", newLit($f))
|
for f in fields: fieldsArray.add newCall(bindSym"ident", newLit($f))
|
||||||
|
|
||||||
template payload(T: untyped, fieldsArray) {.dirty.} =
|
template payload(T: untyped, fieldsArray) {.dirty.} =
|
||||||
bind default, quote, add, getType, newStmtList, newLit, newDotExpr, `$`, `[]`, getAst
|
bind default, quote, add, getType, newStmtList,
|
||||||
|
ident, newLit, newDotExpr, `$`, `[]`, getAst
|
||||||
|
|
||||||
macro enumInstanceSerializedFields*(ins: T,
|
macro enumInstanceSerializedFields*(ins: T,
|
||||||
fieldNameVar, fieldVar,
|
fieldNameVar, fieldVar,
|
||||||
|
@ -154,6 +196,7 @@ macro setSerializedFields*(T: typedesc, fields: varargs[untyped]): untyped =
|
||||||
fieldName = newLit($field)
|
fieldName = newLit($field)
|
||||||
fieldAccessor = newDotExpr(ins, field)
|
fieldAccessor = newDotExpr(ins, field)
|
||||||
|
|
||||||
|
# TODO replace with getAst once it's ready
|
||||||
template fieldPayload(fieldNameVar, fieldName, fieldVar,
|
template fieldPayload(fieldNameVar, fieldName, fieldVar,
|
||||||
fieldAccessor, body) =
|
fieldAccessor, body) =
|
||||||
block:
|
block:
|
||||||
|
@ -166,9 +209,7 @@ macro setSerializedFields*(T: typedesc, fields: varargs[untyped]): untyped =
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
macro enumAllSerializedFields*(typ: type T,
|
macro enumAllSerializedFields*(typ: type T, body: untyped): untyped =
|
||||||
fieldNameVar, fieldTypeVar,
|
|
||||||
body: untyped): untyped =
|
|
||||||
var
|
var
|
||||||
fields = fieldsArray
|
fields = fieldsArray
|
||||||
res = newStmtList()
|
res = newStmtList()
|
||||||
|
@ -177,18 +218,24 @@ macro setSerializedFields*(T: typedesc, fields: varargs[untyped]): untyped =
|
||||||
for field in fields:
|
for field in fields:
|
||||||
let
|
let
|
||||||
fieldName = newLit($field)
|
fieldName = newLit($field)
|
||||||
fieldAccessor = newDotExpr(typ, field)
|
fieldNameVar = ident "fieldName"
|
||||||
|
FieldTypeSym = ident "FieldType"
|
||||||
|
|
||||||
|
# TODO replace with getAst once it's ready
|
||||||
template fieldPayload(fieldNameVar, fieldName,
|
template fieldPayload(fieldNameVar, fieldName,
|
||||||
fieldTypeVar, typ, field,
|
fieldTypeVar, typ, field,
|
||||||
body) =
|
body) =
|
||||||
block:
|
block:
|
||||||
const fieldNameVar = fieldName
|
const fieldNameVar = fieldName
|
||||||
type fieldTypeVar = type(default(typ).field)
|
type fieldTypeVar = type(default(typ).field)
|
||||||
|
|
||||||
|
template fieldCaseDisciminator: auto = ""
|
||||||
|
template fieldCaseBranches: auto = nil
|
||||||
|
|
||||||
body
|
body
|
||||||
|
|
||||||
res.add getAst(fieldPayload(fieldNameVar, fieldName,
|
res.add getAst(fieldPayload(fieldNameVar, fieldName,
|
||||||
fieldTypeVar, typ, field,
|
FieldTypeSym, typ, field,
|
||||||
body))
|
body))
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
@ -212,13 +259,6 @@ proc getReaderAndWriter(customSerializationBody: NimNode): (NimNode, NimNode) =
|
||||||
else:
|
else:
|
||||||
fail n
|
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,
|
proc genCustomSerializationForField(Format, field,
|
||||||
readBody, writeBody: NimNode): NimNode =
|
readBody, writeBody: NimNode): NimNode =
|
||||||
var
|
var
|
||||||
|
|
|
@ -66,6 +66,13 @@ type
|
||||||
HoldsArray* = object
|
HoldsArray* = object
|
||||||
data*: seq[int]
|
data*: seq[int]
|
||||||
|
|
||||||
|
static:
|
||||||
|
assert isCaseObject(CaseObject)
|
||||||
|
assert isCaseObject(CaseObjectRef)
|
||||||
|
|
||||||
|
assert(not isCaseObject(Transaction))
|
||||||
|
assert(not isCaseObject(HoldsSet))
|
||||||
|
|
||||||
Meter.borrowSerialization int
|
Meter.borrowSerialization int
|
||||||
Simple.setSerializedFields distance, x, y
|
Simple.setSerializedFields distance, x, y
|
||||||
|
|
||||||
|
@ -289,4 +296,3 @@ proc executeReaderWriterTests*(Format: type) =
|
||||||
|
|
||||||
executeRoundtripTests(Format)
|
executeRoundtripTests(Format)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue