All Downloads are FREE. Search and download functionalities are using the official Maven repository.

playjson.playjson.scala Maven / Gradle / Ivy

The newest version!
package playjson

object all extends TransformationsOps with RulesOps with ReadsOps
object rules extends RulesOps
object reads extends ReadsOps
object transformation extends TransformationsOps

trait TransformationsOps {

  import play.api.libs.functional._
  import play.api.libs.functional.syntax._
  import play.api.libs.json.Reads._
  import play.api.libs.json._

  case class Transformation(from: JsPath, to: JsPath) {
    def reads =
      __.json.update(to.json.copyFrom(from.json.pick))

    def and(transformation: Transformation): Transformations = Transformations(Seq(this, transformation))
  }

  case class Transformations(transformations: Seq[Transformation]) {

    def and(transformation: Transformation): Transformations = this.copy(
      transformations = transformation +: transformations
    )

    def reads: Reads[JsObject] = {
      val reduce = transformations.map(_.reads).reduceLeft(combine)
      __.json.update(reduce)
    }

    private def combine(r1: Reads[JsObject], r2: Reads[JsObject]): Reads[JsObject] = {
      val b: FunctionalBuilder[Reads]#CanBuild2[JsObject, JsObject] = r1 and r2
      b.reduce
    }
  }

  implicit class TransformationOps(from: JsPath) {
    def to(t: JsPath): Transformation =
      Transformation(from, t)
  }

  def transform(transformations: Transformations): Reads[JsObject] = transformations.reads
  def transform(transformation: Transformation): Reads[JsObject] = Transformations(Seq(transformation)).reads

}


trait RulesOps {

  import play.api.libs.functional.syntax._
  import play.api.libs.json._
  import shapeless.labelled._
  import shapeless.ops.record.{LacksKey, Selector}
  import shapeless.syntax.singleton._
  import shapeless.{::, HList, HNil, LabelledGeneric, Lazy, Witness}

  import scala.annotation.implicitNotFound

  implicit class HListOps[Repr <: HList](l: Repr) {
    def and[V](value: V): V :: Repr = value :: l
  }

  implicit class GenericOps[R](l: R) {
    def and[V](value: V): V :: R :: HNil = value :: l :: HNil
  }

  @implicitNotFound("Implicit not found: Rules type or fields are not valid")
  trait RuleValidation[Repr <: HList, Rules <: HList]

  object RuleValidation {

    implicit def validateHNil[Repr <: HList]: RuleValidation[Repr, HNil] =
      new RuleValidation[Repr, HNil] {}

    implicit def validateSingleton[Repr <: HList, K <: Symbol, V](
      implicit sel: Selector.Aux[Repr, K, V]
    ): RuleValidation[Repr, FieldType[K, Rule[V]] :: HNil] =
      new RuleValidation[Repr, FieldType[K, Rule[V]] :: HNil] {}

    implicit def validateHCons[Repr <: HList, H, R <: HList, K <: Symbol, V](
      implicit sel: Selector.Aux[Repr, K, V],
      validation: RuleValidation[Repr, R]
    ): RuleValidation[Repr, FieldType[K, Rule[V]] :: R] =
      new RuleValidation[Repr, FieldType[K, Rule[V]] :: R] {}
  }

  sealed trait Rule[T]
  case class ReadRule[T](reads: Reads[T])   extends Rule[T]
  case class OrElseRule[T](reads: Reads[T]) extends Rule[T]

  trait ReadsWithRules[T, R <: HList] {
    def withRules(rules: R): Reads[T]
  }

  trait ReadsWithRulesLowerPriority {
    implicit def readsNoRule[T](implicit reads: Reads[T]): ReadsWithRules[T, HNil] = new ReadsWithRules[T, HNil] {
      override def withRules(rules: HNil): Reads[T] = reads
    }

    implicit def readsGeneric[Repr, A, R <: HList](implicit gen: LabelledGeneric.Aux[A, Repr],
                                                   readsRepr: Lazy[ReadsWithRules[Repr, R]]): ReadsWithRules[A, R] =
      new ReadsWithRules[A, R] {
        override def withRules(rules: R): Reads[A] =
          readsRepr.value.withRules(rules).map(r => gen.from(r))
      }
  }

  object ReadsWithRules extends ReadsWithRulesLowerPriority {

