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

mist.api.encoding.ObjectExtractor.scala Maven / Gradle / Ivy

package mist.api.encoding

import mist.api._
import mist.api.data.{JsData, JsMap, JsNull}
import shadedshapeless.labelled.FieldType
import shadedshapeless._
import shadedshapeless.record._

import scala.annotation.implicitNotFound
import scala.reflect.ClassTag

@implicitNotFound("Couldn't find ObjectExtractor instances for ${A}. Ensure that there are JsExtractor instances for every field type of ${A}")
trait ObjectExtractor[A] {
  def `type`: MObj
  def apply(js: JsData): Extraction[A]
}

object ObjectExtractor {

  def apply[A](argType: MObj)(f: JsMap => Extraction[A]): ObjectExtractor[A] = new ObjectExtractor[A] {
    def apply(js: JsData): Extraction[A] = js match {
      case m: JsMap => f(m)
      case other =>
        Failed.InvalidType(argType.toString, other.toString)
    }
    val `type`: MObj = argType
  }

  implicit val hNilExt: ObjectExtractor[HNil] = ObjectExtractor(MObj.empty)(_ => Extracted(HNil))

  implicit def hlistExt[K <: Symbol, H, T <: HList](implicit
    witness: Witness.Aux[K],
    lHExt: Lazy[JsExtractor[H]],
    tExt: ObjectExtractor[T]
  ): ObjectExtractor[FieldType[K, H] :: T] = {
    val key = witness.value.name
    val hExt = lHExt.value.transformFailure(f => Failed.InvalidField(key, f))
    val headType = witness.value.name -> hExt.`type`
    val `type` = MObj(headType +: tExt.`type`.fields)

    ObjectExtractor(`type`)(map => {
      val headV = map.fieldValue(key)
      (hExt(headV), tExt(map)) match {
        case (Extracted(h), Extracted(t)) => Extracted((h :: t).asInstanceOf[FieldType[K, H] :: T])
        case (Extracted(h), f: Failed) => f
        case (f: Failed, Extracted(t)) => f
        case (f1: Failed, f2: Failed) => Failed.toComplex(f1, f2)
      }
    })
  }

  implicit def labelled[A, H <: HList](
    implicit
    labGen: LabelledGeneric.Aux[A, H],
    clzTag: ClassTag[A],
    ext: ObjectExtractor[H]
  ): ObjectExtractor[A] =
    ObjectExtractor(ext.`type`)(map => {
      ext(map) match {
        case Extracted(h) => Extracted(labGen.from(h))
        case f: Failed => Failed.IncompleteObject(clzTag.runtimeClass.getName, f)
      }
    })
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy