-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathXmlWriterMacro.scala
More file actions
117 lines (101 loc) · 3.79 KB
/
XmlWriterMacro.scala
File metadata and controls
117 lines (101 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package org.encalmo.writer.xml
import org.encalmo.utils.AnnotationUtils.*
import org.encalmo.utils.StatementsCache
import org.encalmo.utils.StatementsCache.*
import org.encalmo.utils.TagName
import org.encalmo.utils.TypeTreeIterator
import org.encalmo.writer.xml.XmlOutputBuilder
import scala.quoted.*
/** Macro parts of the XmlWriter toolkit. */
object XmlWriterMacro {
// Set true to be able to debug the macro expansion
// and see the the path taken by the algorithm together with the
// annotations and the type information, and generated code.
// This will show up in the console when the macro is expanded.
transparent inline def shouldDebugMacroExpansion = false
inline def write[A](inline tagName: String, expr: A)(using
builder: XmlOutputBuilder
): Unit =
${ writeImpl[A]('{ tagName }, '{ expr }, '{ builder }, true) }
inline def write[A](expr: A)(using
builder: XmlOutputBuilder
): Unit =
${ writeImpl[A]('{ expr }, '{ builder }) }
def writeImpl[A: Type](
expr: Expr[A],
builder: Expr[XmlOutputBuilder]
)(using
Quotes
): Expr[Unit] = {
given cache: StatementsCache = new StatementsCache
given cache.quotes.type = cache.quotes
import cache.quotes.reflect.*
writeUsingTypeTreeIterator[A](
tagNameCandidate = None,
expr = expr,
builderExpr = builder,
summonTypeclassInstance = true
)
cache.asTerm.asExprOf[Unit]
}
def writeImpl[A: Type](
tagName: Expr[String],
expr: Expr[A],
builder: Expr[XmlOutputBuilder],
summonTypeclassInstance: Boolean
)(using quotes: Quotes): Expr[Unit] = {
given cache: StatementsCache = new StatementsCache
given cache.quotes.type = cache.quotes
import cache.quotes.reflect.*
val tagNameCandidate = Some(tagName.value.map(TagName(_)).getOrElse(TagName(tagName.asTerm)))
writeUsingTypeTreeIterator[A](tagNameCandidate, expr, builder, summonTypeclassInstance)
cache.asTerm.asExprOf[Unit]
}
/** Entry method to write the value of any type to the XML output using TypeTreeIterator and StatementsCache. */
def writeUsingTypeTreeIterator[A: Type](
tagNameCandidate: Option[TagName],
expr: Expr[A],
builderExpr: Expr[XmlOutputBuilder],
summonTypeclassInstance: Boolean
)(using cache: StatementsCache): Unit = {
given cache.quotes.type = cache.quotes
import cache.quotes.reflect.*
val builder = new XmlWriterMacroVisitor.Builder
builder.initialize(builderExpr)
val valueTerm = expr.asTerm match {
case Inlined(_, _, t) => t
case t => t
}
val trace = scala.collection.mutable.Buffer.empty[String]
val annotations = getValueAnnotations(valueTerm)
val tpe = TypeRepr.of[A]
if tpe.typeSymbol.isTypeParam then
report.errorAndAbort(
s"""${tpe.show} is an abstract type parameter and cannot be serialized to XML.
Possible solutions:
- Add inline keyword to the method definition.
- Add (using XmlWriter[${tpe.show}]) to the method definition
- Define a given XmlWriter[${tpe.show}] in the current scope
"""
)
else {
TypeTreeIterator.visitNode(using cache, XmlWriterMacroVisitor)(
tpe = TypeRepr.of[A],
valueTerm = valueTerm,
context = XmlWriterMacroContext(tagNameCandidate = tagNameCandidate, builder = builder, hasTag = false),
isCollectionItem = false,
annotations = annotations,
trace = trace,
debugIndent = if shouldDebugMacroExpansion then 0 else Int.MinValue,
summonTypeclassInstance = summonTypeclassInstance
)
if shouldDebugMacroExpansion then {
report.warning(
trace.mkString("\n")
+ "\n\n--------------------------------\n\n"
+ cache.asTerm.show(using Printer.TreeCode)
)
}
}
}
}