-
Notifications
You must be signed in to change notification settings - Fork 348
/
read.scala
147 lines (111 loc) · 4.35 KB
/
read.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright (c) 2013-2018 Rob Norris and Contributors
// This software is licensed under the MIT License (MIT).
// For more information see LICENSE or https://opensource.org/licenses/MIT
package doobie.util
import cats._
import doobie.free.{ FRS, ResultSetIO }
import doobie.enum.Nullability._
import java.sql.ResultSet
import scala.annotation.implicitNotFound
import shapeless.{ HList, HNil, ::, Generic, Lazy, <:!< }
import shapeless.labelled.{ field, FieldType }
@implicitNotFound("""
Cannot find or construct a Read instance for type:
${A}
This can happen for a few reasons, but the most common case is that a data
member somewhere within this type doesn't have a Get instance in scope. Here are
some debugging hints:
- For Option types, ensure that a Read instance is in scope for the non-Option
version.
- For types you expect to map to a single column ensure that a Get instance is
in scope.
- For case classes, HLists, and shapeless records ensure that each element
has a Read instance in scope.
- Lather, rinse, repeat, recusively until you find the problematic bit.
You can check that an instance exists for Read in the REPL or in your code:
scala> Read[Foo]
and similarly with Get:
scala> Get[Foo]
And find the missing instance and construct it as needed. Refer to Chapter 12
of the book of doobie for more information.
""")
final class Read[A](
val gets: List[(Get[_], NullabilityKnown)],
val unsafeGet: (ResultSet, Int) => A
) {
final lazy val length: Int = gets.length
def map[B](f: A => B): Read[B] =
new Read(gets, (rs, n) => f(unsafeGet(rs, n)))
def ap[B](ff: Read[A => B]): Read[B] =
new Read(gets ++ ff.gets, (rs, n) => ff.unsafeGet(rs, n + length)(unsafeGet(rs, n)))
def get(n: Int): ResultSetIO[A] =
FRS.raw(unsafeGet(_, n))
}
object Read extends LowerPriorityRead {
def apply[A](implicit ev: Read[A]): ev.type = ev
implicit val ReadApply: Apply[Read] =
new Apply[Read] {
def ap[A, B](ff: Read[A => B])(fa: Read[A]): Read[B] = fa.ap(ff)
def map[A, B](fa: Read[A])(f: A => B): Read[B] = fa.map(f)
}
implicit val unit: Read[Unit] =
new Read(Nil, (_, _) => ())
implicit def fromGet[A](implicit ev: Get[A]): Read[A] =
new Read(List((ev, NoNulls)), ev.unsafeGetNonNullable)
implicit def fromGetOption[A](implicit ev: Get[A]): Read[Option[A]] =
new Read(List((ev, Nullable)), ev.unsafeGetNullable)
implicit def recordRead[K <: Symbol, H, T <: HList](
implicit H: Lazy[Read[H]],
T: Lazy[Read[T]]
): Read[FieldType[K, H] :: T] =
new Read[FieldType[K, H] :: T](
H.value.gets ++ T.value.gets,
(rs, n) => field[K](H.value.unsafeGet(rs, n)) :: T.value.unsafeGet(rs, n + H.value.length)
)
}
trait LowerPriorityRead extends EvenLower { this: Read.type =>
implicit def product[H, T <: HList](
implicit H: Lazy[Read[H]],
T: Lazy[Read[T]]
): Read[H :: T] =
new Read[H :: T](
H.value.gets ++ T.value.gets,
(rs, n) => H.value.unsafeGet(rs, n) :: T.value.unsafeGet(rs, n + H.value.length)
)
implicit def emptyProduct: Read[HNil] =
new Read[HNil](Nil, (_, _) => HNil)
implicit def generic[F, G](implicit gen: Generic.Aux[F, G], G: Lazy[Read[G]]): Read[F] =
new Read[F](G.value.gets, (rs, n) => gen.from(G.value.unsafeGet(rs, n)))
}
trait EvenLower {
implicit val ohnil: Read[Option[HNil]] =
new Read[Option[HNil]](Nil, (_, _) => Some(HNil))
implicit def ohcons1[H, T <: HList](
implicit H: Lazy[Read[Option[H]]],
T: Lazy[Read[Option[T]]],
N: H <:!< Option[α] forSome { type α }
): Read[Option[H :: T]] = {
void(N)
new Read[Option[H :: T]](
H.value.gets ++ T.value.gets,
(rs, n) =>
for {
h <- H.value.unsafeGet(rs, n)
t <- T.value.unsafeGet(rs, n + H.value.length)
} yield h :: t
)
}
implicit def ohcons2[H, T <: HList](
implicit H: Lazy[Read[Option[H]]],
T: Lazy[Read[Option[T]]]
): Read[Option[Option[H] :: T]] =
new Read[Option[Option[H] :: T]](
H.value.gets ++ T.value.gets,
(rs, n) => T.value.unsafeGet(rs, n + H.value.length).map(H.value.unsafeGet(rs, n) :: _)
)
implicit def ogeneric[A, Repr <: HList](
implicit G: Generic.Aux[A, Repr],
B: Lazy[Read[Option[Repr]]]
): Read[Option[A]] =
new Read[Option[A]](B.value.gets, B.value.unsafeGet(_, _).map(G.from))
}