Last active
March 13, 2017 06:09
-
-
Save matsu-chara/f58868ebc99bffabe4c385b3c7b01170 to your computer and use it in GitHub Desktop.
get field name of case class and get type checked new value. (scalameta 1.6.0, paradise 3.0.0-M7)
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
package scalaworld.macros | |
package com.folio.account.macros | |
import scala.collection.immutable.Seq | |
import scala.meta._ | |
/** | |
* annotation for case class. | |
* | |
* It generates `FieldNameAndValuePorter` obect in comanion obect, that have these methods for each case class field. | |
* `def fieldName(value: TypeOfField): (String, TypeOfField) = (fieldName, value)` | |
* | |
* @example | |
* {{{ | |
* @FieldNameAndValuePorter | |
* case class Mofu(foo: Int, bar: String) | |
* obect Mofu {} | |
* | |
* Mofu.FieldNameAndValuePorter.foo(1) ==> ("foo", 1) | |
* Mofu.FieldNameAndValuePorter.bar(2) ==> type error Int is not String | |
* }}} | |
*/ | |
class FieldNameAndValuePorter extends scala.annotation.StaticAnnotation { | |
inline def apply(defn: Any): Any = meta { | |
def createPorterObject(paramss: Seq[Seq[Term.Param]]): Defn.Object = { | |
val porters = paramss.flatten.map { param => | |
val fieldName = Term.Name(param.name.syntax) | |
val fieldType = param.decltpe.getOrElse(abort(s"${param.name.syntax} does not have type")) | |
val argName = Term.fresh("value") | |
val returnType = Type.Name(fieldType.syntax) | |
q"""def $fieldName($argName: $fieldType):(_root_.scala.Predef.String, $returnType) = (${fieldName.value}, $argName)""" | |
} | |
q"""object FieldNameAndValuePorter { ..$porters }""" | |
} | |
defn match { | |
// companion object exists | |
case Term.Block(Seq(cls @ Defn.Class(_, name, _, ctor, _), companion: Defn.Object)) => | |
val porterObject = createPorterObject(cls.ctor.paramss) | |
val newCompanion = companion.copy( | |
templ = companion.templ.copy( | |
stats = Some(porterObject +: companion.templ.stats.getOrElse(Nil)) | |
) | |
) | |
Term.Block(Seq(cls, newCompanion)) | |
// companion object does not exists | |
case cls: Defn.Class => | |
val porterObject = createPorterObject(cls.ctor.paramss) | |
val companion = q"object ${Term.Name(cls.name.value)} { $porterObject }" | |
Term.Block(Seq(cls, companion)) | |
// something wrong | |
case _ => | |
println(defn.structure) | |
abort("@FieldNameAndValuePorter must annotate a object.") | |
} | |
} | |
} |
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
package scalaworld.macros | |
import org.scalatest.FunSuite | |
object Mofu | |
@FieldNameAndValuePorter | |
case class Mofu(wan: Int, nyan: String, gau: Double, oh: String) | |
class FieldNameAndValuePorterMain extends FunSuite { | |
test("mofu") { | |
// ("wan",2) | |
println(Mofu.MacroPorter.wan(2)) | |
// ("nyan", "nyaa") | |
println(Mofu.MacroPorter.nyan("nyaa")) | |
// compile error gyan not defined | |
// println(Mofu.MacroPorter.gyan(3)) | |
// compile error wan is not string | |
// println(Mofu.MacroPorter.wan("waon")) | |
} | |
} |
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
obect MofuSql { | |
def update(columnAndValues: Map[Any, String]): Future[Unit] = { | |
someLibrarySql.update(columnAndValues) | |
} | |
} | |
object MofuMain { | |
val (wanValue, nyanValue) = parseSomeRequest() | |
// voilerplate | |
MofuSql.update(Map( | |
"wan" -> wanValue, | |
"nyn" -> nyanValue, // typo! | |
"gau" -> "gaugau" // type unsafe (String is not Double) | |
)) | |
// macro (typo safe, value is type checked) | |
MofuSql.update(Map( | |
Mofu.MacroPorter.wan(wanValue), | |
Mofu.MacroPorter.nyan(nyanValue), | |
Mofu.MacroPorter.gau(1.3) | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
なるほど・・!
http://scalameta.org/tutorial/ が更新されてるのか不明ですが
_root.~
で参照しているっぽいのでダメそうですかね・・(◞‸◟)