isdef/core.go (77 lines of code) (raw):

package isdef import ( "fmt" "reflect" "github.com/elastic/go-lookslike/llpath" "github.com/elastic/go-lookslike/llresult" ) // IsEqual tests that the given object is equal to the actual object. func IsEqual(to interface{}) IsDef { toV := reflect.ValueOf(to) isDefFactory, ok := equalChecks[toV.Type()] // If there are no handlers declared explicitly for this type we perform a deep equality check if !ok { return IsDeepEqual(to) } // We know this is an isdef due to the Register check previously checker := isDefFactory.Call([]reflect.Value{toV})[0].Interface().(IsDef).Checker return Is("equals", func(path llpath.Path, v interface{}) *llresult.Results { return checker(path, v) }) } // KeyPresent checks that the given key is in the map, even if it has a nil value. var KeyPresent = IsDef{Name: "check key present"} // KeyMissing checks that the given key is not present defined. var KeyMissing = IsDef{Name: "check key not present", CheckKeyMissing: true} func init() { MustRegisterEqual(IsEqualToTime) } // InvalidEqualFnError is the error type returned by RegisterEqual when // there is an issue with the given function. type InvalidEqualFnError struct{ msg string } func (e InvalidEqualFnError) Error() string { return fmt.Sprintf("Function is not a valid equal function: %s", e.msg) } // MustRegisterEqual is the panic-ing equivalent of RegisterEqual. func MustRegisterEqual(fn interface{}) { if err := RegisterEqual(fn); err != nil { panic(fmt.Sprintf("Could not register fn as equal! %v", err)) } } var equalChecks = map[reflect.Type]reflect.Value{} // RegisterEqual takes a function of the form fn(v someType) IsDef // and registers it to check equality for that type. func RegisterEqual(fn interface{}) error { fnV := reflect.ValueOf(fn) fnT := fnV.Type() if fnT.Kind() != reflect.Func { return InvalidEqualFnError{"Provided value is not a function"} } if fnT.NumIn() != 1 { return InvalidEqualFnError{"Equal FN should take one argument"} } if fnT.NumOut() != 1 { return InvalidEqualFnError{"Equal FN should return one value"} } if fnT.Out(0) != reflect.TypeOf(IsDef{}) { return InvalidEqualFnError{"Equal FN should return an IsDef"} } inT := fnT.In(0) if _, ok := equalChecks[inT]; ok { return InvalidEqualFnError{fmt.Sprintf("Duplicate Equal FN for type %v encountered!", inT)} } equalChecks[inT] = fnV return nil } // IsDeepEqual checks equality using reflect.DeepEqual. func IsDeepEqual(to interface{}) IsDef { return Is("equals", func(path llpath.Path, v interface{}) *llresult.Results { if reflect.DeepEqual(v, to) { return llresult.ValidResult(path) } return llresult.SimpleResult( path, false, fmt.Sprintf("objects not equal: actual(%T(%v)) != expected(%T(%v))", v, v, to, to), ) }) } // IsNil tests that a value is nil. var IsNil = Is("is nil", func(path llpath.Path, v interface{}) *llresult.Results { if v == nil { return llresult.ValidResult(path) } return llresult.SimpleResult( path, false, fmt.Sprintf("Value %#v is not nil", v), ) })