-
-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathAliasedPropertyVariable.swift
More file actions
79 lines (76 loc) · 3.05 KB
/
Copy pathAliasedPropertyVariable.swift
File metadata and controls
79 lines (76 loc) · 3.05 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
import OrderedCollections
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
/// A variable value containing additional `CodingKey`s for decoding.
///
/// The `AliasedPropertyVariable` customizes decoding and initialization
/// by using the additional `CodingKey`s provided during initialization, by
/// checking if only one of the key has data associated.
struct AliasedPropertyVariable<Wrapped>: PropertyVariable, ComposedVariable
where Wrapped: PropertyVariable {
/// The value wrapped by this instance.
///
/// The wrapped variable's type data is
/// preserved and provided during initialization.
let base: Wrapped
/// Additional possible keys for this variable.
///
/// Represents all the additional `CodingKey`s that
/// this variable could be encoded at.
let additionalKeys: OrderedSet<CodingKeysMap.Key>
/// Provides the code syntax for decoding this variable
/// at the provided location.
///
/// Checks if variable data is present at the primary key or any of the
/// additional keys and decodes data. If none of the keys found or multiple
/// data found at the keys error is thrown.
///
/// - 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 {
guard
!additionalKeys.isEmpty,
case let .container(container, key, method) = location
else { return base.decoding(in: context, from: location) }
var allKeys = [key]
let additionalKeys = additionalKeys.filter { aKey in
return aKey.expr.trimmedDescription != key.trimmedDescription
}
allKeys.append(contentsOf: additionalKeys.map(\.expr))
let keysName: ExprSyntax = "\(CodingKeysMap.Key.name(for: name))Keys"
let keyList = ArrayExprSyntax(expressions: allKeys)
return CodeBlockItemListSyntax {
"""
let \(keysName) = \(keyList).filter { \(container).allKeys.contains($0) }
"""
"""
guard \(keysName).count == 1 else {
let context = DecodingError.Context(
codingPath: \(container).codingPath,
debugDescription: "Invalid number of keys found, expected one."
)
throw DecodingError.typeMismatch(Self.self, context)
}
"""
base.decoding(
in: context,
from: .container(
container, key: "\(keysName)[0]", method: method)
)
}
}
}
extension AliasedPropertyVariable: InitializableVariable
where Wrapped: InitializableVariable {
/// The initialization type of this variable.
///
/// Initialization type is the same as underlying wrapped variable.
typealias Initialization = Wrapped.Initialization
}