diff options
author | Johannes Rudolph <johannes_rudolph@gmx.de> | 2012-05-31 15:50:19 +0200 |
---|---|---|
committer | Johannes Rudolph <johannes_rudolph@gmx.de> | 2012-05-31 15:50:19 +0200 |
commit | e5555bf2da861ce365b21d27309daf4d2de3580f (patch) | |
tree | 024bdf6996833e54b5b917811a1cef88a3c9c6ae /src/main/scala/cc | |
parent | 3bba5c875e0e5335d2fb41b8d82d9ebc1567989b (diff) | |
download | spray-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 |