@@ -160,6 +160,8 @@ extension JSON.NodeAccessor {
160160 }
161161}
162162extension JSON . NodeAccessor {
163+ /// Delete the node at the accessed path, throwing a ``NodeAccessError`` if the location is
164+ /// not writable and data already exists there.
163165 @inlinable public static func &= (
164166 self: inout Self ,
165167 delete: Never ?
@@ -175,6 +177,8 @@ extension JSON.NodeAccessor {
175177 self . state = . writable
176178 }
177179 }
180+ /// Create or update the node at the accessed path, throwing a ``NodeAccessError`` if the
181+ /// location is not writable.
178182 @inlinable public static func &= (
179183 self: inout Self ,
180184 value: consuming JSON . Node
@@ -191,27 +195,59 @@ extension JSON.NodeAccessor {
191195 }
192196 }
193197
198+ /// Modify an existing node at the accessed path, returning nil if no such node exists, or
199+ /// if the accessed path is invalid.
194200 @discardableResult
195201 @inlinable public static func &? < E, T> (
196202 self: inout Self ,
197203 yield: ( inout JSON . Node ) throws ( E ) -> T
198204 ) throws ( E) -> T ? {
205+ if case . occupied( var value) = self . state {
206+ self . state = . writable
207+ defer {
208+ self . state = . occupied( value)
209+ }
210+ return try yield ( & value)
211+ } else {
212+ return nil
213+ }
214+ }
215+
216+ /// Modify the node at the accessed path, creating it with a default value of
217+ /// ``JSON.Node/null``, if it does not exist.
218+ ///
219+ /// Use this when you are confident that the node must be written, and expect to receive
220+ /// an error if incompatible data already exists in the accessed location.
221+ @inlinable public static func & (
222+ self: inout Self ,
223+ yield: ( inout JSON . Node ) throws -> ( )
224+ ) throws {
199225 switch self . state {
200226 case . protected:
201- return nil
202- case . reserved:
203- return nil
227+ throw JSON . NodeAccessError . protected ( self . crumb )
228+ case . reserved( let offender ) :
229+ throw JSON . NodeAccessError . reserved ( self . crumb , offender )
204230 case . writable:
205- return nil
206- case . occupied( var value) :
231+ var value : JSON . Node = . null
232+ try yield ( & value)
233+ self . state = . occupied( value)
234+
235+ case . occupied( let value) :
236+ var value : JSON . Node = consume value
207237 self . state = . writable
208238 defer {
209239 self . state = . occupied( value)
210240 }
211- return try yield ( & value)
241+ try yield ( & value)
212242 }
213243 }
214244
245+ /// Modify the node at the accessed path, allowing the closure to create, update, or delete
246+ /// it. Assigning a value to a node that was originally nil creates it, likewise,
247+ /// assigning nil to a node that was originally non-nil deletes it.
248+ ///
249+ /// If the accessed path is not writable, and the node is assigned a value in the
250+ /// closure — including an explicit ``JSON.Node/null`` — a ``NodeAccessError`` is thrown.
215251 @inlinable public static func & (
216252 self: inout Self ,
217253 yield: ( inout JSON . Node ? ) throws -> ( )
0 commit comments