// 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 }