Update sbt to 1.4.4
1 |
/*
|
|
2 |
* Copyright 2016 Nicolas Rinaudo
|
|
3 |
*
|
|
4 |
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5 |
* you may not use this file except in compliance with the License.
|
|
6 |
* You may obtain a copy of the License at
|
|
7 |
*
|
|
8 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9 |
*
|
|
10 |
* Unless required by applicable law or agreed to in writing, software
|
|
11 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13 |
* See the License for the specific language governing permissions and
|
|
14 |
* limitations under the License.
|
|
15 |
*/
|
|
16 |
|
|
17 |
package kantan.codecs.shapeless |
|
18 |
|
|
19 |
import kantan.codecs.{Decoder, Encoder} |
|
20 |
import kantan.codecs.error.IsError |
|
21 |
import kantan.codecs.export.{DerivedDecoder, DerivedEncoder} |
|
22 |
import shapeless.{:+:, CNil, Coproduct, Generic, HList, Inl, Inr, LabelledGeneric, Lazy} |
|
23 |
|
|
24 |
/** Provides `Codec` instances for case classes and sum types.
|
|
25 |
*
|
|
26 |
* The purpose of this package is to let concrete `Codec` implementations (such as kantan.csv or
|
|
27 |
* kantan.regex) focus on providing instances for `HList` and `Coproduct`. Once such instances exist, this package
|
|
28 |
* will take care of the transformation from and to case classes and sum types.
|
|
29 |
*
|
|
30 |
* Additionally, instances derived that way will be inserted with a sane precedence in the implicit resolution
|
|
31 |
* mechanism. This means, for example, that they will not override bespoke `Option` or `Either` instances.
|
|
32 |
*/
|
|
33 |
trait ShapelessInstances { |
|
34 |
// - Case classes ----------------------------------------------------------------------------------------------------
|
|
35 |
// -------------------------------------------------------------------------------------------------------------------
|
|
36 |
/** Provides an `Encoder` instance for case classes.
|
|
37 |
*
|
|
38 |
* Given a case class `D`, this expects an `Encoder` instance for the `HList` type corresponding to `D`. It will
|
|
39 |
* then simply turn values of type `D` into values of the corresponding `HList`, then let the encoder take it from
|
|
40 |
* there.
|
|
41 |
*/
|
|
42 |
implicit def caseClassEncoder[E, D, T, H <: HList]( |
|
43 |
implicit gen: Generic.Aux[D, H], |
|
44 |
er: Lazy[Encoder[E, H, T]] |
|
45 |
): DerivedEncoder[E, D, T] = |
|
46 | 4 |
DerivedEncoder.from(s => er.value.encode(gen.to(s))) |
47 |
|
|
48 |
/** Similar to [[caseClassEncoder]], but working with `LabelledGeneric` rather than just `Generic`. */
|
|
49 |
implicit def caseClassEncoderFromLabelled[E, D, T, H <: HList]( |
|
50 |
implicit generic: LabelledGeneric.Aux[D, H], |
|
51 |
hEncoder: Lazy[Encoder[E, H, T]] |
|
52 |
): DerivedEncoder[E, D, T] = |
|
53 |
DerivedEncoder.from(value => hEncoder.value.encode(generic.to(value))) |
|
54 |
|
|
55 |
/** Provides a `Decoder` instance for case classes.
|
|
56 |
*
|
|
57 |
* Given a case class `D`, this expects n `Decoder` instance for the `HList` type corresponding to `D`. It will
|
|
58 |
* then rely on that to turn encoded values into an `HList`, then turn the resulting value into a `D`.
|
|
59 |
*/
|
|
60 |
implicit def caseClassDecoder[E, D, F, T, H <: HList]( |
|
61 |
implicit gen: Generic.Aux[D, H], |
|
62 |
dr: Lazy[Decoder[E, H, F, T]] |
|
63 |
): DerivedDecoder[E, D, F, T] = |
|
64 | 4 |
DerivedDecoder.from(s => dr.value.decode(s).map(gen.from)) |
65 |
|
|
66 |
/** Similar to [[caseClassDecoder]], but working with `LabelledGeneric` rather than just `Generic`. */
|
|
67 |
implicit def caseClassDecoderFromLabelled[E, D, F, T, H <: HList]( |
|
68 |
implicit generic: LabelledGeneric.Aux[D, H], |
|
69 |
hDecoder: Lazy[Decoder[E, H, F, T]] |
|
70 |
): DerivedDecoder[E, D, F, T] = |
|
71 |
DerivedDecoder.from(value => hDecoder.value.decode(value).map(generic.from)) |
|
72 |
|
|
73 |
// - Sum types -------------------------------------------------------------------------------------------------------
|
|
74 |
// -------------------------------------------------------------------------------------------------------------------
|
|
75 |
/** Provides an `Encoder` instance for sum types.
|
|
76 |
*
|
|
77 |
* Given a sum type `D`, this expects an `Encoder` instance for the `Coproduct` type corresponding to `D`. It
|
|
78 |
* will then simply turn values of type `D` into values of the corresponding `Coproduct`, then let the encoder take
|
|
79 |
* it from there.
|
|
80 |
*/
|
|
81 |
implicit def sumTypeEncoder[E, D, T, C <: Coproduct]( |
|
82 |
implicit gen: Generic.Aux[D, C], |
|
83 |
er: Lazy[Encoder[E, C, T]] |
|
84 |
): DerivedEncoder[E, D, T] = |
|
85 | 4 |
DerivedEncoder.from(m => er.value.encode(gen.to(m))) |
86 |
|
|
87 |
/** Provides a `Decoder` instance for sum types.
|
|
88 |
*
|
|
89 |
* Given a case class `D`, this expects n `Decoder` instance for the `Coproduct` type corresponding to `D`.
|
|
90 |
* It will then rely on that to turn encoded values into a `Coproduct`, then turn the resulting value into a `D`.
|
|
91 |
*/
|
|
92 |
implicit def sumTypeDecoder[E, D, F, T, C <: Coproduct]( |
|
93 |
implicit gen: Generic.Aux[D, C], |
|
94 |
dr: Lazy[Decoder[E, C, F, T]] |
|
95 |
): DerivedDecoder[E, D, F, T] = |
|
96 | 4 |
DerivedDecoder.from(m => dr.value.decode(m).map(gen.from)) |
97 |
|
|
98 |
// - Coproducts ------------------------------------------------------------------------------------------------------
|
|
99 |
// -------------------------------------------------------------------------------------------------------------------
|
|
100 |
implicit def cnilDecoder[E, F: IsError, T]: Decoder[E, CNil, F, T] = |
|
101 | 4 |
Decoder.from(_ => Left(IsError[F].fromMessage("Attempting to decode CNil"))) |
102 |
|
|
103 |
implicit def coproductDecoder[E, H, D <: Coproduct, F, T]( |
|
104 |
implicit dh: Decoder[E, H, F, T], |
|
105 |
dt: Decoder[E, D, F, T] |
|
106 |
): Decoder[E, H :+: D, F, T] = |
|
107 | 4 |
Decoder.from(e => dh.decode(e).map(Inl.apply).left.flatMap(_ => dt.decode(e).map(Inr.apply))) |
108 |
|
|
109 |
@SuppressWarnings(Array("org.wartremover.warts.Throw")) |
|
110 |
implicit def cnilEncoder[E, D, T]: Encoder[E, CNil, T] = |
|
111 | 4 |
Encoder.from(_ => throw new IllegalStateException("trying to encode CNil, this should not happen")) |
112 |
|
|
113 |
implicit def coproductEncoder[E, H, D <: Coproduct, T]( |
|
114 |
implicit eh: Encoder[E, H, T], |
|
115 |
ed: Encoder[E, D, T] |
|
116 | 4 |
): Encoder[E, H :+: D, T] = Encoder.from { |
117 | 4 |
case Inl(h) => eh.encode(h) |
118 | 4 |
case Inr(d) => ed.encode(d) |
119 |
}
|
|
120 |
}
|
Read our documentation on viewing source code .