From 59fae0e07a33d5b4526f70328ea146236d566990 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Fri, 21 Sep 2012 17:51:01 +0200 Subject: [PATCH] Add JsResult#withFilter to avoid warnings in for-comprehensions Fix JsResult#map to satisfy the identity functor law --- .../scala/play/api/libs/json/JsResult.scala | 38 ++++++++++++++++--- .../play/api/libs/json/JsonValidSpec.scala | 23 +++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/framework/src/play/src/main/scala/play/api/libs/json/JsResult.scala b/framework/src/play/src/main/scala/play/api/libs/json/JsResult.scala index 3aa6f5211f0..ba71d445107 100644 --- a/framework/src/play/src/main/scala/play/api/libs/json/JsResult.scala +++ b/framework/src/play/src/main/scala/play/api/libs/json/JsResult.scala @@ -41,15 +41,16 @@ object JsError { } } -sealed trait JsResult[+A] { +sealed trait JsResult[+A] { self => + def fold[X](invalid: Seq[(JsPath, Seq[ValidationError])] => X, valid: A => X): X = this match { case JsSuccess(v,_) => valid(v) case JsError(e) => invalid(e) } def map[X](f: A => X): JsResult[X] = this match { - case JsSuccess(v,_) => JsSuccess(f(v)) - case JsError(e) => JsError(e) + case JsSuccess(v, path) => JsSuccess(f(v), path) + case e: JsError => e } def filterNot(error:ValidationError)(p: A => Boolean): JsResult[A] = @@ -71,7 +72,34 @@ sealed trait JsResult[+A] { def flatMap[X](f: A => JsResult[X]): JsResult[X] = this match { case JsSuccess(v, path) => f(v).repath(path) - case JsError(e) => JsError(e) + case e: JsError => e + } + + def foreach(f: A => Unit): Unit = this match { + case JsSuccess(a, _) => f(a) + case _ => () + } + + def withFilter(p: A => Boolean) = new WithFilter(p) + + final class WithFilter(p: A => Boolean) { + def map[B](f: A => B): JsResult[B] = self match { + case JsSuccess(a, path) => + if (p(a)) JsSuccess(f(a), path) + else JsError() + case e: JsError => e + } + def flatMap[B](f: A => JsResult[B]): JsResult[B] = self match { + case JsSuccess(a, path) => + if (p(a)) f(a).repath(path) + else JsError() + case e: JsError => e + } + def foreach(f: A => Unit): Unit = self match { + case JsSuccess(a, _) if p(a) => f(a) + case _ => () + } + def withFilter(q: A => Boolean) = new WithFilter(a => p(a) && q(a)) } //def rebase(json: JsValue): JsResult[A] = fold(valid = JsSuccess(_), invalid = (_, e, g) => JsError(json, e, g)) @@ -80,7 +108,7 @@ sealed trait JsResult[+A] { case JsError(es) => JsError(es.map{ case (p, s) => path ++ p -> s }) } - def get:A + def get: A def getOrElse[AA >: A](t: => AA):AA = this match { case JsSuccess(a,_) => a diff --git a/framework/src/play/src/test/scala/play/api/libs/json/JsonValidSpec.scala b/framework/src/play/src/test/scala/play/api/libs/json/JsonValidSpec.scala index e691a95ce33..3a14a64b6f0 100644 --- a/framework/src/play/src/test/scala/play/api/libs/json/JsonValidSpec.scala +++ b/framework/src/play/src/test/scala/play/api/libs/json/JsonValidSpec.scala @@ -482,4 +482,27 @@ object JsonValidSpec extends Specification { } } + "JsResult" should { + + "be usable in for-comprehensions" in { + val res = JsSuccess("foo") + val x = for { + s <- res + if s.size < 5 + } yield 42 + x must equalTo (JsSuccess(42)) + } + + "be a functor" in { + "JsSuccess" in { + val res1: JsResult[String] = JsSuccess("foo", JsPath(List(KeyPathNode("bar")))) + res1.map(identity) must equalTo (res1) + } + + "JsError" in { + val res2: JsResult[String] = JsError(Seq(JsPath(List(KeyPathNode("bar"))) -> Seq(ValidationError("baz.bah")))) + res2.map(identity) must equalTo (res2) + } + } + } }