diff options
-rw-r--r-- | core/src/main/scala/interface.scala | 1 | ||||
-rw-r--r-- | core/src/main/scala/magnolia.scala | 44 | ||||
-rw-r--r-- | examples/src/main/scala/show.scala | 5 | ||||
-rw-r--r-- | tests/src/main/scala/tests.scala | 6 |
4 files changed, 38 insertions, 18 deletions
diff --git a/core/src/main/scala/interface.scala b/core/src/main/scala/interface.scala index 30b473a..54f8ce3 100644 --- a/core/src/main/scala/interface.scala +++ b/core/src/main/scala/interface.scala @@ -94,6 +94,7 @@ trait Param[Typeclass[_], Type] { abstract class CaseClass[Typeclass[_], Type] private[magnolia] ( val typeName: String, val isObject: Boolean, + val isValueClass: Boolean, parametersArray: Array[Param[Typeclass, Type]] ) { diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala index 8c3d823..c29c1fa 100644 --- a/core/src/main/scala/magnolia.scala +++ b/core/src/main/scala/magnolia.scala @@ -177,7 +177,11 @@ object Magnolia { val isCaseClass = classType.map(_.isCaseClass).getOrElse(false) val isCaseObject = classType.map(_.isModuleClass).getOrElse(false) val isSealedTrait = classType.map(_.isSealed).getOrElse(false) - val isValueClass = genericType <:< typeOf[AnyVal] + + val primitives = Set(typeOf[Double], typeOf[Float], typeOf[Short], typeOf[Byte], + typeOf[Int], typeOf[Long], typeOf[Char], typeOf[Boolean]) + + val isValueClass = genericType <:< typeOf[AnyVal] && !primitives.exists(_ =:= genericType) val resultType = appliedType(typeConstructor, genericType) @@ -188,13 +192,14 @@ object Magnolia { val className = obj.name.decodedName.toString val impl = q""" ${c.prefix}.combine($magnoliaObj.caseClass[$typeConstructor, $genericType]( - $className, true, new $arrayCls(0), _ => $obj) + $className, true, false, new $arrayCls(0), _ => $obj) ) """ Some(Typeclass(genericType, impl)) - } else if (isCaseClass) { + } else if (isCaseClass || isValueClass) { val caseClassParameters = genericType.decls.collect { - case m: MethodSymbol if m.isCaseAccessor => m.asMethod + case m: MethodSymbol if m.isCaseAccessor || (isValueClass && m.isParamAccessor) => + m.asMethod } val className = genericType.typeSymbol.name.decodedName.toString @@ -203,6 +208,7 @@ object Magnolia { paramType: c.Type, ref: c.TermName) + val caseParamsReversed: List[CaseParam] = caseClassParameters.foldLeft(List[CaseParam]()) { case (acc, param) => val paramName = param.name.decodedName.toString @@ -214,7 +220,7 @@ object Magnolia { val caseParamOpt = predefinedRef.map { backRef => CaseParam(param, q"()", paramType, backRef.ref) :: acc } - + caseParamOpt.getOrElse { val derivedImplicit = recurse(ProductType(paramName, genericType.toString), genericType, assignedName) { @@ -236,17 +242,19 @@ object Magnolia { val preAssignments = caseParams.map(_.typeclass) - val caseClassCompanion = genericType.companion - val constructorMethod = caseClassCompanion.decl(TermName("apply")).asMethod - val indexedConstructorParams = constructorMethod.paramLists.head.map(_.asTerm).zipWithIndex - - val defaults = indexedConstructorParams.map { - case (p, idx) => - if (p.isParamWithDefault) { - val method = TermName("apply$default$" + (idx + 1)) - q"_root_.scala.Some(${genericType.typeSymbol.companionSymbol.asTerm}.$method)" - } else q"_root_.scala.None" - } + val defaults = if(!isValueClass) { + val caseClassCompanion = genericType.companion + val constructorMethod = caseClassCompanion.decl(TermName("apply")).asMethod + val indexedConstructorParams = constructorMethod.paramLists.head.map(_.asTerm).zipWithIndex + + indexedConstructorParams.map { + case (p, idx) => + if (p.isParamWithDefault) { + val method = TermName("apply$default$" + (idx + 1)) + q"_root_.scala.Some(${genericType.typeSymbol.companionSymbol.asTerm}.$method)" + } else q"_root_.scala.None" + } + } else List(q"_root_.scala.None") val assignments = caseParams.zip(defaults).zipWithIndex.map { case ((CaseParam(param, typeclass, paramType, ref), defaultVal), idx) => @@ -268,6 +276,7 @@ object Magnolia { ${c.prefix}.combine($magnoliaObj.caseClass[$typeConstructor, $genericType]( $className, false, + $isValueClass, $paramsVal, ($fnVal: Param[$typeConstructor, $genericType] => Any) => new $genericType(..${caseParams.zipWithIndex.map { @@ -421,9 +430,10 @@ object Magnolia { * should not be called directly from users' code. */ def caseClass[Tc[_], T](name: String, obj: Boolean, + valClass: Boolean, params: Array[Param[Tc, T]], constructor: (Param[Tc, T] => Any) => T) = - new CaseClass[Tc, T](name, obj, params) { + new CaseClass[Tc, T](name, obj, valClass, params) { def construct[R](param: Param[Tc, T] => R): T = constructor(param) } } diff --git a/examples/src/main/scala/show.scala b/examples/src/main/scala/show.scala index e101c11..fdebb78 100644 --- a/examples/src/main/scala/show.scala +++ b/examples/src/main/scala/show.scala @@ -21,7 +21,10 @@ trait GenericShow[Out] { /** creates a new [[Show]] instance by labelling and joining (with `mkString`) the result of * showing each parameter, and prefixing it with the class name */ def combine[T](ctx: CaseClass[Typeclass, T]): Show[Out, T] = new Show[Out, T] { - def show(value: T) = { + def show(value: T) = if(ctx.isValueClass) { + val param = ctx.parameters.head + param.typeclass.show(param.dereference(value)) + } else { val paramStrings = ctx.parameters.map { param => s"${param.label}=${param.typeclass.show(param.dereference(value))}" } diff --git a/tests/src/main/scala/tests.scala b/tests/src/main/scala/tests.scala index ece1d32..242bd41 100644 --- a/tests/src/main/scala/tests.scala +++ b/tests/src/main/scala/tests.scala @@ -18,6 +18,8 @@ case class Company(name: String) extends Entity case class Person(name: String, age: Int) extends Entity case class Address(line1: String, occupant: Person) +class Length(val value: Int) extends AnyVal + case class Lunchbox(fruit: Fruit, drink: String) object Fruit { import examples._ @@ -170,5 +172,9 @@ object Tests extends TestApp { test("serialize a tuple") { tupleDerivation().show((42, "Hello World")) }.assert(_ == "Tuple2(_1=42,_2=Hello World)") + + test("serialize a value class") { + Show.gen[Length].show(new Length(100)) + }.assert(_ == "100") } } |