/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package diff import ( "reflect" ) func (d *Differ) diffSlice(path []string, a, b reflect.Value, parent interface{}) error { if a.Kind() == reflect.Invalid { d.cl.Add(CREATE, path, nil, exportInterface(b)) return nil } if b.Kind() == reflect.Invalid { d.cl.Add(DELETE, path, exportInterface(a), nil) return nil } if a.Kind() != b.Kind() { return ErrTypeMismatch } if d.comparative(a, b) { return d.diffSliceComparative(path, a, b) } return d.diffSliceGeneric(path, a, b) } func (d *Differ) diffSliceGeneric(path []string, a, b reflect.Value) error { missing := NewComparativeList() slice := sliceTracker{} for i := 0; i < a.Len(); i++ { ae := a.Index(i) if (d.SliceOrdering && !hasAtSameIndex(b, ae, i)) || (!d.SliceOrdering && !slice.has(b, ae, d)) { missing.addA(i, &ae) } } slice = sliceTracker{} for i := 0; i < b.Len(); i++ { be := b.Index(i) if (d.SliceOrdering && !hasAtSameIndex(a, be, i)) || (!d.SliceOrdering && !slice.has(a, be, d)) { missing.addB(i, &be) } } // fallback to comparing based on order in slice if item is missing if len(missing.keys) == 0 { return nil } return d.diffComparative(path, missing, exportInterface(a)) } func (d *Differ) diffSliceComparative(path []string, a, b reflect.Value) error { c := NewComparativeList() for i := 0; i < a.Len(); i++ { ae := a.Index(i) ak := getFinalValue(ae) id := identifier(d.TagName, ak) if id != nil { c.addA(id, &ae) } } for i := 0; i < b.Len(); i++ { be := b.Index(i) bk := getFinalValue(be) id := identifier(d.TagName, bk) if id != nil { c.addB(id, &be) } } return d.diffComparative(path, c, exportInterface(a)) } // keeps track of elements that have already been matched, to stop duplicate matches from occurring type sliceTracker []bool func (st *sliceTracker) has(s, v reflect.Value, d *Differ) bool { if len(*st) != s.Len() { (*st) = make([]bool, s.Len()) } for i := 0; i < s.Len(); i++ { // skip already matched elements if (*st)[i] { continue } x := s.Index(i) var nd Differ nd.Filter = d.Filter nd.customValueDiffers = d.customValueDiffers err := nd.diff([]string{}, x, v, nil) if err != nil { continue } if len(nd.cl) == 0 { (*st)[i] = true return true } } return false } func getFinalValue(t reflect.Value) reflect.Value { switch t.Kind() { case reflect.Interface: return getFinalValue(t.Elem()) case reflect.Ptr: return getFinalValue(reflect.Indirect(t)) default: return t } } func hasAtSameIndex(s, v reflect.Value, atIndex int) bool { // check the element in the slice at atIndex to see if it matches Value, if it is a valid index into the slice if atIndex < s.Len() { x := s.Index(atIndex) return reflect.DeepEqual(exportInterface(x), exportInterface(v)) } return false }