Skip to content

Commit 5c98f31

Browse files
authored
limit encode/decode/etc to types declared by serializationFormat (#84)
These popular names easily fall afoul of ambiguous symbol resolution conflicts, such as when stew/base64 is imported alongside a serialization format and the right arguments are passed * allow setting mimetype in flavor * clean up decode overloads
1 parent e201edc commit 5c98f31

File tree

5 files changed

+87
-71
lines changed

5 files changed

+87
-71
lines changed

serialization.nim

Lines changed: 52 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import
22
std/typetraits,
3-
stew/shims/macros, faststreams/[inputs, outputs],
3+
stew/shims/macros,
4+
faststreams/[inputs, outputs],
45
./serialization/[object_serialization, errors, formats]
56

6-
export
7-
inputs, outputs, object_serialization, errors, formats
7+
export inputs, outputs, object_serialization, errors, formats
88

9-
template encode*(Format: type, value: auto, params: varargs[untyped]): auto =
9+
template encode*(
10+
Format: type SerializationFormat, value: auto, params: varargs[untyped]
11+
): auto =
1012
mixin init, Writer, writeValue, PreferredOutputType
11-
block: # https://github.com/nim-lang/Nim/issues/22874
13+
block: # https://github.com/nim-lang/Nim/issues/22874
1214
{.noSideEffect.}:
1315
# We assume that there is no side-effects here, because we are
1416
# using a `memoryOutput`. The computed side-effects are coming
@@ -24,21 +26,23 @@ template encode*(Format: type, value: auto, params: varargs[untyped]): auto =
2426
raise (ref Defect)() # a memoryOutput cannot have an IOError
2527

2628
# TODO Nim cannot make sense of this initialization by var param?
27-
proc readValue*(reader: var auto, T: type): T {.gcsafe, raises: [SerializationError, IOError].} =
29+
proc readValue*(
30+
reader: var auto, T: type
31+
): T {.gcsafe, raises: [SerializationError, IOError].} =
2832
{.warning[ProveInit]: false.}
2933
mixin readValue
3034
result = default(T)
3135
reader.readValue(result)
3236
{.warning[ProveInit]: true.}
3337

34-
template decode*(Format: distinct type,
35-
input: string,
36-
RecordType: distinct type,
37-
params: varargs[untyped]): auto =
38-
# TODO, this is dusplicated only due to a Nim bug:
39-
# If `input` was `string|openArray[byte]`, it won't match `seq[byte]`
38+
template decode*(
39+
Format: type SerializationFormat,
40+
input: string|openArray[char]|seq[byte]|openArray[byte],
41+
RecordType: type,
42+
params: varargs[untyped],
43+
): auto =
4044
mixin init, Reader
41-
block: # https://github.com/nim-lang/Nim/issues/22874
45+
block: # https://github.com/nim-lang/Nim/issues/22874
4246
{.noSideEffect.}:
4347
# We assume that there are no side-effects here, because we are
4448
# using a `memoryInput`. The computed side-effects are coming
@@ -52,31 +56,12 @@ template decode*(Format: distinct type,
5256
except IOError:
5357
raise (ref Defect)() # memory inputs cannot raise an IOError
5458

55-
template decode*(Format: distinct type,
56-
input: openArray[byte],
57-
RecordType: distinct type,
58-
params: varargs[untyped]): auto =
59-
# TODO, this is dusplicated only due to a Nim bug:
60-
# If `input` was `string|openArray[byte]`, it won't match `seq[byte]`
61-
mixin init, Reader
62-
block: # https://github.com/nim-lang/Nim/issues/22874
63-
{.noSideEffect.}:
64-
# We assume that there are no side-effects here, because we are
65-
# using a `memoryInput`. The computed side-effects are coming
66-
# from the fact that the dynamic dispatch mechanisms used in
67-
# faststreams may be reading from a file or a network device.
68-
try:
69-
var stream = unsafeMemoryInput(input)
70-
type ReaderType = Reader(Format)
71-
var reader = unpackArgs(init, [ReaderType, stream, params])
72-
reader.readValue(RecordType)
73-
except IOError:
74-
raise (ref Defect)() # memory inputs cannot raise an IOError
75-
76-
template loadFile*(Format: distinct type,
77-
filename: string,
78-
RecordType: distinct type,
79-
params: varargs[untyped]): auto =
59+
template loadFile*(
60+
Format: type SerializationFormat,
61+
filename: string,
62+
RecordType: type,
63+
params: varargs[untyped],
64+
): auto =
8065
mixin init, Reader, readValue
8166

8267
var stream = memFileInput(filename)
@@ -87,13 +72,20 @@ template loadFile*(Format: distinct type,
8772
finally:
8873
close stream
8974

90-
template loadFile*[RecordType](Format: type,
91-
filename: string,
92-
record: var RecordType,
93-
params: varargs[untyped]) =
75+
template loadFile*[RecordType](
76+
Format: type SerializationFormat,
77+
filename: string,
78+
record: var RecordType,
79+
params: varargs[untyped],
80+
) =
9481
record = loadFile(Format, filename, RecordType, params)
9582

96-
template saveFile*(Format: type, filename: string, value: auto, params: varargs[untyped]) =
83+
template saveFile*(
84+
Format: type SerializationFormat,
85+
filename: string,
86+
value: auto,
87+
params: varargs[untyped],
88+
) =
9789
mixin init, Writer, writeValue
9890

9991
var stream = fileOutput(filename)
@@ -107,29 +99,24 @@ template saveFile*(Format: type, filename: string, value: auto, params: varargs[
10799
template borrowSerialization*(Alias: type) {.dirty.} =
108100
bind distinctBase
109101

110-
proc writeValue*[Writer](
111-
writer: var Writer, value: Alias) {.raises: [IOError].} =
102+
proc writeValue*[Writer](writer: var Writer, value: Alias) {.raises: [IOError].} =
112103
mixin writeValue
113104
writeValue(writer, distinctBase value)
114105

115106
proc readValue*[Reader](reader: var Reader, value: var Alias) =
116107
mixin readValue
117108
value = Alias reader.readValue(distinctBase Alias)
118109

119-
template borrowSerialization*(Alias: distinct type,
120-
OriginalType: distinct type) {.dirty.} =
121-
122-
proc writeValue*[Writer](
123-
writer: var Writer, value: Alias) {.raises: [IOError].} =
110+
template borrowSerialization*(Alias: type, OriginalType: type) {.dirty.} =
111+
proc writeValue*[Writer](writer: var Writer, value: Alias) {.raises: [IOError].} =
124112
mixin writeValue
125113
writeValue(writer, OriginalType value)
126114

127115
proc readValue*[Reader](reader: var Reader, value: var Alias) =
128116
mixin readValue
129117
value = Alias reader.readValue(OriginalType)
130118

131-
template serializesAsBase*(SerializedType: distinct type,
132-
Format: distinct type) =
119+
template serializesAsBase*(SerializedType: type, Format: type SerializationFormat) =
133120
mixin Reader, Writer
134121

135122
type ReaderType = Reader(Format)
@@ -143,25 +130,28 @@ template serializesAsBase*(SerializedType: distinct type,
143130
mixin readValue
144131
value = SerializedType reader.readValue(distinctBase SerializedType)
145132

146-
macro serializesAsBaseIn*(SerializedType: type,
147-
Formats: varargs[untyped]) =
133+
macro serializesAsBaseIn*(SerializedType: type, Formats: varargs[untyped]) =
148134
result = newStmtList()
149135
for Fmt in Formats:
150136
result.add newCall(bindSym"serializesAsBase", SerializedType, Fmt)
151137

152-
template readValue*(stream: InputStream,
153-
Format: type,
154-
ValueType: type,
155-
params: varargs[untyped]): untyped =
138+
template readValue*(
139+
stream: InputStream,
140+
Format: type SerializationFormat,
141+
ValueType: type,
142+
params: varargs[untyped],
143+
): untyped =
156144
mixin Reader, init, readValue
157145
type ReaderType = Reader(Format)
158146
var reader = unpackArgs(init, [ReaderType, stream, params])
159147
readValue reader, ValueType
160148

161-
template writeValue*(stream: OutputStream,
162-
Format: type,
163-
value: auto,
164-
params: varargs[untyped]) =
149+
template writeValue*(
150+
stream: OutputStream,
151+
Format: type SerializationFormat,
152+
value: auto,
153+
params: varargs[untyped],
154+
) =
165155
mixin Writer, init, writeValue
166156
type WriterType = Writer(Format)
167157
var writer = unpackArgs(init, [WriterType, stream, params])

serialization/errors.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ type
44
CustomSerializationError* = object of SerializationError
55

66
method formatMsg*(err: ref SerializationError, filename: string): string
7-
{.gcsafe, base, raises: [Defect].} =
7+
{.gcsafe, base, raises: [].} =
88
"Serialisation error while processing " & filename & ":" & err.msg
99

serialization/formats.nim

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,32 @@ import
44
type
55
DefaultFlavor* = object
66

7+
SerializationFormat* {.inheritable, pure.} = object
8+
## Marker type for serialization formats created with `serializationFormat`
9+
## and `createFlavor`, for which encode/decoode and other serialization-based
10+
## formats are supported
11+
712
template serializationFormatImpl(Name: untyped,
813
mimeTypeName: static string = "") {.dirty.} =
914
# This indirection is required in order to be able to generate the
1015
# `mimeType` accessor template. Without the indirection, the template
1116
# mechanism of Nim will try to expand the `mimeType` param in the position
1217
# of the `mimeType` template name which will result in error.
13-
type Name* = object
18+
type Name* = object of SerializationFormat
1419
template mimeType*(T: type Name): string = mimeTypeName
1520

1621
template serializationFormat*(Name: untyped, mimeType: static string = "") =
1722
serializationFormatImpl(Name, mimeType)
1823

19-
template setReader*(Format, FormatReader: distinct type) =
24+
template setReader*(Format: type SerializationFormat, FormatReader: distinct type) =
2025
when arity(FormatReader) > 1:
2126
template ReaderType*(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
2227
template Reader*(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
2328
else:
2429
template ReaderType*(T: type Format): type = FormatReader
2530
template Reader*(T: type Format): type = FormatReader
2631

27-
template setWriter*(Format, FormatWriter, PreferredOutput: distinct type) =
32+
template setWriter*(Format: type SerializationFormat, FormatWriter, PreferredOutput: distinct type) =
2833
when arity(FormatWriter) > 1:
2934
template WriterType*(T: type Format, F: distinct type = DefaultFlavor): type = FormatWriter[F]
3035
template Writer*(T: type Format, F: distinct type = DefaultFlavor): type = FormatWriter[F]
@@ -34,12 +39,20 @@ template setWriter*(Format, FormatWriter, PreferredOutput: distinct type) =
3439

3540
template PreferredOutputType*(T: type Format): type = PreferredOutput
3641

37-
template createFlavor*(ModifiedFormat, FlavorName: untyped) =
38-
type FlavorName* = object
42+
template createFlavor*(
43+
ModifiedFormat: type SerializationFormat,
44+
FlavorName: untyped,
45+
mimeTypeName: static string = ""
46+
) =
47+
type FlavorName* = object of SerializationFormat
3948
template Reader*(T: type FlavorName): type = Reader(ModifiedFormat, FlavorName)
4049
template Writer*(T: type FlavorName): type = Writer(ModifiedFormat, FlavorName)
4150
template PreferredOutputType*(T: type FlavorName): type = PreferredOutputType(ModifiedFormat)
42-
template mimeType*(T: type FlavorName): string = mimeType(ModifiedFormat)
51+
template mimeType*(T: type FlavorName): string =
52+
when mimeTypeName == "":
53+
mimeType(ModifiedFormat)
54+
else:
55+
mimeTypeName
4356

4457
template toObjectType(T: type): untyped =
4558
typeof(T()[])

tests/otherencode.nim

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type
2+
Base64* = object
3+
Base64Pad* = object
4+
Base64Types* = Base64 | Base64Pad
5+
6+
func encode*(
7+
btype: typedesc[Base64Types], inbytes: openArray[byte]
8+
): string {.inline.} =
9+
discard

tests/test_reader.nim

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import
22
unittest2,
3-
../serialization
3+
../serialization,
4+
./otherencode
45

56
{.used.}
67

@@ -23,7 +24,10 @@ suite "object serialization":
2324
test "readValue":
2425
let z = Xyz.decode("", TestObj)
2526
check z.number == 13
26-
27+
2728
var r: XyzReader
2829
let x = r.readValue(TestObj)
2930
check x.number == 13
31+
32+
# Make sure we don't encroach on other uses of "encode"
33+
discard Base64Pad.encode(@[byte 1, 2 ,3])

0 commit comments

Comments
 (0)