123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- discard """
- action: compile
- """
- # https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102
- # failed with "for a 'var' type a variable needs to be passed; but 'uint64(result)' is immutable"
- import
- std/[typetraits, macros]
- type
- DefaultFlavor = object
- template serializationFormatImpl(Name: untyped) {.dirty.} =
- type Name = object
- template serializationFormat(Name: untyped) =
- serializationFormatImpl(Name)
- template setReader(Format, FormatReader: distinct type) =
- when arity(FormatReader) > 1:
- template Reader(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
- else:
- template ReaderType(T: type Format): type = FormatReader
- template Reader(T: type Format): type = FormatReader
- template useDefaultReaderIn(T: untyped, Flavor: type) =
- mixin Reader
- template readValue(r: var Reader(Flavor), value: var T) =
- mixin readRecordValue
- readRecordValue(r, value)
- import mvaruintconv
- type
- FieldTag[RecordType: object; fieldName: static string] = distinct void
- func declval*(T: type): T {.compileTime.} =
- default(ptr T)[]
- macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
- var typeAst = getType(T)[1]
- var typeImpl: NimNode
- let isSymbol = not typeAst.isTuple
- if not isSymbol:
- typeImpl = typeAst
- else:
- typeImpl = getImpl(typeAst)
- result = newStmtList()
- var i = 0
- for field in recordFields(typeImpl):
- let
- fieldIdent = field.name
- realFieldName = newLit($fieldIdent.skipPragma)
- fieldName = realFieldName
- fieldIndex = newLit(i)
- let fieldNameDefs =
- if isSymbol:
- quote:
- const fieldName {.inject, used.} = `fieldName`
- const realFieldName {.inject, used.} = `realFieldName`
- else:
- quote:
- const fieldName {.inject, used.} = $`fieldIndex`
- const realFieldName {.inject, used.} = $`fieldIndex`
- let field =
- if isSymbol:
- quote do: declval(`T`).`fieldIdent`
- else:
- quote do: declval(`T`)[`fieldIndex`]
- result.add quote do:
- block:
- `fieldNameDefs`
- template FieldType: untyped {.inject, used.} = typeof(`field`)
- `body`
- # echo repr(result)
- template enumAllSerializedFields(T: type, body): untyped =
- enumAllSerializedFieldsImpl(T, body)
- type
- FieldReader[RecordType, Reader] = tuple[
- fieldName: string,
- reader: proc (rec: var RecordType, reader: var Reader)
- {.gcsafe, nimcall.}
- ]
- proc totalSerializedFieldsImpl(T: type): int =
- mixin enumAllSerializedFields
- enumAllSerializedFields(T): inc result
- template totalSerializedFields(T: type): int =
- (static(totalSerializedFieldsImpl(T)))
- template GetFieldType(FT: type FieldTag): type =
- typeof field(declval(FT.RecordType), FT.fieldName)
- proc makeFieldReadersTable(RecordType, ReaderType: distinct type,
- numFields: static[int]):
- array[numFields, FieldReader[RecordType, ReaderType]] =
- mixin enumAllSerializedFields, handleReadException
- var idx = 0
- enumAllSerializedFields(RecordType):
- proc readField(obj: var RecordType, reader: var ReaderType)
- {.gcsafe, nimcall.} =
- mixin readValue
- type F = FieldTag[RecordType, realFieldName]
- field(obj, realFieldName) = reader.readValue(GetFieldType(F))
- result[idx] = (fieldName, readField)
- inc idx
- proc fieldReadersTable(RecordType, ReaderType: distinct type): auto =
- mixin readValue
- type T = RecordType
- const numFields = totalSerializedFields(T)
- var tbl {.threadvar.}: ref array[numFields, FieldReader[RecordType, ReaderType]]
- if tbl == nil:
- tbl = new typeof(tbl)
- tbl[] = makeFieldReadersTable(RecordType, ReaderType, numFields)
- return addr(tbl[])
- proc readValue(reader: var auto, T: type): T =
- mixin readValue
- reader.readValue(result)
- template decode(Format: distinct type,
- input: string,
- RecordType: distinct type): auto =
- mixin Reader
- block: # https://github.com/nim-lang/Nim/issues/22874
- var reader: Reader(Format)
- reader.readValue(RecordType)
- template readValue(Format: type,
- ValueType: type): untyped =
- mixin Reader, init, readValue
- var reader: Reader(Format)
- readValue reader, ValueType
- template parseArrayImpl(numElem: untyped,
- actionValue: untyped) =
- actionValue
- serializationFormat Json
- template createJsonFlavor(FlavorName: untyped,
- skipNullFields = false) {.dirty.} =
- type FlavorName = object
- template Reader(T: type FlavorName): type = Reader(Json, FlavorName)
- type
- JsonReader[Flavor = DefaultFlavor] = object
- Json.setReader JsonReader
- template parseArray(r: var JsonReader; body: untyped) =
- parseArrayImpl(idx): body
- template parseArray(r: var JsonReader; idx: untyped; body: untyped) =
- parseArrayImpl(idx): body
- proc readRecordValue[T](r: var JsonReader, value: var T) =
- type
- ReaderType {.used.} = type r
- T = type value
- discard T.fieldReadersTable(ReaderType)
- proc readValue[T](r: var JsonReader, value: var T) =
- mixin readValue
- when value is seq:
- r.parseArray:
- readValue(r, value[0])
- elif value is object:
- readRecordValue(r, value)
- type
- RemoteSignerInfo = object
- id: uint32
- RemoteKeystore = object
- proc readValue(reader: var JsonReader, value: var RemoteKeystore) =
- discard reader.readValue(seq[RemoteSignerInfo])
- createJsonFlavor RestJson
- useDefaultReaderIn(RemoteSignerInfo, RestJson)
- proc readValue(reader: var JsonReader[RestJson], value: var uint64) =
- discard reader.readValue(string)
- discard Json.decode("", RemoteKeystore)
- block: # https://github.com/nim-lang/Nim/issues/22874
- var reader: Reader(RestJson)
- discard reader.readValue(RemoteSignerInfo)
|