summaryrefslogtreecommitdiff
path: root/src/main/scala/cc
diff options
context:
space:
mode:
authorJohannes Rudolph <johannes_rudolph@gmx.de>2012-05-31 15:50:19 +0200
committerJohannes Rudolph <johannes_rudolph@gmx.de>2012-05-31 15:50:19 +0200
commite5555bf2da861ce365b21d27309daf4d2de3580f (patch)
tree024bdf6996833e54b5b917811a1cef88a3c9c6ae /src/main/scala/cc
parent3bba5c875e0e5335d2fb41b8d82d9ebc1567989b (diff)
downloadspray-json-e5555bf2da861ce365b21d27309daf4d2de3580f.tar.gz
spray-json-e5555bf2da861ce365b21d27309daf4d2de3580f.tar.bz2
spray-json-e5555bf2da861ce365b21d27309daf4d2de3580f.zip
move into its own package
Diffstat (limited to 'src/main/scala/cc')
-rw-r--r--src/main/scala/cc/spray/json/lenses/JsonLenses.scala (renamed from src/main/scala/cc/spray/json/JsonLenses.scala)75
1 files changed, 51 insertions, 24 deletions
diff --git a/src/main/scala/cc/spray/json/JsonLenses.scala b/src/main/scala/cc/spray/json/lenses/JsonLenses.scala
index cf602b1..8871f9d 100644
--- a/src/main/scala/cc/spray/json/JsonLenses.scala
+++ b/src/main/scala/cc/spray/json/lenses/JsonLenses.scala
@@ -1,6 +1,5 @@
package cc.spray.json
-
-import annotation.tailrec
+package lenses
object JsonLenses {
type JsPred = JsValue => Boolean
@@ -22,16 +21,18 @@ object JsonLenses {
case Left(e) => throw e
}
}
+
implicit def orThrow[B](e: Either[Throwable, B]): GetOrThrow[B] = GetOrThrow(e)
trait MonadicReader[T] {
def read(js: JsValue): Validated[T]
}
+
object MonadicReader {
implicit def safeMonadicReader[T: JsonReader]: MonadicReader[T] = new MonadicReader[T] {
def read(js: JsValue): Validated[T] =
safe(js.convertTo[T])
- }
+ }
}
def safe[T](body: => T): Validated[T] =
@@ -47,9 +48,11 @@ object JsonLenses {
case None => unexpected(message)
}
}
+
implicit def validateOption[T](o: Option[T]): ValidateOption[T] = ValidateOption(o)
- trait Update extends (JsValue => JsValue) { outer =>
+ trait Update extends (JsValue => JsValue) {
+ outer =>
def apply(value: JsValue): JsValue
def &&(next: Update): Update = new Update {
@@ -58,18 +61,22 @@ object JsonLenses {
}
implicit def strToField(name: String): ScalarProjection = field(name)
+
implicit def symbolToField(sym: Symbol): ScalarProjection = field(sym.name)
case class RichJsValue(value: JsValue) {
def update(updater: Update): JsValue = updater(value)
+
def update[T: JsonWriter, M[_]](lens: UpdateLens, pValue: T): JsValue = lens ! set(pValue) apply value
// This can't be simplified because we don't want the type constructor
// of projection to appear in the type paramater list.
def extract[T: MonadicReader](p: Projection[Id]): T =
p.get[T](value)
+
def extract[T: MonadicReader](p: Projection[Option]): Option[T] =
p.get[T](value)
+
def extract[T: MonadicReader](p: Projection[Seq]): Seq[T] =
p.get[T](value)
@@ -110,7 +117,7 @@ object JsonLenses {
/**
* Given a parent JsValue, tries to extract the child value.
* @return `Right(value)` if the projection succeeds. `Left(error)` if the projection
- * fails.
+ * fails.
*/
def retr: JsValue => Validated[M[JsValue]]
@@ -147,32 +154,43 @@ object JsonLenses {
trait Join[M1[_], M2[_], R[_]] {
def get(outer: Ops[M1], inner: Ops[M2]): Ops[R]
}
+
object Join {
def apply[M1[_], M2[_], R[_]](f: ((Ops[M1], Ops[M2])) => Ops[R]): Join[M1, M2, R] = new Join[M1, M2, R] {
def get(outer: Ops[M1], inner: Ops[M2]): Ops[R] = f(outer, inner)
}
implicit def joinWithSeq[M2[_]]: Join[Seq, M2, Seq] = Join(_._1)
+
implicit def joinWithScalar[M2[_]]: Join[Id, M2, M2] = Join(_._2)
+
implicit def joinWithOptionWithId: Join[Option, Id, Option] = Join(_._1)
+
implicit def joinOptionWithOption: Join[Option, Option, Option] = Join(_._1)
+
implicit def joinOptionWithSeq: Join[Option, Seq, Seq] = Join(_._2)
}
trait Ops[M[_]] {
def flatMap[T, U](els: M[T])(f: T => Seq[U]): Seq[U]
+
def allRight[T](v: Seq[Validated[T]]): Validated[M[T]]
+
def toSeq[T](v: Validated[M[T]]): Seq[Validated[T]]
def map[T, U](els: M[T])(f: T => U): Seq[U] =
flatMap(els)(v => Seq(f(v)))
}
+
object Ops {
implicit def idOps: Ops[Id] = new Ops[Id] {
def flatMap[T, U](els: T)(f: T => Seq[U]): Seq[U] = f(els)
+
def allRight[T](v: Seq[Validated[T]]): Validated[T] = v.head
+
def toSeq[T](v: Validated[T]): Seq[Validated[T]] = Seq(v)
}
+
implicit def optionOps: Ops[Option] = new Ops[Option] {
def flatMap[T, U](els: Option[T])(f: T => Seq[U]): Seq[U] =
els.toSeq.flatMap(f)
@@ -190,6 +208,7 @@ object JsonLenses {
case Left(e) => Seq(Left(e))
}
}
+
implicit def seqOps: Ops[Seq] = new Ops[Seq] {
def flatMap[T, U](els: Seq[T])(f: T => Seq[U]): Seq[U] =
els.flatMap(f)
@@ -214,7 +233,8 @@ object JsonLenses {
}
}
- trait ProjectionImpl[M[_]] extends Projection[M] { outer =>
+ trait ProjectionImpl[M[_]] extends Projection[M] {
+ outer =>
def tryGet[T: MonadicReader](p: JsValue): Validated[M[T]] =
retr(p).flatMap(mapValue(_)(_.as[T]))
@@ -231,6 +251,7 @@ object JsonLenses {
def /[M2[_], R[_]](next: Projection[M2])(implicit ev: Join[M2, M, R]): Projection[R] = new ProjectionImpl[R] {
val ops: Ops[R] = ev.get(next.ops, outer.ops)
+
def retr: JsValue => Validated[R[JsValue]] = parent =>
for {
outerV <- outer.retr(parent)
@@ -244,6 +265,7 @@ object JsonLenses {
private[this] def mapValue[T](value: M[JsValue])(f: JsValue => Validated[T]): Validated[M[T]] =
ops.allRight(ops.map(value)(f))
}
+
abstract class Proj[M[_]](implicit val ops: Ops[M]) extends ProjectionImpl[M]
def field(name: String): ScalarProjection = new Proj[Id] {
@@ -251,19 +273,21 @@ object JsonLenses {
for {
res <- f(getField(parent))
}
- yield JsObject(fields = parent.asJsObject.fields + (name -> res))
+ yield JsObject(fields = parent.asJsObject.fields + (name -> res))
def retr: JsValue => SafeJsValue = v =>
getField(v)
- def getField(v: JsValue): SafeJsValue = asObj(v) flatMap { o =>
- o.fields.get(name).getOrError("Expected field '%s' in '%s'" format (name, v))
+ def getField(v: JsValue): SafeJsValue = asObj(v) flatMap {
+ o =>
+ o.fields.get(name).getOrError("Expected field '%s' in '%s'" format(name, v))
}
+
def asObj(v: JsValue): Validated[JsObject] = v match {
case o: JsObject =>
Right(o)
case e@_ =>
- unexpected("Not a json object: "+e)
+ unexpected("Not a json object: " + e)
}
}
@@ -271,12 +295,12 @@ object JsonLenses {
def updated(f: SafeJsValue => SafeJsValue)(parent: JsValue): SafeJsValue = parent match {
case JsArray(elements) =>
if (idx < elements.size) {
- val (headEls, element::tail) = elements.splitAt(idx)
+ val (headEls, element :: tail) = elements.splitAt(idx)
f(Right(element)) map (v => JsArray(headEls ::: v :: tail))
} else
- unexpected("Too little elements in array: %s size: %d index: %d" format (parent, elements.size, idx))
+ unexpected("Too little elements in array: %s size: %d index: %d" format(parent, elements.size, idx))
case e@_ =>
- unexpected("Not a json array: "+e)
+ unexpected("Not a json array: " + e)
}
def retr: JsValue => SafeJsValue = {
@@ -284,8 +308,8 @@ object JsonLenses {
if (idx < elements.size)
Right(elements(idx))
else
- outOfBounds("Too little elements in array: %s size: %d index: %d" format (a, elements.size, idx))
- case e@_ => unexpected("Not a json array: "+e)
+ outOfBounds("Too little elements in array: %s size: %d index: %d" format(a, elements.size, idx))
+ case e@_ => unexpected("Not a json array: " + e)
}
}
@@ -303,16 +327,16 @@ object JsonLenses {
def updated(f: SafeJsValue => SafeJsValue)(parent: JsValue): SafeJsValue = parent match {
case JsArray(elements) =>
ops.allRight(elements.map(x => f(Right(x)))).map(JsArray(_: _*))
- case e@_ => unexpected("Not a json array: "+e)
+ case e@_ => unexpected("Not a json array: " + e)
}
def retr: JsValue => Validated[Seq[JsValue]] = {
case JsArray(elements) => Right(elements)
- case e@_ => unexpected("Not a json array: "+e)
+ case e@_ => unexpected("Not a json array: " + e)
}
}
- /** Alias for `elements` */
+ /**Alias for `elements`*/
def * = elements
def filter(pred: JsPred): SeqProjection = new Proj[Seq] {
@@ -320,15 +344,16 @@ object JsonLenses {
case JsArray(elements) =>
ops.allRight(elements.map(x => if (pred(x)) f(Right(x)) else Right(x))).map(JsArray(_: _*))
- case e@_ =>unexpected("Not a json array: "+e)
+ case e@_ => unexpected("Not a json array: " + e)
}
def retr: JsValue => Validated[Seq[JsValue]] = {
case JsArray(elements) =>
Right(elements.filter(pred))
- case e@_ => unexpected("Not a json array: "+e)
+ case e@_ => unexpected("Not a json array: " + e)
}
}
+
def find(pred: JsPred): OptProjection = new Proj[Option] {
def updated(f: SafeJsValue => SafeJsValue)(parent: JsValue): SafeJsValue = parent match {
case JsArray(elements) =>
@@ -340,18 +365,18 @@ object JsonLenses {
case _ =>
Right(parent)
}
- case e@_ => unexpected("Not a json array: "+e)
+ case e@_ => unexpected("Not a json array: " + e)
}
def retr: JsValue => Validated[Option[JsValue]] = {
case JsArray(elements) => Right(elements.find(pred))
- case e@_ => unexpected("Not a json array: "+e)
+ case e@_ => unexpected("Not a json array: " + e)
}
}
def set[T: JsonWriter](t: T): Operation = new Operation {
def apply(value: SafeJsValue): SafeJsValue =
- // ignore existence of old value
+ // ignore existence of old value
Right(t.toJson)
}
@@ -361,12 +386,13 @@ object JsonLenses {
def apply(value: SafeJsValue): SafeJsValue = value.flatMap(apply)
}
- def updated[T: MonadicReader: JsonWriter](f: T => T): Operation = new MapOperation {
+ def updated[T: MonadicReader : JsonWriter](f: T => T): Operation = new MapOperation {
def apply(value: JsValue): SafeJsValue =
value.as[T] map (v => f(v).toJson)
}
def append(update: Update): Operation = ???
+
def update(update: Update): Operation = ???
def extract[M[_], T](value: Projection[M])(f: M[T] => Update): Operation = ???
@@ -374,5 +400,6 @@ object JsonLenses {
def ??? = sys.error("NYI")
def unexpected(message: String) = Left(new RuntimeException(message))
+
def outOfBounds(message: String) = Left(new IndexOutOfBoundsException(message))
} \ No newline at end of file