Reland #3670, i.e. refactorImplicits

This reverts commit 429814a610.
This commit is contained in:
Isaac Levy 2017-10-09 18:46:02 -04:00
parent 429814a610
commit a5f64a22e2
17 changed files with 560 additions and 407 deletions

View file

@ -52,7 +52,7 @@ trait LilaSocket { self: LilaController =>
}
f(ctx).map { resultOrSocket =>
resultOrSocket.right.map {
case (readIn, writeOut) => (e, i) => {
case (readIn, writeOut) => (e: Enumerator[A], i: Iteratee[A, Unit]) => {
writeOut |>> i
e &> Enumeratee.mapInput { in =>
if (limiter(ip, 1)(true)) in
@ -61,6 +61,7 @@ trait LilaSocket { self: LilaController =>
Input.EOF
}
} |>> readIn
()
}
}
}

View file

@ -105,7 +105,7 @@ object Practice extends LilaController {
FormFuResult(form) { err =>
env.api.structure.get map { html.practice.config(_, err) }
} { text =>
env.api.config.set(text).valueOr(_ => funit) >>-
~env.api.config.set(text).right.toOption >>-
env.api.structure.clear >>
Env.mod.logApi.practiceConfig(me.id) inject Redirect(routes.Practice.config)
}

View file

@ -9,15 +9,7 @@ import play.twirl.api.Html
import lila.api.Env.{ current => apiEnv }
object Environment
extends scalaz.syntax.ToIdOps
with scalaz.std.OptionInstances
with scalaz.std.OptionFunctions
with scalaz.std.StringInstances
with scalaz.syntax.std.ToOptionIdOps
with scalalib.OrnicarMonoid.Instances
with scalalib.Zero.Instances
with scalalib.OrnicarOption
with lila.LilaSteroids
extends lila.Lilaisms
with StringHelper
with JsonHelper
with AssetHelper

View file

@ -1,6 +1,5 @@
package lila.common
import lila.common.PimpedJson._
import play.api.libs.json.{ Json, OWrites }
case class LightUser(

View file

@ -1,154 +1,75 @@
package lila
import org.joda.time.DateTime
import ornicar.scalalib
import ornicar.scalalib.Zero
import scala.util.Try
import scala.concurrent.duration._
import ornicar.scalalib
import org.joda.time.DateTime
import com.typesafe.config.Config
import play.api.libs.json.{ JsObject, JsValue }
import lila.base._
trait Lilaisms
extends scalalib.Validation
extends LilaTypes
with scalalib.Common
with scalalib.Regex
with scalalib.OrnicarMonoid.Instances
with scalalib.Zero.Syntax
with scalalib.Zero.Instances
with scalalib.OrnicarOption
with scalalib.OrnicarNonEmptyList
with scalaz.std.OptionInstances
with scalaz.std.OptionFunctions
with scalaz.syntax.std.ToOptionIdOps
with scalaz.std.ListInstances
with scalalib.OrnicarOption
with scalalib.OrnicarMonoids
with scalalib.Regex
with scalalib.Validation
with scalalib.Zeros
with scalalib.Zero.Syntax
with scalaz.std.ListFunctions
with scalaz.syntax.std.ToListOps
with scalaz.std.ListInstances
with scalaz.std.OptionFunctions
with scalaz.std.OptionInstances
with scalaz.std.StringInstances
with scalaz.std.TupleInstances
with scalaz.syntax.ToIdOps
with scalaz.syntax.ToEqualOps
with scalaz.syntax.std.ToListOps
with scalaz.syntax.std.ToOptionIdOps
with scalaz.syntax.ToApplyOps
with scalaz.syntax.ToValidationOps
with scalaz.syntax.ToEqualOps
with scalaz.syntax.ToFunctorOps
with scalaz.syntax.ToIdOps
with scalaz.syntax.ToMonoidOps
with scalaz.syntax.ToTraverseOps
with scalaz.syntax.ToShowOps
with scalaz.syntax.ToTraverseOps
with scalaz.syntax.ToValidationOps {
with LilaSteroids
@inline implicit def toPimpedFuture[A](f: Fu[A]) = new PimpedFuture(f)
@inline implicit def toPimpedFutureBoolean(f: Fu[Boolean]) = new PimpedFutureBoolean(f)
@inline implicit def toPimpedFutureOption[A](f: Fu[Option[A]]) = new PimpedFutureOption(f)
@inline implicit def toPimpedFutureValid[A](f: Fu[Valid[A]]) = new PimpedFutureValid(f)
@inline implicit def toPimpedTraversableFuture[A, M[X] <: TraversableOnce[X]](t: M[Fu[A]]) =
new PimpedTraversableFuture(t)
trait LilaSteroids {
import Wrappers._
@inline implicit def toPimpedJsObject(jo: JsObject) = new PimpedJsObject(jo)
@inline implicit def toPimpedJsValue(jv: JsValue) = new PimpedJsValue(jv)
@inline implicit def toLilaPimpedOption[A](a: Option[A]) = new LilaPimpedOption(a)
@inline implicit def toLilaPimpedTryList[A](l: List[Try[A]]) = new LilaPimpedTryList(l)
@inline implicit def toLilaPimpedList[A](l: List[A]) = new LilaPimpedList(l)
@inline implicit def toLilaPimpedSeq[A](l: List[A]) = new LilaPimpedSeq(l)
@inline implicit def toLilaPimpedDateTime(d: DateTime) = new LilaPimpedDateTime(d)
@inline implicit def toLilaPimpedBoolean(b: Boolean) = new LilaPimpedBoolean(b)
@inline implicit def toLilaPimpedInt(i: Int) = new LilaPimpedInt(i)
@inline implicit def toLilaPimpedFloat(f: Float) = new LilaPimpedFloat(f)
@inline implicit def toLilaPimpedDouble(d: Double) = new LilaPimpedDouble(d)
@inline implicit def toLilaPimpedByteArray(ba: Array[Byte]) = new LilaPimpedByteArray(ba)
@inline implicit def toPimpedBoolean(b: Boolean) = new PimpedBoolean(b)
@inline implicit def toPimpedInt(i: Int) = new PimpedInt(i)
@inline implicit def toPimpedLong(l: Long) = new PimpedLong(l)
@inline implicit def toPimpedFloat(f: Float) = new PimpedFloat(f)
@inline implicit def toPimpedDouble(d: Double) = new PimpedDouble(d)
@inline implicit def toPimpedTryList[A](l: List[Try[A]]) = new PimpedTryList(l)
@inline implicit def toPimpedList[A](l: List[A]) = new PimpedList(l)
@inline implicit def toPimpedSeq[A](l: List[A]) = new PimpedSeq(l)
@inline implicit def toPimpedByteArray(ba: Array[Byte]) = new PimpedByteArray(ba)
@inline implicit def toPimpedOption[A](a: Option[A]) = new PimpedOption(a)
@inline implicit def toPimpedString(s: String) = new PimpedString(s)
@inline implicit def toPimpedConfig(c: Config) = new PimpedConfig(c)
@inline implicit def toPimpedDateTime(d: DateTime) = new PimpedDateTime(d)
@inline implicit def toPimpedValid[A](v: Valid[A]) = new PimpedValid(v)
@inline implicit def toPimpedTry[A](t: Try[A]) = new PimpedTry(t)
@inline implicit def toPimpedEither[A, B](e: Either[A, B]) = new PimpedEither(e)
@inline implicit def toPimpedFiniteDuration(d: FiniteDuration) = new PimpedFiniteDuration(d)
@inline implicit def toPimpedActorSystem(a: akka.actor.ActorSystem) = new PimpedActorSystem(a)
implicit val dateTimeOrdering: Ordering[DateTime] = Ordering.fromLessThan(_ isBefore _)
}
object Wrappers {
final class LilaPimpedDateTime(private val date: DateTime) extends AnyVal {
def getSeconds: Long = date.getMillis / 1000
def getCentis: Long = date.getMillis / 10
}
final class LilaPimpedTryList[A](private val list: List[Try[A]]) extends AnyVal {
def sequence: Try[List[A]] = (Try(List[A]()) /: list) {
(a, b) => a flatMap (c => b map (d => d :: c))
} map (_.reverse)
}
final class LilaPimpedList[A](private val list: List[A]) extends AnyVal {
def sortLike[B](other: List[B], f: A => B): List[A] = list.sortWith {
case (x, y) => other.indexOf(f(x)) < other.indexOf(f(y))
}
}
final class LilaPimpedSeq[A](private val seq: Seq[A]) extends AnyVal {
def has(a: A) = seq contains a
}
/*
* Replaces scalaz boolean ops
* so ?? works on Zero and not Monoid
*/
final class LilaPimpedBoolean(private val self: Boolean) extends AnyVal {
def ??[A](a: => A)(implicit z: Zero[A]): A = if (self) a else Zero[A].zero
def !(f: => Unit) = if (self) f
def fold[A](t: => A, f: => A): A = if (self) t else f
def ?[X](t: => X) = new { def |(f: => X) = if (self) t else f }
def option[A](a: => A): Option[A] = if (self) Some(a) else None
}
final class LilaPimpedInt(private val self: Int) extends AnyVal {
def atLeast(bottomValue: Int): Int = self max bottomValue
def atMost(topValue: Int): Int = self min topValue
}
final class LilaPimpedFloat(private val self: Float) extends AnyVal {
def atLeast(bottomValue: Float): Float = self max bottomValue
def atMost(topValue: Float): Float = self min topValue
}
final class LilaPimpedDouble(private val self: Double) extends AnyVal {
def atLeast(bottomValue: Double): Double = self max bottomValue
def atMost(topValue: Double): Double = self min topValue
}
final class LilaPimpedByteArray(private val self: Array[Byte]) extends AnyVal {
def toBase64 = java.util.Base64.getEncoder.encodeToString(self)
}
/*
* Replaces scalaz option ops
* so ~ works on Zero and not Monoid
*/
final class LilaPimpedOption[A](private val self: Option[A]) extends AnyVal {
import scalaz.std.{ option => o }
def fold[X](some: A => X, none: => X): X = self match {
case None => none
case Some(a) => some(a)
}
def |(a: => A): A = self getOrElse a
def unary_~(implicit z: Zero[A]): A = self getOrElse z.zero
def orDefault(implicit z: Zero[A]): A = self getOrElse z.zero
def toSuccess[E](e: => E): scalaz.Validation[E, A] = o.toSuccess(self)(e)
def toFailure[B](b: => B): scalaz.Validation[A, B] = o.toFailure(self)(b)
def toTry(err: => Exception): Try[A] =
self.fold[Try[A]](scala.util.Failure(err))(scala.util.Success.apply)
def err(message: => String): A = self.getOrElse(sys.error(message))
def ifNone(n: => Unit): Unit = if (self.isEmpty) n
def has(a: A) = self contains a
}
}
final class PimpedActorSystem(private val a: akka.actor.ActorSystem) extends AnyVal {
def lilaBus = lila.common.Bus(a)
}

View file

@ -1,22 +1,11 @@
package lila
import scala.concurrent.duration.{ Duration, FiniteDuration, MILLISECONDS }
import scala.concurrent.Future
import scala.util.Try
import ornicar.scalalib
import scalaz.{ Monad, Monoid, OptionT, ~> }
trait PackageObject extends Lilaisms with WithFuture {
// case object Key(value: String) extends AnyVal with StringValue
trait StringValue extends Any {
def value: String
override def toString = value
}
trait IntValue extends Any {
def value: Int
override def toString = value.toString
}
trait PackageObject extends Lilaisms {
implicit val playExecutionContext = play.api.libs.concurrent.Execution.defaultContext
def !![A](msg: String): Valid[A] = msg.failureNel[A]
@ -31,52 +20,31 @@ trait PackageObject extends Lilaisms with WithFuture {
def apply[A](a: M[Option[A]]) = new OptionT[M, A](a)
}
implicit def fuMonoid[A: Monoid]: Monoid[Fu[A]] =
Monoid.instance((x, y) => x zip y map {
case (a, b) => a b
}, fuccess([A]))
implicit val monadFu = 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
}
type ~[+A, +B] = Tuple2[A, B]
object ~ {
def apply[A, B](x: A, y: B) = Tuple2(x, y)
def unapply[A, B](x: Tuple2[A, B]): Option[Tuple2[A, B]] = Some(x)
}
implicit final class LilaPimpedString(s: String) {
def parseIntOption(str: String): Option[Int] =
Try(java.lang.Integer.parseInt(str)).toOption
def boot[A](v: => A): A = lila.common.Chronometer.syncEffect(v) { lap =>
lila.log.boot.info(s"${lap.millis}ms $s")
}
}
def parseFloatOption(str: String): Option[Float] =
Try(java.lang.Float.parseFloat(str)).toOption
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 parseLongOption(str: String): Option[Long] = try {
Some(java.lang.Long.parseLong(str))
} catch {
case e: NumberFormatException => None
}
def parseLongOption(str: String): Option[Long] =
Try(java.lang.Long.parseLong(str)).toOption
def intBox(in: Range.Inclusive)(v: Int): Int =
math.max(in.start, math.min(v, in.end))
@ -87,110 +55,6 @@ trait PackageObject extends Lilaisms with WithFuture {
def doubleBox(in: Range.Inclusive)(v: Double): Double =
math.max(in.start, math.min(v, in.end))
import play.api.libs.json._
import scalalib.Zero
implicit def playExecutionContext = play.api.libs.concurrent.Execution.defaultContext
val directEC = lila.PimpedFuture.DirectExecutionContext
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 def toPimpedConfig(c: com.typesafe.config.Config) = new common.LilaPimpedConfig(c)
implicit def LilaPimpedFuture[A](fua: Fu[A]): PimpedFuture.LilaPimpedFuture[A] =
new PimpedFuture.LilaPimpedFuture(fua)
implicit final class LilaPimpedFutureZero[A: Zero](fua: Fu[A]) {
def nevermind: Fu[A] = fua recover {
case e: lila.base.LilaException => zero[A]
case e: java.util.concurrent.TimeoutException => zero[A]
case e: Exception =>
lila.log("common").warn("Future.nevermind", e)
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 dmap (!_)
}
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)
}
implicit final class LilaPimpedFiniteDuration(self: FiniteDuration) {
def toCentis = chess.Centis {
// divide by Double, then round, to avoid rounding issues with just `/10`!
math.round {
if (self.unit eq MILLISECONDS) self.length / 10d
else self.toMillis / 10d
}
}
def abs = if (self.length < 0) -self else self
}
implicit val LilaFiniteDurationZero: Zero[FiniteDuration] = Zero.instance(Duration.Zero)
implicit val LilaCentisZero: Zero[chess.Centis] = Zero instance chess.Centis(0)
object makeTimeout {
import akka.util.Timeout
@ -205,16 +69,4 @@ trait PackageObject extends Lilaisms with WithFuture {
def seconds(s: Int): Timeout = Timeout(s.seconds)
def minutes(m: Int): Timeout = Timeout(m.minutes)
}
}
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(base.LilaException(a))
def fufail[A](a: Failures): Fu[A] = fufail(base.LilaException(a))
val funit = fuccess(())
}
}

View file

@ -4,9 +4,10 @@ import play.api.libs.json._
object PimpedJson {
def anyValWriter[O, A: Writes](f: O => A): Writes[O] = Writes[O] { o =>
def anyValWriter[O, A: Writes](f: O => A) = Writes[O] { o =>
Json toJson f(o)
}
def intAnyValWriter[O](f: O => Int): Writes[O] = anyValWriter[O, Int](f)
def stringAnyValWriter[O](f: O => String): Writes[O] = anyValWriter[O, String](f)
@ -14,85 +15,4 @@ object PimpedJson {
def intIsoWriter[O](iso: Iso[Int, O]): Writes[O] = anyValWriter[O, Int](iso.to)
def stringIsoReader[O](iso: Iso[String, O]): Reads[O] = Reads.of[String] map iso.from
implicit final class LilaPimpedJsObject(val js: JsObject) extends AnyVal {
def str(key: String): Option[String] =
(js \ key).asOpt[String]
def int(key: String): Option[Int] =
(js \ key).asOpt[Int]
def long(key: String): Option[Long] =
(js \ key).asOpt[Long]
def boolean(key: String): Option[Boolean] =
(js \ key).asOpt[Boolean]
def obj(key: String): Option[JsObject] =
(js \ key).asOpt[JsObject]
def arr(key: String): Option[JsArray] =
(js \ key).asOpt[JsArray]
def arrAs[A](key: String)(as: JsValue => Option[A]): Option[List[A]] =
arr(key) map { j =>
(j.value.map(as)(scala.collection.breakOut): List[Option[A]]).flatten
}
def ints(key: String): Option[List[Int]] = arrAs(key)(_.asOpt[Int])
def strs(key: String): Option[List[String]] = arrAs(key)(_.asOpt[String])
def objs(key: String): Option[List[JsObject]] = arrAs(key)(_.asOpt[JsObject])
def get[A: Reads](key: String): Option[A] =
(js \ key).asOpt[A]
def noNull = JsObject {
js.fields collect {
case (key, value) if value != JsNull => key -> value
}
}
def add(pair: (String, Boolean)): JsObject =
if (pair._2) js + (pair._1 -> JsBoolean(true))
else js
def add[A: Writes](pair: (String, Option[A])): JsObject =
pair._2.fold(js) { a => js + (pair._1 -> Json.toJson(a)) }
}
implicit final class LilaPimpedJsValue(val js: JsValue) extends AnyVal {
def str(key: String): Option[String] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[String]
}
def int(key: String): Option[Int] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[Int]
}
def long(key: String): Option[Long] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[Long]
}
def boolean(key: String): Option[Boolean] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[Boolean]
}
def obj(key: String): Option[JsObject] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[JsObject]
}
def arr(key: String): Option[JsArray] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[JsArray]
}
}
}
}

