forked from lichess-org/lila
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PackageObject.scala
276 lines (200 loc) · 8.2 KB
/
PackageObject.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
package lila
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.Future
import ornicar.scalalib
import scalaz.{ Monad, Monoid, OptionT, ~> }
trait PackageObject extends Steroids with WithFuture {
// case object Key(value: String) extends AnyVal with StringValue
trait StringValue extends Any {
def value: String
override def toString = value
}
def !![A](msg: String): Valid[A] = msg.failureNel[A]
def nowNanos: Long = System.nanoTime()
def nowMillis: Long = System.currentTimeMillis()
def nowTenths: Long = nowMillis / 100
def nowSeconds: Int = (nowMillis / 1000).toInt
implicit final def runOptionT[F[+_], A](ot: OptionT[F, A]): F[Option[A]] = ot.run
// from scalaz. We don't want to import all OptionTFunctions, because of the clash with `some`
def optionT[M[_]] = new (({ type λ[α] = M[Option[α]] })#λ ~>({ type λ[α] = OptionT[M, α] })#λ) {
def apply[A](a: M[Option[A]]) = new OptionT[M, A](a)
}
implicit final class LilaPimpedString(s: String) {
def boot[A](v: => A): A = { play.api.Logger("boot").info(s); v }
}
implicit final class LilaPimpedValid[A](v: Valid[A]) {
def future: Fu[A] = v fold (errs => fufail(errs.shows), fuccess)
}
implicit final class LilaPimpedTry[A](v: scala.util.Try[A]) {
def fold[B](fe: Exception => B, fa: A => B): B = v match {
case scala.util.Failure(e: Exception) => fe(e)
case scala.util.Failure(e) => throw e
case scala.util.Success(a) => fa(a)
}
def future: Fu[A] = fold(Future.failed, fuccess)
}
def parseIntOption(str: String): Option[Int] = try {
Some(java.lang.Integer.parseInt(str))
}
catch {
case e: NumberFormatException => None
}
def parseFloatOption(str: String): Option[Float] = try {
Some(java.lang.Float.parseFloat(str))
}
catch {
case e: NumberFormatException => None
}
def intBox(in: Range.Inclusive)(v: Int): Int =
math.max(in.start, math.min(v, in.end))
def floatBox(in: Range.Inclusive)(v: Float): Float =
math.max(in.start, math.min(v, in.end))
def doubleBox(in: Range.Inclusive)(v: Double): Double =
math.max(in.start, math.min(v, in.end))
}
trait WithFuture extends scalalib.Validation {
type Fu[+A] = Future[A]
type Funit = Fu[Unit]
def fuccess[A](a: A) = Future successful a
def fufail[A <: Throwable, B](a: A): Fu[B] = Future failed a
def fufail[A](a: String): Fu[A] = fufail(common.LilaException(a))
def fufail[A](a: Failures): Fu[A] = fufail(common.LilaException(a))
val funit = fuccess(())
implicit def SprayPimpedFuture[T](fut: Future[T]) =
new spray.util.pimps.PimpedFuture[T](fut)
}
trait WithPlay { self: PackageObject =>
import play.api.libs.json._
import scalalib.Zero
implicit def execontext = play.api.libs.concurrent.Execution.defaultContext
implicit val LilaFutureMonad = new Monad[Fu] {
override def map[A, B](fa: Fu[A])(f: A => B) = fa map f
def point[A](a: => A) = fuccess(a)
def bind[A, B](fa: Fu[A])(f: A => Fu[B]) = fa flatMap f
}
implicit def LilaFuMonoid[A: Monoid]: Monoid[Fu[A]] =
Monoid.instance((x, y) => x zip y map {
case (a, b) => a ⊹ b
}, fuccess(∅[A]))
implicit def LilaFuZero[A: Zero]: Zero[Fu[A]] =
Zero.instance(fuccess(zero[A]))
implicit val LilaJsObjectZero: Zero[JsObject] =
Zero.instance(JsObject(Seq.empty))
implicit def LilaJsResultZero[A]: Zero[JsResult[A]] =
Zero.instance(JsError(Seq.empty))
implicit final class LilaTraversableFuture[A, M[X] <: TraversableOnce[X]](t: M[Fu[A]]) {
def sequenceFu(implicit cbf: scala.collection.generic.CanBuildFrom[M[Fu[A]], A, M[A]]) =
Future sequence t
}
implicit final class LilaPimpedFuture[A](fua: Fu[A]) {
def >>-(sideEffect: => Unit): Fu[A] = fua andThen {
case _ => sideEffect
}
def >>[B](fub: => Fu[B]): Fu[B] = fua flatMap (_ => fub)
def void: Funit = fua map (_ => Unit)
def inject[B](b: => B): Fu[B] = fua map (_ => b)
def injectAnyway[B](b: => B): Fu[B] = fua.fold(_ => b, _ => b)
def effectFold(fail: Exception => Unit, succ: A => Unit) {
fua onComplete {
case scala.util.Failure(e: Exception) => fail(e)
case scala.util.Failure(e) => throw e // Throwables
case scala.util.Success(e) => succ(e)
}
}
def andThenAnyway(sideEffect: => Unit): Fu[A] = {
fua onComplete {
case scala.util.Failure(_) => sideEffect
case scala.util.Success(_) => sideEffect
}
fua
}
def fold[B](fail: Exception => B, succ: A => B): Fu[B] =
fua map succ recover { case e: Exception => fail(e) }
def flatFold[B](fail: Exception => Fu[B], succ: A => Fu[B]): Fu[B] =
fua flatMap succ recoverWith { case e: Exception => fail(e) }
def logFailure(logger: => String, msg: Exception => String): Fu[A] = fua ~ (_ onFailure {
case e: Exception => play.api.Logger(logger).warn(msg(e))
})
def logFailure(logger: => String): Fu[A] = logFailure(logger, _.toString)
def addEffect(effect: A => Unit) = fua ~ (_ foreach effect)
def addFailureEffect(effect: Exception => Unit) = fua ~ (_ onFailure {
case e: Exception => effect(e)
})
def addEffects(fail: Exception => Unit, succ: A => Unit): Fu[A] =
fua andThen {
case scala.util.Failure(e: Exception) => fail(e)
case scala.util.Failure(e) => throw e // Throwables
case scala.util.Success(e) => succ(e)
}
def mapFailure(f: Exception => Exception) = fua recover {
case cause: Exception => throw f(cause)
}
def prefixFailure(p: => String) = fua mapFailure { e =>
common.LilaException(s"$p ${e.getMessage}")
}
def thenPp: Fu[A] = fua ~ {
_.effectFold(
e => println("[failure] " + e),
a => println("[success] " + a)
)
}
def thenPp(msg: String): Fu[A] = fua ~ {
_.effectFold(
e => println(s"[$msg] [failure] $e"),
a => println(s"[$msg] [success] $a")
)
}
def awaitSeconds(seconds: Int): A = {
import scala.concurrent.duration._
scala.concurrent.Await.result(fua, seconds.seconds)
}
def withTimeout(duration: FiniteDuration, error: => Throwable)(implicit system: akka.actor.ActorSystem): Fu[A] = {
Future firstCompletedOf Seq(fua,
akka.pattern.after(duration, system.scheduler)(fufail(error)))
}
def chronometer = lila.common.Chronometer(fua)
def mon(path: lila.mon.RecPath) = chronometer.mon(path).result
}
implicit final class LilaPimpedFutureZero[A: Zero](fua: Fu[A]) {
def nevermind: Fu[A] = fua recover {
case e: lila.common.LilaException => zero[A]
case e: java.util.concurrent.TimeoutException => zero[A]
}
}
implicit final class LilaPimpedFutureOption[A](fua: Fu[Option[A]]) {
def flatten(msg: => String): Fu[A] = fua flatMap {
_.fold[Fu[A]](fufail(msg))(fuccess(_))
}
def orElse(other: => Fu[Option[A]]): Fu[Option[A]] = fua flatMap {
_.fold(other) { x => fuccess(x.some) }
}
def getOrElse(other: => Fu[A]): Fu[A] = fua flatMap { _.fold(other)(fuccess) }
}
implicit final class LilaPimpedFutureValid[A](fua: Fu[Valid[A]]) {
def flatten: Fu[A] = fua flatMap { _.fold[Fu[A]](fufail(_), fuccess(_)) }
}
implicit final class LilaPimpedFutureBoolean(fua: Fu[Boolean]) {
def >>&(fub: => Fu[Boolean]): Fu[Boolean] =
fua flatMap { _.fold(fub, fuccess(false)) }
def >>|(fub: => Fu[Boolean]): Fu[Boolean] =
fua flatMap { _.fold(fuccess(true), fub) }
def unary_! = fua map (!_)
}
implicit final class LilaPimpedBooleanWithFuture(self: Boolean) {
def optionFu[A](v: => Fu[A]): Fu[Option[A]] = if (self) v map (_.some) else fuccess(none)
}
implicit final class LilaPimpedActorSystem(self: akka.actor.ActorSystem) {
def lilaBus = lila.common.Bus(self)
}
object makeTimeout {
import akka.util.Timeout
import scala.concurrent.duration._
implicit val short = seconds(1)
implicit val large = seconds(5)
implicit val larger = seconds(30)
implicit val veryLarge = minutes(5)
def apply(duration: FiniteDuration) = Timeout(duration)
def seconds(s: Int): Timeout = Timeout(s.seconds)
def minutes(m: Int): Timeout = Timeout(m.minutes)
}
}