@@ -172,72 +172,83 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err
172
172
}
173
173
out := make ([]interface {}, 0 , int (math .Max (float64 (rLen ), float64 (lLen ))))
174
174
175
- lhsOrder := make ([]fieldpath.PathElement , 0 , lLen )
176
-
177
- // First, collect all LHS children.
178
- observedLHS := fieldpath .MakePathElementValueMap (lLen )
179
- if lhs != nil {
180
- for i := 0 ; i < lhs .Length (); i ++ {
181
- child := lhs .At (i )
175
+ createPathElementsValues := func (name string , list value.List ) ([]fieldpath.PathElement , fieldpath.PathElementValueMap , ValidationErrors ) {
176
+ var errs ValidationErrors
177
+ length := 0
178
+ if list != nil {
179
+ length = list .Length ()
180
+ }
181
+ observed := fieldpath .MakePathElementValueMap (length )
182
+ pes := make ([]fieldpath.PathElement , 0 , length )
183
+ for i := 0 ; i < length ; i ++ {
184
+ child := list .At (i )
182
185
pe , err := listItemToPathElement (w .allocator , w .schema , t , i , child )
183
186
if err != nil {
184
- errs = append (errs , errorf ("lhs : element %v: %v" , i , err .Error ())... )
187
+ errs = append (errs , errorf ("%s : element %v: %v" , name , i , err .Error ())... )
185
188
// If we can't construct the path element, we can't
186
189
// even report errors deeper in the schema, so bail on
187
190
// this element.
188
191
continue
189
192
}
190
- if _ , ok := observedLHS .Get (pe ); ok {
191
- errs = append (errs , errorf ("lhs: duplicate entries for key %v" , pe .String ())... )
193
+ if _ , _ , found := observed .Get (pe ); found {
194
+ errs = append (errs , errorf ("%s: duplicate entries for key %v" , name , pe .String ())... )
195
+ continue
192
196
}
193
- observedLHS .Insert (pe , child )
194
- lhsOrder = append (lhsOrder , pe )
197
+ observed .Insert (pe , child , i )
198
+ pes = append (pes , pe )
195
199
}
200
+ return pes , observed , errs
196
201
}
197
202
198
- // Then merge with RHS children.
199
- observedRHS := fieldpath .MakePathElementSet (rLen )
200
- if rhs != nil {
201
- for i := 0 ; i < rhs .Length (); i ++ {
202
- child := rhs .At (i )
203
- pe , err := listItemToPathElement (w .allocator , w .schema , t , i , child )
204
- if err != nil {
205
- errs = append (errs , errorf ("rhs: element %v: %v" , i , err .Error ())... )
206
- // If we can't construct the path element, we can't
207
- // even report errors deeper in the schema, so bail on
208
- // this element.
209
- continue
210
- }
211
- if observedRHS .Has (pe ) {
212
- errs = append (errs , errorf ("rhs: duplicate entries for key %v" , pe .String ())... )
213
- continue
214
- }
215
- observedRHS .Insert (pe )
203
+ lhsOrder , observedLHS , lhsErrs := createPathElementsValues ("lhs" , lhs )
204
+ errs = append (errs , lhsErrs ... )
205
+ rhsOrder , observedRHS , rhsErrs := createPathElementsValues ("rhs" , rhs )
206
+ errs = append (errs , rhsErrs ... )
207
+
208
+ lLen , rLen = len (lhsOrder ), len (rhsOrder )
209
+ for lI , rI := 0 , 0 ; lI < lLen || rI < rLen ; {
210
+ merge := func (pe fieldpath.PathElement , lChild , rChild value.Value ) {
216
211
w2 := w .prepareDescent (pe , t .ElementType )
217
- w2 .rhs = child
218
- if lchild , ok := observedLHS .Get (pe ); ok {
219
- w2 .lhs = lchild
220
- }
212
+ w2 .lhs = lChild
213
+ w2 .rhs = rChild
221
214
errs = append (errs , w2 .merge (pe .String )... )
222
215
if w2 .out != nil {
223
216
out = append (out , * w2 .out )
224
217
}
225
218
w .finishDescent (w2 )
226
219
}
227
- }
228
-
229
- for _ , pe := range lhsOrder {
230
- if observedRHS .Has (pe ) {
220
+ if lI < lLen && rI < rLen && lhsOrder [lI ].Equals (rhsOrder [rI ]) {
221
+ // merge LHS & RHS items
222
+ pe := lhsOrder [lI ]
223
+ lChild , _ , _ := observedLHS .Get (pe )
224
+ rChild , _ , _ := observedRHS .Get (pe )
225
+ merge (pe , lChild , rChild )
226
+ lI ++
227
+ rI ++
231
228
continue
232
229
}
233
- value , _ := observedLHS .Get (pe )
234
- w2 := w .prepareDescent (pe , t .ElementType )
235
- w2 .lhs = value
236
- errs = append (errs , w2 .merge (pe .String )... )
237
- if w2 .out != nil {
238
- out = append (out , * w2 .out )
230
+ if lI < lLen {
231
+ if _ , index , ok := observedRHS .Get (lhsOrder [lI ]); ok && index < rI {
232
+ // Skip the LHS item because it has already appeared
233
+ lI ++
234
+ continue
235
+ } else if ! ok {
236
+ // Take the LHS item, without a matching RHS item to merge with
237
+ pe := lhsOrder [lI ]
238
+ lChild , _ , _ := observedLHS .Get (pe )
239
+ merge (pe , lChild , nil )
240
+ lI ++
241
+ continue
242
+ }
243
+ }
244
+ if rI < rLen {
245
+ // Take the RHS item, merge with matching LHS item if possible
246
+ pe := rhsOrder [rI ]
247
+ rChild , _ , _ := observedRHS .Get (pe )
248
+ lChild , _ , _ := observedLHS .Get (pe ) // may be nil
249
+ merge (pe , lChild , rChild )
250
+ rI ++
239
251
}
240
- w .finishDescent (w2 )
241
252
}
242
253
243
254
if len (out ) > 0 {
0 commit comments