forked from lichess-org/lila
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Handlers.scala
149 lines (122 loc) · 5.25 KB
/
Handlers.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
package lila.db
import cats.data.NonEmptyList
import org.joda.time.DateTime
import reactivemongo.api.bson._
import reactivemongo.api.bson.exceptions.TypeDoesNotMatchException
import scala.util.{ Failure, Success, Try }
import lila.common.Iso._
import lila.common.{ EmailAddress, IpAddress, Iso, NormalizedEmailAddress }
import chess.format.FEN
import chess.variant.Variant
trait Handlers {
implicit val BSONJodaDateTimeHandler = quickHandler[DateTime](
{ case v: BSONDateTime => new DateTime(v.value) },
v => BSONDateTime(v.getMillis)
)
def isoHandler[A, B](iso: Iso[B, A])(implicit handler: BSONHandler[B]): BSONHandler[A] =
new BSONHandler[A] {
def readTry(x: BSONValue) = handler.readTry(x) map iso.from
def writeTry(x: A) = handler writeTry iso.to(x)
}
def isoHandler[A, B](to: A => B, from: B => A)(implicit handler: BSONHandler[B]): BSONHandler[A] =
isoHandler(Iso(from, to))
def stringIsoHandler[A](implicit iso: StringIso[A]): BSONHandler[A] =
BSONStringHandler.as[A](iso.from, iso.to)
def stringAnyValHandler[A](to: A => String, from: String => A): BSONHandler[A] =
stringIsoHandler(Iso(from, to))
def intIsoHandler[A](implicit iso: IntIso[A]): BSONHandler[A] = BSONIntegerHandler.as[A](iso.from, iso.to)
def intAnyValHandler[A](to: A => Int, from: Int => A): BSONHandler[A] = intIsoHandler(Iso(from, to))
def booleanIsoHandler[A](implicit iso: BooleanIso[A]): BSONHandler[A] =
BSONBooleanHandler.as[A](iso.from, iso.to)
def booleanAnyValHandler[A](to: A => Boolean, from: Boolean => A): BSONHandler[A] =
booleanIsoHandler(Iso(from, to))
def doubleIsoHandler[A](implicit iso: DoubleIso[A]): BSONHandler[A] =
BSONDoubleHandler.as[A](iso.from, iso.to)
def doubleAnyValHandler[A](to: A => Double, from: Double => A): BSONHandler[A] =
doubleIsoHandler(Iso(from, to))
def floatIsoHandler[A](implicit iso: FloatIso[A]): BSONHandler[A] =
BSONFloatHandler.as[A](iso.from, iso.to)
def floatAnyValHandler[A](to: A => Float, from: Float => A): BSONHandler[A] =
floatIsoHandler(Iso(from, to))
def dateIsoHandler[A](implicit iso: Iso[DateTime, A]): BSONHandler[A] =
BSONJodaDateTimeHandler.as[A](iso.from, iso.to)
def quickHandler[T](read: PartialFunction[BSONValue, T], write: T => BSONValue): BSONHandler[T] =
new BSONHandler[T] {
def readTry(bson: BSONValue) =
read
.andThen(Success(_))
.applyOrElse(
bson,
(b: BSONValue) => handlerBadType(b)
)
def writeTry(t: T) = Success(write(t))
}
def tryHandler[T](read: PartialFunction[BSONValue, Try[T]], write: T => BSONValue): BSONHandler[T] =
new BSONHandler[T] {
def readTry(bson: BSONValue) =
read.applyOrElse(
bson,
(b: BSONValue) => handlerBadType(b)
)
def writeTry(t: T) = Success(write(t))
}
def handlerBadType[T](b: BSONValue): Try[T] =
Failure(TypeDoesNotMatchException("BSONValue", b.getClass.getSimpleName))
def handlerBadValue[T](msg: String): Try[T] =
Failure(new IllegalArgumentException(msg))
def stringMapHandler[V](implicit
reader: BSONReader[Map[String, V]],
writer: BSONWriter[Map[String, V]]
) =
new BSONHandler[Map[String, V]] {
def readTry(bson: BSONValue) = reader readTry bson
def writeTry(v: Map[String, V]) = writer writeTry v
}
def typedMapHandler[K, V: BSONReader: BSONWriter](keyIso: StringIso[K]) =
stringMapHandler[V].as[Map[K, V]](
_.map { case (k, v) => keyIso.from(k) -> v },
_.map { case (k, v) => keyIso.to(k) -> v }
)
implicit def bsonArrayToNonEmptyListHandler[T](implicit handler: BSONHandler[T]) = {
def listWriter = collectionWriter[T, List[T]]
def listReader = collectionReader[List, T]
tryHandler[NonEmptyList[T]](
{ case array: BSONArray =>
listReader.readTry(array).flatMap {
_.toNel toTry s"BSONArray is empty, can't build NonEmptyList"
}
},
nel => listWriter.writeTry(nel.toList).get
)
}
implicit object BSONNullWriter extends BSONWriter[BSONNull.type] {
def writeTry(n: BSONNull.type) = Success(BSONNull)
}
implicit val ipAddressHandler = isoHandler[IpAddress, String](ipAddressIso)
implicit val emailAddressHandler = isoHandler[EmailAddress, String](emailAddressIso)
implicit val normalizedEmailAddressHandler =
isoHandler[NormalizedEmailAddress, String](normalizedEmailAddressIso)
implicit val colorBoolHandler = BSONBooleanHandler.as[chess.Color](chess.Color.fromWhite, _.white)
implicit val FENHandler: BSONHandler[FEN] = stringAnyValHandler[FEN](_.value, FEN.apply)
implicit val modeHandler = BSONBooleanHandler.as[chess.Mode](chess.Mode.apply, _.rated)
val variantByKeyHandler: BSONHandler[Variant] = quickHandler[Variant](
{
case BSONString(v) => Variant orDefault v
case _ => Variant.default
},
v => BSONString(v.key)
)
val clockConfigHandler = tryHandler[chess.Clock.Config](
{ case doc: BSONDocument =>
for {
limit <- doc.getAsTry[Int]("limit")
inc <- doc.getAsTry[Int]("increment")
} yield chess.Clock.Config(limit, inc)
},
c =>
BSONDocument(
"limit" -> c.limitSeconds,
"increment" -> c.incrementSeconds
)
)
}