-
Notifications
You must be signed in to change notification settings - Fork 10
/
RedisResult.scala
159 lines (137 loc) · 5.99 KB
/
RedisResult.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
152
153
154
155
156
157
158
159
package io.chrisdavenport.rediculous
import cats._
import cats.syntax.all._
import scodec.bits.ByteVector
trait RedisResult[+A]{
def decode(resp: Resp): Either[Resp, A]
}
object RedisResult extends RedisResultLowPriority{
def apply[A](implicit ev: RedisResult[A]): ev.type = ev
implicit val functor: Functor[RedisResult] = new Functor[RedisResult]{
def map[A, B](fa: RedisResult[A])(f: A => B): RedisResult[B] = new RedisResult[B] {
def decode(resp: Resp): Either[Resp,B] = fa.decode(resp).map(f)
}
}
implicit val resp: RedisResult[Resp] = new RedisResult[Resp]{
def decode(resp: Resp): Either[Resp,Resp] = Right(resp)
}
implicit val string : RedisResult[String] = new RedisResult[String]{
def decode(resp: Resp): Either[Resp,String] = resp match {
case Resp.SimpleString(value) => value.asRight
case Resp.BulkString(Some(value)) => value.decodeUtf8.leftMap(_ => resp)
case otherwise => otherwise.asLeft
}
}
implicit val bytevector: RedisResult[ByteVector] = new RedisResult[ByteVector] {
def decode(resp: Resp): Either[Resp,ByteVector] = resp match {
case Resp.BulkString(Some(value)) => value.asRight
case otherwise => otherwise.asLeft
}
}
implicit def option[A: RedisResult]: RedisResult[Option[A]] = new RedisResult[Option[A]] {
def decode(resp: Resp): Either[Resp,Option[A]] = resp match {
case Resp.BulkString(None) => None.asRight
case Resp.Array(None) => None.asRight
case otherwise => RedisResult[A].decode(otherwise).map(_.some)
}
}
implicit val status: RedisResult[RedisProtocol.Status] = new RedisResult[RedisProtocol.Status] {
def decode(resp: Resp): Either[Resp,RedisProtocol.Status] = resp match {
case Resp.SimpleString(value) => Either.right(value match {
case "OK" => RedisProtocol.Status.Ok
case "PONG" => RedisProtocol.Status.Pong
case otherwise => RedisProtocol.Status.Status(otherwise)
})
case otherwise => Left(otherwise)
}
}
implicit val redisType: RedisResult[RedisProtocol.RedisType] = new RedisResult[RedisProtocol.RedisType] {
def decode(resp: Resp): Either[Resp,RedisProtocol.RedisType] = resp match {
case Resp.SimpleString(value) => Either.right(value match {
case "none" => RedisProtocol.RedisType.None
case "string" => RedisProtocol.RedisType.String
case "hash" => RedisProtocol.RedisType.Hash
case "list" => RedisProtocol.RedisType.List
case "set" => RedisProtocol.RedisType.Set
case "stream" => RedisProtocol.RedisType.Stream
case "zset" => RedisProtocol.RedisType.ZSet
case _ => throw RedisError.Generic(s"Rediculous: Unhandled red type: $value")
})
case r => Left(r)
}
}
implicit val bool: RedisResult[Boolean] = new RedisResult[Boolean] {
def decode(resp: Resp): Either[Resp,Boolean] = resp match {
case Resp.Integer(1) => Right(true)
case Resp.Integer(0) => Right(false)
case Resp.BulkString(None) => Right(false) // Lua boolean false
case r => Left(r)
}
}
implicit val long: RedisResult[Long] = new RedisResult[Long] {
def decode(resp: Resp): Either[Resp,Long] = resp match {
case Resp.Integer(l) => Right(l)
case other => RedisResult[String].decode(other)
.flatMap(s => Either.catchNonFatal(s.toLong).leftMap(_ => resp))
}
}
// Increment
implicit val double: RedisResult[Double] = new RedisResult[Double] {
def decode(resp: Resp): Either[Resp,Double] = RedisResult[String].decode(resp)
.flatMap(s => Either.catchNonFatal(s.toDouble).leftMap(_ => resp))
}
implicit val int: RedisResult[Int] = long.map(_.toInt) // Integers are longs in redis, use at your own risk.
implicit def tuple[A: RedisResult, B: RedisResult]: RedisResult[(A, B)] = new RedisResult[(A, B)] {
def decode(resp: Resp): Either[Resp,(A, B)] = resp match {
case Resp.Array(Some(x ::y :: Nil)) => (RedisResult[A].decode(x), RedisResult[B].decode(y)).tupled
case otherwise => Left(otherwise)
}
}
implicit def tuple3[A: RedisResult, B: RedisResult, C: RedisResult]: RedisResult[(A, B, C)] = new RedisResult[(A, B, C)] {
def decode(resp: Resp): Either[Resp,(A, B, C)] = resp match {
case Resp.Array(Some(x ::y :: z :: Nil)) => (RedisResult[A].decode(x), RedisResult[B].decode(y), RedisResult[C].decode(z)).tupled
case otherwise => Left(otherwise)
}
}
implicit def tuple4[A: RedisResult, B: RedisResult, C: RedisResult, D: RedisResult]: RedisResult[(A, B, C, D)] = new RedisResult[(A, B, C, D)] {
def decode(resp: Resp): Either[Resp,(A, B, C, D)] = resp match {
case Resp.Array(Some(a :: b :: c :: d :: Nil)) =>
(
RedisResult[A].decode(a),
RedisResult[B].decode(b),
RedisResult[C].decode(c),
RedisResult[D].decode(d),
).tupled
case otherwise => Left(otherwise)
}
}
implicit def kv[K: RedisResult, V: RedisResult]: RedisResult[List[(K, V)]] =
new RedisResult[List[(K, V)]] {
def decode(resp: Resp): Either[Resp,List[(K, V)]] = {
def pairs(l: List[Resp]): Either[Resp,List[(K, V)]] =
Monad[Either[Resp, *]].tailRecM[(List[Resp], List[(K, V)]), List[(K, V)]]((l, Nil)){
case (l, acc) =>
l match {
case Nil => Right(Right(acc))
case _ :: Nil => Left(resp)
case x1 :: x2 :: xs => for {
k <- RedisResult[K].decode(x1)
v <- RedisResult[V].decode(x2)
} yield Left((xs, (k, v) :: acc))
}
}.map(_.reverse)
resp match {
case Resp.Array(Some(rs)) => pairs(rs)
case otherwise => Left(otherwise)
}
}
}
}
private[rediculous] trait RedisResultLowPriority {
implicit def list[A: RedisResult]: RedisResult[List[A]] = new RedisResult[List[A]] {
def decode(resp: Resp): Either[Resp,List[A]] = resp match {
case Resp.Array(Some(x)) => x.traverse(RedisResult[A].decode)
case other => Left(other)
}
}
}