aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorGeorgi Krastev <joro.kr.21@gmail.com>2017-11-29 12:36:04 +0100
committerGeorgi Krastev <joro.kr.21@gmail.com>2017-11-29 18:04:24 +0100
commite9b52248855f9252520a232b28a841ea382922a1 (patch)
tree2811ad3c3231d1656ae5d69412bc579abf8fbb07 /core
parent93ff9742cf8cd9f2c60986ff4f7af8d24e268b1f (diff)
downloadmagnolia-e9b52248855f9252520a232b28a841ea382922a1.tar.gz
magnolia-e9b52248855f9252520a232b28a841ea382922a1.tar.bz2
magnolia-e9b52248855f9252520a232b28a841ea382922a1.zip
Add support for repeated (vararg) parameters
Added a `Param.repeated` flag to `interface`.
Diffstat (limited to 'core')
-rw-r--r--core/shared/src/main/scala/interface.scala11
-rw-r--r--core/shared/src/main/scala/magnolia.scala29
2 files changed, 33 insertions, 7 deletions
diff --git a/core/shared/src/main/scala/interface.scala b/core/shared/src/main/scala/interface.scala
index 02c1cc5..193a6f9 100644
--- a/core/shared/src/main/scala/interface.scala
+++ b/core/shared/src/main/scala/interface.scala
@@ -46,6 +46,17 @@ trait Param[Typeclass[_], Type] {
/** the name of the parameter */
def label: String
+ /** flag indicating a repeated (aka. vararg) parameter
+ *
+ * For example, for a case class,
+ * <pre>
+ * case class Account(id: String, emails: String*)
+ * </pre>
+ * the [[Param]] instance corresponding to the `emails` parameter would be `repeated` and have a
+ * [[PType]] equal to the type `Seq[String]`. Note that only the last parameter of a case class
+ * can be repeated. */
+ def repeated: Boolean
+
/** the typeclass instance associated with this parameter
*
* This is the instance of the type `Typeclass[PType]` which will have been discovered by
diff --git a/core/shared/src/main/scala/magnolia.scala b/core/shared/src/main/scala/magnolia.scala
index 2113dff..c4c13e6 100644
--- a/core/shared/src/main/scala/magnolia.scala
+++ b/core/shared/src/main/scala/magnolia.scala
@@ -69,6 +69,9 @@ object Magnolia {
val magnoliaPkg = q"_root_.magnolia"
val scalaPkg = q"_root_.scala"
+ val repeatedParamClass = definitions.RepeatedParamClass
+ val scalaSeqType = typeOf[Seq[_]].typeConstructor
+
val prefixType = c.prefix.tree.tpe
def companionRef(tpe: Type): Tree = {
@@ -239,6 +242,7 @@ object Magnolia {
val className = genericType.typeSymbol.name.decodedName.toString
case class CaseParam(sym: c.universe.MethodSymbol,
+ repeated: Boolean,
typeclass: c.Tree,
paramType: c.Type,
ref: c.TermName)
@@ -246,13 +250,21 @@ object Magnolia {
val caseParamsReversed = caseClassParameters.foldLeft[List[CaseParam]](Nil) {
(acc, param) =>
val paramName = param.name.decodedName.toString
- val paramType = param.returnType.substituteTypes(genericType.etaExpand.typeParams,
- genericType.typeArgs)
+ val paramTypeSubstituted = param.returnType.substituteTypes(
+ genericType.etaExpand.typeParams,
+ genericType.typeArgs)
+
+ val (repeated, paramType) = paramTypeSubstituted match {
+ case TypeRef(_, `repeatedParamClass`, typeArgs) =>
+ true -> appliedType(scalaSeqType, typeArgs)
+ case tpe =>
+ false -> tpe
+ }
val predefinedRef = acc.find(_.paramType == paramType)
val caseParamOpt = predefinedRef.map { backRef =>
- CaseParam(param, q"()", paramType, backRef.ref) :: acc
+ CaseParam(param, repeated, q"()", paramType, backRef.ref) :: acc
}
caseParamOpt.getOrElse {
@@ -265,7 +277,7 @@ object Magnolia {
val ref = TermName(c.freshName("paramTypeclass"))
val assigned = q"""val $ref = $derivedImplicit"""
- CaseParam(param, assigned, paramType, ref) :: acc
+ CaseParam(param, repeated, assigned, paramType, ref) :: acc
}
}
@@ -299,10 +311,10 @@ object Magnolia {
} else List(q"$scalaPkg.None")
val assignments = caseParams.zip(defaults).zipWithIndex.map {
- case ((CaseParam(param, typeclass, paramType, ref), defaultVal), idx) =>
+ case ((CaseParam(param, repeated, typeclass, paramType, ref), defaultVal), idx) =>
q"""$paramsVal($idx) = $magnoliaPkg.Magnolia.param[$typeConstructor, $genericType,
$paramType](
- ${param.name.decodedName.toString}, $ref, $defaultVal, _.${param.name}
+ ${param.name.decodedName.toString}, $repeated, $ref, $defaultVal, _.${param.name}
)"""
}
@@ -323,7 +335,8 @@ object Magnolia {
($fnVal: $magnoliaPkg.Param[$typeConstructor, $genericType] => Any) =>
new $genericType(..${caseParams.zipWithIndex.map {
case (typeclass, idx) =>
- q"$fnVal($paramsVal($idx)).asInstanceOf[${typeclass.paramType}]"
+ val arg = q"$fnVal($paramsVal($idx)).asInstanceOf[${typeclass.paramType}]"
+ if (typeclass.repeated) q"$arg: _*" else arg
}})
))
}"""
@@ -456,11 +469,13 @@ object Magnolia {
* This method is intended to be called only from code generated by the Magnolia macro, and
* should not be called directly from users' code. */
def param[Tc[_], T, P](name: String,
+ isRepeated: Boolean,
typeclassParam: Tc[P],
defaultVal: => Option[P],
deref: T => P) = new Param[Tc, T] {
type PType = P
def label: String = name
+ def repeated: Boolean = isRepeated
def default: Option[PType] = defaultVal
def typeclass: Tc[PType] = typeclassParam
def dereference(t: T): PType = deref(t)