package dispatch.json
trait Extract[T] {
def unapply(js: JsValue): Option[T]
}
trait Insert[T] extends Extract[T] {
def << (t: T)(js: JsValue): JsObject
}
case class Property[T](sym: Symbol, ext: Extract[T]) extends Extract[T] with Insert[T] {
def unapply(js: JsValue) = js match {
case js: JsObject => js.self.get(JsString(sym)) flatMap ext.unapply
case _ => None
}
def << (t: T)(js: JsValue) = js match {
case JsObject(m) => JsObject(m + (JsString(sym) -> JsValue(t)))
case js => error("Unable to replace property in " + js)
}
}
case class Child[T, E <: Insert[T]](parent: Option[Obj], self: E) extends Extract[T] with Insert[T] {
def unapply(js: JsValue) = parent map { parent => js match {
case parent(self(t)) => Some(t)
} } getOrElse { js match {
case self(t) => Some(t)
case _ => None
}
}
def << (t: T)(js: JsValue) = parent map { parent => js match {
case parent(my_js) => (parent << (self << t)(my_js))(js)
} } getOrElse (self << t)(js)
}
class Obj(sym: Symbol)(implicit parent: Option[Obj])
extends Child[JsObject, Property[JsObject]](parent, Property(sym, Js.obj)) {
implicit val ctx = Some(this)
}
trait Js {
object str extends Extract[String] {
def unapply(js: JsValue) = js match {
case JsString(v) => Some(v)
case _ => None
}
}
object num extends Extract[BigDecimal] {
def unapply(js: JsValue) = js match {
case JsNumber(v) => Some(v)
case _ => None
}
}
object bool extends Extract[Boolean] {
def unapply(js: JsValue) = js match {
case JsBoolean(v) => Some(v)
case _ => None
}
}
object obj extends Extract[JsObject] {
def unapply(js: JsValue) = js match {
case JsObject(v) => Some(JsObject(v))
case _ => None
}
}
object list extends Extract[List[JsValue]] {
def unapply(js: JsValue) = js match {
case JsArray(v) => Some(v)
case _ => None
}
def ! [T](ext: Extract[T]) = new Extract[List[T]] {
def unapply(js: JsValue) = js match {
case list(l) => Some(l map { _ match { case ext(v) => v } } )
case _ => None
}
}
}
implicit val ctx: Option[Obj] = None
type JsF[T] = JsValue => T
type JsIF = JsF[JsObject]
implicit def sym_add_operators[T](sym: Symbol) = new SymOp(sym)
case class SymOp(sym: Symbol) {
def ? [T](cst: Extract[T])(implicit parent: Option[Obj]) =
new Child[T, Property[T]](parent, Property(sym, cst))
def ! [T](cst: Extract[T]): JsF[T] =
new Property(sym, cst).unapply _ andThen { _.get }
def << [T](t: T): JsIF = _ match {
case JsObject(m) => JsObject(m + (JsString(sym) -> JsValue(t)))
case js => error("Unable to replace property in " + js)
}
def << (f: JsIF): JsIF = js => (this << f((this ! obj)(js)))(js)
}
def %[A,B](a: JsF[A], b: JsF[B])(js: JsValue) = (a(js), b(js))
def %[A,B,C](a: JsF[A], b: JsF[B], c: JsF[C])(js: JsValue) = (a(js), b(js), c(js))
def %[A,B,C,D](a: JsF[A], b: JsF[B], c: JsF[C], d: JsF[D])(js: JsValue) = (a(js), b(js), c(js), d(js))
}
object Js extends Js {
def apply(): JsValue = JsObject()
def apply(stream: java.io.InputStream): JsValue = JsValue.fromStream(stream)
def apply(string: String): JsValue = JsValue.fromString(string)
implicit def ext2fun[T](ext: Extract[T]): JsF[T] = ext.unapply _ andThen { _.get }
implicit def Request2JsonRequest(r: dispatch.Request) = new JsonRequest(r)
class JsonRequest(r: Request) {
def ># [T](block: json.Js.JsF[T]) = r >> { stm => block(json.Js(stm)) }
}
}