我想写一个宏:
@MetaRest case class User(
@get id : Int,
@get @post @patch name : String,
@get @post email : String,
registeredOn : DateTime
)
生成以下代码:
object User {
case class Get(id: Int, name: String, email: String)
case class Post(name: String, email: String
case class Patch(name: Option[String]) // Note - the Option type here!
}
我在这里取得了不错的进展:https://github.com/pathikrit/metarest
我的尝试:https://github.com/pathikrit/metarest/blob/master/src/main/scala/com/github/pathikrit/MetaRest.scala
class MetaRest extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro MetaRest.impl
}
object MetaRest {
sealed trait MethodAnnotations extends StaticAnnotation
class get extends MethodAnnotations
class put extends MethodAnnotations
class post extends MethodAnnotations
class patch extends MethodAnnotations
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def modifiedCompanion(compDeclOpt: Option[ModuleDef], className: TypeName, fields: List[ValDef]) = {
val result = fields.flatMap {field =>
field.mods.annotations.collect { // TODO: shorten this - make use of the fact that all extend sealed trait MethodAnnotations
case q"new get" => "get" -> field
case q"new put" => "put" -> field
case q"new post" => "post" -> field
case q"new patch" => "patch" -> field
}
} groupBy (_._1) mapValues(_ map (_._2.duplicate))
println(result("get"))
// TODO: ----------------------------------------------------------------------------------------
val getRequestModel = q"""case class Get(id: Int, name: String, email: String)"""
val postRequestModel = q"""case class Post(name: String, email: String)"""
val patchRequestModel = q"""case class Patch(name: Option[String])"""
// ------------------------------------------------------------------------------------------------
compDeclOpt map { compDecl =>
val q"object $obj extends ..$bases { ..$body }" = compDecl
q"""
object $obj extends ..$bases {
..$body
$getRequestModel
$postRequestModel
$patchRequestModel
}
"""
} getOrElse {
q"""
object ${className.toTermName} {
$getRequestModel
$postRequestModel
$patchRequestModel
}
"""
}
}
def modifiedDeclaration(classDecl: ClassDef, compDeclOpt: Option[ModuleDef] = None) = {
val q"case class $className(..$fields) extends ..$bases { ..$body }" = classDecl
val compDecl = modifiedCompanion(compDeclOpt, className, fields)
c.Expr(q"""
$classDecl
$compDecl
""")
}
annottees.map(_.tree) match {
case (classDecl: ClassDef) :: Nil => modifiedDeclaration(classDecl)
case (classDecl: ClassDef) :: (compDecl: ModuleDef) :: Nil => modifiedDeclaration(classDecl, Some(compDecl))
case _ => c.abort(c.enclosingPosition, "@MetaRest must annotate a class")
}
}
}
通过注释对字段进行干净分组并生成Get/Post
类的最佳方法是什么?另外,对于Patch
类 - 如何将所有字段转换为Option[original.type]
?
答案 0 :(得分:1)
实际上我做到了: https://github.com/pathikrit/metarest/commit/1d1af92b71179385c8521d00a5059dd21996c448?diff=split
这很简单:
q"$accessor val $vname: $tpe" => q"$accessor val $vname: Option[$tpe]"