diff options
author | Georgi Krastev <joro.kr.21@gmail.com> | 2017-11-29 12:36:04 +0100 |
---|---|---|
committer | Georgi Krastev <joro.kr.21@gmail.com> | 2017-11-29 18:04:24 +0100 |
commit | e9b52248855f9252520a232b28a841ea382922a1 (patch) | |
tree | 2811ad3c3231d1656ae5d69412bc579abf8fbb07 /core/shared/src | |
parent | 93ff9742cf8cd9f2c60986ff4f7af8d24e268b1f (diff) | |
download | magnolia-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/shared/src')
-rw-r--r-- | core/shared/src/main/scala/interface.scala | 11 | ||||
-rw-r--r-- | core/shared/src/main/scala/magnolia.scala | 29 |
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) |