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

zeroformatter.Accessor.scala Maven / Gradle / Ivy

package zeroformatter

import scala.language.dynamics
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

final case class Accessor[A: Formatter](
  value: A,
  cache: Option[Array[Byte]] = None,
  offset: Int = 0,
  byteSize: Int = 0,
  updated: Boolean = false
) extends Dynamic {

  def selectDynamic(field: String): Any =
    macro AccessorMacros.selectDynamic[A]

  def updateDynamic(field: String)(value: Any): Any =
    macro AccessorMacros.updateDynamic[A]
}

object Accessor {

  implicit def accessorFormatter[A](implicit F: Formatter[A]): Formatter[Accessor[A]] = new Formatter[Accessor[A]] {

    override val length = F.length

    override def serialize(encoder: Encoder, offset: Int, value: Accessor[A]) =
      value.cache match {
        case Some(_) if value.updated =>
          F.serialize(encoder, offset, value.value)
        case Some(c) =>
          encoder.writeByteArray(offset, c, value.offset, value.byteSize)
        case None =>
          F.serialize(encoder, offset, value.value)
    }

    override def deserialize(decoder: Decoder) = {
      val o = decoder.offset
      Accessor(
        F.deserialize(decoder),
        Some(decoder.buffer),
        o,
        decoder.offset - o
      )
    }
  }
}

class AccessorMacros(override val c: whitebox.Context) extends FormatterMacros(c) {

  import c.universe._

  def nameLiteral(field: Tree): String =
    field match {
      case Literal(Constant(value: String)) => value
      case _ => abort(s"Expression $field does not evaluate to String literal")
    }

  def selectDynamic[A: WeakTypeTag](field: Tree): Tree = {
    val tpe = weakTypeOf[A]
    val name = nameLiteral(field)

    if(isProduct(tpe)) {
      if(fieldsOf(tpe).exists { case (n, t) => n.decodedName.toString == name }) {
        q"${c.prefix}.value.${TermName(name)}"
      }
      else abort(s"$tpe#$name not found")
    }
    else abort(s"$tpe is not case class")
  }

  def updateDynamic[A: WeakTypeTag](field: Tree)(value: Tree): Tree = {
    val tpe = weakTypeOf[A]
    val ctorDtor = CtorDtor(weakTypeOf[Accessor[A]])
    val Struct = typeOf[Struct].typeSymbol
    val self = c.prefix
    val name = nameLiteral(field)
    val fields = fieldsOf(tpe)

    if(isProduct(tpe)) {
      val indexes = getIndexes(tpe, fields)
      if(indexes.nonEmpty) {
        indexes.collectFirst {
          case (n, _) if n == name => n
        }
          .getOrElse(abort(s"$tpe#$name not found"))
      }
      else if(tpe.baseClasses.contains(Struct)){
        fields.collectFirst { case (n, _) if  n.decodedName.toString == name =>
          n
        }
          .getOrElse(abort(s"$tpe#$name not found"))
      }
      else abort(s"$tpe is not case class")

      ctorDtor.construct(List(
        q"$self.value.copy(${TermName(name)} = $value)",
        q"updated = true"
      ))
    }
    else abort(s"$tpe is not case class")
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy