Skip to content

Commit a886487

Browse files
committed
Introduce Definition, Format, and annotation support for enhanced RW definitions
1 parent 92af807 commit a886487

25 files changed

Lines changed: 1390 additions & 475 deletions

core/jvm/src/main/scala/fabric/rw/PlatformRWImplicits.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
package fabric.rw
2323

2424
import fabric._
25-
import fabric.define.DefType
25+
import fabric.define.{Definition, DefType}
2626

2727
import java.io.File
2828
import java.nio.file.{Path, Paths}
@@ -31,12 +31,12 @@ trait PlatformRWImplicits {
3131
implicit lazy val pathRW: RW[Path] = RW.from[Path](
3232
r = p => str(p.toString),
3333
w = j => Paths.get(j.asString),
34-
d = DefType.Str
34+
d = Definition(DefType.Str)
3535
)
3636

3737
implicit lazy val fileRW: RW[File] = RW.from[File](
3838
r = f => str(f.getPath),
3939
w = j => new File(j.asString),
40-
d = DefType.Str
40+
d = Definition(DefType.Str)
4141
)
4242
}

core/shared/src/main/scala-2/fabric/rw/RWMacros.scala

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,106 @@
2222
package fabric.rw
2323

2424
import fabric.JsonWrapper
25-
import fabric.define.DefType
25+
import fabric.define.{Definition, DefType, Format}
2626

2727
import scala.reflect.macros.blackbox
2828

2929
object RWMacros {
30+
private def fullTypeName(context: blackbox.Context)(tpe: context.universe.Type): String = {
31+
import context.universe._
32+
val base = tpe.typeSymbol.fullName
33+
val args = tpe.typeArgs
34+
if (args.isEmpty) base
35+
else s"$base[${args.map(fullTypeName(context)(_)).mkString(", ")}]"
36+
}
37+
38+
private def generateGenericTypes(context: blackbox.Context)(tpe: context.universe.Type): List[context.universe.Tree] = {
39+
import context.universe._
40+
val typeArgs = tpe.typeArgs
41+
val typeParams = tpe.typeSymbol.asClass.typeParams
42+
if (typeArgs.isEmpty) Nil
43+
else typeParams.zip(typeArgs).map { case (param, arg) =>
44+
val name = param.name.decodedName.toString
45+
q"GenericType($name, implicitly[RW[$arg]].definition)"
46+
}
47+
}
48+
49+
private def extractFieldGenericNames(context: blackbox.Context)(tpe: context.universe.Type): Map[String, String] = {
50+
import context.universe._
51+
val typeParams = tpe.typeSymbol.asClass.typeParams.map(_.name.decodedName.toString).toSet
52+
if (typeParams.isEmpty) Map.empty
53+
else {
54+
tpe.decls
55+
.collectFirst {
56+
case m: MethodSymbol if m.isPrimaryConstructor => m.paramLists.head
57+
}
58+
.getOrElse(Nil)
59+
.flatMap { field =>
60+
val fieldName = field.asTerm.name.decodedName.toString
61+
val fieldType = field.typeSignature
62+
findTypeParamName(context)(fieldType, typeParams).map(fieldName -> _)
63+
}
64+
.toMap
65+
}
66+
}
67+
68+
private def findTypeParamName(
69+
context: blackbox.Context
70+
)(tpe: context.universe.Type, typeParamNames: Set[String]): Option[String] = {
71+
val name = tpe.typeSymbol.name.decodedName.toString
72+
if (typeParamNames.contains(name)) Some(name)
73+
else tpe.typeArgs.flatMap(findTypeParamName(context)(_, typeParamNames)).headOption
74+
}
75+
76+
private def extractFieldFormats(
77+
context: blackbox.Context
78+
)(fields: List[context.universe.Symbol]): Map[String, context.universe.Tree] = {
79+
import context.universe._
80+
fields.flatMap { field =>
81+
val key = field.asTerm.name.decodedName.toString
82+
field.annotations
83+
.collectFirst {
84+
case ann if ann.tree.tpe =:= typeOf[format] =>
85+
ann.tree.children.tail.headOption match {
86+
case Some(arg) => Some(key -> arg)
87+
case None => None
88+
}
89+
}
90+
.getOrElse(None)
91+
}.toMap
92+
}
93+
94+
private def extractDeprecatedFields(context: blackbox.Context)(fields: List[context.universe.Symbol]): Set[String] = {
95+
import context.universe._
96+
fields.flatMap { field =>
97+
val isDeprecated = field.annotations.exists(_.tree.tpe =:= typeOf[fieldDeprecated])
98+
if (isDeprecated) Some(field.asTerm.name.decodedName.toString) else None
99+
}.toSet
100+
}
101+
102+
private def generateFieldDefaults(context: blackbox.Context)(
103+
fields: List[context.universe.Symbol],
104+
defaults: Map[Int, context.universe.MethodSymbol],
105+
companion: context.universe.Symbol,
106+
tpe: context.universe.Type
107+
): List[context.universe.Tree] = {
108+
import context.universe._
109+
fields.zipWithIndex.flatMap { case (field, index) =>
110+
defaults.get(index).map { m =>
111+
val key = field.asTerm.name.decodedName.toString
112+
val returnType = tpe.decl(field.asTerm.name).typeSignature.asSeenFrom(tpe, tpe.typeSymbol.asClass)
113+
q"$key -> implicitly[Reader[$returnType]].read($companion.$m)"
114+
}
115+
}
116+
}
117+
30118
def caseClassD[T](
31119
context: blackbox.Context
32-
)(implicit t: context.WeakTypeTag[T]): context.Expr[DefType] = {
120+
)(implicit t: context.WeakTypeTag[T]): context.Expr[Definition] = {
33121
import context.universe._
34122

35123
val tpe = t.tpe
36-
val className = tpe.typeSymbol.asClass.fullName
124+
val className = fullTypeName(context)(tpe)
37125
val companion: Symbol = tpe.typeSymbol.companion
38126
val defaults = defaultsFor(context)(companion)
39127
tpe.decls.collectFirst {
@@ -73,11 +161,32 @@ object RWMacros {
73161
q"$key -> implicitly[RW[$returnType]].definition"
74162
}
75163
val allFieldDefs = fieldDefs ++ serializedDefs
76-
context.Expr[DefType](q"""
164+
val genericTypes = generateGenericTypes(context)(tpe)
165+
val fieldGenericNames = extractFieldGenericNames(context)(tpe)
166+
val fieldGenericNamesEntries = fieldGenericNames.map { case (k, v) => q"$k -> $v" }.toList
167+
val fieldFormats = extractFieldFormats(context)(fields)
168+
val fieldFormatsEntries = fieldFormats.map { case (k, v) => q"$k -> $v" }.toList
169+
val deprecatedFields = extractDeprecatedFields(context)(fields)
170+
val deprecatedFieldsList = deprecatedFields.toList
171+
val fieldDefaultEntries = generateFieldDefaults(context)(fields, defaults, companion, tpe)
172+
context.Expr[Definition](q"""
77173
import _root_.fabric._
78174
import _root_.fabric.define._
175+
import _root_.scala.collection.immutable.VectorMap
79176

80-
DefType.Obj(Some($className), ..$allFieldDefs)
177+
Definition.applyFieldDefaults(
178+
Definition.applyFieldDeprecations(
179+
Definition.applyFieldFormats(
180+
Definition.applyGenericNames(
181+
Definition(DefType.Obj(VectorMap(..$allFieldDefs)), className = Some($className), genericTypes = List(..$genericTypes)),
182+
Map(..$fieldGenericNamesEntries)
183+
),
184+
Map(..$fieldFormatsEntries)
185+
),
186+
Set(..$deprecatedFieldsList)
187+
),
188+
Map(..$fieldDefaultEntries)
189+
)
81190
""")
82191
case None =>
83192
val caseObjects = companion.typeSignature.members.collect {
@@ -87,8 +196,7 @@ object RWMacros {
87196
if (caseObjects.isEmpty) {
88197
context.abort(context.enclosingPosition, "Not a valid case class or sealed trait with case objects")
89198
} else {
90-
// Generate a DefType for an enumeration by delegating to RW.enumeration
91-
context.Expr[DefType](q"""
199+
context.Expr[Definition](q"""
92200
import _root_.fabric._
93201
import _root_.fabric.define._
94202
import _root_.fabric.rw._
@@ -278,6 +386,7 @@ object RWMacros {
278386
val innerType = tpe.decl(name).typeSignature.asSeenFrom(tpe, tpe.typeSymbol.asClass)
279387
val companion = tpe.typeSymbol.companion
280388
val className = tpe.typeSymbol.fullName
389+
val genericTypes = generateGenericTypes(context)(tpe)
281390
return context.Expr[RW[T]](q"""
282391
import _root_.fabric._
283392
import _root_.fabric.rw._
@@ -287,7 +396,7 @@ object RWMacros {
287396
private val innerRW = implicitly[RW[$innerType]]
288397
override def read(t: $tpe): Json = innerRW.read(t.$name)
289398
override def write(value: Json): $tpe = $companion(innerRW.write(value))
290-
override val definition: DefType = innerRW.definition.withClassName($className)
399+
override val definition: Definition = innerRW.definition.withClassName($className).copy(genericTypes = List(..$genericTypes))
291400
}
292401
""")
293402
case _ => // fall through to normal path
@@ -308,7 +417,7 @@ object RWMacros {
308417

309418
override def read(t: $tpe): Json = r.read(t)
310419
override def write(value: Json): $tpe = w.write(value)
311-
override def definition: DefType = $definition
420+
override def definition: Definition = $definition
312421
}
313422
""")
314423
}

0 commit comments

Comments
 (0)