Last active
June 28, 2018 10:32
-
-
Save scalway/88f178e8a59e48154c2d67947d985ca4 to your computer and use it in GitHub Desktop.
UPickle Magnolia Derive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package rabbi | |
import magnolia._ | |
import ujson.{ObjVisitor, Visitor} | |
import upickle.{Api, AttributeTagged} | |
import scala.language.experimental.macros | |
import scala.reflect.ClassTag | |
trait UpickleSupportCommon { | |
type Typeclass[T] | |
type CC[T] = CaseClass[Typeclass, T] | |
type ST[T] = SealedTrait[Typeclass, T] | |
@deprecatedOverriding("dont use it from your code. It is intendet to be used only by " + | |
"magnolia and is visible 4 u just because i couldn't figure out how to hide it successfully.", "0.1") | |
protected val upickleApi0:Api | |
private def key_@(arg:Seq[Any]) = arg.collectFirst { | |
case x:upickle.key => x.s | |
} | |
/** | |
* TODO make all methods protected! We cannot do that due to UpickleDerivationMixin but | |
* it is deprecated and probably would be deleted in near future! | |
*/ | |
def ccName[T](ctx:CC[T]) = { | |
key_@(ctx.annotations).getOrElse(ctx.typeName.full) | |
} | |
def paramLabel[F[_], E](arg:Param[F, E]) = { | |
key_@(arg.annotations).getOrElse(arg.label) | |
} | |
def dispatch0[T](ctx: ST[T]) = ctx.subtypes.map(_.typeclass) | |
} | |
trait UpickleSupportR extends UpickleSupportCommon { | |
import upickleApi0._ | |
type Typeclass[T] <: Reader[T] | |
def deriveCaseRTagged[T](ctx:CC[T]) = { | |
new TaggedReader.Leaf[T](ccName[T](ctx), deriveCaseR(ctx)) | |
} | |
def deriveCaseR[T](ctx:CC[T]) = JsObjR.map[T] { r => | |
ctx.construct(p => { | |
r.value.get(paramLabel(p)) match { | |
case Some(x) => readJs(x)(p.typeclass) | |
case None => p.default.get | |
} | |
}) | |
} | |
} | |
trait UpickleSupportW extends UpickleSupportCommon { | |
import upickleApi0._ | |
type Typeclass[T] <: Writer[T] | |
def deriveCaseWTagged[T:ClassTag](ctx:CC[T]) = { | |
new TaggedWriter.Leaf[T](implicitly, ccName[T](ctx), deriveCaseW(ctx)) | |
} | |
def deriveCaseW[T](ctx:CC[T]) = new CaseW[T] { | |
override def writeToObject[R](ww: ObjVisitor[_, R], v: T): Unit = { | |
ctx.parameters.zipWithIndex.foreach { case (arg, i) => | |
val argWriter = arg.typeclass | |
val value = arg.dereference(v) | |
if (!arg.default.contains(value)) { | |
ww.visitKey(objectAttributeKeyWriteMap(paramLabel(arg)), -1) | |
ww.visitValue( | |
argWriter.write( | |
ww.subVisitor.asInstanceOf[Visitor[Any, Nothing]], | |
value | |
), | |
-1 | |
) | |
} | |
} | |
} | |
} | |
} | |
class UpickleDerivationW[A <: Api](@deprecated("1") override val upickleApi0:Api) extends UpickleSupportW { | |
import upickleApi0._ | |
type Typeclass[T] = Writer[T] | |
def combine[T:ClassTag](ctx: CC[T]): Typeclass[T] = deriveCaseWTagged(ctx) | |
def dispatch[T](ctx: ST[T]): Typeclass[T] = Writer.merge[T](dispatch0(ctx) :_*) | |
implicit def autoWriter[T]: Typeclass[T] = macro Magnolia.gen[T] | |
def writerOf[T]: Typeclass[T] = macro Magnolia.gen[T] | |
} | |
class UpickleDerivationR[A <: Api](@deprecated("1") override val upickleApi0:A) extends UpickleSupportR { | |
import upickleApi0._ | |
type Typeclass[T] = Reader[T] | |
def combine[T:ClassTag](ctx: CC[T]): Typeclass[T] = deriveCaseRTagged(ctx) | |
def dispatch[T](ctx: ST[T]): Typeclass[T] = Reader.merge[T](dispatch0(ctx) :_*) | |
implicit def autoReader[T]: Typeclass[T] = macro Magnolia.gen[T] | |
def readerOf[T]: Typeclass[T] = macro Magnolia.gen[T] | |
} | |
class UpickleDerivation[A <: Api](@deprecated("1") override val upickleApi0:A) extends UpickleSupportW with UpickleSupportR { | |
import upickleApi0._ | |
type Typeclass[T] = ReadWriter[T] | |
def combine[T:ClassTag](ctx: CC[T]): Typeclass[T] = ReadWriter.join[T](deriveCaseRTagged(ctx), deriveCaseWTagged(ctx)) | |
def dispatch[T](ctx: ST[T]): Typeclass[T] = ReadWriter.merge[T](dispatch0(ctx) :_*) | |
implicit def autoReadWriterOf[T]: Typeclass[T] = macro Magnolia.gen[T] | |
def readWriterOf[T]: Typeclass[T] = macro Magnolia.gen[T] | |
} | |
@deprecated( | |
"""use with caution! It is only reason why we have public fields in Support classes and they leaks with import.""".stripMargin, "0.1") | |
trait UpickleDerivationMixin { self:Api => | |
type Typeclass[T] = ReadWriter[T] | |
protected val deriveSupport = new UpickleSupportW with UpickleSupportR { | |
val upickleApi0:self.type = self | |
type Typeclass[T] = upickleApi0.Typeclass[T] | |
} | |
import deriveSupport._ | |
def combine[T:ClassTag](ctx: CC[T]): Typeclass[T] = ReadWriter.join[T](deriveCaseRTagged(ctx), deriveCaseWTagged(ctx)) | |
def dispatch[T](ctx: ST[T]): Typeclass[T] = ReadWriter.merge[T](dispatch0(ctx) :_*) | |
implicit def gen[T]: Typeclass[T] = macro Magnolia.gen[T] | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//----------------------------------------------------------------- | |
// HOW TO USE IT | |
//----------------------------------------------------------------- | |
/** | |
* Preffered way of using it is create special object in your Api class that'll bring | |
* deriviation to your application when imported. | |
* | |
* {{{ | |
* import UpApi._ | |
* import UpApi.derive.autoReadWriter | |
* | |
* implicit val msgRW = implicitly[ReadWriter[Message]] | |
* }}} | |
* | |
* if `Message` is recursive in more complicated way use lazy val instead! | |
* | |
* {{{ | |
* implicit lazy val msgRW = implicitly[ReadWriter[Message]] | |
* }}} | |
* | |
* If you are not interested in automatic deriviation then import upickleDerive method | |
* | |
* {{{ | |
* import UpApi._ | |
* import UpApi.derive.readWriterOf | |
* implicit val msgRW = readWriterOf[Message] | |
* }}} | |
* */ | |
object UpApi extends AttributeTagged { | |
val derive = new UpickleDerivation(this) | |
} | |
/** | |
* If you need only Reader or Writer You can create just instance that creates it. | |
* | |
* {{{ | |
* import UpApi._ | |
* import UpApi.deriveW.autoReadWriter | |
* implicit val msgW = implicitly[Writer[Message]] | |
* }}} | |
* | |
* or more explicitly: | |
* | |
* {{{ | |
* import UpApi._ | |
* import UpApi.deriveW.writerOf | |
* implicit val msgW = writerOf[Message] | |
* }}} | |
* */ | |
object UpApi2 extends AttributeTagged { | |
val deriveW = new UpickleDerivationW(this) | |
val deriveR = new UpickleDerivationR(this) | |
} | |
/** | |
* You can also simply create derivation for existing api and use it with companion to it | |
* | |
* {{{ | |
* import upickle.default._ | |
* import UpDerive.autoReadWriterOf | |
* | |
* implicit val msgRW = implicitly[ReadWriter[Message]] | |
* }}} | |
*/ | |
object UpDerive extends UpickleDerivation(upickle.default) | |
/** | |
* You can use UpickleDerivationMixin to create own api with automatic | |
* deriviation turned on by default. It is risky and should be avoided i guess | |
* because it can create so much garbage for you... but you can. | |
* | |
* {{{ | |
* import UpApiAuto._ | |
* | |
* implicit val msgRW = implicitly[ReadWriter[Message]] | |
* }}} | |
* */ | |
object UpApiAuto extends AttributeTagged with UpickleDerivationMixin {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment