@@ -6,49 +6,64 @@ import shapeless.labelled.FieldType
6
6
/** Automatically derive RLP codecs for case classes. */
7
7
object RLPImplicitDerivations {
8
8
9
+ /** Support introspecting on what happened during encoding the tail. */
10
+ case class FieldInfo (isOptional : Boolean )
11
+
9
12
/** Case classes get encoded as lists, not values,
10
13
* which is an extra piece of information we want
11
14
* to be able to rely on during derivation.
12
15
*/
13
16
trait RLPListEncoder [T ] extends RLPEncoder [T ] {
14
- override def encode (obj : T ): RLPList
17
+ def encodeList (obj : T ): (RLPList , List [FieldInfo ])
18
+
19
+ override def encode (obj : T ): RLPEncodeable =
20
+ encodeList(obj)._1
15
21
}
16
22
object RLPListEncoder {
17
- def apply [T ](f : T => RLPList ): RLPListEncoder [T ] =
23
+ def apply [T ](f : T => ( RLPList , List [ FieldInfo ]) ): RLPListEncoder [T ] =
18
24
new RLPListEncoder [T ] {
19
- override def encode (obj : T ) = f(obj)
25
+ override def encodeList (obj : T ) = f(obj)
20
26
}
21
27
}
22
28
23
29
/** Encoder for the empty list of fields. */
24
30
implicit val deriveHNilRLPListEncoder : RLPListEncoder [HNil ] =
25
- RLPListEncoder (_ => RLPList ())
31
+ RLPListEncoder (_ => RLPList () -> Nil )
26
32
27
33
/** Encoder that takes a list of fields which are the labelled generic
28
34
* representation of a case class and turns it into an RLPList by
29
35
* combining the RLP encoding of the head with the RLPList encoding of
30
36
* the tail of the field list.
31
37
*
32
38
* This variant deals with trailing optional fields in the case classes,
33
- * which are omitted from the RLP list, instead of being as empty list items .
39
+ * which can be omitted from the RLP list, instead of being added as empty lists .
34
40
*/
35
41
implicit def deriveOptionHListRLPListEncoder [K , H , T <: HList ](implicit
36
42
hEncoder : Lazy [RLPEncoder [H ]],
37
43
tEncoder : Lazy [RLPListEncoder [T ]],
38
44
ev : H <:< Option [_]
39
45
): RLPListEncoder [FieldType [K , H ] :: T ] = {
40
- // Create an encoder that takes a list of fields.
46
+ val hInfo = FieldInfo (isOptional = true )
47
+ // Create an encoder that takes a list of field values.
41
48
RLPListEncoder { case head :: tail =>
42
- val tRLP = tEncoder.value.encode(tail)
43
- // If the fields is empty and the tail serialized to an RLPList is
44
- // also empty then it looks like a trailing field which is either
45
- // the last field of the class, or is followed by empty fields.
46
- if (head.isEmpty && tRLP.items.isEmpty)
47
- tRLP
48
- else {
49
- val hRLP = hEncoder.value.encode(head)
50
- hRLP :: tRLP
51
- }
49
+ val (tRLP, tInfos) = tEncoder.value.encodeList(tail)
50
+ val htRLP =
51
+ if (tInfos.forall(_.isOptional)) {
52
+ // This is still a trailing optional field, so we can insert it as a value or omit it.
53
+ hEncoder.value.encode(head) match {
54
+ case RLPList (hRLP) =>
55
+ hRLP :: tRLP
56
+ case RLPList () if tRLP.items.isEmpty =>
57
+ tRLP
58
+ case hRLP =>
59
+ hRLP :: tRLP
60
+ }
61
+ } else {
62
+ // We're no longer in a trailing position, so insert it as a list of 0 or 1 items.
63
+ hEncoder.value.encode(head) :: tRLP
64
+ }
65
+
66
+ htRLP -> (hInfo :: tInfos)
52
67
}
53
68
}
54
69
@@ -58,21 +73,22 @@ object RLPImplicitDerivations {
58
73
tEncoder : Lazy [RLPListEncoder [T ]],
59
74
ev : H <:!< Option [_]
60
75
): RLPListEncoder [FieldType [K , H ] :: T ] = {
76
+ val hInfo = FieldInfo (isOptional = false )
61
77
RLPListEncoder { case head :: tail =>
62
78
val hRLP = hEncoder.value.encode(head)
63
- val tRLP = tEncoder.value.encode (tail)
64
- hRLP :: tRLP
79
+ val ( tRLP, tInfos) = tEncoder.value.encodeList (tail)
80
+ ( hRLP :: tRLP, hInfo :: tInfos)
65
81
}
66
82
}
67
83
68
84
/** Derive an encoder for a case class based on its labelled generic record representation. */
69
85
implicit def deriveLabelledGenericRLPListEncoder [T , Rec ](implicit
70
86
// Auto-derived by Shapeless.
71
87
generic : LabelledGeneric .Aux [T , Rec ],
72
- // Derived by `deriveHListRLPListEncoder `.
88
+ // Derived by `deriveOptionHListRLPListEncoder` and `deriveNonOptionHListRLPListEncoder `.
73
89
recEncoder : Lazy [RLPListEncoder [Rec ]]
74
90
): RLPListEncoder [T ] = RLPListEncoder { value =>
75
- recEncoder.value.encode (generic.to(value))
91
+ recEncoder.value.encodeList (generic.to(value))
76
92
}
77
93
78
94
}
0 commit comments