View file

@ -1,6 +1,6 @@
package lila.base
import ornicar.scalalib
import ornicar.scalalib.ValidTypes._
trait LilaException extends Exception {
val message: String
@ -9,10 +9,9 @@ trait LilaException extends Exception {
override def toString = message
}
object LilaException extends scalalib.Validation
with scalaz.syntax.ToShowOps {
object LilaException extends scalaz.syntax.ToShowOps {
def apply(msg: String): LilaException = new LilaException {
def apply(msg: String) = new LilaException {
val message = msg
}

View file

@ -0,0 +1,41 @@
package lila.base
import scala.concurrent.duration.Duration
import scala.concurrent.Future
import org.joda.time.DateTime
import ornicar.scalalib.{ Zero, ValidTypes }
import play.api.libs.json.{ JsObject, JsError }
trait LilaTypes extends ValidTypes {
trait StringValue extends Any {
def value: String
override def toString = value
}
trait IntValue extends Any {
def value: Int
override def toString = value.toString
}
type Fu[A] = Future[A]
type Funit = Fu[Unit]
@inline def fuccess[A](a: A): Fu[A] = Future.successful(a)
def fufail[X](t: Throwable): Fu[X] = Future.failed(t)
def fufail[X](s: String): Fu[X] = fufail(LilaException(s))
def fufail[X](f: Failures): Fu[X] = fufail(LilaException(f))
val funit = fuccess(())
implicit def fuZero[A](implicit az: Zero[A]) = new Zero[Fu[A]] {
def zero = fuccess(az.zero)
}
implicit val durationZero: Zero[Duration] = Zero.instance(Duration.Zero)
implicit val jsObjectZero = Zero.instance(JsObject(Seq.empty))
implicit val jsResultZero = Zero.instance(JsError(Seq.empty))
implicit val dateTimeOrdering: Ordering[DateTime] = Ordering.fromLessThan(_ isBefore _)
}
object LilaTypes extends LilaTypes

View file

@ -1,4 +1,4 @@
package lila.common
package lila.base
import scala.concurrent.duration._
import java.util.concurrent.TimeUnit

View file

@ -0,0 +1,176 @@
package lila.base
import play.api.libs.concurrent.Execution.Implicits._
import scala.concurrent.duration._
import scala.concurrent.{ Future, ExecutionContext }
import LilaTypes._
import ornicar.scalalib.Zero
object DirectExecutionContext extends ExecutionContext {
override def execute(command: Runnable): Unit = command.run()
override def reportFailure(cause: Throwable): Unit =
throw new IllegalStateException("lila DirectExecutionContext failure", cause)
}
final class PimpedFuture[A](private val fua: Fu[A]) extends AnyVal {
private type Fu[A] = Future[A]
def dmap[B](f: A => B): Fu[B] = fua.map(f)(DirectExecutionContext)
def dforeach[B](f: A => Unit): Unit = fua.foreach(f)(DirectExecutionContext)
def >>-(sideEffect: => Unit): Fu[A] = fua andThen {
case _ => sideEffect
}
def >>[B](fub: => Fu[B]): Fu[B] = fua flatMap (_ => fub)
def void: Fu[Unit] = fua.map(_ => ())(DirectExecutionContext)
def inject[B](b: => B): Fu[B] = fua.map(_ => b)(DirectExecutionContext)
def injectAnyway[B](b: => B): Fu[B] = 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 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: => lila.log.Logger, msg: Exception => String): Fu[A] =
addFailureEffect { e => logger.warn(msg(e), e) }
def logFailure(logger: => lila.log.Logger): Fu[A] = logFailure(logger, _.toString)
def addFailureEffect(effect: Exception => Unit) = {
fua onFailure {
case e: Exception => effect(e)
}
fua
}
def addEffect(effect: A => Unit): Fu[A] = {
fua foreach effect
fua
}
def addEffects(fail: Exception => Unit, succ: A => Unit): Fu[A] = {
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)
}
fua
}
def addEffectAnyway(inAnyCase: => Unit): Fu[A] = {
fua onComplete {
case _ => inAnyCase
}
fua
}
def mapFailure(f: Exception => Exception) = fua recover {
case cause: Exception => throw f(cause)
}
def prefixFailure(p: => String) = mapFailure { e =>
LilaException(s"$p ${e.getMessage}")
}
def thenPp: Fu[A] = {
effectFold(
e => println("[failure] " + e),
a => println("[success] " + a)
)
fua
}
def thenPp(msg: String): Fu[A] = {
effectFold(
e => println(s"[$msg] [failure] $e"),
a => println(s"[$msg] [success] $a")
)
fua
}
def await(duration: FiniteDuration): A =
scala.concurrent.Await.result(fua, duration)
def awaitOrElse(duration: FiniteDuration, default: => A): A = try {
scala.concurrent.Await.result(fua, duration)
} catch {
case _: Exception => default
}
def awaitSeconds(seconds: Int): A =
await(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)(Future failed error)
)
}
def withTimeoutDefault(duration: FiniteDuration, default: => A)(implicit system: akka.actor.ActorSystem): Fu[A] = {
Future firstCompletedOf Seq(
fua,
akka.pattern.after(duration, system.scheduler)(Future(default))
)
}
def chronometer = lila.common.Chronometer(fua)
def mon(path: lila.mon.RecPath) = chronometer.mon(path).result
def nevermind(implicit z: Zero[A]): Fu[A] = fua recover {
case e: LilaException => z.zero
case e: java.util.concurrent.TimeoutException => z.zero
case e: Exception =>
lila.log("common").warn("Future.nevermind", e)
z.zero
}
}
final class PimpedFutureBoolean(private val fua: Fu[Boolean]) extends AnyVal {
def >>&(fub: => Fu[Boolean]): Fu[Boolean] =
fua flatMap { if (_) fub else fuccess(false) }
def >>|(fub: => Fu[Boolean]): Fu[Boolean] =
fua flatMap { if (_) fuccess(true) else fub }
def unary_! = fua.map { !_ }(DirectExecutionContext)
}
final class PimpedFutureOption[A](private val fua: Fu[Option[A]]) extends AnyVal {
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(Some(x)) }
}
def getOrElse(other: => Fu[A]): Fu[A] = fua flatMap { _.fold(other)(fuccess) }
}
final class PimpedFutureValid[A](private val fua: Fu[Valid[A]]) extends AnyVal {
def flatten: Fu[A] = fua flatMap { _.fold[Fu[A]](fufail(_), fuccess(_)) }
}
final class PimpedTraversableFuture[A, M[X] <: TraversableOnce[X]](private val t: M[Fu[A]]) extends AnyVal {
import scala.collection.generic.CanBuildFrom
def sequenceFu(implicit cbf: CanBuildFrom[M[Fu[A]], A, M[A]]): Fu[M[A]] =
Future.sequence(t)
}

View file

@ -0,0 +1,84 @@
package lila.base
import play.api.libs.json._
final class PimpedJsObject(private val js: JsObject) extends AnyVal {
def str(key: String): Option[String] =
(js \ key).asOpt[String]
def int(key: String): Option[Int] =
(js \ key).asOpt[Int]
def long(key: String): Option[Long] =
(js \ key).asOpt[Long]
def boolean(key: String): Option[Boolean] =
(js \ key).asOpt[Boolean]
def obj(key: String): Option[JsObject] =
(js \ key).asOpt[JsObject]
def arr(key: String): Option[JsArray] =
(js \ key).asOpt[JsArray]
def arrAs[A](key: String)(as: JsValue => Option[A]): Option[List[A]] =
arr(key) map { j =>
(j.value.map(as)(scala.collection.breakOut): List[Option[A]]).flatten
}
def ints(key: String): Option[List[Int]] = arrAs(key)(_.asOpt[Int])
def strs(key: String): Option[List[String]] = arrAs(key)(_.asOpt[String])
def objs(key: String): Option[List[JsObject]] = arrAs(key)(_.asOpt[JsObject])
def get[A: Reads](key: String): Option[A] =
(js \ key).asOpt[A]
def noNull = JsObject {
js.fields collect {
case (key, value) if value != JsNull => key -> value
}
}
def add(pair: (String, Boolean)): JsObject =
if (pair._2) js + (pair._1 -> JsBoolean(true))
else js
def add[A: Writes](pair: (String, Option[A])): JsObject =
pair._2.fold(js) { a => js + (pair._1 -> Json.toJson(a)) }
}
final class PimpedJsValue(private val js: JsValue) extends AnyVal {
def str(key: String): Option[String] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[String]
}
def int(key: String): Option[Int] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[Int]
}
def long(key: String): Option[Long] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[Long]
}
def boolean(key: String): Option[Boolean] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[Boolean]
}
def obj(key: String): Option[JsObject] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[JsObject]
}
def arr(key: String): Option[JsArray] =
js.asOpt[JsObject] flatMap { obj =>
(obj \ key).asOpt[JsArray]
}
}

View file

@ -0,0 +1,54 @@
package lila.base
import java.lang.Math.{ min, max }
import scala.concurrent.ExecutionContext
import LilaTypes._
import ornicar.scalalib.Zero
final class PimpedBoolean(private val self: Boolean) extends AnyVal {
/**
* Replaces scalaz boolean ops
* so ?? works on Zero and not Monoid
*/
def ??[A](a: => A)(implicit z: Zero[A]): A = if (self) a else z.zero
def !(f: => Unit) = if (self) f
def fold[A](t: => A, f: => A): A = if (self) t else f
def ?[X](t: => X) = new { def |(f: => X) = if (self) t else f }
def option[A](a: => A): Option[A] = if (self) Some(a) else None
def optionFu[A](v: => Fu[A])(implicit ec: ExecutionContext): Fu[Option[A]] =
if (self) v map { Some(_) } else fuccess(None)
}
final class PimpedLong(private val self: Long) extends AnyVal {
def atLeast(bottomValue: Long): Long = max(self, bottomValue)
def atMost(topValue: Long): Long = min(self, topValue)
}
final class PimpedInt(private val self: Int) extends AnyVal {
def atLeast(bottomValue: Int): Int = max(self, bottomValue)
def atMost(topValue: Int): Int = min(self, topValue)
}
final class PimpedFloat(private val self: Float) extends AnyVal {
def atLeast(bottomValue: Float): Float = max(self, bottomValue)
def atMost(topValue: Float): Float = min(self, topValue)
}
final class PimpedDouble(private val self: Double) extends AnyVal {
def atLeast(bottomValue: Double): Double = max(self, bottomValue)
def atMost(topValue: Double): Double = min(self, topValue)
}

