forked from tpolecat/doobie
-
Notifications
You must be signed in to change notification settings - Fork 0
/
read.scala
151 lines (119 loc) · 4.94 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
147
148
149
150
151
// Copyright (c) 2013-2020 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.Apply
import doobie.FRS
import doobie.enumerated.Nullability
import doobie.enumerated.Nullability.*
import doobie.free.resultset.ResultSetIO
import java.sql.ResultSet
import scala.annotation.implicitNotFound
import scala.collection.immutable.ArraySeq
import scala.collection.mutable
@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 auto derivation ensure `doobie.util.Read.Auto.*` is being imported
- 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 and tuples ensure that each element has a Read instance in scope.
- Lather, rinse, repeat, recursively 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.
""")
trait Read[A] { self =>
def gets: ArraySeq[(Get[?], NullabilityKnown)]
def length: Int = gets.length
def unsafeGet(rs: ResultSet, i: Int): A
final def get: ResultSetIO[A] = FRS.raw(unsafeGet(_, 1))
final def map[B](f: A => B): Read[B] = new Read[B] {
override val gets = self.gets
override def unsafeGet(rs: ResultSet, i: Int) = f(self.unsafeGet(rs, i))
}
final def ap[B](ff: Read[A => B]): Read[B] = new Read[B] {
override val gets = ff.gets ++ self.gets
override def unsafeGet(rs: ResultSet, i: Int) = ff.unsafeGet(rs, i)(self.unsafeGet(rs, i + ff.gets.size))
}
final def map2[B, Z](fb: Read[B])(f: (A, B) => Z): Read[Z] = new Read[Z] {
override val gets = self.gets ++ fb.gets
override def unsafeGet(rs: ResultSet, i: Int) = f(self.unsafeGet(rs, i), fb.unsafeGet(rs, i + self.gets.size))
}
final def product[B](fb: Read[B]): Read[(A, B)] = map2(fb) { case t @ (_, _) => t }
}
object Read extends Read1 {
def apply[A](implicit ev: Read[A]): ev.type = ev
object Auto extends ReadAutoPlatform
implicit val ReadApply: Apply[Read] = new Apply[Read] {
override def map[A, B](fa: Read[A])(f: A => B) = fa.map(f)
override def ap[A, B](ff: Read[A => B])(fa: Read[A]) = fa.ap(ff)
override def map2[A, B, Z](fa: Read[A], fb: Read[B])(f: (A, B) => Z) = fa.map2(fb)(f)
override def product[A, B](fa: Read[A], fb: Read[B]) = fa.product(fb)
}
implicit val unit: Read[Unit] = new Read[Unit] {
override val gets = ArraySeq.empty
override def unsafeGet(rs: ResultSet, i: Int) = ()
}
implicit def fromGet[A](implicit G: Get[A]): Read[A] = new Read[A] {
override val gets = ArraySeq((G, NoNulls))
override def unsafeGet(rs: ResultSet, i: Int) = G.unsafeGetNonNullable(rs, i)
}
implicit def fromGetOption[A](implicit G: Get[A], ev: A <:!< Option[?]): Read[Option[A]] = {
val _ = ev
new Read[Option[A]] {
override val gets = ArraySeq((G, Nullable))
override def unsafeGet(rs: ResultSet, i: Int) = G.unsafeGetNullable(rs, i)
}
}
@SuppressWarnings(Array(
"org.wartremover.warts.MutableDataStructures",
"org.wartremover.warts.Var",
"org.wartremover.warts.While",
))
private[doobie] def build(instances: Iterable[Read[?]])(rs: ResultSet, index: Int) = {
var i = index
val arr = mutable.ArrayBuilder.make[Any]
val it = instances.iterator
while (it.hasNext) {
val instance = it.next()
val a = instance.unsafeGet(rs, i)
val _ = arr.addOne(a)
i = i + instance.gets.size
}
arr.result()
}
}
sealed trait Read1 extends ReadPlatform { this: Read.type =>
implicit def optional[A](implicit R: Read[A], ev: A <:!< Option[?]): Read[Option[A]] = {
val _ = ev
new Read[Option[A]] {
override val gets = R.gets.map { case (g, _) => (g, Nullable) }
@SuppressWarnings(Array("org.wartremover.warts.Var", "org.wartremover.warts.While"))
override def unsafeGet(rs: ResultSet, i: Int) = {
// if first item is null or gets is empty => return None
var allNull = true
var nonNullableIsNull = false
var _i = i
val iterator = R.gets.iterator
while (iterator.hasNext && !nonNullableIsNull) {
val (get, n) = iterator.next()
get.unsafeGetNullable(rs, _i) match {
case None =>
if (n == Nullability.NoNulls) nonNullableIsNull = true
case Some(_) =>
allNull = false
}
_i = _i + 1
}
if (allNull || nonNullableIsNull) None else Option(R.unsafeGet(rs, i))
}
}
}
}