Skip to content

Commit 3ed9d6e

Browse files
feature: Adds visitor editing support
1 parent 47267e6 commit 3ed9d6e

File tree

2 files changed

+116
-27
lines changed

2 files changed

+116
-27
lines changed

Sources/GraphQL/Language/AST.swift

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,28 @@ public enum NodeResult {
174174
return .node(array[key])
175175
}
176176
}
177+
178+
func set(value: NodeResult, key: IndexPathElement) -> Self? {
179+
switch self {
180+
case let .node(node):
181+
guard let key = key.keyValue else {
182+
return nil
183+
}
184+
node.set(value: value, key: key)
185+
return .node(node)
186+
case var .array(array):
187+
switch value {
188+
case let .node(value):
189+
guard let key = key.indexValue else {
190+
return nil
191+
}
192+
array[key] = value
193+
return .array(array)
194+
case let .array(value):
195+
return .array(value)
196+
}
197+
}
198+
}
177199
}
178200

179201
/**
@@ -183,15 +205,17 @@ public protocol Node {
183205
var kind: Kind { get }
184206
var loc: Location? { get }
185207
func get(key: String) -> NodeResult?
186-
func set(value: Node?, key: String)
208+
func set(value: NodeResult?, key: String)
187209
}
188210

189211
public extension Node {
190212
func get(key _: String) -> NodeResult? {
191213
return nil
192214
}
193215

194-
func set(value _: Node?, key _: String) {}
216+
func set(value: NodeResult?, key: String) {
217+
print("TODO: Should be implemented on each type!")
218+
}
195219
}
196220

197221
extension Name: Node {}
@@ -252,7 +276,7 @@ extension Name: Equatable {
252276
public final class Document {
253277
public let kind: Kind = .document
254278
public let loc: Location?
255-
public let definitions: [Definition]
279+
public var definitions: [Definition]
256280

257281
init(loc: Location? = nil, definitions: [Definition]) {
258282
self.loc = loc
@@ -270,6 +294,24 @@ public final class Document {
270294
return nil
271295
}
272296
}
297+
298+
public func set(value: NodeResult?, key: String) {
299+
guard let value = value else {
300+
return
301+
}
302+
switch key {
303+
case "definitions":
304+
guard
305+
case let .array(values) = value,
306+
let definitions = values as? [Definition]
307+
else {
308+
return
309+
}
310+
self.definitions = definitions
311+
default:
312+
return
313+
}
314+
}
273315
}
274316

275317
extension Document: Equatable {
@@ -323,10 +365,10 @@ public final class OperationDefinition {
323365
public let kind: Kind = .operationDefinition
324366
public let loc: Location?
325367
public let operation: OperationType
326-
public let name: Name?
327-
public let variableDefinitions: [VariableDefinition]
328-
public let directives: [Directive]
329-
public let selectionSet: SelectionSet
368+
public var name: Name?
369+
public var variableDefinitions: [VariableDefinition]
370+
public var directives: [Directive]
371+
public var selectionSet: SelectionSet
330372

331373
init(
332374
loc: Location? = nil,
@@ -364,6 +406,48 @@ public final class OperationDefinition {
364406
return nil
365407
}
366408
}
409+
410+
public func set(value: NodeResult?, key: String) {
411+
guard let value = value else {
412+
return
413+
}
414+
switch key {
415+
case "name":
416+
guard
417+
case let .node(node) = value,
418+
let name = node as? Name
419+
else {
420+
return
421+
}
422+
self.name = name
423+
case "variableDefinitions":
424+
guard
425+
case let .array(values) = value,
426+
let variableDefinitions = values as? [VariableDefinition]
427+
else {
428+
return
429+
}
430+
self.variableDefinitions = variableDefinitions
431+
case "directives":
432+
guard
433+
case let .array(values) = value,
434+
let directives = values as? [Directive]
435+
else {
436+
return
437+
}
438+
self.directives = directives
439+
case "selectionSet":
440+
guard
441+
case let .node(value) = value,
442+
let selectionSet = value as? SelectionSet
443+
else {
444+
return
445+
}
446+
self.selectionSet = selectionSet
447+
default:
448+
return
449+
}
450+
}
367451
}
368452

369453
extension OperationDefinition: Hashable {

Sources/GraphQL/Language/Visitor.swift

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,22 @@ func visit(root: Node, visitor: Visitor, keyMap: [Kind: [String]] = [:]) -> Node
123123
}
124124
}
125125
} else {
126-
let clone = node
127-
node = clone
128-
for (editKey, editValue) in edits {
129-
if case .node(let node) = node {
130-
node.set(value: editValue, key: editKey.keyValue!)
126+
if case let .node(n) = node {
127+
for (editKey, editValue) in edits {
128+
if let editValue = editValue {
129+
if let key = editKey.keyValue {
130+
n.set(value: .node(editValue), key: key)
131+
}
132+
}
131133
}
134+
node = .node(n)
132135
}
133136
}
137+
138+
// Since Swift cannot mutate node in-place, we must pass the changes up to parent.
139+
if let key = key, let node = node {
140+
parent = parent?.set(value: node, key: key)
141+
}
134142
}
135143

136144
index = stack!.index
@@ -139,15 +147,8 @@ func visit(root: Node, visitor: Visitor, keyMap: [Kind: [String]] = [:]) -> Node
139147
inArray = stack!.inArray
140148
stack = stack!.prev
141149
} else if let parent = parent {
142-
key = inArray ? index : keys[index]
143-
144-
switch parent {
145-
case let .node(parent):
146-
node = parent.get(key: key!.keyValue!)
147-
case let .array(parent):
148-
node = .node(parent[key!.indexValue!])
149-
}
150-
150+
key = keys[index]
151+
node = parent.get(key: key!)
151152
if node == nil {
152153
continue
153154
}
@@ -176,9 +177,7 @@ func visit(root: Node, visitor: Visitor, keyMap: [Kind: [String]] = [:]) -> Node
176177

177178
if case .break = result {
178179
break
179-
}
180-
181-
if case .skip = result {
180+
} else if case .skip = result {
182181
if !isLeaving {
183182
_ = path.popLast()
184183
continue
@@ -204,12 +203,13 @@ func visit(root: Node, visitor: Visitor, keyMap: [Kind: [String]] = [:]) -> Node
204203
_ = path.popLast()
205204
} else {
206205
stack = Stack(index: index, keys: keys, edits: edits, inArray: inArray, prev: stack)
207-
inArray = node!.isArray
208206
switch node! {
209207
case let .node(node):
208+
inArray = false
210209
keys = visitorKeys[node.kind] ?? []
211210
case let .array(array):
212-
keys = array.map { _ in "root" }
211+
inArray = true
212+
keys = Array(array.indices) // array.map { _ in "root" }
213213
}
214214
index = -1
215215
edits = []
@@ -225,7 +225,12 @@ func visit(root: Node, visitor: Visitor, keyMap: [Kind: [String]] = [:]) -> Node
225225
return nextEditNode
226226
}
227227

228-
return root
228+
switch node! {
229+
case let .node(root): // This should be equal to root, with any relevant edits
230+
return root
231+
case let .array(array): // This should never occur
232+
return array[0]
233+
}
229234
}
230235

231236
final class Stack {

0 commit comments

Comments
 (0)