Skip to content

Commit a448b68

Browse files
committed
Support trait familly in type classes
1 parent 7d24a31 commit a448b68

File tree

7 files changed

+53
-24
lines changed

7 files changed

+53
-24
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ lazy val supportedScalaVersions = List(scala212, scala213)
55
lazy val commonSettings = Seq(
66
name := "play-json-mapping",
77
organization := "null-vector",
8-
version := "1.0.4",
8+
version := "1.1.0",
99
scalaVersion := scala213,
1010
crossScalaVersions := supportedScalaVersions,
1111
scalacOptions := Seq(

core/src/test/scala/org/nullvector/JsonMapperSpec.scala renamed to core/src/test/scala/org/nullvector/api/json/JsonMapperSpec.scala

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
package org.nullvector
1+
package org.nullvector.api.json
22

3-
import org.nullvector.domian._
3+
import java.time.LocalDateTime
4+
import java.util.Locale
5+
6+
import org.joda.time.DateTime
7+
import org.nullvector.api.json.domian.{DaysOpen, Location, Monday, Money, OperationSchedule, Place, Product, ProductId, Resident, Sunday, SupportedTypes}
48
import org.scalatest.flatspec.AnyFlatSpec
59
import org.scalatest.matchers.should.Matchers._
610
import play.api.libs.json.{Format, Json, JsonConfiguration, Reads, Writes}
@@ -38,6 +42,16 @@ class JsonMapperSpec extends AnyFlatSpec {
3842
jsValue.as[OperationSchedule].availableDay shouldBe Monday
3943
}
4044

45+
it should "mapping of trait family inside type class" in {
46+
import JsonMapper._
47+
val example = DaysOpen(List(Monday, Sunday))
48+
implicit val conf = JsonConfiguration(typeNaming = typeNaming)
49+
implicit val x = mappingOf[DaysOpen]
50+
val jsValue = example.asJson
51+
52+
jsValue.as[DaysOpen] shouldBe example
53+
}
54+
4155
it should "create a format mapping with enum" in {
4256
import JsonMapper._
4357
implicit val m: Format[Money] = mappingOf[Money]
@@ -67,27 +81,28 @@ class JsonMapperSpec extends AnyFlatSpec {
6781

6882
it should "creat mapping with AnyVal" in {
6983
import JsonMapper._
70-
implicit val m = mappingOf[Product]
84+
implicit val m = mappingOf[domian.Product]
7185

7286
val json = Product(new ProductId(23), "Train").asJson
7387
println(json)
7488
}
7589

7690
it should "creat a read mapping with AnyVal" in {
7791
import JsonMapper._
78-
implicit val m = readsOf[Product]
92+
implicit val m = readsOf[domian.Product]
7993

8094
Json
8195
.parse("""{"productId":23,"name":"Train"}""")
82-
.as[Product] shouldBe Product(new ProductId(23), "Train")
96+
.as[domian.Product] shouldBe Product(new ProductId(23), "Train")
8397
}
8498

8599
it should "creat a write mapping with AnyVal" in {
86100
import JsonMapper._
87-
implicit val m = writesOf[Product]
101+
implicit val m = writesOf[domian.Product]
88102

89103
Product(new ProductId(23), "Train")
90104
.asJson.toString() shouldBe """{"productId":23,"name":"Train"}"""
91105
}
106+
92107
}
93108

core/src/test/scala/org/nullvector/domian/Domin.scala renamed to core/src/test/scala/org/nullvector/api/json/domian/Domin.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
package org.nullvector.domian
1+
package org.nullvector.api.json.domian
2+
3+
import java.time.LocalDateTime
4+
import java.util.Locale
5+
6+
import org.joda.time.DateTime
27

38
case class Location(lat: Double, long: Double)
49
case class Resident(name: String, age: Int, role: Option[String])
@@ -18,6 +23,8 @@ case object Monday extends Day
1823

1924
case object Sunday extends Day
2025

26+
case class DaysOpen(days: List[Day])
27+
2128
case class Money(amount: BigDecimal, currency: Money.Currency) {
2229

2330
def +(aMoney: Money): Money = copy(amount + aMoney.amount)
@@ -36,3 +43,9 @@ object Money extends Enumeration {
3643
final class ProductId(val int: Int) extends AnyVal
3744

3845
case class Product(productId: ProductId, name: String)
46+
47+
case class SupportedTypes(
48+
localDateTime: LocalDateTime,
49+
dateTime: DateTime,
50+
locale: Locale
51+
)

macros/src/main/scala/org/nullvector/JsonMapper.scala renamed to macros/src/main/scala/org/nullvector/api/json/JsonMapper.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.nullvector
1+
package org.nullvector.api.json
22

33
import play.api.libs.json.JsonConfiguration.Aux
44
import play.api.libs.json.JsonNaming.SnakeCase

macros/src/main/scala/org/nullvector/JsonMapperMacroFactory.scala renamed to macros/src/main/scala/org/nullvector/api/json/JsonMapperMacroFactory.scala

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
package org.nullvector
1+
package org.nullvector.api.json
22

3-
import org.nullvector.tree.Tree
3+
import org.nullvector.api.json.tree.{Tree => NTree}
44
import play.api.libs.json.{Format, JsonConfiguration, Reads, Writes}
55

66
import scala.reflect.macros.blackbox
@@ -65,47 +65,48 @@ private object JsonMapperMacroFactory {
6565

6666

6767
private def extractTypes(context: blackbox.Context)
68-
(rootType: context.universe.Type): org.nullvector.tree.Tree[context.universe.Type] = {
68+
(rootType: context.universe.Type): NTree[context.universe.Type] = {
6969
import context.universe._
7070
val enumType = context.typeOf[Enumeration]
7171
val anyValType = context.typeOf[AnyVal]
7272

73-
def extractAll(caseType: context.universe.Type): org.nullvector.tree.Tree[context.universe.Type] = {
73+
def extractAll(caseType: context.universe.Type): NTree[context.universe.Type] = {
7474
def isSupprtedTrait(aTypeClass: ClassSymbol) = aTypeClass.isTrait && aTypeClass.isSealed && !aTypeClass.fullName.startsWith("scala")
75+
def isCaseOrTrait(aType: context.universe.Type) = aType.typeSymbol.asClass.isCaseClass || isSupprtedTrait(aType.typeSymbol.asClass)
7576

7677
def extaracCaseClassesFromTypeArgs(classType: Type): List[Type] = {
7778
classType.typeArgs.collect {
78-
case argType if argType.typeSymbol.asClass.isCaseClass => List(classType, argType)
79+
case argType if isCaseOrTrait(argType) => List(classType, argType)
7980
case t => extaracCaseClassesFromTypeArgs(t)
8081
}.flatten
8182
}
8283

8384
val caseTypeAsClass = caseType.typeSymbol.asClass
85+
8486
if (caseTypeAsClass.isCaseClass) {
85-
Tree(caseType,
87+
tree.Tree(caseType,
8688
caseType.decls
8789
.collect { case method: MethodSymbol if method.isCaseAccessor =>
8890
val returnType = method.returnType
8991
returnType.toString // This is needed to materialize the type (WTF!!)
9092
returnType
9193
}
9294
.collect {
93-
case aType if aType <:< anyValType => List(Tree(aType))
95+
case aType if aType <:< anyValType => List(NTree(aType))
9496
case aType if aType.typeSymbol.owner.isType &&
9597
aType.typeSymbol.owner.asType.toType =:= enumType =>
96-
List(Tree(aType))
97-
case aType if aType.typeSymbol.asClass.isCaseClass || isSupprtedTrait(aType.typeSymbol.asClass) => List(extractAll(aType))
98+
List(NTree(aType))
99+
case aType if isCaseOrTrait(aType) => List(extractAll(aType))
98100
case aType => extaracCaseClassesFromTypeArgs(aType).map(arg => extractAll(arg))
99101
}
100102
.flatten.toList
101-
102103
)
103104
}
104105
else if (isSupprtedTrait(caseTypeAsClass)) {
105106
val subclasses = caseTypeAsClass.knownDirectSubclasses
106-
Tree(caseType, subclasses.map(aType => extractAll(aType.asClass.toType)).toList)
107+
tree.Tree(caseType, subclasses.map(aType => extractAll(aType.asClass.toType)).toList)
107108
}
108-
else Tree.empty
109+
else NTree.empty
109110
}
110111

111112
extractAll(rootType)

macros/src/main/scala/org/nullvector/tree/Tree.scala renamed to macros/src/main/scala/org/nullvector/api/json/tree/Tree.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.nullvector.tree
1+
package org.nullvector.api.json.tree
22

33
sealed trait Tree[E] extends Iterable[E] {
44

macros/src/test/scala/org/nullvector/TreeSpec.scala renamed to macros/src/test/scala/org/nullvector/api/json/TreeSpec.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
package org.nullvector
1+
package org.nullvector.api.json
22

3-
import org.nullvector.tree.Tree
3+
import org.nullvector.api.json.tree.Tree
44
import org.scalatest.flatspec.AnyFlatSpec
55
import org.scalatest.matchers.should.Matchers
66

0 commit comments

Comments
 (0)