forked from tpolecat/doobie
-
Notifications
You must be signed in to change notification settings - Fork 0
/
analysis.scala
128 lines (111 loc) · 2.93 KB
/
analysis.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
// 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.testing
import cats.effect.kernel.Sync
import cats.syntax.show.*
import doobie.free.connection.ConnectionIO
import doobie.util.Colors
import doobie.util.analysis.*
import doobie.util.pos.Pos
import doobie.util.pretty.*
import doobie.util.query.Query
import doobie.util.query.Query0
import doobie.util.transactor.Transactor
import doobie.util.update.Update
import doobie.util.update.Update0
import org.tpolecat.typename.*
trait UnsafeRun[F[_]] {
def unsafeRunSync[A](fa: F[A]): A
}
/**
* Common base trait for various checkers and matchers.
*/
trait CheckerBase[M[_]] {
// Effect type, required instances
implicit def M: Sync[M]
implicit def U: UnsafeRun[M]
def transactor: Transactor[M]
def colors: Colors = Colors.Ansi
}
/** Common data for all query-like types. */
final case class AnalysisArgs(
typeName: String,
pos: Option[Pos],
sql: String,
analysis: ConnectionIO[Analysis],
) {
val cleanedSql = Block(
sql.linesIterator
.map(_.trim)
.filterNot(_.isEmpty)
.toList,
)
private val location =
pos
.map(f => show"${f.file}:${f.line}")
.getOrElse("(source location unknown)")
val header: String = show"$typeName defined at $location"
}
/** Information from [[doobie.util.analysis.Analysis]], prepared for output. */
final case class AnalysisReport(
header: String,
sql: Block,
items: List[AnalysisReport.Item],
) {
val succeeded: Boolean = items.forall(_.error.isEmpty)
}
object AnalysisReport {
final case class Item(description: String, error: Option[Block])
}
/** Typeclass for query-like objects. */
trait Analyzable[T] {
def unpack(t: T): AnalysisArgs
}
object Analyzable {
def apply[T](implicit ev: Analyzable[T]): Analyzable[T] = ev
def unpack[T](t: T)(implicit T: Analyzable[T]): AnalysisArgs =
T.unpack(t)
def instance[T](
impl: T => AnalysisArgs,
): Analyzable[T] =
new Analyzable[T] {
def unpack(t: T) = impl(t)
}
implicit def analyzableQuery[A: TypeName, B: TypeName]: Analyzable[Query[A, B]] =
instance { q =>
AnalysisArgs(
s"Query[${typeName[A]}, ${typeName[B]}]",
q.pos,
q.sql,
q.analysis,
)
}
implicit def analyzableQuery0[A: TypeName]: Analyzable[Query0[A]] =
instance { q =>
AnalysisArgs(
s"Query0[${typeName[A]}]",
q.pos,
q.sql,
q.analysis,
)
}
implicit def analyzableUpdate[A: TypeName]: Analyzable[Update[A]] =
instance { q =>
AnalysisArgs(
s"Update[${typeName[A]}]",
q.pos,
q.sql,
q.analysis,
)
}
implicit val analyzableUpdate0: Analyzable[Update0] =
instance { q =>
AnalysisArgs(
s"Update0",
q.pos,
q.sql,
q.analysis,
)
}
}