    implicit def readHNil[R <: HList]: ReadsWithRules[HNil, R] = new ReadsWithRules[HNil, R] {
      override def withRules(rules: R): Reads[HNil] = Reads[HNil] { json =>
        JsSuccess(HNil)
      }
    }

    implicit def readNoRuleForHead[K <: Symbol, H, T <: HList, R <: HList](
                                                                            implicit witness: Witness.Aux[K],
                                                                            noRule: LacksKey[R, K],
                                                                            readsH: Reads[H],
                                                                            readsT: ReadsWithRules[T, R]
                                                                          ): ReadsWithRules[FieldType[K, H] :: T, R] =
      new ReadsWithRules[FieldType[K, H] :: T, R] {
        override def withRules(rules: R): Reads[FieldType[K, H] :: T] = {
          val name = witness.value.name
          val rH   = (__ \ name).read(readsH)
          (rH and readsT.withRules(rules))((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T])
        }
      }

    implicit def readRuleForHead[K <: Symbol, H, T <: HList, R <: HList](
      implicit witness: Witness.Aux[K],
      readsH: Reads[H],
      at: Selector.Aux[R, K, Rule[H]],
      readsT: ReadsWithRules[T, R]
    ): ReadsWithRules[FieldType[K, H] :: T, R] =
      new ReadsWithRules[FieldType[K, H] :: T, R] {
        override def withRules(rules: R): Reads[FieldType[K, H] :: T] = {
          val name                    = witness.value.name
          val additionalRule: Rule[H] = at(rules)
          val rH = additionalRule match {
            case ReadRule(reads)   => (__ \ name).read[H](reads)
            case OrElseRule(reads) => (__ \ name).read[H](readsH).orElse(reads)
          }
          (rH and readsT.withRules(rules))((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T])
        }
      }

  }

  case class JsonRead[A]() {

    def withRules[R <: HList, ARepr <: HList](rules: R)(
      implicit readWithRule: ReadsWithRules[A, R],
      genA: LabelledGeneric.Aux[A, ARepr],
      validation: RuleValidation[ARepr, R]
    ): Reads[A] =
      readWithRule.withRules(rules)


    def withRules[R, ARepr <: HList](rules: R)(
      implicit readWithRule: ReadsWithRules[A, R :: HNil],
      genA: LabelledGeneric.Aux[A, ARepr],
      validation: RuleValidation[ARepr, R :: HNil]
    ): Reads[A] =
      readWithRule.withRules(rules :: HNil)
  }

  def jsonRead[T]: JsonRead[T] = JsonRead[T]()

  def read[T](implicit reads: Reads[T]): Rule[T] = ReadRule(reads)

  def orElse[T](or: Reads[T]): Rule[T] = OrElseRule(or)
  def orElse[T](or: T): Rule[T] = OrElseRule(Reads.pure(or))

}

trait ReadsOps {
  import play.api.libs.functional.syntax._
  import play.api.libs.json._
  import shapeless.labelled._
  import shapeless.syntax.singleton._
  import shapeless.{::, HList, HNil, LabelledGeneric, Lazy, Witness}

  trait HReads[T] {
    def reads: Reads[T]
  }

  trait HReadsLowerPriority {

    implicit def readsGeneric[Repr <: HList, A](
      implicit
      gen: LabelledGeneric.Aux[A, Repr],
      readsRepr: Lazy[HReads[Repr]]
    ): HReads[A] = new HReads[A] {
      override def reads: Reads[A] = Reads[A] { json =>
        readsRepr.value.reads.reads(json).map(gen.from)
      }
    }

  }

  object HReads extends HReadsLowerPriority {

    implicit def readsHNil: HReads[HNil] = new HReads[HNil] {
      override def reads = Reads { json =>
        JsSuccess(HNil)
      }
    }


    implicit def readHCons[K <: Symbol, H, T <: HList](
      implicit
      witness: Witness.Aux[K],
      readsH: Reads[H],
      readsT: Lazy[HReads[T]]
    ): HReads[FieldType[K, H] :: T] = new HReads[FieldType[K, H] :: T] {
      override def reads: Reads[FieldType[K, H] :: T] = {
        val name = witness.value.name
        val readHead = (__ \ name).read[H](readsH)
        (readHead and readsT.value.reads) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T])
      }
    }

  }

  def hReads[A](implicit readsInst: HReads[A]): Reads[A] = readsInst.reads

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy