-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #186 from twitter/ianoc/refactorMacros
Ianoc/refactor macros
- Loading branch information
Showing
15 changed files
with
330 additions
and
332 deletions.
There are no files selected for viewing
12 changes: 0 additions & 12 deletions
12
...ion-macros-common/src/main/scala/com/twitter/bijection/macros/common/MacroImplicits.scala
This file was deleted.
Oops, something went wrong.
38 changes: 0 additions & 38 deletions
38
...on-macros-common/src/test/scala/com/twitter/bijection/macros/common/MacroDepHygiene.scala
This file was deleted.
Oops, something went wrong.
9 changes: 8 additions & 1 deletion
9
...tter/bijection/macros/common/Macros.scala → ...r/bijection/macros/MacroAnnotations.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
72 changes: 72 additions & 0 deletions
72
bijection-macros/src/main/scala/com/twitter/bijection/macros/impl/CaseClassToMap.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package com.twitter.bijection.macros.impl | ||
|
||
import scala.collection.mutable.{ Map => MMap } | ||
import scala.language.experimental.macros | ||
import scala.reflect.macros.Context | ||
import scala.reflect.runtime.universe._ | ||
import scala.util.Try | ||
|
||
import com.twitter.bijection._ | ||
import com.twitter.bijection.macros.{ IsCaseClass, MacroGenerated } | ||
|
||
private[bijection] object CaseClassToMap { | ||
def caseClassToMapImplWithOption[T](c: Context)(recursivelyApply: c.Expr[Boolean])(proof: c.Expr[IsCaseClass[T]])(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] = { | ||
import c.universe._ | ||
recursivelyApply.tree match { | ||
case q"""true""" => caseClassToMapNoProofImpl(c)(T) | ||
case q"""false""" => caseClassToMapNoProofImplNonRecursive(c)(T) | ||
} | ||
} | ||
|
||
def caseClassToMapImpl[T](c: Context)(proof: c.Expr[IsCaseClass[T]])(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] = | ||
caseClassToMapNoProofImpl(c)(T) | ||
|
||
def caseClassToMapImplNonRecursive[T](c: Context)(proof: c.Expr[IsCaseClass[T]])(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] = | ||
caseClassToMapNoProofImplNonRecursive(c)(T) | ||
|
||
def caseClassToMapNoProofImplNonRecursive[T](c: Context)(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] = caseClassToMapNoProofImplCommon(c, false)(T) | ||
|
||
// TODO the only diff between this and the above is the case match and the converters. it's easy to gate this on the boolean | ||
def caseClassToMapNoProofImpl[T](c: Context)(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] = caseClassToMapNoProofImplCommon(c, true)(T) | ||
|
||
def caseClassToMapNoProofImplCommon[T](c: Context, recursivelyApply: Boolean)(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] = { | ||
import c.universe._ | ||
//TODO can make error handling better? | ||
val companion = T.tpe.typeSymbol.companionSymbol | ||
|
||
val getPutConv = T.tpe.declarations.collect { case m: MethodSymbol if m.isCaseAccessor => m }.zipWithIndex.map { | ||
case (m, idx) => | ||
val returnType = m.returnType | ||
val accStr = m.name.toTermName.toString | ||
returnType match { | ||
case tpe if recursivelyApply && IsCaseClassImpl.isCaseClassType(c)(tpe) => | ||
val conv = newTermName("c2m_" + idx) | ||
(q"""$conv.invert(m($accStr).asInstanceOf[_root_.scala.collection.immutable.Map[String, Any]]).get""", | ||
q"""($accStr, $conv(t.$m))""", | ||
Some(q"""val $conv = implicitly[_root_.com.twitter.bijection.Injection[$tpe, _root_.scala.collection.immutable.Map[String, Any]]]""")) //TODO cache these | ||
case tpe => | ||
(q"""m($accStr).asInstanceOf[$returnType]""", | ||
q"""($accStr, t.$m)""", | ||
None) | ||
} | ||
} | ||
|
||
val getters = getPutConv.map(_._1) | ||
val putters = getPutConv.map(_._2) | ||
val converters = getPutConv.flatMap(_._3) | ||
|
||
c.Expr[Injection[T, Map[String, Any]]](q""" | ||
new Injection[$T, _root_.scala.collection.immutable.Map[String, Any]] with MacroGenerated { | ||
override def apply(t: $T): _root_.scala.collection.immutable.Map[String, Any] = { | ||
..$converters | ||
_root_.scala.collection.immutable.Map[String, Any](..$putters) | ||
} | ||
override def invert(m: _root_.scala.collection.immutable.Map[String, Any]): _root_.scala.util.Try[ $T ] = { | ||
..$converters | ||
try { _root_.scala.util.Success($companion(..$getters)) } catch { case _root_.scala.util.control.NonFatal(e) => _root_.scala.util.Failure(e) } | ||
} | ||
} | ||
""") | ||
|
||
} | ||
} |
86 changes: 86 additions & 0 deletions
86
bijection-macros/src/main/scala/com/twitter/bijection/macros/impl/CaseClassToTuple.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package com.twitter.bijection.macros.impl | ||
|
||
import scala.collection.mutable.{ Map => MMap } | ||
import scala.language.experimental.macros | ||
import scala.reflect.macros.Context | ||
import scala.reflect.runtime.universe._ | ||
import scala.util.Try | ||
|
||
import com.twitter.bijection._ | ||
import com.twitter.bijection.macros.{ IsCaseClass, MacroGenerated } | ||
|
||
private[bijection] object CaseClassToTuple { | ||
def caseClassToTupleImplWithOption[T, Tup](c: Context)(recursivelyApply: c.Expr[Boolean])(proof: c.Expr[IsCaseClass[T]])(implicit T: c.WeakTypeTag[T], Tup: c.WeakTypeTag[Tup]): c.Expr[Bijection[T, Tup]] = { | ||
import c.universe._ | ||
recursivelyApply match { | ||
case q"""true""" => caseClassToTupleNoProofImpl(c)(T, Tup) | ||
case q"""false""" => caseClassToTupleNoProofImplNonRecursive(c)(T, Tup) | ||
case _ => caseClassToTupleNoProofImpl(c)(T, Tup) | ||
} | ||
} | ||
|
||
// Entry point | ||
def caseClassToTupleImpl[T, Tup](c: Context)(proof: c.Expr[IsCaseClass[T]])(implicit T: c.WeakTypeTag[T], Tup: c.WeakTypeTag[Tup]): c.Expr[Bijection[T, Tup]] = | ||
caseClassToTupleNoProofImpl(c)(T, Tup) | ||
|
||
def caseClassToTupleImplNonRecursive[T, Tup](c: Context)(proof: c.Expr[IsCaseClass[T]])(implicit T: c.WeakTypeTag[T], Tup: c.WeakTypeTag[Tup]): c.Expr[Bijection[T, Tup]] = | ||
caseClassToTupleNoProofImplNonRecursive(c)(T, Tup) | ||
|
||
def caseClassToTupleNoProofImplNonRecursive[T, Tup](c: Context)(implicit T: c.WeakTypeTag[T], | ||
Tup: c.WeakTypeTag[Tup]): c.Expr[Bijection[T, Tup]] = | ||
caseClassToTupleNoProofImplCommon(c, false)(T, Tup) | ||
|
||
def caseClassToTupleNoProofImpl[T, Tup](c: Context)(implicit T: c.WeakTypeTag[T], | ||
Tup: c.WeakTypeTag[Tup]): c.Expr[Bijection[T, Tup]] = | ||
caseClassToTupleNoProofImplCommon(c, true)(T, Tup) | ||
|
||
def caseClassToTupleNoProofImplCommon[T, Tup](c: Context, | ||
recursivelyApply: Boolean)(implicit T: c.WeakTypeTag[T], | ||
Tup: c.WeakTypeTag[Tup]): c.Expr[Bijection[T, Tup]] = { | ||
import c.universe._ | ||
val tupUtils = new TupleUtils[c.type](c) | ||
val convCache = MMap.empty[Type, TermName] | ||
|
||
//TODO can make error handling better | ||
val companion = T.tpe.typeSymbol.companionSymbol | ||
val getPutConv = T.tpe | ||
.declarations | ||
.collect { case m: MethodSymbol if m.isCaseAccessor => m } | ||
.zip(tupUtils.tupleCaseClassEquivalent(T.tpe)) | ||
.zip(Tup.tpe.declarations.collect { case m: MethodSymbol if m.isCaseAccessor => m }) | ||
.zipWithIndex | ||
.map { | ||
case (((tM, treeEquiv), tupM), idx) => | ||
tM.returnType match { | ||
case tpe if recursivelyApply && IsCaseClassImpl.isCaseClassType(c)(tpe) => | ||
val needDeclaration = !convCache.contains(tpe) | ||
val conv = convCache.getOrElseUpdate(tpe, newTermName("c2t_" + idx)) | ||
(q"""$conv.invert(tup.$tupM)""", | ||
q"""$conv(t.$tM)""", | ||
if (needDeclaration) Some(q"""val $conv = implicitly[_root_.com.twitter.bijection.Bijection[${tM.returnType}, $treeEquiv]]""") else None) // cache these | ||
case tpe => | ||
(q"""tup.$tupM""", | ||
q"""t.$tM""", | ||
None) | ||
} | ||
} | ||
|
||
val getters = getPutConv.map(_._1) | ||
val putters = getPutConv.map(_._2) | ||
val converters = getPutConv.flatMap(_._3) | ||
|
||
c.Expr[Bijection[T, Tup]](q""" | ||
new Bijection[$T,$Tup] with MacroGenerated { | ||
override def apply(t: $T): $Tup = { | ||
..$converters | ||
(..$putters) | ||
} | ||
override def invert(tup: $Tup): $T = { | ||
..$converters | ||
$companion(..$getters) | ||
} | ||
} | ||
""") | ||
} | ||
} | ||
//TODO test serialization of them |
8 changes: 4 additions & 4 deletions
8
...ection/macros/common/impl/MacroImpl.scala → ...jection/macros/impl/IsCaseClassImpl.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.