-
-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathDefaultValueVariable.swift
More file actions
142 lines (134 loc) · 5.35 KB
/
DefaultValueVariable.swift
File metadata and controls
142 lines (134 loc) · 5.35 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
/// A variable value containing default expression for decoding failure.
///
/// The `DefaultValueVariable` customizes decoding and initialization
/// by using the default expression provided during initialization:
/// * For initializing variable in case of decoding failure.
/// * For providing default value to variable in memberwise initializer(s).
struct DefaultValueVariable<Wrapped>: ComposedVariable, PropertyVariable
where
Wrapped: PropertyVariable, Wrapped.Initialization == RequiredInitialization
{
/// The customization options for `DefaultValueVariable`.
///
/// `DefaultValueVariable` uses the instance of this type,
/// provided during initialization, for customizing code generation.
struct Options {
/// The default expression used when decoding fails for missing value.
///
/// This expression is provided during initialization and used to
/// generate fallback decoding syntax.
let onMissingExpr: ExprSyntax
/// The default expression used when decoding fails for errors other
/// than missing value.
///
/// This expression is provided during initialization and used to
/// generate fallback decoding syntax.
let onErrorExpr: ExprSyntax?
}
/// The value wrapped by this instance.
///
/// The wrapped variable's type data is
/// preserved and provided during initialization.
let base: Wrapped
/// The options for customizations.
///
/// Options is provided during initialization.
let options: Options
/// Whether the variable is to
/// be decoded.
///
/// Always `true` for this type.
var decode: Bool? { true }
/// Whether the variable is to
/// be encoded.
///
/// Always `true` for this type.
var encode: Bool? { true }
/// Whether the variable type requires `Decodable` conformance.
///
/// Provides whether underlying variable type requires
/// `Decodable` conformance.
var requireDecodable: Bool? { base.requireDecodable }
/// Whether the variable type requires `Encodable` conformance.
///
/// Provides whether underlying variable type requires
/// `Encodable` conformance.
var requireEncodable: Bool? { base.requireEncodable }
/// The fallback behavior when decoding fails.
///
/// In the event this decoding this variable is failed,
/// appropriate fallback would be applied.
///
/// This variable will be initialized with default expression(s)
/// provided, if decoding fails.
var decodingFallback: DecodingFallback {
let expr: ExprSyntax = "\(decodePrefix)\(name)"
let fallback: CodeBlockItemSyntax = "\(expr) = \(options.onMissingExpr)"
guard
let onErrorExpr = options.onErrorExpr
else { return .onlyIfMissing([fallback]) }
return .ifMissing([fallback], ifError: "\(expr) = \(onErrorExpr)")
}
/// Provides the code syntax for decoding this variable
/// at the provided location.
///
/// Providing missing case expression for value missing case.
/// If other errors expression provided, wraps code syntax for
/// decoding of the underlying variable value in `do` block and
/// initializes with default expression in the `catch` block.
///
/// - Parameters:
/// - context: The context in which to perform the macro expansion.
/// - location: The decoding location for the variable.
///
/// - Returns: The generated variable decoding code.
func decoding(
in context: some MacroExpansionContext,
from location: PropertyCodingLocation
) -> CodeBlockItemListSyntax {
let method: ExprSyntax = "decodeIfPresent"
let newLocation: PropertyCodingLocation =
switch location {
case .coder(let decoder, _):
.coder(decoder, method: method)
case .container(let container, let key, _):
.container(container, key: key, method: method)
}
let doClauses = base.decoding(in: context, from: newLocation)
guard !doClauses.isEmpty else { return "" }
let mSyntax = CodeBlockItemListSyntax {
for clause in doClauses.dropLast() {
clause
}
"\(doClauses.last!) ?? \(options.onMissingExpr)"
}
guard let onErrorExpr = options.onErrorExpr else { return mSyntax }
let catchClauses = CatchClauseListSyntax {
CatchClauseSyntax { "\(decodePrefix)\(name) = \(onErrorExpr)" }
}
return CodeBlockItemListSyntax {
DoStmtSyntax(catchClauses: catchClauses) {
mSyntax
}
}
}
/// Indicates the initialization type for this variable.
///
/// Provides default initialization value in initialization
/// function parameter.
///
/// - Parameter context: The context in which to perform
/// the macro expansion.
/// - Returns: The type of initialization for variable.
func initializing(
in context: some MacroExpansionContext
) -> RequiredInitializationWithDefaultValue {
let initialization = base.initializing(in: context)
return .init(base: initialization, expr: options.onMissingExpr)
}
}
extension DefaultValueVariable: AssociatedVariable
where Wrapped: AssociatedVariable {}