284 lines
6.2 KiB
Go
284 lines
6.2 KiB
Go
|
package gojsonschema
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"sync"
|
||
|
"text/template"
|
||
|
)
|
||
|
|
||
|
var errorTemplates errorTemplate = errorTemplate{template.New("errors-new"), sync.RWMutex{}}
|
||
|
|
||
|
// template.Template is not thread-safe for writing, so some locking is done
|
||
|
// sync.RWMutex is used for efficiently locking when new templates are created
|
||
|
type errorTemplate struct {
|
||
|
*template.Template
|
||
|
sync.RWMutex
|
||
|
}
|
||
|
|
||
|
type (
|
||
|
// RequiredError. ErrorDetails: property string
|
||
|
RequiredError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// InvalidTypeError. ErrorDetails: expected, given
|
||
|
InvalidTypeError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// NumberAnyOfError. ErrorDetails: -
|
||
|
NumberAnyOfError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// NumberOneOfError. ErrorDetails: -
|
||
|
NumberOneOfError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// NumberAllOfError. ErrorDetails: -
|
||
|
NumberAllOfError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// NumberNotError. ErrorDetails: -
|
||
|
NumberNotError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// MissingDependencyError. ErrorDetails: dependency
|
||
|
MissingDependencyError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// InternalError. ErrorDetails: error
|
||
|
InternalError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// EnumError. ErrorDetails: allowed
|
||
|
EnumError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// ArrayNoAdditionalItemsError. ErrorDetails: -
|
||
|
ArrayNoAdditionalItemsError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// ArrayMinItemsError. ErrorDetails: min
|
||
|
ArrayMinItemsError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// ArrayMaxItemsError. ErrorDetails: max
|
||
|
ArrayMaxItemsError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// ItemsMustBeUniqueError. ErrorDetails: type
|
||
|
ItemsMustBeUniqueError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// ArrayMinPropertiesError. ErrorDetails: min
|
||
|
ArrayMinPropertiesError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// ArrayMaxPropertiesError. ErrorDetails: max
|
||
|
ArrayMaxPropertiesError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// AdditionalPropertyNotAllowedError. ErrorDetails: property
|
||
|
AdditionalPropertyNotAllowedError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// InvalidPropertyPatternError. ErrorDetails: property, pattern
|
||
|
InvalidPropertyPatternError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// StringLengthGTEError. ErrorDetails: min
|
||
|
StringLengthGTEError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// StringLengthLTEError. ErrorDetails: max
|
||
|
StringLengthLTEError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// DoesNotMatchPatternError. ErrorDetails: pattern
|
||
|
DoesNotMatchPatternError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// DoesNotMatchFormatError. ErrorDetails: format
|
||
|
DoesNotMatchFormatError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// MultipleOfError. ErrorDetails: multiple
|
||
|
MultipleOfError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// NumberGTEError. ErrorDetails: min
|
||
|
NumberGTEError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// NumberGTError. ErrorDetails: min
|
||
|
NumberGTError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// NumberLTEError. ErrorDetails: max
|
||
|
NumberLTEError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
|
||
|
// NumberLTError. ErrorDetails: max
|
||
|
NumberLTError struct {
|
||
|
ResultErrorFields
|
||
|
}
|
||
|
)
|
||
|
|
||
|
// newError takes a ResultError type and sets the type, context, description, details, value, and field
|
||
|
func newError(err ResultError, context *jsonContext, value interface{}, locale locale, details ErrorDetails) {
|
||
|
var t string
|
||
|
var d string
|
||
|
switch err.(type) {
|
||
|
case *RequiredError:
|
||
|
t = "required"
|
||
|
d = locale.Required()
|
||
|
case *InvalidTypeError:
|
||
|
t = "invalid_type"
|
||
|
d = locale.InvalidType()
|
||
|
case *NumberAnyOfError:
|
||
|
t = "number_any_of"
|
||
|
d = locale.NumberAnyOf()
|
||
|
case *NumberOneOfError:
|
||
|
t = "number_one_of"
|
||
|
d = locale.NumberOneOf()
|
||
|
case *NumberAllOfError:
|
||
|
t = "number_all_of"
|
||
|
d = locale.NumberAllOf()
|
||
|
case *NumberNotError:
|
||
|
t = "number_not"
|
||
|
d = locale.NumberNot()
|
||
|
case *MissingDependencyError:
|
||
|
t = "missing_dependency"
|
||
|
d = locale.MissingDependency()
|
||
|
case *InternalError:
|
||
|
t = "internal"
|
||
|
d = locale.Internal()
|
||
|
case *EnumError:
|
||
|
t = "enum"
|
||
|
d = locale.Enum()
|
||
|
case *ArrayNoAdditionalItemsError:
|
||
|
t = "array_no_additional_items"
|
||
|
d = locale.ArrayNoAdditionalItems()
|
||
|
case *ArrayMinItemsError:
|
||
|
t = "array_min_items"
|
||
|
d = locale.ArrayMinItems()
|
||
|
case *ArrayMaxItemsError:
|
||
|
t = "array_max_items"
|
||
|
d = locale.ArrayMaxItems()
|
||
|
case *ItemsMustBeUniqueError:
|
||
|
t = "unique"
|
||
|
d = locale.Unique()
|
||
|
case *ArrayMinPropertiesError:
|
||
|
t = "array_min_properties"
|
||
|
d = locale.ArrayMinProperties()
|
||
|
case *ArrayMaxPropertiesError:
|
||
|
t = "array_max_properties"
|
||
|
d = locale.ArrayMaxProperties()
|
||
|
case *AdditionalPropertyNotAllowedError:
|
||
|
t = "additional_property_not_allowed"
|
||
|
d = locale.AdditionalPropertyNotAllowed()
|
||
|
case *InvalidPropertyPatternError:
|
||
|
t = "invalid_property_pattern"
|
||
|
d = locale.InvalidPropertyPattern()
|
||
|
case *StringLengthGTEError:
|
||
|
t = "string_gte"
|
||
|
d = locale.StringGTE()
|
||
|
case *StringLengthLTEError:
|
||
|
t = "string_lte"
|
||
|
d = locale.StringLTE()
|
||
|
case *DoesNotMatchPatternError:
|
||
|
t = "pattern"
|
||
|
d = locale.DoesNotMatchPattern()
|
||
|
case *DoesNotMatchFormatError:
|
||
|
t = "format"
|
||
|
d = locale.DoesNotMatchFormat()
|
||
|
case *MultipleOfError:
|
||
|
t = "multiple_of"
|
||
|
d = locale.MultipleOf()
|
||
|
case *NumberGTEError:
|
||
|
t = "number_gte"
|
||
|
d = locale.NumberGTE()
|
||
|
case *NumberGTError:
|
||
|
t = "number_gt"
|
||
|
d = locale.NumberGT()
|
||
|
case *NumberLTEError:
|
||
|
t = "number_lte"
|
||
|
d = locale.NumberLTE()
|
||
|
case *NumberLTError:
|
||
|
t = "number_lt"
|
||
|
d = locale.NumberLT()
|
||
|
}
|
||
|
|
||
|
err.SetType(t)
|
||
|
err.SetContext(context)
|
||
|
err.SetValue(value)
|
||
|
err.SetDetails(details)
|
||
|
details["field"] = err.Field()
|
||
|
|
||
|
if _, exists := details["context"]; !exists && context != nil {
|
||
|
details["context"] = context.String()
|
||
|
}
|
||
|
|
||
|
err.SetDescription(formatErrorDescription(d, details))
|
||
|
}
|
||
|
|
||
|
// formatErrorDescription takes a string in the default text/template
|
||
|
// format and converts it to a string with replacements. The fields come
|
||
|
// from the ErrorDetails struct and vary for each type of error.
|
||
|
func formatErrorDescription(s string, details ErrorDetails) string {
|
||
|
|
||
|
var tpl *template.Template
|
||
|
var descrAsBuffer bytes.Buffer
|
||
|
var err error
|
||
|
|
||
|
errorTemplates.RLock()
|
||
|
tpl = errorTemplates.Lookup(s)
|
||
|
errorTemplates.RUnlock()
|
||
|
|
||
|
if tpl == nil {
|
||
|
errorTemplates.Lock()
|
||
|
tpl = errorTemplates.New(s)
|
||
|
|
||
|
if ErrorTemplateFuncs != nil {
|
||
|
tpl.Funcs(ErrorTemplateFuncs)
|
||
|
}
|
||
|
|
||
|
tpl, err = tpl.Parse(s)
|
||
|
errorTemplates.Unlock()
|
||
|
|
||
|
if err != nil {
|
||
|
return err.Error()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
err = tpl.Execute(&descrAsBuffer, details)
|
||
|
if err != nil {
|
||
|
return err.Error()
|
||
|
}
|
||
|
|
||
|
return descrAsBuffer.String()
|
||
|
}
|