| // Copyright (C) 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package compare |
| |
| import ( |
| "fmt" |
| "reflect" |
| ) |
| |
| // Action is the optional return value type of functions passes to |
| // Register and Custom.Register. |
| type Action int |
| |
| const ( |
| // Done is returned by custom comparison functions when the two objects |
| // require no further comparisons. |
| Done Action = iota |
| |
| // Fallback is returned by custom comparison functions when the comparison of |
| // the two objects should continue with the fallback comparison method. |
| Fallback |
| ) |
| |
| type customKey struct { |
| reference reflect.Type |
| value reflect.Type |
| } |
| |
| var ( |
| globalCustom = &Custom{} |
| comparatorType = reflect.TypeOf(Comparator{}) |
| actionType = reflect.TypeOf(Done) |
| ) |
| |
| // Custom is a collection of custom comparators that will be used instead of |
| // the default comparison methods when comparing objects of the registered types. |
| type Custom struct { |
| funcs map[customKey]reflect.Value |
| } |
| |
| // Register assigns the function f with signature func(comparator, T, T) to |
| // be used as the comparator for instances of type T when using |
| // Custom.Compare(). f may return nothing or a CompareAction. |
| // Register will panic if f does not match the expected signature, or if a |
| // comparator for type T has already been registered with this Custom. |
| func (c *Custom) Register(f interface{}) { |
| v := reflect.ValueOf(f) |
| t := v.Type() |
| if t.Kind() != reflect.Func { |
| panic(fmt.Sprintf("Invalid function %v", t)) |
| } |
| if t.NumIn() != 3 { |
| panic(fmt.Sprintf("Compare functions must have 3 args, got %v", t)) |
| } |
| if t.In(0) != comparatorType { |
| panic(fmt.Sprintf("First argument must be compare.Comparator, got %v", t.In(0))) |
| } |
| if !(t.NumOut() == 0 || (t.NumOut() == 1 && t.Out(0) == actionType)) { |
| panic(fmt.Sprintf("Compare functions must either have no return values or a single Action")) |
| } |
| key := customKey{t.In(1), t.In(2)} |
| if key.reference != key.value { |
| panic(fmt.Sprintf("Comparison arguments must be of the same type, got %v and %v", key.reference, key.value)) |
| } |
| if c.funcs == nil { |
| c.funcs = map[customKey]reflect.Value{} |
| } else if _, found := c.funcs[key]; found { |
| panic(fmt.Sprintf("%v to %v already registered", key.reference, key.value)) |
| } |
| c.funcs[key] = v |
| } |
| |
| // Compare delivers all the differences it finds to the specified Handler. |
| // Compare uses the list of custom comparison handlers registered with |
| // Custom.Register(), falling back to the default comparison method for the type |
| // when no custom comparison function has been registered with this custom. |
| // If the reference and value are equal, the handler will never be invoked. |
| func (c *Custom) Compare(reference, value interface{}, handler Handler) { |
| compare(reference, value, handler, c) |
| } |
| |
| // Diff returns the differences between the reference and the value. |
| // Diff uses the list of custom comparison handlers registered with |
| // Custom.Register(), falling back to the default comparison method for the type |
| // when no custom comparison function has been registered with this custom. |
| // The maximum number of differences is controlled by limit, which must be >0. |
| // If they compare equal, the length of the returned slice will be 0. |
| func (c *Custom) Diff(reference, value interface{}, limit int) []Path { |
| diffs := make(collect, 0, limit) |
| c.Compare(reference, value, diffs.add) |
| return ([]Path)(diffs) |
| } |
| |
| func (c *Custom) call(key customKey, args []reflect.Value) Action { |
| if c == nil { |
| return Fallback |
| } |
| |
| comparator, found := c.funcs[key] |
| if !found { |
| return c.fallback().call(key, args) |
| } |
| |
| action := Done |
| if res := comparator.Call(args); len(res) > 0 { |
| action = res[0].Interface().(Action) |
| } |
| |
| switch action { |
| case Done: |
| return Done |
| case Fallback: |
| return c.fallback().call(key, args) |
| default: |
| panic(fmt.Errorf("Unknown action %v", action)) |
| } |
| } |
| |
| func (c *Custom) fallback() *Custom { |
| if c == globalCustom { |
| return nil |
| } |
| return globalCustom |
| } |