-
-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathCodedAt.swift
More file actions
128 lines (122 loc) · 5.31 KB
/
Copy pathCodedAt.swift
File metadata and controls
128 lines (122 loc) · 5.31 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
import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxMacros
/// Attribute type for `CodedAt` macro-attribute.
///
/// This type can validate`CodedAt` macro-attribute
/// usage and extract data for `Codable` macro to
/// generate implementation.
package struct CodedAt: PropertyAttribute {
/// The node syntax provided
/// during initialization.
let node: AttributeSyntax
/// Creates a new instance with the provided node.
///
/// The initializer fails to create new instance if the name
/// of the provided node is different than this attribute.
///
/// - Parameter node: The attribute syntax to create with.
/// - Returns: Newly created attribute instance.
init?(from node: AttributeSyntax) {
guard
node.attributeName.as(IdentifierTypeSyntax.self)!
.name.text == Self.name
else { return nil }
self.node = node
}
/// Builds diagnoser that can validate this macro
/// attached declaration.
///
/// The following conditions are checked by the
/// built diagnoser:
/// * Macro usage is not duplicated for the same declaration.
/// * If macro is attached to enum/protocol declaration:
/// * This attribute must be combined with `Codable`
/// attribute.
/// * This attribute isn't used combined with `UnTagged`
/// attribute.
/// * else:
/// * Attached declaration is a variable declaration.
/// * Attached declaration is not a grouped variable
/// declaration.
/// * Attached declaration is not a static variable
/// declaration.
/// * This attribute isn't used combined with `CodedIn`
/// and `IgnoreCoding` attribute.
///
/// - Returns: The built diagnoser instance.
func diagnoser() -> DiagnosticProducer {
return AggregatedDiagnosticProducer {
cantDuplicate()
`if`(
isEnum || isProtocol,
AggregatedDiagnosticProducer {
mustBeCombined(with: Codable.self)
cantBeCombined(with: UnTagged.self)
cantBeCombined(with: DecodedAt.self)
cantBeCombined(with: EncodedAt.self)
},
else: AggregatedDiagnosticProducer {
attachedToUngroupedVariable()
attachedToNonStaticVariable()
cantBeCombined(with: DecodedAt.self)
cantBeCombined(with: EncodedAt.self)
cantBeCombined(with: CodedIn.self)
cantBeCombined(with: IgnoreCoding.self)
}
)
}
}
}
extension Registration
where Var == ExternallyTaggedEnumSwitcher, Decl == EnumDeclSyntax {
/// Checks if enum declares internal tagging.
///
/// Checks if identifier path provided with `CodedAt` macro,
/// identifier type is used if `CodedAs` macro provided falling back to
/// the `fallbackType` passed.
///
/// - Parameters:
/// - encodeContainer: The container for case variation encoding.
/// - identifier: The identifier name to use.
/// - fallbackType: The fallback identifier type to use if not provided.
/// - codingKeys: The map where `CodingKeys` maintained.
/// - context: The context in which to perform the macro expansion.
/// - variableBuilder: The builder action for building identifier.
/// - switcherBuilder: The further builder action if check succeeds.
///
/// - Returns: Type-erased variable registration applying builders
/// if succeeds, otherwise current variable type-erased registration.
func checkForInternalTagging<Variable, Switcher>(
encodeContainer: TokenSyntax,
identifier: TokenSyntax, fallbackType: TypeSyntax,
codingKeys: CodingKeysMap, context: some MacroExpansionContext,
variableBuilder: @escaping (
PathRegistration<EnumDeclSyntax, BasicPropertyVariable>
) -> PathRegistration<EnumDeclSyntax, Variable>,
switcherBuilder: @escaping (
Registration<Decl, Key, InternallyTaggedEnumSwitcher<Variable>>
) -> Registration<Decl, Key, Switcher>
) -> Registration<Decl, Key, AnyEnumSwitcher>
where Variable: PropertyVariable, Switcher: EnumSwitcherVariable {
let tagAttr = CodedAt(from: decl)
let decodeTagAttr = DecodedAt(from: decl)
let encodeTagAttr = EncodedAt(from: decl)
let path = tagAttr?.keyPath(withExisting: []) ?? []
let decodedPath = decodeTagAttr?.keyPath(withExisting: path) ?? path
let encodedPath = encodeTagAttr?.keyPath(withExisting: path) ?? path
guard
!decodedPath.isEmpty && !encodedPath.isEmpty
else { return self.updating(with: variable.any) }
let typeAttr = CodedAs(from: decl)
let keyPath = PathKey(decoding: decodedPath, encoding: encodedPath)
let variable = InternallyTaggedEnumSwitcher(
encodeContainer: encodeContainer, identifier: identifier,
identifierType: typeAttr?.type ?? fallbackType,
keyPath: keyPath, codingKeys: codingKeys,
decl: decl, context: context, variableBuilder: variableBuilder
)
let newRegistration = switcherBuilder(self.updating(with: variable))
return newRegistration.updating(with: newRegistration.variable.any)
}
}