未验证 提交 2df4ffb2 编写于 作者: 梦境迷离's avatar 梦境迷离 提交者: GitHub

opt (#201)

上级 08a99b34
......@@ -61,7 +61,9 @@ object Cache {
override def getTField(key: String, field: CaseClassField)(implicit
keyBuilder: CacheKeyBuilder[String]
): Future[Option[field.Field]] =
getT(key).map(opt => opt.flatMap(t => CaseClassExtractor.getFieldValueUnSafely[cache.Out](t, field)))
getT(key).map(opt =>
opt.flatMap(t => CaseClassExtractor.ofValue[cache.Out](t, field).asInstanceOf[Option[field.Field]])
)
override def clear(): Future[Unit] = cache.clear()
}
......@@ -91,7 +93,7 @@ object Cache {
override def getTField(key: String, field: CaseClassField)(implicit
keyBuilder: CacheKeyBuilder[String]
): Identity[Option[field.Field]] =
getT(key).flatMap(t => CaseClassExtractor.getFieldValueUnSafely[cache.Out](t, field))
getT(key).flatMap(t => CaseClassExtractor.ofValue[cache.Out](t, field).asInstanceOf[Option[field.Field]])
override def clear(): Identity[Unit] = cache.clear()
}
......
......@@ -35,42 +35,25 @@ import scala.util.{ Failure, Success }
*/
object CaseClassExtractor {
def getFieldValueUnSafely[T: ru.TypeTag](obj: T, field: CaseClassField)(implicit
classTag: ClassTag[T]
): Option[field.Field] = {
val mirror = ru.runtimeMirror(getClass.getClassLoader)
val fieldOption = scala.util.Try(
getMethods[T]
.filter(_.name.toTermName.decodedName.toString == field.stringify)
.map(m => mirror.reflect(obj).reflectField(m).get)
.headOption
.map(_.asInstanceOf[field.Field])
)
fieldOption match {
case Success(value) => value
case Failure(exception) => exception.printStackTrace(); None
}
}
def getMethods[T: ru.TypeTag]: List[ru.MethodSymbol] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
def getFieldValueSafely[T <: Product, Field](t: T, name: String): Option[Field] = macro macroImpl[T, Field]
/** Using the characteristics of the product type to get the field value should force the conversion externally
* (safely).
*/
def ofValue[T <: Product](t: T, field: CaseClassField): Option[Any] = macro macroImpl[T]
def macroImpl[T: c.WeakTypeTag, Field: c.WeakTypeTag](
def macroImpl[T: c.WeakTypeTag](
c: whitebox.Context
)(t: c.Expr[T], name: c.Expr[String]): c.Expr[Option[Field]] = {
)(t: c.Expr[T], field: c.Expr[CaseClassField]): c.Expr[Option[Any]] = {
import c.universe._
val typ = TypeName(c.weakTypeOf[Field].typeSymbol.name.decodedName.toString)
// scalafmt: { maxColumn = 400 }
val tree =
q"""
if ($t == null) None else {
val idx = $t.productElementNames.indexOf($name)
Option($t.productElement(idx).asInstanceOf[$typ])
val _field = $field
_field.${TermName(CaseClassField.fieldNamesTermName)}.find(kv => kv._2 == _field.${TermName(CaseClassField.stringifyTermName)})
.map(kv => $t.productElement(kv._1))
}
"""
exprPrintTree[Option[Field]](c)(tree)
exprPrintTree[Option[Any]](c)(tree)
}
......@@ -83,4 +66,28 @@ object CaseClassExtractor {
)
c.Expr[Field](resTree)
}
/** Using scala reflect to get the field value (safely).
*/
@deprecated
def reflectValue[T: ru.TypeTag](obj: T, field: CaseClassField)(implicit
classTag: ClassTag[T]
): Option[field.Field] = {
val mirror = ru.runtimeMirror(getClass.getClassLoader)
val fieldOption = scala.util.Try(
getMethods[T]
.filter(_.name.toTermName.decodedName.toString == field.stringify)
.map(m => mirror.reflect(obj).reflectField(m).get)
.headOption
.map(_.asInstanceOf[field.Field])
)
fieldOption match {
case Success(value) => value
case Failure(exception) => exception.printStackTrace(); None
}
}
def getMethods[T: ru.TypeTag]: List[ru.MethodSymbol] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
}
......@@ -31,13 +31,18 @@ trait CaseClassField {
def stringify: String
type Field
/** product index -> name, scala2.11 2.12 not `productElementNames`
*/
val fieldIndexNames: Map[Int, String]
}
object CaseClassField {
final val classNameTermName = "CaseClassField"
final val stringifyTermName = "stringify"
final val fieldTermName = "Field"
final val classNameTermName = "CaseClassField"
final val stringifyTermName = "stringify"
final val fieldTermName = "Field"
final val fieldNamesTermName = "fieldIndexNames"
def apply[T <: Product](field: T => Any): CaseClassField = macro selectFieldMacroImpl[T]
......@@ -74,17 +79,20 @@ object CaseClassField {
}
val fieldNameTypeName = TermName(s"${CaseClassField.classNameTermName}$$$fieldName")
val res = q"""
val res =
q"""
case object $fieldNameTypeName extends $packageName.${TypeName(CaseClassField.classNameTermName)} {
override def ${TermName(CaseClassField.stringifyTermName)}: String = $fieldName
override type ${TypeName(CaseClassField.fieldTermName)} = $genericType
override val ${TermName(CaseClassField.fieldNamesTermName)} =
(${caseClassParams.indices.toList} zip ${caseClassParams.map(_.name.decodedName.toString)}).toMap
}
$fieldNameTypeName
"""
exprPrintTree[CaseClassField](c)(res)
}
private def getCaseClassParams[T: c.WeakTypeTag](c: whitebox.Context): List[c.Symbol] = {
def getCaseClassParams[T: c.WeakTypeTag](c: whitebox.Context): List[c.Symbol] = {
import c.universe._
val parameters = c.weakTypeOf[T].resultType.member(TermName("<init>")).typeSignature.paramLists
if (parameters.size > 1) {
......
......@@ -31,10 +31,9 @@ import org.scalatest.matchers.should.Matchers
class CaseClassExtractorTest extends AnyFlatSpec with Matchers {
"CaseClassExtractorTest1" should "safe" in {
val obj = TestEntity("name", "id", "key", Some(1))
val keyField = CaseClassField[TestEntity](_.key)
val key =
CaseClassExtractor.getFieldValueSafely[TestEntity, keyField.Field](obj, keyField.stringify)
val obj = TestEntity("name", "id", "key", Some(1))
val key = CaseClassExtractor.ofValue(obj, CaseClassField[TestEntity](_.key))
println(key)
assert(key == Option("key"))
}
......@@ -47,9 +46,8 @@ class CaseClassExtractorTest extends AnyFlatSpec with Matchers {
}
"CaseClassExtractorTest3" should "unsafe" in {
val obj = TestEntity("name", "id", "key", Some(1))
val key: Option[CaseClassField#Field] =
CaseClassExtractor.getFieldValueUnSafely(obj, CaseClassField[TestEntity](_.key))
val obj = TestEntity("name", "id", "key", Some(1))
val key: Option[CaseClassField#Field] = CaseClassExtractor.reflectValue(obj, CaseClassField[TestEntity](_.key))
assert(key == Option("key"))
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册