Skip to content

Commit 040d707

Browse files
committed
Add the implementation body for observation macros
1 parent 19e9f36 commit 040d707

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import SwiftSyntax
2+
import SwiftSyntaxMacros
3+
4+
private extension DeclSyntaxProtocol {
5+
var isObservableStoredProperty: Bool {
6+
guard let property = self.as(VariableDeclSyntax.self),
7+
let binding = property.bindings.first
8+
else {
9+
return false
10+
}
11+
12+
return binding.accessor == nil
13+
}
14+
}
15+
16+
public struct ObservableMacro: MemberMacro, MemberAttributeMacro {
17+
18+
// MARK: - MemberMacro
19+
public static func expansion(
20+
of node: AttributeSyntax,
21+
providingMembersOf declaration: some DeclGroupSyntax,
22+
in context: some MacroExpansionContext
23+
) throws -> [DeclSyntax] {
24+
guard let identified = declaration.asProtocol(IdentifiedDeclSyntax.self) else {
25+
return []
26+
}
27+
28+
let parentName = identified.identifier
29+
30+
let registrar: DeclSyntax =
31+
"""
32+
let _registrar = ObservationRegistrar<\(parentName)>()
33+
"""
34+
35+
let transactions: DeclSyntax =
36+
"""
37+
public nonisolated func transactions<Delivery>(for keyPaths: KeyPaths<\(parentName)>, isolation: Delivery) -> ObservedTransactions<\(parentName), Delivery> where Delivery: Actor {
38+
_registrar.transactions(for: keyPaths, isolation: isolation)
39+
}
40+
"""
41+
42+
let changes: DeclSyntax =
43+
"""
44+
public nonisolated func changes<Member>(for keyPath: KeyPath<\(parentName), Member>) -> ObservedChanges<\(parentName), Member> where Member: Sendable {
45+
_registrar.changes(for: keyPath)
46+
}
47+
"""
48+
49+
let memberList = MemberDeclListSyntax(
50+
declaration.members.members.filter {
51+
$0.decl.isObservableStoredProperty
52+
}
53+
)
54+
55+
let storageStruct: DeclSyntax =
56+
"""
57+
private struct _Storage {
58+
\(memberList)
59+
}
60+
"""
61+
62+
let storage: DeclSyntax =
63+
"""
64+
private var _storage = _Storage()
65+
"""
66+
67+
return [
68+
registrar,
69+
transactions,
70+
changes,
71+
storageStruct,
72+
storage,
73+
]
74+
}
75+
76+
// MARK: - MemberAttributeMacro
77+
public static func expansion(
78+
of node: AttributeSyntax,
79+
attachedTo declaration: some DeclGroupSyntax,
80+
providingAttributesFor member: DeclSyntax,
81+
in context: some MacroExpansionContext
82+
) throws -> [SwiftSyntax.AttributeSyntax] {
83+
guard member.isObservableStoredProperty else {
84+
return []
85+
}
86+
87+
return [
88+
AttributeSyntax(
89+
attributeName: SimpleTypeIdentifierSyntax(
90+
name: .identifier("ObservableProperty")
91+
)
92+
)
93+
]
94+
}
95+
}
96+
97+
public struct ObservablePropertyMacro: AccessorMacro {
98+
public static func expansion(
99+
of node: AttributeSyntax,
100+
providingAccessorsOf declaration: some DeclSyntaxProtocol,
101+
in context: some MacroExpansionContext
102+
) throws -> [AccessorDeclSyntax] {
103+
guard let property = declaration.as(VariableDeclSyntax.self),
104+
let binding = property.bindings.first,
105+
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier,
106+
binding.accessor == nil
107+
else {
108+
return []
109+
}
110+
111+
if identifier.text == "_registrar" || identifier.text == "_storage" { return [] }
112+
113+
let getAccessor: AccessorDeclSyntax =
114+
"""
115+
get {
116+
_registrar.access(self, keyPath: \\.\(identifier))
117+
return _storage.\(identifier)
118+
}
119+
"""
120+
121+
let setAccessor: AccessorDeclSyntax =
122+
"""
123+
set {
124+
_registrar.withMutation(self, keyPath: \\.\(identifier)) {
125+
_storage.\(identifier) = newValue
126+
}
127+
}
128+
"""
129+
130+
return [getAccessor, setAccessor]
131+
}
132+
}

0 commit comments

Comments
 (0)