-
-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathDefaultValueVariable.swift
More file actions
127 lines (119 loc) · 4.66 KB
/
DefaultValueVariable.swift
File metadata and controls
127 lines (119 loc) · 4.66 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
@_implementationOnly import SwiftSyntax
@_implementationOnly import SwiftSyntaxBuilder
@_implementationOnly 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: RequiredVariableInitialization
{
/// 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.
///
/// This expression is provided during initialization
/// and used to generate non-failable decoding syntax
/// by using this when decoding fails.
let expr: 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
/// provided, if decoding fails.
var decodingFallback: DecodingFallback {
return .ifError("\(decodePrefix)\(name) = \(options.expr)")
}
/// Provides the code syntax for decoding this variable
/// at the provided location.
///
/// 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 catchClauses = CatchClauseListSyntax {
CatchClauseSyntax { "\(decodePrefix)\(name) = \(options.expr)" }
}
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 "" }
return CodeBlockItemListSyntax {
DoStmtSyntax(catchClauses: catchClauses) {
for clause in doClauses.dropLast() {
clause
}
"\(doClauses.last!) ?? \(options.expr)"
}
}
}
/// 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.expr)
}
}
extension DefaultValueVariable: AssociatedVariable
where Wrapped: AssociatedVariable {}