View file

@ -0,0 +1,22 @@
package lila.base
import java.util.Base64
import scala.util.Try
final class PimpedTryList[A](private val list: List[Try[A]]) extends AnyVal {
def sequence: Try[List[A]] = Try(list map { _.get })
}
final class PimpedList[A](private val list: List[A]) extends AnyVal {
def sortLike[B](other: List[B], f: A => B): List[A] = list.sortWith {
(x, y) => other.indexOf(f(x)) < other.indexOf(f(y))
}
}
final class PimpedSeq[A](private val seq: Seq[A]) extends AnyVal {
def has(a: A) = seq contains a
}
final class PimpedByteArray(private val self: Array[Byte]) extends AnyVal {
def toBase64 = Base64.getEncoder.encodeToString(self)
}

View file

@ -0,0 +1,93 @@
package lila.base
import java.util.concurrent.TimeUnit
import scala.concurrent.duration._
import scala.concurrent.Future
import scala.util.Try
import com.typesafe.config.Config
import org.joda.time.DateTime
import ornicar.scalalib.Zero
import scalaz._
import Scalaz._
import LilaTypes._
final class PimpedOption[A](private val self: Option[A]) extends AnyVal {
import scalaz.std.{ option => o }
def fold[X](some: A => X, none: => X): X = self.fold(none)(some)
def |(a: => A): A = self getOrElse a
def unary_~(implicit z: Zero[A]): A = self getOrElse z.zero
def orDefault(implicit z: Zero[A]): A = self getOrElse z.zero
def toSuccess[E](e: => E): scalaz.Validation[E, A] = o.toSuccess(self)(e)
def toFailure[B](b: => B): scalaz.Validation[A, B] = o.toFailure(self)(b)
def toTry(err: => Exception): Try[A] =
self.fold[Try[A]](scala.util.Failure(err))(scala.util.Success.apply)
def err(message: => String): A = self.getOrElse(sys.error(message))
def ifNone(n: => Unit): Unit = if (self.isEmpty) n
def has(a: A) = self contains a
}
final class PimpedString(private val s: String) extends AnyVal {
def boot[A](v: => A): A = lila.common.Chronometer.syncEffect(v) { lap =>
lila.log.boot.info(s"${lap.millis}ms $s")
}
}
final class PimpedConfig(private val config: Config) extends AnyVal {
def millis(name: String): Int = config.getDuration(name, TimeUnit.MILLISECONDS).toInt
def seconds(name: String): Int = config.getDuration(name, TimeUnit.SECONDS).toInt
def duration(name: String): FiniteDuration = millis(name).millis
}
final class PimpedDateTime(private val date: DateTime) extends AnyVal {
def getSeconds: Long = date.getMillis / 1000
def getCentis: Long = date.getMillis / 10
}
final class PimpedValid[A](private val v: Valid[A]) extends AnyVal {
def future: Fu[A] = v fold (errs => fufail(errs.shows), fuccess)
}
final class PimpedTry[A](private val v: Try[A]) extends AnyVal {
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)
}
final class PimpedEither[A, B](private val v: Either[A, B]) extends AnyVal {
import ornicar.scalalib.ValidTypes
def toValid: Valid[B] = ValidTypes.eitherToValid(v)
}
final class PimpedFiniteDuration(private val d: FiniteDuration) extends AnyVal {
def toCentis = chess.Centis {
// divide by Double, then round, to avoid rounding issues with just `/10`!
math.round {
if (d.unit eq MILLISECONDS) d.length / 10d
else d.toMillis / 10d
}
}
def abs = if (d.length < 0) -d else d
}

