-
Notifications
You must be signed in to change notification settings - Fork 348
/
copyout.scala
136 lines (116 loc) · 5.78 KB
/
copyout.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
// Copyright (c) 2013-2017 Rob Norris
// This software is licensed under the MIT License (MIT).
// For more information see LICENSE or https://opensource.org/licenses/MIT
package doobie.postgres.free
import cats.~>
import cats.effect.Async
import cats.free.{ Free => FF } // alias because some algebras have an op called Free
import org.postgresql.copy.{ CopyOut => PGCopyOut }
@SuppressWarnings(Array("org.wartremover.warts.Overloading"))
object copyout { module =>
// Algebra of operations for PGCopyOut. Each accepts a visitor as an alternatie to pattern-matching.
sealed trait CopyOutOp[A] {
def visit[F[_]](v: CopyOutOp.Visitor[F]): F[A]
}
// Free monad over CopyOutOp.
type CopyOutIO[A] = FF[CopyOutOp, A]
// Module of instances and constructors of CopyOutOp.
object CopyOutOp {
// Given a PGCopyOut we can embed a CopyOutIO program in any algebra that understands embedding.
implicit val CopyOutOpEmbeddable: Embeddable[CopyOutOp, PGCopyOut] =
new Embeddable[CopyOutOp, PGCopyOut] {
def embed[A](j: PGCopyOut, fa: FF[CopyOutOp, A]) = Embedded.CopyOut(j, fa)
}
// Interface for a natural tansformation CopyOutOp ~> F encoded via the visitor pattern.
// This approach is much more efficient than pattern-matching for large algebras.
trait Visitor[F[_]] extends (CopyOutOp ~> F) {
final def apply[A](fa: CopyOutOp[A]): F[A] = fa.visit(this)
// Common
def raw[A](f: PGCopyOut => A): F[A]
def embed[A](e: Embedded[A]): F[A]
def delay[A](a: () => A): F[A]
def handleErrorWith[A](fa: CopyOutIO[A], f: Throwable => CopyOutIO[A]): F[A]
def async[A](k: (Either[Throwable, A] => Unit) => Unit): F[A]
// PGCopyOut
def cancelCopy: F[Unit]
def getFieldCount: F[Int]
def getFieldFormat(a: Int): F[Int]
def getFormat: F[Int]
def getHandledRowCount: F[Long]
def isActive: F[Boolean]
def readFromCopy: F[Array[Byte]]
def readFromCopy(a: Boolean): F[Array[Byte]]
}
// Common operations for all algebras.
final case class Raw[A](f: PGCopyOut => A) extends CopyOutOp[A] {
def visit[F[_]](v: Visitor[F]) = v.raw(f)
}
final case class Embed[A](e: Embedded[A]) extends CopyOutOp[A] {
def visit[F[_]](v: Visitor[F]) = v.embed(e)
}
final case class Delay[A](a: () => A) extends CopyOutOp[A] {
def visit[F[_]](v: Visitor[F]) = v.delay(a)
}
final case class HandleErrorWith[A](fa: CopyOutIO[A], f: Throwable => CopyOutIO[A]) extends CopyOutOp[A] {
def visit[F[_]](v: Visitor[F]) = v.handleErrorWith(fa, f)
}
final case class Async1[A](k: (Either[Throwable, A] => Unit) => Unit) extends CopyOutOp[A] {
def visit[F[_]](v: Visitor[F]) = v.async(k)
}
// PGCopyOut-specific operations.
final case object CancelCopy extends CopyOutOp[Unit] {
def visit[F[_]](v: Visitor[F]) = v.cancelCopy
}
final case object GetFieldCount extends CopyOutOp[Int] {
def visit[F[_]](v: Visitor[F]) = v.getFieldCount
}
final case class GetFieldFormat(a: Int) extends CopyOutOp[Int] {
def visit[F[_]](v: Visitor[F]) = v.getFieldFormat(a)
}
final case object GetFormat extends CopyOutOp[Int] {
def visit[F[_]](v: Visitor[F]) = v.getFormat
}
final case object GetHandledRowCount extends CopyOutOp[Long] {
def visit[F[_]](v: Visitor[F]) = v.getHandledRowCount
}
final case object IsActive extends CopyOutOp[Boolean] {
def visit[F[_]](v: Visitor[F]) = v.isActive
}
final case object ReadFromCopy extends CopyOutOp[Array[Byte]] {
def visit[F[_]](v: Visitor[F]) = v.readFromCopy
}
final case class ReadFromCopy1(a: Boolean) extends CopyOutOp[Array[Byte]] {
def visit[F[_]](v: Visitor[F]) = v.readFromCopy(a)
}
}
import CopyOutOp._
// Smart constructors for operations common to all algebras.
val unit: CopyOutIO[Unit] = FF.pure[CopyOutOp, Unit](())
def raw[A](f: PGCopyOut => A): CopyOutIO[A] = FF.liftF(Raw(f))
def embed[F[_], J, A](j: J, fa: FF[F, A])(implicit ev: Embeddable[F, J]): FF[CopyOutOp, A] = FF.liftF(Embed(ev.embed(j, fa)))
def delay[A](a: => A): CopyOutIO[A] = FF.liftF(Delay(() => a))
def handleErrorWith[A](fa: CopyOutIO[A], f: Throwable => CopyOutIO[A]): CopyOutIO[A] = FF.liftF[CopyOutOp, A](HandleErrorWith(fa, f))
def raiseError[A](err: Throwable): CopyOutIO[A] = delay(throw err)
def async[A](k: (Either[Throwable, A] => Unit) => Unit): CopyOutIO[A] = FF.liftF[CopyOutOp, A](Async1(k))
// Smart constructors for CopyOut-specific operations.
val cancelCopy: CopyOutIO[Unit] = FF.liftF(CancelCopy)
val getFieldCount: CopyOutIO[Int] = FF.liftF(GetFieldCount)
def getFieldFormat(a: Int): CopyOutIO[Int] = FF.liftF(GetFieldFormat(a))
val getFormat: CopyOutIO[Int] = FF.liftF(GetFormat)
val getHandledRowCount: CopyOutIO[Long] = FF.liftF(GetHandledRowCount)
val isActive: CopyOutIO[Boolean] = FF.liftF(IsActive)
val readFromCopy: CopyOutIO[Array[Byte]] = FF.liftF(ReadFromCopy)
def readFromCopy(a: Boolean): CopyOutIO[Array[Byte]] = FF.liftF(ReadFromCopy1(a))
// CopyOutIO is an Async
implicit val AsyncCopyOutIO: Async[CopyOutIO] =
new Async[CopyOutIO] {
val M = FF.catsFreeMonadForFree[CopyOutOp]
def pure[A](x: A): CopyOutIO[A] = M.pure(x)
def handleErrorWith[A](fa: CopyOutIO[A])(f: Throwable => CopyOutIO[A]): CopyOutIO[A] = module.handleErrorWith(fa, f)
def raiseError[A](e: Throwable): CopyOutIO[A] = module.raiseError(e)
def async[A](k: (Either[Throwable,A] => Unit) => Unit): CopyOutIO[A] = module.async(k)
def flatMap[A, B](fa: CopyOutIO[A])(f: A => CopyOutIO[B]): CopyOutIO[B] = M.flatMap(fa)(f)
def tailRecM[A, B](a: A)(f: A => CopyOutIO[Either[A, B]]): CopyOutIO[B] = M.tailRecM(a)(f)
def suspend[A](thunk: => CopyOutIO[A]): CopyOutIO[A] = M.flatten(module.delay(thunk))
}
}