diff --git a/serialization.nim b/serialization.nim index e8e7941..8bba9f8 100644 --- a/serialization.nim +++ b/serialization.nim @@ -1,14 +1,16 @@ import std/typetraits, - stew/shims/macros, faststreams/[inputs, outputs], + stew/shims/macros, + faststreams/[inputs, outputs], ./serialization/[object_serialization, errors, formats] -export - inputs, outputs, object_serialization, errors, formats +export inputs, outputs, object_serialization, errors, formats -template encode*(Format: type, value: auto, params: varargs[untyped]): auto = +template encode*( + Format: type SerializationFormat, value: auto, params: varargs[untyped] +): auto = mixin init, Writer, writeValue, PreferredOutputType - block: # https://github.com/nim-lang/Nim/issues/22874 + 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 @@ -24,21 +26,23 @@ template encode*(Format: type, value: auto, params: varargs[untyped]): auto = 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].} = +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]` +template decode*( + Format: type SerializationFormat, + input: string|openArray[char]|seq[byte]|openArray[byte], + RecordType: type, + params: varargs[untyped], +): auto = mixin init, Reader - block: # https://github.com/nim-lang/Nim/issues/22874 + 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 @@ -52,31 +56,12 @@ template decode*(Format: distinct type, 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 = +template loadFile*( + Format: type SerializationFormat, + filename: string, + RecordType: type, + params: varargs[untyped], +): auto = mixin init, Reader, readValue var stream = memFileInput(filename) @@ -87,13 +72,20 @@ template loadFile*(Format: distinct type, finally: close stream -template loadFile*[RecordType](Format: type, - filename: string, - record: var RecordType, - params: varargs[untyped]) = +template loadFile*[RecordType]( + Format: type SerializationFormat, + 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]) = +template saveFile*( + Format: type SerializationFormat, + filename: string, + value: auto, + params: varargs[untyped], +) = mixin init, Writer, writeValue var stream = fileOutput(filename) @@ -107,8 +99,7 @@ template saveFile*(Format: type, filename: string, value: auto, params: varargs[ template borrowSerialization*(Alias: type) {.dirty.} = bind distinctBase - proc writeValue*[Writer]( - writer: var Writer, value: Alias) {.raises: [IOError].} = + proc writeValue*[Writer](writer: var Writer, value: Alias) {.raises: [IOError].} = mixin writeValue writeValue(writer, distinctBase value) @@ -116,11 +107,8 @@ template borrowSerialization*(Alias: type) {.dirty.} = 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].} = +template borrowSerialization*(Alias: type, OriginalType: type) {.dirty.} = + proc writeValue*[Writer](writer: var Writer, value: Alias) {.raises: [IOError].} = mixin writeValue writeValue(writer, OriginalType value) @@ -128,8 +116,7 @@ template borrowSerialization*(Alias: distinct type, mixin readValue value = Alias reader.readValue(OriginalType) -template serializesAsBase*(SerializedType: distinct type, - Format: distinct type) = +template serializesAsBase*(SerializedType: type, Format: type SerializationFormat) = mixin Reader, Writer type ReaderType = Reader(Format) @@ -143,25 +130,28 @@ template serializesAsBase*(SerializedType: distinct type, mixin readValue value = SerializedType reader.readValue(distinctBase SerializedType) -macro serializesAsBaseIn*(SerializedType: type, - Formats: varargs[untyped]) = +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 = +template readValue*( + stream: InputStream, + Format: type SerializationFormat, + 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]) = +template writeValue*( + stream: OutputStream, + Format: type SerializationFormat, + value: auto, + params: varargs[untyped], +) = mixin Writer, init, writeValue type WriterType = Writer(Format) var writer = unpackArgs(init, [WriterType, stream, params]) diff --git a/serialization/errors.nim b/serialization/errors.nim index 587a39c..4554bdf 100644 --- a/serialization/errors.nim +++ b/serialization/errors.nim @@ -4,6 +4,6 @@ type CustomSerializationError* = object of SerializationError method formatMsg*(err: ref SerializationError, filename: string): string - {.gcsafe, base, raises: [Defect].} = + {.gcsafe, base, raises: [].} = "Serialisation error while processing " & filename & ":" & err.msg diff --git a/serialization/formats.nim b/serialization/formats.nim index e79ba62..3b947f1 100644 --- a/serialization/formats.nim +++ b/serialization/formats.nim @@ -4,19 +4,24 @@ import type DefaultFlavor* = object + SerializationFormat* {.inheritable, pure.} = object + ## Marker type for serialization formats created with `serializationFormat` + ## and `createFlavor`, for which encode/decoode and other serialization-based + ## formats are supported + template serializationFormatImpl(Name: untyped, mimeTypeName: static string = "") {.dirty.} = # This indirection is required in order to be able to generate the # `mimeType` accessor template. Without the indirection, the template # mechanism of Nim will try to expand the `mimeType` param in the position # of the `mimeType` template name which will result in error. - type Name* = object + type Name* = object of SerializationFormat template mimeType*(T: type Name): string = mimeTypeName template serializationFormat*(Name: untyped, mimeType: static string = "") = serializationFormatImpl(Name, mimeType) -template setReader*(Format, FormatReader: distinct type) = +template setReader*(Format: type SerializationFormat, FormatReader: distinct type) = when arity(FormatReader) > 1: template ReaderType*(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F] template Reader*(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F] @@ -24,7 +29,7 @@ template setReader*(Format, FormatReader: distinct type) = template ReaderType*(T: type Format): type = FormatReader template Reader*(T: type Format): type = FormatReader -template setWriter*(Format, FormatWriter, PreferredOutput: distinct type) = +template setWriter*(Format: type SerializationFormat, FormatWriter, PreferredOutput: distinct type) = when arity(FormatWriter) > 1: template WriterType*(T: type Format, F: distinct type = DefaultFlavor): type = FormatWriter[F] template Writer*(T: type Format, F: distinct type = DefaultFlavor): type = FormatWriter[F] @@ -34,12 +39,20 @@ template setWriter*(Format, FormatWriter, PreferredOutput: distinct type) = template PreferredOutputType*(T: type Format): type = PreferredOutput -template createFlavor*(ModifiedFormat, FlavorName: untyped) = - type FlavorName* = object +template createFlavor*( + ModifiedFormat: type SerializationFormat, + FlavorName: untyped, + mimeTypeName: static string = "" +) = + type FlavorName* = object of SerializationFormat template Reader*(T: type FlavorName): type = Reader(ModifiedFormat, FlavorName) template Writer*(T: type FlavorName): type = Writer(ModifiedFormat, FlavorName) template PreferredOutputType*(T: type FlavorName): type = PreferredOutputType(ModifiedFormat) - template mimeType*(T: type FlavorName): string = mimeType(ModifiedFormat) + template mimeType*(T: type FlavorName): string = + when mimeTypeName == "": + mimeType(ModifiedFormat) + else: + mimeTypeName template toObjectType(T: type): untyped = typeof(T()[]) diff --git a/tests/otherencode.nim b/tests/otherencode.nim new file mode 100644 index 0000000..5317d48 --- /dev/null +++ b/tests/otherencode.nim @@ -0,0 +1,9 @@ +type + Base64* = object + Base64Pad* = object + Base64Types* = Base64 | Base64Pad + +func encode*( + btype: typedesc[Base64Types], inbytes: openArray[byte] +): string {.inline.} = + discard diff --git a/tests/test_reader.nim b/tests/test_reader.nim index 2545209..9ca209f 100644 --- a/tests/test_reader.nim +++ b/tests/test_reader.nim @@ -1,6 +1,7 @@ import unittest2, - ../serialization + ../serialization, + ./otherencode {.used.} @@ -23,7 +24,10 @@ suite "object serialization": test "readValue": let z = Xyz.decode("", TestObj) check z.number == 13 - + var r: XyzReader let x = r.readValue(TestObj) check x.number == 13 + +# Make sure we don't encroach on other uses of "encode" +discard Base64Pad.encode(@[byte 1, 2 ,3])