Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specify derived typeclass for newtypes in PolyDerivation #222

Merged
merged 3 commits into from
Feb 19, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name := "derevo"
import com.typesafe.sbt.SbtGit.git

val publishVersion = "0.12.0"
val publishVersion = "0.12.1"

val common = List(
scalaVersion := "2.13.4",
Expand Down
13 changes: 7 additions & 6 deletions circe/src/main/scala/derevo/circe/circe.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import derevo.{Derevo, Derivation, PolyDerivation, delegating}
import io.circe.derivation.renaming
import io.circe.{Codec, Decoder, Encoder}
import derevo.NewTypeDerivation
import derevo.SpecificDerivation

@delegating("io.circe.derivation.deriveDecoder")
object decoder extends Derivation[Decoder] with NewTypeDerivation[Decoder] {
Expand All @@ -18,7 +19,7 @@ object decoder extends Derivation[Decoder] with NewTypeDerivation[Decoder] {
}

@delegating("io.circe.derivation.deriveEncoder")
object encoder extends PolyDerivation[Encoder, Encoder.AsObject] with NewTypeDerivation[Encoder] {
object encoder extends SpecificDerivation[Encoder, Encoder.AsObject, Encoder] with NewTypeDerivation[Encoder] {
def instance[A]: Encoder.AsObject[A] = macro Derevo.delegate[Encoder.AsObject, A]

/** @param arg1 naming function. For example io.circe.derivation.renaming.snakeCase
Expand All @@ -29,7 +30,7 @@ object encoder extends PolyDerivation[Encoder, Encoder.AsObject] with NewTypeDer
}

@delegating("io.circe.derivation.deriveCodec")
object codec extends PolyDerivation[Codec, Codec.AsObject] with NewTypeDerivation[Codec] {
object codec extends SpecificDerivation[Codec, Codec.AsObject, Codec] with NewTypeDerivation[Codec] {
def instance[A]: Codec.AsObject[A] = macro Derevo.delegate[Codec.AsObject, A]

/** @param arg1 naming function. For example io.circe.derivation.renaming.snakeCase
Expand All @@ -45,12 +46,12 @@ object snakeDecoder extends Derivation[Decoder] {
}

@delegating("io.circe.derivation.deriveEncoder", renaming.snakeCase, None)
object snakeEncoder extends PolyDerivation[Encoder, Encoder.AsObject] {
object snakeEncoder extends SpecificDerivation[Encoder, Encoder.AsObject, Encoder] {
def instance[A]: Encoder.AsObject[A] = macro Derevo.delegate[Encoder.AsObject, A]
}

@delegating("io.circe.derivation.deriveCodec", renaming.snakeCase, false, None)
object snakeCodec extends PolyDerivation[Codec, Codec.AsObject] {
object snakeCodec extends SpecificDerivation[Codec, Codec.AsObject, Encoder] {
def instance[A]: Codec.AsObject[A] = macro Derevo.delegate[Codec.AsObject, A]
}

Expand All @@ -60,11 +61,11 @@ object kebabDecoder extends Derivation[Decoder] {
}

@delegating("io.circe.derivation.deriveEncoder", renaming.kebabCase, None)
object kebabEncoder extends PolyDerivation[Encoder, Encoder.AsObject] {
object kebabEncoder extends SpecificDerivation[Encoder, Encoder.AsObject, Encoder] {
def instance[A]: Encoder.AsObject[A] = macro Derevo.delegate[Encoder.AsObject, A]
}

@delegating("io.circe.derivation.deriveCodec", renaming.kebabCase, false, None)
object kebabCodec extends PolyDerivation[Codec, Codec.AsObject] {
object kebabCodec extends SpecificDerivation[Codec, Codec.AsObject, Encoder] {
def instance[A]: Codec.AsObject[A] = macro Derevo.delegate[Codec.AsObject, A]
}
20 changes: 20 additions & 0 deletions circe/src/test/scala/derevo/circe/CirceDerivationSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import io.circe.parser._
import io.circe.derivation.renaming
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.must.Matchers
import io.estatico.newtype.macros.newtype
import CirceDerivationSpec.{Moe, Nya}

class CirceDerivationSpec extends AnyFlatSpec with Matchers {
"Circe derivation" should "derive simple codecs" in {
Expand Down Expand Up @@ -87,6 +89,15 @@ class CirceDerivationSpec extends AnyFlatSpec with Matchers {
it should "reject decode with inserted params" in {
decode[ShelloBaz](shelloKebab).left.getOrElse(null) mustBe a[DecodingFailure]
}

it should "encode object newtype" in {
val bar: SealedTrait = SealedTrait.Bar(143)
assert(Moe(bar).asJson === (bar).asJson)
}

it should "encode string newtype" in {
assert(Nya("uwu").asJson === "uwu".asJson)
}
}

@derive(encoder(identity, Some("type")), decoder(identity, false, Some("type")))
Expand All @@ -98,5 +109,14 @@ object SealedTrait {

@derive(encoder, decoder)
case class Baz(baz: String) extends SealedTrait
}

object CirceDerivationSpec {
@derive(encoder, decoder)
@newtype
case class Moe(tutu: SealedTrait)

@derive(encoder, decoder)
@newtype
case class Nya(string: String)
}
26 changes: 14 additions & 12 deletions core/src/main/scala/derevo/Derevo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Derevo(val c: blackbox.Context) {

val instanceDefs = Vector(
)
val IsSpecificDerivation = isInstanceDef[PolyDerivation[Any, Any]]()
val IsSpecificDerivation = isInstanceDef[SpecificDerivation[Any, Any, Any]]()
val IsDerivation = isInstanceDef[Derivation[Any]]()

val HKDerivation = new DerivationList(
Expand Down Expand Up @@ -188,7 +188,8 @@ class Derevo(val c: blackbox.Context) {
}

if (allTparams.isEmpty) {
val resT = mkAppliedType(mode.to, tq"$typRef")
val resTc = if (newType.isDefined) mode.newtype else mode.to
val resT = mkAppliedType(resTc, tq"$typRef")
q"""
@java.lang.SuppressWarnings(scala.Array("org.wartremover.warts.All", "scalafix:All", "all"))
implicit val $tn: $resT = $call
Expand Down Expand Up @@ -244,6 +245,7 @@ class Derevo(val c: blackbox.Context) {
val name: String,
val from: Type,
val to: Type,
val newtype: Type,
val drop: Int,
val cascade: Boolean
)
Expand All @@ -253,25 +255,25 @@ class Derevo(val c: blackbox.Context) {
val name = c.freshName(mangledName)

c.typecheck(obj).tpe match {
case IsSpecificDerivation(f, t, d) => new NameAndTypes(name, f, t, d, true)
case IsDerivation(f, t, d) => new NameAndTypes(name, f, t, d, true)
case HKDerivation(f, t, d) => new NameAndTypes(name, f, t, d, false)
case _ => abort(s"$obj seems not extending InstanceDef traits")
case IsSpecificDerivation(f, t, nt, d) => new NameAndTypes(name, f, t, nt, d, true)
case IsDerivation(f, t, nt, d) => new NameAndTypes(name, f, t, nt, d, true)
case HKDerivation(f, t, nt, d) => new NameAndTypes(name, f, t, nt, d, false)
case _ => abort(s"$obj seems not extending InstanceDef traits")
}
}

class DerivationList(ds: IsInstanceDef*) {
def unapply(objType: Type): Option[(Type, Type, Int)] =
def unapply(objType: Type): Option[(Type, Type, Type, Int)] =
ds.iterator.flatMap(_.unapply(objType)).collectFirst { case x => x }
}

class IsInstanceDef(t: Type, drop: Int) {
val constrSymbol = t.typeConstructor.typeSymbol
def unapply(objType: Type): Option[(Type, Type, Int)] =
val constrSymbol = t.typeConstructor.typeSymbol
def unapply(objType: Type): Option[(Type, Type, Type, Int)] =
objType.baseType(constrSymbol) match {
case TypeRef(_, _, List(tc)) => Some((tc, tc, drop))
case TypeRef(_, _, List(from, to)) => Some((from, to, drop))
case _ => None
case TypeRef(_, _, List(tc)) => Some((tc, tc, tc, drop))
case TypeRef(_, _, List(from, to, nt)) => Some((from, to, nt, drop))
case _ => None
}
}
def isInstanceDef[T: TypeTag](dropTParams: Int = 0) = new IsInstanceDef(typeOf[T], dropTParams)
Expand Down
19 changes: 10 additions & 9 deletions core/src/main/scala/derevo/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ package derevo {

//* numeration according to https://docs.tofu.tf/docs/internal/kind-enumeration
sealed trait InstanceDef
trait Derivation[TC[_]] extends InstanceDef
trait DerivationKN1[TC[f[_]]] extends InstanceDef
trait DerivationKN2[TC[bf[_, _]]] extends InstanceDef
trait DerivationKN3[TC[alg[f[_]]]] extends InstanceDef
trait DerivationKN4[TC[tr[f[_], _]]] extends InstanceDef
trait DerivationKN5[TC[tf[_, _, _]]] extends InstanceDef
trait DerivationKN11[TC[alg[bf[_, _]]]] extends InstanceDef
trait DerivationKN17[TC[alg[btr[_, _], _, _]]] extends InstanceDef
trait PolyDerivation[FromTC[_], ToTC[_]] extends InstanceDef
trait Derivation[TC[_]] extends InstanceDef
trait DerivationKN1[TC[f[_]]] extends InstanceDef
trait DerivationKN2[TC[bf[_, _]]] extends InstanceDef
trait DerivationKN3[TC[alg[f[_]]]] extends InstanceDef
trait DerivationKN4[TC[tr[f[_], _]]] extends InstanceDef
trait DerivationKN5[TC[tf[_, _, _]]] extends InstanceDef
trait DerivationKN11[TC[alg[bf[_, _]]]] extends InstanceDef
trait DerivationKN17[TC[alg[btr[_, _], _, _]]] extends InstanceDef
trait SpecificDerivation[FromTC[_], ToTC[_], NT[_]] extends InstanceDef

}

Expand All @@ -34,4 +34,5 @@ package object derevo {
type DerivationBi2[TC[alf[bf[_, _]]]] = DerivationKN11[TC]
type DerivationTr[TC[T[f[_], a]]] = DerivationKN4[TC]
type DerivationBiTr[TC[T[f[_, _], a, b]]] = DerivationKN17[TC]
type PolyDerivation[FromTC[_], ToTC[_]] = SpecificDerivation[FromTC, ToTC, ToTC]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ package object reactivemongo {

@delegating("reactivemongo.bson.Macros.writer")
object bsonDocumentWriter
extends PolyDerivation[BsonValueWriter, BSONDocumentWriter] with NewTypeDerivation[BSONDocumentWriter] {
extends SpecificDerivation[BsonValueWriter, BSONDocumentWriter, BsonValueWriter]
with NewTypeDerivation[BSONDocumentWriter] {
def instance[A]: BSONDocumentWriter[A] = macro Derevo.delegate[BSONDocumentWriter, A]
}

@delegating("reactivemongo.bson.Macros.reader")
object bsonDocumentReader
extends PolyDerivation[BsonValueReader, BSONDocumentReader] with NewTypeDerivation[BSONDocumentReader] {
extends SpecificDerivation[BsonValueReader, BSONDocumentReader, BsonValueReader]
with NewTypeDerivation[BSONDocumentReader] {
def instance[A]: BSONDocumentReader[A] = macro Derevo.delegate[BSONDocumentReader, A]
}
}
6 changes: 4 additions & 2 deletions tethys/src/main/scala/derevo/tethys/tethys.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package derevo.tethys

import derevo.{Derevo, Derivation, PolyDerivation, delegating}
import derevo.{Derevo, Derivation, delegating}
import tethys.derivation.builder._
import tethys.{JsonObjectWriter, JsonReader, JsonWriter}
import derevo.NewTypeDerivation
import derevo.SpecificDerivation

@delegating("tethys.derivation.semiauto.jsonReader")
object tethysReader extends Derivation[JsonReader] with NewTypeDerivation[JsonReader] {
Expand All @@ -14,7 +15,8 @@ object tethysReader extends Derivation[JsonReader] with NewTypeDerivation[JsonRe
}

@delegating("tethys.derivation.semiauto.jsonWriter")
object tethysWriter extends PolyDerivation[JsonWriter, JsonObjectWriter] with NewTypeDerivation[JsonWriter] {
object tethysWriter
extends SpecificDerivation[JsonWriter, JsonObjectWriter, JsonWriter] with NewTypeDerivation[JsonWriter] {
def instance[A]: JsonObjectWriter[A] = macro Derevo.delegate[JsonObjectWriter, A]

def apply[A](arg: WriterDerivationConfig): JsonObjectWriter[A] =
Expand Down