Skip to content

Commit eefd9f8

Browse files
Pasklukaszlenart
authored andcommitted
Implements magnolia config
1 parent af8627c commit eefd9f8

File tree

4 files changed

+72
-6
lines changed

4 files changed

+72
-6
lines changed

core/src/main/scala/magnolia1/interface.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,16 @@ final class debug(typeNamePart: String = "") extends scala.annotation.StaticAnno
478478

479479
private[magnolia1] final case class EarlyExit[E](e: E) extends Exception with util.control.NoStackTrace
480480

481+
trait Config {
482+
type Proxy <: Singleton { type Typeclass[A] }
483+
type Ignore <: annotation.Annotation
484+
val readOnly: Boolean
485+
val minFields: Int
486+
val maxFields: Int
487+
val minCases: Int
488+
val maxCases: Int
489+
}
490+
481491
object MagnoliaUtil {
482492

483493
final def checkParamLengths(fieldValues: Seq[Any], paramsLength: Int, typeName: String): Unit =

core/src/main/scala/magnolia1/magnolia.scala

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ import scala.collection.mutable
77
import scala.language.higherKinds
88
import scala.reflect.macros._
99

10+
private case class MagnoliaConfig[ProxyType, IgnoreType](
11+
proxyType: ProxyType,
12+
ignoreType: IgnoreType,
13+
readOnly: Boolean = false,
14+
minFields: Int = -1,
15+
maxFields: Int = Int.MaxValue,
16+
minCases: Int = -1,
17+
maxCases: Int = Int.MaxValue
18+
)
19+
1020
/** the object which defines the Magnolia macro */
1121
object Magnolia {
1222
import CompileTimeState._
@@ -42,12 +52,32 @@ object Magnolia {
4252
* split[T](sealedTrait: SealedTrait[Typeclass, T]): Typeclass[T] = ... </pre> will suffice, however the qualifications regarding
4353
* additional type parameters and implicit parameters apply equally to `split` as to `join`.
4454
*/
45-
def gen[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = Stack.withContext(c) { (stack, depth) =>
55+
def genWith[T: c.WeakTypeTag, C <: Config with Singleton: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
56+
import c.universe._
57+
58+
val weakConfig = weakTypeOf[C]
59+
val proxyType: c.Type = weakConfig.decl(TypeName("Proxy")).info
60+
val ignoreType: c.Type = weakConfig.decl(TypeName("Ignore")).info
61+
val NullaryMethodType(ConstantType(Constant(readOnly: Boolean))) = weakConfig.decl(TermName("readOnly")).info
62+
val NullaryMethodType(ConstantType(Constant(minFields: Int))) = weakConfig.decl(TermName("minFields")).info
63+
val NullaryMethodType(ConstantType(Constant(maxFields: Int))) = weakConfig.decl(TermName("maxFields")).info
64+
val NullaryMethodType(ConstantType(Constant(minCases: Int))) = weakConfig.decl(TermName("minCases")).info
65+
val NullaryMethodType(ConstantType(Constant(maxCases: Int))) = weakConfig.decl(TermName("maxCases")).info
66+
67+
genMacro[T, c.Type, c.Type](c, Some(MagnoliaConfig(proxyType, ignoreType, readOnly, minFields, maxFields, minCases, maxCases)))
68+
}
69+
70+
def gen[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
71+
genMacro(c, None)
72+
}
73+
74+
private def genMacro[T: c.WeakTypeTag, ProxyType, IgnoreType](c: whitebox.Context, config: Option[MagnoliaConfig[ProxyType, IgnoreType]]): c.Tree = Stack.withContext(c) { (stack, depth) =>
4675
import c.internal._
4776
import c.universe._
4877
import definitions._
4978

5079
val genericType = weakTypeOf[T]
80+
5181
val genericSymbol = genericType.typeSymbol
5282

5383
def error(message: => String): Nothing = c.abort(c.enclosingPosition, if (depth > 1) "" else s"magnolia: $message")
@@ -318,6 +348,12 @@ object Magnolia {
318348
val resultType = appliedType(typeConstructor, genericType)
319349
val typeName = c.freshName(TermName("typeName"))
320350

351+
def isIgnored(termSymbol: c.universe.TermSymbol): Boolean = {
352+
config.map(_.ignoreType).exists { ignoreType =>
353+
termSymbol.annotations.map(_.tree.symbol).contains(ignoreType)
354+
}
355+
}
356+
321357
def typeNameOf(tpe: Type): Tree = {
322358
val symbol = tpe.typeSymbol
323359
val typeArgNames = for (typeArg <- tpe.typeArgs) yield typeNameOf(typeArg)
@@ -383,8 +419,11 @@ object Magnolia {
383419
.map(_.map(_.asTerm))
384420

385421
val caseClassParameters = genericType.decls.sorted.collect(
386-
if (isValueClass) { case p: TermSymbol if p.isParamAccessor && p.isMethod => p }
387-
else { case p: TermSymbol if p.isCaseAccessor && !p.isMethod => p }
422+
if (isValueClass) {
423+
case p: TermSymbol if p.isParamAccessor && p.isMethod => p
424+
} else {
425+
case p: TermSymbol if p.isCaseAccessor && !p.isMethod && !isIgnored(p) => p
426+
}
388427
)
389428

390429
val (factoryObject, factoryMethod) = {

examples/src/main/scala/magnolia1/examples/csv.scala

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
package magnolia1.examples
22

3-
import magnolia1.{CaseClass, Magnolia, SealedTrait}
3+
import magnolia1.{CaseClass, Config, Magnolia, SealedTrait}
44

55
import scala.language.experimental.macros
66

77
trait Csv[A] {
88
def apply(a: A): List[String]
99
}
1010

11+
object CsvConfig extends Config {
12+
type Proxy = Csv.type
13+
type Ignore = transient
14+
final val readOnly = true
15+
final val minFields = 0
16+
final val maxFields = -1
17+
final val minCases = 0
18+
final val maxCases = -1
19+
}
20+
1121
object Csv {
1222
type Typeclass[A] = Csv[A]
1323

@@ -22,9 +32,13 @@ object Csv {
2232
def apply(a: A): List[String] = ctx.split(a)(sub => sub.typeclass(sub.cast(a)))
2333
}
2434

25-
implicit def deriveCsv[A]: Csv[A] = macro Magnolia.gen[A]
35+
@transient
36+
val ignoreMe: String = "ignored value"
37+
38+
implicit def deriveCsv[A]: Csv[A] = macro Magnolia.genWith[A, CsvConfig.type]
2639

2740
implicit val csvStr: Csv[String] = new Csv[String] {
2841
def apply(a: String): List[String] = List(a)
2942
}
43+
3044
}

test/src/test/scala/magnolia1/tests/tests.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ sealed trait AttributeParent
5252

5353
case class `%%`(`/`: Int, `#`: String)
5454

55-
case class Param(a: String, b: String)
55+
case class Param(a: String, b: String) {
56+
@transient
57+
val daad = "Test"
58+
}
5659
case class TestEntry(param: Param)
5760
object TestEntry {
5861
def apply(): TestEntry = TestEntry(Param("", ""))

0 commit comments

Comments
 (0)