diff --git a/chimney-macro-commons/src/main/scala-2/io/scalaland/chimney/internal/compiletime/TypesPlatform.scala b/chimney-macro-commons/src/main/scala-2/io/scalaland/chimney/internal/compiletime/TypesPlatform.scala index 534c47b6c..c6b3e89f1 100644 --- a/chimney-macro-commons/src/main/scala-2/io/scalaland/chimney/internal/compiletime/TypesPlatform.scala +++ b/chimney-macro-commons/src/main/scala-2/io/scalaland/chimney/internal/compiletime/TypesPlatform.scala @@ -54,10 +54,6 @@ private[compiletime] trait TypesPlatform extends Types { this: DefinitionsPlatfo // everyone has the same baseClasses, everyone reports to have public primaryConstructor (which is ). // The only different in behavior is that one prints com.my.Enum and another com.my.Enum(MyValue). val javaEnumRegexpFormat = raw"^(.+)\((.+)\)$$".r - def isJavaEnumValue(tpe: c.Type): Boolean = - tpe.typeSymbol.isJavaEnum && javaEnumRegexpFormat.pattern - .matcher(tpe.toString) - .matches() // 2.12 doesn't have .matches } import platformSpecific.* diff --git a/chimney-macro-commons/src/main/scala-2/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypesPlatform.scala b/chimney-macro-commons/src/main/scala-2/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypesPlatform.scala index 8b5f02825..7c8dd0555 100644 --- a/chimney-macro-commons/src/main/scala-2/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypesPlatform.scala +++ b/chimney-macro-commons/src/main/scala-2/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypesPlatform.scala @@ -52,10 +52,17 @@ trait ProductTypesPlatform extends ProductTypes { this: DefinitionsPlatform => isPOJO[A] && A.tpe.typeSymbol.asClass.isCaseClass def isCaseObject[A](implicit A: Type[A]): Boolean = { val sym = A.tpe.typeSymbol - def isScala2Enum = sym.asClass.isCaseClass - def isScala3Enum = sym.isStatic && sym.isFinal // parameterless case in S3 cannot be checked for "case" - def isScalaEnum = sym.isModuleClass && (isScala2Enum || isScala3Enum) - sym.isPublic && (isScalaEnum || isJavaEnumValue(A.tpe)) + sym.isPublic && sym.isModuleClass && sym.asClass.isCaseClass + } + def isCaseVal[A](implicit A: Type[A]): Boolean = { + val sym = A.tpe.typeSymbol + sym.isPublic && sym.isModuleClass && sym.isStatic && sym.isFinal // parameterless case in S3 cannot be checked for "case" + } + def isJavaEnumValue[A](implicit A: Type[A]): Boolean = { + val sym = A.tpe.typeSymbol + sym.isPublic && sym.isJavaEnum && javaEnumRegexpFormat.pattern + .matcher(A.tpe.toString) + .matches() // 2.12 doesn't have .matches } def isJavaBean[A](implicit A: Type[A]): Boolean = { val mem = A.tpe.members @@ -103,9 +110,9 @@ trait ProductTypesPlatform extends ProductTypes { this: DefinitionsPlatform => val A = Type[A].tpe val sym = A.typeSymbol - if (isJavaEnumValue(A)) { + if (isJavaEnumValue[A]) { Some(Product.Constructor(ListMap.empty, _ => c.Expr[A](q"$A"))) - } else if (isCaseObject[A]) { + } else if (isCaseObject[A] || isCaseVal[A]) { Some(Product.Constructor(ListMap.empty, _ => c.Expr[A](q"${sym.asClass.module}"))) } else if (isPOJO[A]) { val primaryConstructor = diff --git a/chimney-macro-commons/src/main/scala-3/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypesPlatform.scala b/chimney-macro-commons/src/main/scala-3/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypesPlatform.scala index e65a09f3a..bb777436a 100644 --- a/chimney-macro-commons/src/main/scala-3/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypesPlatform.scala +++ b/chimney-macro-commons/src/main/scala-3/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypesPlatform.scala @@ -43,8 +43,6 @@ trait ProductTypesPlatform extends ProductTypes { this: DefinitionsPlatform => def isJavaSetterOrVar(setter: Symbol): Boolean = isJavaSetter(setter) || isVar(setter) - def isJavaEnumValue[A: Type]: Boolean = - Type[A] <:< scala.quoted.Type.of[java.lang.Enum[?]] && !TypeRepr.of[A].typeSymbol.isAbstract } import platformSpecific.* @@ -61,10 +59,14 @@ trait ProductTypesPlatform extends ProductTypes { this: DefinitionsPlatform => } def isCaseObject[A](implicit A: Type[A]): Boolean = { val sym = TypeRepr.of(using A).typeSymbol - def isScala2Enum = sym.flags.is(Flags.Case | Flags.Module) - def isScala3Enum = sym.flags.is(Flags.Case | Flags.Enum | Flags.JavaStatic) - sym.isPublic && (isScala2Enum || isScala3Enum || isJavaEnumValue[A]) + sym.isPublic && sym.flags.is(Flags.Case | Flags.Module) } + def isCaseVal[A](implicit A: Type[A]): Boolean = { + val sym = TypeRepr.of(using A).typeSymbol + sym.isPublic && sym.flags.is(Flags.Case | Flags.Enum | Flags.JavaStatic) + } + def isJavaEnumValue[A: Type]: Boolean = + Type[A] <:< scala.quoted.Type.of[java.lang.Enum[?]] && !TypeRepr.of[A].typeSymbol.isAbstract def isJavaBean[A](implicit A: Type[A]): Boolean = { val sym = TypeRepr.of(using A).typeSymbol val mem = sym.declarations @@ -129,14 +131,11 @@ trait ProductTypesPlatform extends ProductTypes { this: DefinitionsPlatform => ) def parseConstructor[A: Type]: Option[Product.Constructor[A]] = - if isCaseObject[A] then { + if isCaseObject[A] || isCaseVal[A] || isJavaEnumValue[A] then { val A = TypeRepr.of[A] val sym = A.typeSymbol - // TODO: actually a duplication of isScala3Enum from isCaseObject - def isScala3Enum = sym.flags.is(Flags.Case | Flags.Enum | Flags.JavaStatic) - - if isScala3Enum || isJavaEnumValue[A] then Some(Product.Constructor(ListMap.empty, _ => Ref(sym).asExprOf[A])) + if isCaseVal[A] || isJavaEnumValue[A] then Some(Product.Constructor(ListMap.empty, _ => Ref(sym).asExprOf[A])) else Some(Product.Constructor(ListMap.empty, _ => Ref(sym.companionModule).asExprOf[A])) } else if isPOJO[A] then { val A = TypeRepr.of[A] diff --git a/chimney-macro-commons/src/main/scala/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypes.scala b/chimney-macro-commons/src/main/scala/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypes.scala index d09469e61..e9284c635 100644 --- a/chimney-macro-commons/src/main/scala/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypes.scala +++ b/chimney-macro-commons/src/main/scala/io/scalaland/chimney/internal/compiletime/datatypes/ProductTypes.scala @@ -86,9 +86,22 @@ trait ProductTypes { this: Definitions => protected val ProductType: ProductTypesModule protected trait ProductTypesModule { this: ProductType.type => + /** Any class with a public constructor... explicitly excluding: primitives, String and Java enums */ def isPOJO[A](implicit A: Type[A]): Boolean + + /** Class defined with "case class" */ def isCaseClass[A](implicit A: Type[A]): Boolean + + /** Class defined with "case object" */ def isCaseObject[A](implicit A: Type[A]): Boolean + + /** Scala 3 enum's case without parameters (a "val" under the hood, NOT an "object") */ + def isCaseVal[A](implicit A: Type[A]): Boolean + + /** Java enum value - not the abstract enum type, but the concrete enum value */ + def isJavaEnumValue[A](implicit A: Type[A]): Boolean + + /** Any POJO with a public DEFAULT constructor... and at least 1 setter or var */ def isJavaBean[A](implicit A: Type[A]): Boolean def parseExtraction[A: Type]: Option[Product.Extraction[A]]