Skip to content

Instantly share code, notes, and snippets.

@DylanLukes
Created April 12, 2017 09:55
Show Gist options
  • Save DylanLukes/e938849f113052205608c1eac5ae0f1d to your computer and use it in GitHub Desktop.
Save DylanLukes/e938849f113052205608c1eac5ae0f1d to your computer and use it in GitHub Desktop.
import scala.collection.immutable.Seq
import scala.meta._
class ScalaJSDefinedApply extends scala.annotation.StaticAnnotation {
inline def apply(defn: Any): Any = meta {
def collectFields(trt: Defn.Trait) = {
val params = trt.collect {
case dv @ Defn.Val(_, pats, Some(tpe), q"js.undefined") =>
pats.map { case Pat.Var.Term(name) =>
(name, tpe, q"js.undefined")
}
case dv @ Defn.Val(_, _, None, q"js.undefined") =>
abort(s"@ScalaJSDefinedApply: $dv must have explicit type declarations.")
case dv @ Defn.Val(_, _, _, _) =>
abort(s"@ScalaJSDefinedApply: $dv must have = js.undefined.")
}
params.flatten
}
def genApply(tname: Type.Name, fields: Seq[(Term.Name, Type, Term)]): Defn.Def = {
val params = fields.map { case (name, tpe, default) =>
param"$name: $tpe = $default"
}
val args = fields.map { case (name, _, _) =>
arg"(${Lit.String(name.value)}, $name)"
}
q"def apply(..$params): $tname = js.Dynamic.literal(..$args).asInstanceOf[$tname]"
}
defn match {
case dt @ Defn.Trait(_, tname, _, _, _) =>
val fields = collectFields(dt)
val applyMethod = genApply(tname, fields)
val companion = q"object ${Term.Name(tname.value)} { $applyMethod }"
// println(companion)
Term.Block(Seq(dt, companion))
case _ =>
abort("@ScalaJSDefinedApply must annotate a trait.")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment