nim-serialization/serialization.nim

169 lines
6.3 KiB
Nim

import
std/typetraits,
stew/shims/macros, faststreams/[inputs, outputs],
./serialization/[object_serialization, errors, formats]
export
inputs, outputs, object_serialization, errors, formats
template encode*(Format: type, value: auto, params: varargs[untyped]): auto =
mixin init, Writer, writeValue, PreferredOutputType
block: # https://github.com/nim-lang/Nim/issues/22874
{.noSideEffect.}:
# We assume that there is no side-effects here, because we are
# using a `memoryOutput`. The computed side-effects are coming
# from the fact that the dynamic dispatch mechanisms used in
# faststreams may be writing to a file or a network device.
try:
var s = memoryOutput()
type WriterType = Writer(Format)
var writer = unpackArgs(init, [WriterType, s, params])
writeValue writer, value
s.getOutput PreferredOutputType(Format)
except IOError:
raise (ref Defect)() # a memoryOutput cannot have an IOError
# TODO Nim cannot make sense of this initialization by var param?
proc readValue*(reader: var auto, T: type): T {.gcsafe, raises: [SerializationError, IOError].} =
{.warning[ProveInit]: false.}
mixin readValue
result = default(T)
reader.readValue(result)
{.warning[ProveInit]: true.}
template decode*(Format: distinct type,
input: string,
RecordType: distinct type,
params: varargs[untyped]): auto =
# TODO, this is dusplicated only due to a Nim bug:
# If `input` was `string|openArray[byte]`, it won't match `seq[byte]`
mixin init, Reader
block: # https://github.com/nim-lang/Nim/issues/22874
{.noSideEffect.}:
# We assume that there are no side-effects here, because we are
# using a `memoryInput`. The computed side-effects are coming
# from the fact that the dynamic dispatch mechanisms used in
# faststreams may be reading from a file or a network device.
try:
var stream = unsafeMemoryInput(input)
type ReaderType = Reader(Format)
var reader = unpackArgs(init, [ReaderType, stream, params])
reader.readValue(RecordType)
except IOError:
raise (ref Defect)() # memory inputs cannot raise an IOError
template decode*(Format: distinct type,
input: openArray[byte],
RecordType: distinct type,
params: varargs[untyped]): auto =
# TODO, this is dusplicated only due to a Nim bug:
# If `input` was `string|openArray[byte]`, it won't match `seq[byte]`
mixin init, Reader
block: # https://github.com/nim-lang/Nim/issues/22874
{.noSideEffect.}:
# We assume that there are no side-effects here, because we are
# using a `memoryInput`. The computed side-effects are coming
# from the fact that the dynamic dispatch mechanisms used in
# faststreams may be reading from a file or a network device.
try:
var stream = unsafeMemoryInput(input)
type ReaderType = Reader(Format)
var reader = unpackArgs(init, [ReaderType, stream, params])
reader.readValue(RecordType)
except IOError:
raise (ref Defect)() # memory inputs cannot raise an IOError
template loadFile*(Format: distinct type,
filename: string,
RecordType: distinct type,
params: varargs[untyped]): auto =
mixin init, Reader, readValue
var stream = memFileInput(filename)
try:
type ReaderType = Reader(Format)
var reader = unpackArgs(init, [ReaderType, stream, params])
reader.readValue(RecordType)
finally:
close stream
template loadFile*[RecordType](Format: type,
filename: string,
record: var RecordType,
params: varargs[untyped]) =
record = loadFile(Format, filename, RecordType, params)
template saveFile*(Format: type, filename: string, value: auto, params: varargs[untyped]) =
mixin init, Writer, writeValue
var stream = fileOutput(filename)
try:
type WriterType = Writer(Format)
var writer = unpackArgs(init, [WriterType, stream, params])
writer.writeValue(value)
finally:
close stream
template borrowSerialization*(Alias: type) {.dirty.} =
bind distinctBase
proc writeValue*[Writer](
writer: var Writer, value: Alias) {.raises: [IOError].} =
mixin writeValue
writeValue(writer, distinctBase value)
proc readValue*[Reader](reader: var Reader, value: var Alias) =
mixin readValue
value = Alias reader.readValue(distinctBase Alias)
template borrowSerialization*(Alias: distinct type,
OriginalType: distinct type) {.dirty.} =
proc writeValue*[Writer](
writer: var Writer, value: Alias) {.raises: [IOError].} =
mixin writeValue
writeValue(writer, OriginalType value)
proc readValue*[Reader](reader: var Reader, value: var Alias) =
mixin readValue
value = Alias reader.readValue(OriginalType)
template serializesAsBase*(SerializedType: distinct type,
Format: distinct type) =
mixin Reader, Writer
type ReaderType = Reader(Format)
type WriterType = Writer(Format)
template writeValue*(writer: var WriterType, value: SerializedType) =
mixin writeValue
writeValue(writer, distinctBase value)
template readValue*(reader: var ReaderType, value: var SerializedType) =
mixin readValue
value = SerializedType reader.readValue(distinctBase SerializedType)
macro serializesAsBaseIn*(SerializedType: type,
Formats: varargs[untyped]) =
result = newStmtList()
for Fmt in Formats:
result.add newCall(bindSym"serializesAsBase", SerializedType, Fmt)
template readValue*(stream: InputStream,
Format: type,
ValueType: type,
params: varargs[untyped]): untyped =
mixin Reader, init, readValue
type ReaderType = Reader(Format)
var reader = unpackArgs(init, [ReaderType, stream, params])
readValue reader, ValueType
template writeValue*(stream: OutputStream,
Format: type,
value: auto,
params: varargs[untyped]) =
mixin Writer, init, writeValue
type WriterType = Writer(Format)
var writer = unpackArgs(init, [WriterType, stream, params])
writeValue writer, value