Skip to content

Instantly share code, notes, and snippets.

@nafg

nafg/paramCodec.scala

Last active Mar 10, 2020
Embed
What would you like to do?
Deriving circe with Magnolia but only as a fallback
import scala.language.experimental.macros
import scala.reflect.ClassTag
import io.circe.{Decoder, Encoder, Json}
import magnolia._
class ParamEncoder[T](val underlying: Encoder[T]) extends AnyVal
trait ParamEncoderLowPriority {
type Typeclass[T] = ParamEncoder[T]
def combine[T](caseClass: CaseClass[Typeclass, T]): Typeclass[T] =
new Typeclass(Encoder.instance { value =>
Json.obj(caseClass.parameters.map(p => p.label -> p.typeclass.underlying(p.dereference(value))): _*)
})
implicit def gen[T]: Typeclass[T] = macro Magnolia.gen[T]
}
object ParamEncoder extends ParamEncoderLowPriority {
implicit def default[A](implicit A: Encoder[A]): Typeclass[A] = new Typeclass(A)
}
class ParamDecoder[T](val underlying: Decoder[T]) extends AnyVal
trait ParamDecoderLowPriority {
type Typeclass[T] = ParamDecoder[T]
def combine[T](caseClass: CaseClass[Typeclass, T]): Typeclass[T] =
new Typeclass(Decoder.instance { cursor =>
caseClass.constructEither(param => cursor.get(param.label)(param.typeclass.underlying)).left.map(_.head)
})
implicit def gen[T]: Typeclass[T] = macro Magnolia.gen[T]
}
object ParamDecoder extends ParamDecoderLowPriority {
implicit def default[A](implicit A: Decoder[A]): Typeclass[A] = new Typeclass(A)
}
@nafg

This comment has been minimized.

Copy link
Owner Author

@nafg nafg commented Mar 8, 2020

Note that it isn't important that default is in the object and gen is in the trait, since default is considered more "specific." However, for whatever reason it doesn't work without splitting it into a trait one way or the other.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment