1
1
package org .nullvector
2
2
3
- import play .api .libs .json .{Format , JsValue , Reads , Writes }
3
+ import play .api .libs .json .{Format , JsValue , JsonConfiguration , OFormat , OWrites , Reads , Writes }
4
4
5
5
import scala .reflect .macros .blackbox
6
6
7
7
private object JsonMapperMacroFactory {
8
8
9
- implicit class MapOnPair [+ T1 , + T2 ](pair : (T1 , T2 )) {
10
- def map [A1 , A2 ](f : (T1 , T2 ) => (A1 , A2 )): (A1 , A2 ) = f(pair._1, pair._2)
11
- }
12
-
13
9
private val supportedClassTypes = List (
14
10
" scala.Option" ,
15
11
" scala.collection.immutable.List" ,
@@ -21,54 +17,139 @@ private object JsonMapperMacroFactory {
21
17
22
18
23
19
def writesOf [E ](context : blackbox.Context )
24
- (implicit domainTypeTag : context.WeakTypeTag [E ]): context.Expr [Writes [E ]] = {
20
+ (implicit domainTypeTag : context.WeakTypeTag [E ]): context.Expr [Writes [E ]] = {
21
+ buildExpression(context, WritesMapperFilter )(None )
22
+ }
25
23
26
- import context .universe ._
27
- val typeOfWrites = context.typeOf[Writes [_]]
28
- val typeOfReads = context.typeOf[Reads [_]]
29
- val typeOfFormat = context.typeOf[Format [_]]
24
+ def writesWithConfigOf [E ](context : blackbox.Context )
25
+ (jsonConfiguration : context.Expr [JsonConfiguration ])
26
+ (implicit domainTypeTag : context.WeakTypeTag [E ]): context.Expr [Writes [E ]] = {
27
+ buildExpression(context, WritesMapperFilter )(Some (jsonConfiguration))
28
+ }
29
+
30
+ def readsOf [E ](context : blackbox.Context )
31
+ (implicit domainTypeTag : context.WeakTypeTag [E ]): context.Expr [Reads [E ]] = {
32
+ buildExpression(context, ReadsMapperFilter )(None )
33
+ }
34
+
35
+ def readsWithConfigOf [E ](context : blackbox.Context )
36
+ (jsonConfiguration : context.Expr [JsonConfiguration ])
37
+ (implicit domainTypeTag : context.WeakTypeTag [E ]): context.Expr [Reads [E ]] = {
38
+ buildExpression(context, ReadsMapperFilter )(Some (jsonConfiguration))
39
+ }
40
+
41
+ def mappingOf [E ](context : blackbox.Context )
42
+ (implicit domainTypeTag : context.WeakTypeTag [E ]): context.Expr [Format [E ]] = {
43
+ buildExpression(context, FormatMapperFilter )(None )
44
+ }
45
+
46
+ def mappingWithConfigOf [E ](context : blackbox.Context )
47
+ (jsonConfiguration : context.Expr [JsonConfiguration ])
48
+ (implicit domainTypeTag : context.WeakTypeTag [E ]): context.Expr [Format [E ]] = {
49
+ buildExpression(context, FormatMapperFilter )(Some (jsonConfiguration))
50
+ }
30
51
52
+ private def buildExpression [E ](context : blackbox.Context , mapperFilter : MapperFilter )
53
+ (jsonConfiguration : Option [context.Expr [JsonConfiguration ]])
54
+ (implicit domainTypeTag : context.WeakTypeTag [E ]): context.Expr [Format [E ]] = {
55
+
56
+ import context .universe ._
31
57
32
- val (toBeImplicit, toBeMainWriter) = extractCaseTypes(context)(domainTypeTag.tpe).toList.reverse.distinct.partition(_ != domainTypeTag.tpe)
58
+ val (toBeImplicit, toBeMainWriter) = extractTypes(context)(domainTypeTag.tpe).toList.reverse.distinct.partition(_ != domainTypeTag.tpe)
59
+ val implicitWriters = mapperFilter.filterTypes(context)(toBeImplicit)
33
60
34
- val implicitWriters = toBeImplicit
35
- .filter { caseType =>
36
- context.inferImplicitValue(appliedType(typeOfWrites, caseType)).isEmpty ||
37
- context.inferImplicitValue(appliedType(typeOfFormat, caseType)).isEmpty
38
- }
39
- .map(caseType => q """ private implicit val ${TermName (context.freshName())} = play.api.libs.json.Json.writes[ $caseType] """ )
61
+ val config = jsonConfiguration
62
+ .map(confExpr => q " private implicit val ${TermName (context.freshName())} = $confExpr" )
63
+ .getOrElse(EmptyTree )
40
64
41
65
val code =
42
66
q """
67
+ import play.api.libs.json._
68
+ import play.api.libs.json.Json._
69
+ $config
43
70
.. $implicitWriters
44
- play.api.libs.json.Json.writes[ ${ toBeMainWriter.head} ]
71
+ ${mapperFilter.mapperExpression(context)( toBeMainWriter.head)}
45
72
"""
46
- println(code)
47
- context.Expr [Writes [E ]](code)
73
+ context.Expr [Format [E ]](code)
48
74
}
49
75
50
- private def extractCaseTypes (context : blackbox.Context )
51
- ( caseType : context.universe.Type ): org.nullvector.Tree [context.universe.Type ] = {
76
+ private def extractTypes (context : blackbox.Context )
77
+ ( aType : context.universe.Type ): org.nullvector.Tree [context.universe.Type ] = {
52
78
import context .universe ._
53
79
80
+ def isSupprtedTrait (aTypeClass : ClassSymbol ) = aTypeClass.isTrait && aTypeClass.isSealed && ! aTypeClass.fullName.startsWith(" scala" )
81
+
54
82
def extaracCaseClassesFromSupportedTypeClasses (classType : Type ): List [Type ] = {
55
83
if (supportedClassTypes.contains(classType.typeSymbol.fullName)) classType.typeArgs.collect {
56
84
case argType if argType.typeSymbol.asClass.isCaseClass => List (classType, argType)
57
85
case t => extaracCaseClassesFromSupportedTypeClasses(t)
58
86
}.flatten else Nil
59
87
}
60
88
61
- if (caseType.typeSymbol.asClass.isCaseClass) {
62
- Tree (caseType,
63
- caseType.decls.toList
89
+ val aTypeClass : context.universe.ClassSymbol = aType.typeSymbol.asClass
90
+
91
+ if (aTypeClass.isCaseClass) {
92
+ Tree (aType,
93
+ aType.decls.toList
64
94
.collect { case method : MethodSymbol if method.isCaseAccessor => method.returnType }
65
95
.collect {
66
- case aType if aType.typeSymbol.asClass.isCaseClass => List (extractCaseTypes (context)(aType))
67
- case aType => extaracCaseClassesFromSupportedTypeClasses(aType).map(arg => extractCaseTypes (context)(arg))
96
+ case aType if aType.typeSymbol.asClass.isCaseClass || isSupprtedTrait(aType.typeSymbol.asClass) => List (extractTypes (context)(aType))
97
+ case aType => extaracCaseClassesFromSupportedTypeClasses(aType).map(arg => extractTypes (context)(arg))
68
98
}.flatten
69
99
)
70
100
}
101
+ else if (isSupprtedTrait(aTypeClass)) {
102
+ Tree (aType, aTypeClass.knownDirectSubclasses.map(aType => extractTypes(context)(aType.asClass.toType)).toList)
103
+ }
71
104
else Tree .empty
72
105
}
73
106
107
+ sealed trait MapperFilter {
108
+ def filterTypes (context : blackbox.Context )(types : List [context.Type ]): List [context.Tree ]
109
+
110
+ def mapperExpression (context : blackbox.Context )(tpe : context.Type ): context.Tree
111
+ }
112
+
113
+ object FormatMapperFilter extends MapperFilter {
114
+
115
+ override def filterTypes (context : blackbox.Context )(types : List [context.Type ]): List [context.Tree ] = {
116
+ import context .universe ._
117
+ val typeOfFormat = context.typeOf[Format [_]]
118
+ types.filter(aType => context.inferImplicitValue(appliedType(typeOfFormat, aType)).isEmpty)
119
+ .map(aType => context.parse(s """ private implicit val ${context.freshName()}: Format[ $aType] = format[ $aType] """ ))
120
+ }
121
+
122
+ override def mapperExpression (context : blackbox.Context )(tpe : context.Type ): context.Tree = {
123
+ context.parse(s " format[ $tpe] " )
124
+ }
125
+ }
126
+
127
+ object WritesMapperFilter extends MapperFilter {
128
+
129
+ override def filterTypes (context : blackbox.Context )(types : List [context.Type ]): List [context.Tree ] = {
130
+ import context .universe ._
131
+ val typeOfFormat = context.typeOf[Writes [_]]
132
+ types.filter(aType => context.inferImplicitValue(appliedType(typeOfFormat, aType)).isEmpty)
133
+ .map(aType => context.parse(s """ private implicit val ${context.freshName()}: Writes[ $aType] = writes[ $aType] """ ))
134
+ }
135
+
136
+ override def mapperExpression (context : blackbox.Context )(tpe : context.Type ): context.Tree = {
137
+ context.parse(s " writes[ $tpe] " )
138
+ }
139
+ }
140
+
141
+ object ReadsMapperFilter extends MapperFilter {
142
+
143
+ override def filterTypes (context : blackbox.Context )(types : List [context.Type ]): List [context.Tree ] = {
144
+ import context .universe ._
145
+ val typeOfFormat = context.typeOf[Reads [_]]
146
+ types.filter(aType => context.inferImplicitValue(appliedType(typeOfFormat, aType)).isEmpty)
147
+ .map(aType => context.parse(s """ private implicit val ${context.freshName()}: Reads[ $aType] = reads[ $aType] """ ))
148
+ }
149
+
150
+ override def mapperExpression (context : blackbox.Context )(tpe : context.Type ): context.Tree = {
151
+ context.parse(s " reads[ $tpe] " )
152
+ }
153
+ }
154
+
74
155
}
0 commit comments