Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
梦境迷离
Scala Macro Tools
提交
64e48bfe
S
Scala Macro Tools
项目概览
梦境迷离
/
Scala Macro Tools
上一次同步 1 年多
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
Scala Macro Tools
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
64e48bfe
编写于
6月 23, 2022
作者:
梦境迷离
提交者:
GitHub
6月 23, 2022
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
support default value (#208)
* support default value
上级
aae4c5c2
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
123 addition
and
32 deletion
+123
-32
smt-common/src/main/scala/org/bitlap/common/MacroCache.scala
smt-common/src/main/scala/org/bitlap/common/MacroCache.scala
+2
-0
smt-common/src/main/scala/org/bitlap/common/Transformable.scala
...mmon/src/main/scala/org/bitlap/common/Transformable.scala
+8
-0
smt-common/src/main/scala/org/bitlap/common/TransformerMacro.scala
...n/src/main/scala/org/bitlap/common/TransformerMacro.scala
+84
-32
smt-common/src/test/scala/org/bitlap/common/TransformableTest.scala
.../src/test/scala/org/bitlap/common/TransformableTest.scala
+29
-0
未找到文件。
smt-common/src/main/scala/org/bitlap/common/MacroCache.scala
浏览文件 @
64e48bfe
...
...
@@ -44,4 +44,6 @@ object MacroCache {
lazy
val
classFieldNameMapping
:
mutable.Map
[
Int
,
mutable.Map
[
String
,
String
]]
=
mutable
.
Map
.
empty
lazy
val
classFieldTypeMapping
:
mutable.Map
[
Int
,
mutable.Map
[
String
,
Any
]]
=
mutable
.
Map
.
empty
lazy
val
classFieldDefaultValueMapping
:
mutable.Map
[
Int
,
mutable.Map
[
String
,
Any
]]
=
mutable
.
Map
.
empty
}
smt-common/src/main/scala/org/bitlap/common/Transformable.scala
浏览文件 @
64e48bfe
...
...
@@ -66,6 +66,14 @@ class Transformable[From, To] {
)
:
Transformable
[
From
,
To
]
=
macro
TransformerMacro
.
mapNameImpl
[
From
,
To
,
FromField
,
ToField
]
/** Defines default value for missing field to successfully create `To` object. This method has the lowest priority.
*
* Only the `selectToField` field does not have the same name found in the `From` and is not in the name mapping.
*/
@unchecked
def
setDefaultValue
[
ToField
](
selectToField
:
To
=>
ToField
,
defaultValue
:
ToField
)
:
Transformable
[
From
,
To
]
=
macro
TransformerMacro
.
setDefaultValueImpl
[
From
,
To
,
ToField
]
def
instance
:
Transformer
[
From
,
To
]
=
macro
TransformerMacro
.
instanceImpl
[
From
,
To
]
}
...
...
smt-common/src/main/scala/org/bitlap/common/TransformerMacro.scala
浏览文件 @
64e48bfe
...
...
@@ -33,10 +33,13 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
import
c.universe._
protected
val
packageName
=
q
"_root_.org.bitlap.common"
private
val
builderFunctionPrefix
=
"_TransformableFunction$"
private
val
annoBuilderPrefix
=
"_AnonObjectTransformable$"
private
val
fromTermName
=
TermName
(
"from"
)
import
scala.collection.immutable
protected
val
packageName
=
q
"_root_.org.bitlap.common"
private
val
builderFunctionPrefix
=
"_TransformableFunction$"
private
val
builderDefaultValuePrefix$
=
"_TransformableDefaultValue$"
private
val
annoBuilderPrefix
=
"_AnonObjectTransformable$"
private
val
fromTermName
=
TermName
(
"from"
)
def
mapTypeImpl
[
From
,
To
,
FromField
,
ToField
](
selectFromField
:
Expr
[
From
=>
FromField
],
...
...
@@ -51,6 +54,19 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
exprPrintTree
[
Transformable
[
From
,
To
]](
force
=
false
,
tree
)
}
def
setDefaultValueImpl
[
From
,
To
,
ToField
](
selectToField
:
Expr
[
To
=>
ToField
],
defaultValue
:
Expr
[
ToField
]
)
:
Expr
[
Transformable
[
From
,
To
]]
=
{
val
Function
(
_
,
Select
(
_
,
toName
))
=
selectToField
.
tree
val
builderId
=
getBuilderId
(
annoBuilderPrefix
)
MacroCache
.
classFieldDefaultValueMapping
.
getOrElseUpdate
(
builderId
,
mutable
.
Map
.
empty
)
.
update
(
toName
.
decodedName
.
toString
,
defaultValue
)
val
tree
=
q
"new ${c.prefix.actualType}"
exprPrintTree
[
Transformable
[
From
,
To
]](
force
=
false
,
tree
)
}
def
mapNameImpl
[
From
,
To
,
FromField
,
ToField
](
selectFromField
:
Expr
[
From
=>
FromField
],
selectToField
:
Expr
[
To
=>
ToField
]
...
...
@@ -98,14 +114,27 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
}
private
def
getPreTree
:
Iterable
[
Tree
]
=
{
val
customTrees
=
MacroCache
.
classFieldTypeMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
val
(
_
,
preTrees
)
=
customTrees
.
collect
{
case
(
key
,
expr
:
Expr
[
Tree
]
@unchecked
)
=>
val
customFunctionTrees
=
buildPreTrees
(
MacroCache
.
classFieldTypeMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
)
val
customDefaultValueTrees
=
buildPreTrees
(
MacroCache
.
classFieldDefaultValueMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
)
customFunctionTrees
++
customDefaultValueTrees
}
private
def
buildPreTrees
(
mapping
:
mutable.Map
[
String
,
Any
])
:
Iterable
[
Tree
]
=
{
val
(
_
,
preTrees
)
=
mapping
.
collect
{
case
(
key
,
expr
:
Expr
[
Tree
]
@unchecked
)
=>
val
wrapName
=
(
prefix
:
String
)
=>
TermName
(
prefix
+
key
)
expr
.
tree
match
{
case
buildFunction
:
Function
=>
val
functionName
=
TermName
(
builderFunctionPrefix
+
key
)
key
->
q
"lazy val $functionName: ${buildFunction.tpe} = $buildFunction"
case
function
:
Function
=>
key
->
q
"lazy val ${wrapName(builderFunctionPrefix)}: ${function.tpe} = $function"
case
tree
:
Tree
=>
key
->
q
"lazy val ${wrapName(builderDefaultValuePrefix$)} = $tree"
}
}.
unzip
preTrees
}
...
...
@@ -114,32 +143,47 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
val
fromClassName
=
resolveClassTypeName
[
From
]
val
toClassInfo
=
getCaseClassFieldInfo
[
To
]()
val
fromClassInfo
=
getCaseClassFieldInfo
[
From
]()
if
(
fromClassInfo
.
size
<
toClassInfo
.
size
)
{
c
.
abort
(
c
.
enclosingPosition
,
s
"From type: `$fromClassName` has fewer fields than To type: `$toClassName` and cannot be transformed"
)
}
val
customDefaultValueMapping
=
MacroCache
.
classFieldDefaultValueMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
val
customFieldNameMapping
=
MacroCache
.
classFieldNameMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
val
customFieldTypeMapping
=
MacroCache
.
classFieldTypeMapping
.
getOrElse
(
getBuilderId
(
annoBuilderPrefix
),
mutable
.
Map
.
empty
)
c
.
info
(
c
.
enclosingPosition
,
s
"Field Name Mapping:$customFieldNameMapping"
,
force
=
true
)
c
.
info
(
c
.
enclosingPosition
,
s
"Field Type Mapping:$customFieldTypeMapping"
,
force
=
true
)
c
.
info
(
c
.
enclosingPosition
,
s
"Field default value mapping: $customDefaultValueMapping"
,
force
=
true
)
c
.
info
(
c
.
enclosingPosition
,
s
"Field name mapping: $customFieldNameMapping"
,
force
=
true
)
c
.
info
(
c
.
enclosingPosition
,
s
"Field type mapping: $customFieldTypeMapping"
,
force
=
true
)
val
missingFields
=
toClassInfo
.
map
(
_
.
fieldName
).
filterNot
(
fromClassInfo
.
map
(
_
.
fieldName
).
contains
)
val
missingExcludeMappingName
=
missingFields
.
filterNot
(
customFieldNameMapping
.
contains
)
if
(
missingExcludeMappingName
.
nonEmpty
)
{
val
noDefaultValueFields
=
missingExcludeMappingName
.
filterNot
(
customDefaultValueMapping
.
keySet
.
contains
)
if
(
noDefaultValueFields
.
nonEmpty
)
{
c
.
abort
(
c
.
enclosingPosition
,
s
"From type: `$fromClassName` has fewer fields than To type: `$toClassName` and cannot be transformed!"
+
s
"\nMissing field mapping: `$fromClassName`.? => `$toClassName`.`${missingExcludeMappingName.mkString("
,
")}`."
+
s
"\nPlease consider using `setName` or `setDefaultValue` method for `$toClassName`.${missingExcludeMappingName
.mkString("
,
")}!"
)
}
}
val
fields
=
toClassInfo
.
map
{
field
=>
val
fromFieldName
=
customFieldNameMapping
.
get
(
field
.
fieldName
)
val
realToFieldName
=
fromFieldName
.
fold
(
field
.
fieldName
)(
x
=>
x
)
// scalafmt: { maxColumn = 400 }
fromFieldName
match
{
case
Some
(
fromName
)
if
customFieldTypeMapping
.
contains
(
fromName
)
=>
q
"""${TermName(builderFunctionPrefix + fromName)}.apply(${q"$fromTermName.${TermName(realToFieldName)}"})"""
q
"""${TermName(
field.fieldName)} = ${TermName(
builderFunctionPrefix + fromName)}.apply(${q"$fromTermName.${TermName(realToFieldName)}"})"""
case
None
if
customFieldTypeMapping
.
contains
(
field
.
fieldName
)
=>
q
"""${TermName(builderFunctionPrefix + field.fieldName)}.apply(${q"$fromTermName.${TermName(realToFieldName)}"})"""
q
"""${TermName(
field.fieldName)} = ${TermName(
builderFunctionPrefix + field.fieldName)}.apply(${q"$fromTermName.${TermName(realToFieldName)}"})"""
case
_
=>
checkFieldGetFieldTerm
[
From
](
realToFieldName
,
fromClassInfo
.
find
(
_
.
fieldName
==
realToFieldName
),
field
field
,
customDefaultValueMapping
)
}
}
...
...
@@ -153,24 +197,30 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
private
def
checkFieldGetFieldTerm
[
From:
WeakTypeTag
](
realFromFieldName
:
String
,
fromFieldOpt
:
Option
[
FieldInformation
],
toField
:
FieldInformation
toField
:
FieldInformation
,
customDefaultValueMapping
:
mutable.Map
[
String
,
Any
]
)
:
Tree
=
{
val
fromFieldTerm
=
q
"$fromTermName.${TermName(realFromFieldName)}"
val
fromClassName
=
resolveClassTypeName
[
From
]
if
(
fromFieldOpt
.
isEmpty
)
{
if
(
fromFieldOpt
.
isEmpty
&&
!
customDefaultValueMapping
.
keySet
.
contains
(
toField
.
fieldName
)
)
{
c
.
abort
(
c
.
enclosingPosition
,
s
"value `$realFromFieldName` is not a member of `$fromClassName`, Please consider using `setName` method!"
s
"The value `$realFromFieldName` is not a member of `$fromClassName`!"
+
s
"\nPlease consider using `setDefaultValue` method!"
)
return
fromFieldTerm
}
val
fromField
=
fromFieldOpt
.
get
if
(!(
fromField
.
fieldType
weak_<:<
toField
.
fieldType
))
{
tryForWrapType
(
fromFieldTerm
,
fromField
,
toField
)
}
else
{
fromFieldTerm
fromFieldOpt
match
{
case
Some
(
fromField
)
if
!(
fromField
.
fieldType
weak_<:<
toField
.
fieldType
)
=>
tryForWrapType
(
fromFieldTerm
,
fromField
,
toField
)
case
Some
(
fromField
)
if
fromField
.
fieldType
weak_<:<
toField
.
fieldType
=>
q
"${TermName(toField.fieldName)} = $fromFieldTerm"
case
_
=>
val
value
=
q
"""${TermName(builderDefaultValuePrefix$ + toField.fieldName)}"""
q
"${TermName(toField.fieldName)} = $value"
}
}
...
...
@@ -186,16 +236,18 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
(
collectionsFlags1
.
isVector
&&
collectionsFlags2
.
isVector
)
||
(
collectionsFlags1
.
isOption
&&
collectionsFlags2
.
isOption
))
&&
genericType1
.
nonEmpty
&&
genericType2
.
nonEmpty
=>
// scalafmt: { maxColumn = 400 }
q
"""
$packageName.Transformer[$fromFieldType, $toFieldType].transform($fromFieldTerm)
$
{TermName(toField.fieldName)} = $
packageName.Transformer[$fromFieldType, $toFieldType].transform($fromFieldTerm)
"""
case
(
information1
,
information2
)
=>
c
.
warning
(
c
.
enclosingPosition
,
s
"No implicit `Transformer` is defined for ${information1.fieldType} => ${information2.fieldType}, which may cause compilation errors!!!"
+
s
"Please consider using `setType` method, or define an `Transformer[${information1.fieldType}, ${information2.fieldType}]` implicit !"
s
"
\n
Please consider using `setType` method, or define an `Transformer[${information1.fieldType}, ${information2.fieldType}]` implicit !"
)
q
"""$packageName.Transformer[${information1.fieldType}, ${information2.fieldType}].transform($fromFieldTerm)"""
// scalafmt: { maxColumn = 400 }
q
"""${TermName(toField.fieldName)} = $packageName.Transformer[${information1.fieldType}, ${information2.fieldType}].transform($fromFieldTerm)"""
}
}
smt-common/src/test/scala/org/bitlap/common/TransformableTest.scala
浏览文件 @
64e48bfe
...
...
@@ -355,4 +355,33 @@ class TransformableTest extends AnyFlatSpec with Matchers {
a
.
transform
[
A2
].
toString
shouldEqual
"A2(hello,1,2,None)"
}
"TransformableTest setDefaultValue"
should
"ok"
in
{
case
class
A1
(
a
:
String
,
b
:
Int
,
cc
:
Long
)
case
class
A2
(
a
:
String
,
b
:
Int
,
c
:
Int
,
d
:
Option
[
String
])
val
a
=
A1
(
"hello"
,
1
,
2
)
implicit
val
b
:
Transformer
[
A1
,
A2
]
=
Transformable
[
A1
,
A2
]
.
setName
(
_
.
cc
,
_
.
c
)
.
setType
[
Long
,
Int
](
_
.
cc
,
fromField
=>
fromField
.
toInt
)
.
setDefaultValue
(
_
.
d
,
None
)
.
instance
a
.
transform
[
A2
].
toString
shouldEqual
"A2(hello,1,2,None)"
}
"TransformableTest not setDefaultValue"
should
"compile failed"
in
{
"""
| case class A1(a: String, b: Int, cc: Long)
| case class A2(a: String, b: Int, c: Int, d: Option[String])
|
| val a = A1("hello", 1, 2)
|
| implicit val b: Transformer[A1, A2] = Transformable[A1, A2]
| .setName(_.cc, _.c)
| .setType[Long, Int](_.cc, fromField => fromField.toInt)
| .instance
|"""
.
stripMargin
shouldNot
compile
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录