Skip to content

Commit

Permalink
codec (feature): Support Scala 3 Enum
Browse files Browse the repository at this point in the history
  • Loading branch information
xerial committed Nov 12, 2023
1 parent 188e2e9 commit 110b093
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package wvlet.airframe.codec

import wvlet.airspec.AirSpec

object Scala3EnumCodecTest extends AirSpec:
enum Color:
case Red, Green, Blue

test("pack Scala 3 Enum") {
val codec = MessageCodec.of[Color]
Color.values.foreach { c =>
val packed = codec.pack(c)
debug(packed)
val unpacked = codec.unpack(packed)
unpacked shouldBe c
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ trait Surface extends Serializable {
def isMap: Boolean = classOf[Map[_, _]].isAssignableFrom(rawType)
def isArray: Boolean = this.isInstanceOf[ArraySurface]

/**
* True if this surface is a Scala3 enum. In Scala 2, it always returns false
*/
def isEnum: Boolean = false

def objectFactory: Option[ObjectFactory] = None
def withOuter(outer: AnyRef): Surface = this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,44 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) {
'{ new GenericSurface(${ clsOf(a1) }, typeArgs = ${ Expr.ofSeq(typeArgs) }.toIndexedSeq) }
case r: Refinement =>
newGenericSurfaceOf(r.info)
case t if t <:< TypeRepr.of[scala.reflect.Enum] =>
/**
* Build a code for finding Enum instance from an input string value: {{ (cl: Class[_], s: String) =>
* Try(EnumType.valueOf(s)).toOption }}
*/
val enumType = t.typeSymbol.companionModule
val valueOfMethod = enumType.methodMember("valueOf").headOption match {
case Some(m) => m
case None =>
sys.error(s"valueOf method not found in ${t}")
}
val newFn = Lambda(
owner = Symbol.spliceOwner,
tpe = MethodType(List("cl", "s"))(
_ => List(TypeRepr.of[Class[_]], TypeRepr.of[String]),
_ => TypeRepr.of[Option[Any]]
),
rhsFn = (sym: Symbol, paramRefs: List[Tree]) => {
val strVarRef = paramRefs(1).asExprOf[String].asTerm
val expr: Term =
Select
.unique(
Apply(Select.unique(Ref(t.typeSymbol.companionModule), "valueOf"), List(strVarRef)),
"asInstanceOf"
).appliedToType(TypeRepr.of[Any])
val expr2 = ('{
scala.util.Try(${ expr.asExprOf[Any] }).toOption
}).asExprOf[Option[Any]].asTerm
expr2.changeOwner(sym)
}
)

'{
EnumSurface(
${ clsOf(t) },
${ newFn.asExprOf[(Class[_], String) => Option[Any]] }
)
}
case t if hasStringUnapply(t) =>
// Build EnumSurface.apply code
// EnumSurface(classOf[t], { (cl: Class[_], s: String) => (companion object).unapply(s).asInstanceOf[Option[Any]] }
Expand All @@ -447,7 +485,6 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) {
val strVarRef = paramRefs(1).asExprOf[String].asTerm
val expr = Select.unique(Apply(m, List(strVarRef)), "asInstanceOf").appliedToType(TypeRepr.of[Option[Any]])
expr.changeOwner(sym)

}
)
'{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@ case class OptionSurface(override val rawType: Class[_], elementSurface: Surface
}
case class JavaEnumSurface(override val rawType: Class[_]) extends GenericSurface(rawType)

/**
* Enum-like surface for Scala 2.x and Scala 3
* @param rawType
* @param stringExtractor
*/
case class EnumSurface(override val rawType: Class[_], stringExtractor: (Class[_], String) => Option[Any])
extends GenericSurface(rawType)

Expand Down

0 comments on commit 110b093

Please sign in to comment.