140 lines
4 KiB
Go
140 lines
4 KiB
Go
|
// Licensed to Elasticsearch B.V. under one or more contributor
|
||
|
// license agreements. See the NOTICE file distributed with
|
||
|
// this work for additional information regarding copyright
|
||
|
// ownership. Elasticsearch B.V. licenses this file to you 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 mapval
|
||
|
|
||
|
import "fmt"
|
||
|
|
||
|
// Results the results of executing a schema.
|
||
|
// They are a flattened map (using dotted paths) of all the values []ValueResult representing the results
|
||
|
// of the IsDefs.
|
||
|
type Results struct {
|
||
|
Fields map[string][]ValueResult
|
||
|
Valid bool
|
||
|
}
|
||
|
|
||
|
// NewResults creates a new Results object.
|
||
|
func NewResults() *Results {
|
||
|
return &Results{
|
||
|
Fields: make(map[string][]ValueResult),
|
||
|
Valid: true,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SimpleResult provides a convenient and simple method for creating a *Results object for a single validation.
|
||
|
// It's a very common way for validators to return a *Results object, and is generally simpler than
|
||
|
// using SingleResult.
|
||
|
func SimpleResult(path Path, valid bool, msg string, args ...interface{}) *Results {
|
||
|
vr := ValueResult{valid, fmt.Sprintf(msg, args...)}
|
||
|
return SingleResult(path, vr)
|
||
|
}
|
||
|
|
||
|
// SingleResult returns a *Results object with a single validated value at the given path
|
||
|
// using the provided ValueResult as its sole validation.
|
||
|
func SingleResult(path Path, result ValueResult) *Results {
|
||
|
r := NewResults()
|
||
|
r.record(path, result)
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
func (r *Results) merge(other *Results) {
|
||
|
for path, valueResults := range other.Fields {
|
||
|
for _, valueResult := range valueResults {
|
||
|
r.record(MustParsePath(path), valueResult)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *Results) mergeUnderPrefix(prefix Path, other *Results) {
|
||
|
if len(prefix) == 0 {
|
||
|
// If the prefix is empty, just use standard merge
|
||
|
// No need to add the dots
|
||
|
r.merge(other)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for path, valueResults := range other.Fields {
|
||
|
for _, valueResult := range valueResults {
|
||
|
parsed := MustParsePath(path)
|
||
|
r.record(prefix.Concat(parsed), valueResult)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *Results) record(path Path, result ValueResult) {
|
||
|
if r.Fields[path.String()] == nil {
|
||
|
r.Fields[path.String()] = []ValueResult{result}
|
||
|
} else {
|
||
|
r.Fields[path.String()] = append(r.Fields[path.String()], result)
|
||
|
}
|
||
|
|
||
|
if !result.Valid {
|
||
|
r.Valid = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// EachResult executes the given callback once per Value result.
|
||
|
// The provided callback can return true to keep iterating, or false
|
||
|
// to stop.
|
||
|
func (r Results) EachResult(f func(Path, ValueResult) bool) {
|
||
|
for path, pathResults := range r.Fields {
|
||
|
for _, result := range pathResults {
|
||
|
if !f(MustParsePath(path), result) {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DetailedErrors returns a new Results object consisting only of error data.
|
||
|
func (r *Results) DetailedErrors() *Results {
|
||
|
errors := NewResults()
|
||
|
r.EachResult(func(path Path, vr ValueResult) bool {
|
||
|
if !vr.Valid {
|
||
|
errors.record(path, vr)
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
})
|
||
|
return errors
|
||
|
}
|
||
|
|
||
|
// ValueResultError is used to represent an error validating an individual value.
|
||
|
type ValueResultError struct {
|
||
|
path Path
|
||
|
valueResult ValueResult
|
||
|
}
|
||
|
|
||
|
// Error returns the error that occurred during validation with its context included.
|
||
|
func (vre ValueResultError) Error() string {
|
||
|
return fmt.Sprintf("@path '%s': %s", vre.path, vre.valueResult.Message)
|
||
|
}
|
||
|
|
||
|
// Errors returns a list of error objects, one per failed value validation.
|
||
|
func (r Results) Errors() []error {
|
||
|
errors := make([]error, 0)
|
||
|
|
||
|
r.EachResult(func(path Path, vr ValueResult) bool {
|
||
|
if !vr.Valid {
|
||
|
errors = append(errors, ValueResultError{path, vr})
|
||
|
}
|
||
|
return true
|
||
|
})
|
||
|
|
||
|
return errors
|
||
|
}
|