View file

@ -70,7 +70,7 @@ private final class Indexer(storage: Storage, sequencer: ActorRef) {
PovToEntry(game, user.id, provisional = nb < 10).addFailureEffect { e =>
println(e)
e.printStackTrace
} map (_.toOption)
} map (_.right.toOption)
}
val query = gameQuery(user) ++ $doc(Game.BSONFields.createdAt $gte from)
GameRepo.sortedCursor(query, Query.sortChronological)

View file

@ -23,13 +23,12 @@ object Dependencies {
}
val scalaz = "org.scalaz" %% "scalaz-core" % "7.2.15"
val scalalib = "com.github.ornicar" %% "scalalib" % "6.4"
val scalalib = "com.github.ornicar" %% "scalalib" % "6.5"
val typesafeConfig = "com.typesafe" % "config" % "1.3.1"
val findbugs = "com.google.code.findbugs" % "jsr305" % "3.0.1"
val hasher = "com.roundeights" %% "hasher" % "1.2.0"
val jodaTime = "joda-time" % "joda-time" % "2.9.9"
val chess = "org.lichess" %% "scalachess" % "6.18"
val chess = "org.lichess" %% "scalachess" % "6.19"
val maxmind = "com.sanoma.cda" %% "maxmind-geoip2-scala" % "1.2.3-THIB"
val prismic = "io.prismic" %% "scala-kit" % "1.2.11-THIB"
val java8compat = "org.scala-lang.modules" %% "scala-java8-compat" % "0.8.0"