123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- package cmp
- import (
- "fmt"
- "reflect"
- "runtime"
- "strings"
- "github.com/google/go-cmp/cmp/internal/function"
- )
- type Option interface {
-
-
-
-
-
-
- filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption
- }
- type applicableOption interface {
- Option
-
- apply(s *state, vx, vy reflect.Value)
- }
- type coreOption interface {
- Option
- isCore()
- }
- type core struct{}
- func (core) isCore() {}
- type Options []Option
- func (opts Options) filter(s *state, vx, vy reflect.Value, t reflect.Type) (out applicableOption) {
- for _, opt := range opts {
- switch opt := opt.filter(s, vx, vy, t); opt.(type) {
- case ignore:
- return ignore{}
- case invalid:
- out = invalid{}
- case *comparer, *transformer, Options:
- switch out.(type) {
- case nil:
- out = opt
- case invalid:
-
- case *comparer, *transformer, Options:
- out = Options{out, opt}
- }
- }
- }
- return out
- }
- func (opts Options) apply(s *state, _, _ reflect.Value) {
- const warning = "ambiguous set of applicable options"
- const help = "consider using filters to ensure at most one Comparer or Transformer may apply"
- var ss []string
- for _, opt := range flattenOptions(nil, opts) {
- ss = append(ss, fmt.Sprint(opt))
- }
- set := strings.Join(ss, "\n\t")
- panic(fmt.Sprintf("%s at %#v:\n\t%s\n%s", warning, s.curPath, set, help))
- }
- func (opts Options) String() string {
- var ss []string
- for _, opt := range opts {
- ss = append(ss, fmt.Sprint(opt))
- }
- return fmt.Sprintf("Options{%s}", strings.Join(ss, ", "))
- }
- func FilterPath(f func(Path) bool, opt Option) Option {
- if f == nil {
- panic("invalid path filter function")
- }
- if opt := normalizeOption(opt); opt != nil {
- return &pathFilter{fnc: f, opt: opt}
- }
- return nil
- }
- type pathFilter struct {
- core
- fnc func(Path) bool
- opt Option
- }
- func (f pathFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption {
- if f.fnc(s.curPath) {
- return f.opt.filter(s, vx, vy, t)
- }
- return nil
- }
- func (f pathFilter) String() string {
- fn := getFuncName(reflect.ValueOf(f.fnc).Pointer())
- return fmt.Sprintf("FilterPath(%s, %v)", fn, f.opt)
- }
- func FilterValues(f interface{}, opt Option) Option {
- v := reflect.ValueOf(f)
- if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() {
- panic(fmt.Sprintf("invalid values filter function: %T", f))
- }
- if opt := normalizeOption(opt); opt != nil {
- vf := &valuesFilter{fnc: v, opt: opt}
- if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 {
- vf.typ = ti
- }
- return vf
- }
- return nil
- }
- type valuesFilter struct {
- core
- typ reflect.Type
- fnc reflect.Value
- opt Option
- }
- func (f valuesFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption {
- if !vx.IsValid() || !vy.IsValid() {
- return invalid{}
- }
- if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) {
- return f.opt.filter(s, vx, vy, t)
- }
- return nil
- }
- func (f valuesFilter) String() string {
- fn := getFuncName(f.fnc.Pointer())
- return fmt.Sprintf("FilterValues(%s, %v)", fn, f.opt)
- }
- func Ignore() Option { return ignore{} }
- type ignore struct{ core }
- func (ignore) isFiltered() bool { return false }
- func (ignore) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return ignore{} }
- func (ignore) apply(_ *state, _, _ reflect.Value) { return }
- func (ignore) String() string { return "Ignore()" }
- type invalid struct{ core }
- func (invalid) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return invalid{} }
- func (invalid) apply(s *state, _, _ reflect.Value) {
- const help = "consider using AllowUnexported or cmpopts.IgnoreUnexported"
- panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help))
- }
- func Transformer(name string, f interface{}) Option {
- v := reflect.ValueOf(f)
- if !function.IsType(v.Type(), function.Transformer) || v.IsNil() {
- panic(fmt.Sprintf("invalid transformer function: %T", f))
- }
- if name == "" {
- name = "λ"
- }
- if !isValid(name) {
- panic(fmt.Sprintf("invalid name: %q", name))
- }
- tr := &transformer{name: name, fnc: reflect.ValueOf(f)}
- if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 {
- tr.typ = ti
- }
- return tr
- }
- type transformer struct {
- core
- name string
- typ reflect.Type
- fnc reflect.Value
- }
- func (tr *transformer) isFiltered() bool { return tr.typ != nil }
- func (tr *transformer) filter(s *state, _, _ reflect.Value, t reflect.Type) applicableOption {
- for i := len(s.curPath) - 1; i >= 0; i-- {
- if t, ok := s.curPath[i].(*transform); !ok {
- break
- } else if tr == t.trans {
- return nil
- }
- }
- if tr.typ == nil || t.AssignableTo(tr.typ) {
- return tr
- }
- return nil
- }
- func (tr *transformer) apply(s *state, vx, vy reflect.Value) {
-
-
- s.curPath.push(&transform{pathStep{tr.fnc.Type().Out(0)}, tr})
- defer s.curPath.pop()
- vx = s.callTRFunc(tr.fnc, vx)
- vy = s.callTRFunc(tr.fnc, vy)
- s.compareAny(vx, vy)
- }
- func (tr transformer) String() string {
- return fmt.Sprintf("Transformer(%s, %s)", tr.name, getFuncName(tr.fnc.Pointer()))
- }
- func Comparer(f interface{}) Option {
- v := reflect.ValueOf(f)
- if !function.IsType(v.Type(), function.Equal) || v.IsNil() {
- panic(fmt.Sprintf("invalid comparer function: %T", f))
- }
- cm := &comparer{fnc: v}
- if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 {
- cm.typ = ti
- }
- return cm
- }
- type comparer struct {
- core
- typ reflect.Type
- fnc reflect.Value
- }
- func (cm *comparer) isFiltered() bool { return cm.typ != nil }
- func (cm *comparer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applicableOption {
- if cm.typ == nil || t.AssignableTo(cm.typ) {
- return cm
- }
- return nil
- }
- func (cm *comparer) apply(s *state, vx, vy reflect.Value) {
- eq := s.callTTBFunc(cm.fnc, vx, vy)
- s.report(eq, vx, vy)
- }
- func (cm comparer) String() string {
- return fmt.Sprintf("Comparer(%s)", getFuncName(cm.fnc.Pointer()))
- }
- func AllowUnexported(types ...interface{}) Option {
- if !supportAllowUnexported {
- panic("AllowUnexported is not supported on purego builds, Google App Engine Standard, or GopherJS")
- }
- m := make(map[reflect.Type]bool)
- for _, typ := range types {
- t := reflect.TypeOf(typ)
- if t.Kind() != reflect.Struct {
- panic(fmt.Sprintf("invalid struct type: %T", typ))
- }
- m[t] = true
- }
- return visibleStructs(m)
- }
- type visibleStructs map[reflect.Type]bool
- func (visibleStructs) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption {
- panic("not implemented")
- }
- type reporter interface {
-
-
-
-
-
-
- Option
-
-
-
-
-
- Report(x, y reflect.Value, eq bool, p Path)
- }
- func normalizeOption(src Option) Option {
- switch opts := flattenOptions(nil, Options{src}); len(opts) {
- case 0:
- return nil
- case 1:
- return opts[0]
- default:
- return opts
- }
- }
- func flattenOptions(dst, src Options) Options {
- for _, opt := range src {
- switch opt := opt.(type) {
- case nil:
- continue
- case Options:
- dst = flattenOptions(dst, opt)
- case coreOption:
- dst = append(dst, opt)
- default:
- panic(fmt.Sprintf("invalid option type: %T", opt))
- }
- }
- return dst
- }
- func getFuncName(p uintptr) string {
- fnc := runtime.FuncForPC(p)
- if fnc == nil {
- return "<unknown>"
- }
- name := fnc.Name()
- if strings.HasSuffix(name, ")-fm") || strings.HasSuffix(name, ")·fm") {
-
- name = strings.TrimSuffix(name, ")-fm")
- name = strings.TrimSuffix(name, ")·fm")
- if i := strings.LastIndexByte(name, '('); i >= 0 {
- methodName := name[i+1:]
- if j := strings.LastIndexByte(methodName, '.'); j >= 0 {
- methodName = methodName[j+1:]
- }
- name = name[:i] + methodName
- }
- }
- if i := strings.LastIndexByte(name, '/'); i >= 0 {
-
- name = name[i+1:]
- }
- return name
- }
|