Merge pull request #127 from ricardomaraschini/text-template
Text template
diff --git a/README.md b/README.md
index 187da61..127bdd1 100644
--- a/README.md
+++ b/README.md
@@ -197,9 +197,9 @@
**err.Details()**: *gojsonschema.ErrorDetails* Returns a map[string]interface{} of additional error details specific to the error. For example, GTE errors will have a "min" value, LTE will have a "max" value. See errors.go for a full description of all the error details. Every error always contains a "field" key that holds the value of *err.Field()*
-Note in most cases, the err.Details() will be used to generate replacement strings in your locales. and not used directly i.e.
+Note in most cases, the err.Details() will be used to generate replacement strings in your locales, and not used directly. These strings follow the text/template format i.e.
```
-%field% must be greater than or equal to %min%
+{{.field}} must be greater than or equal to {{.min}}
```
## Formats
diff --git a/errors.go b/errors.go
index 5146cbb..f22fa65 100644
--- a/errors.go
+++ b/errors.go
@@ -1,10 +1,12 @@
package gojsonschema
import (
- "fmt"
- "strings"
+ "bytes"
+ "text/template"
)
+var errorTemplates *template.Template
+
type (
// RequiredError. ErrorDetails: property string
RequiredError struct {
@@ -230,13 +232,32 @@
err.SetDescription(formatErrorDescription(d, details))
}
-// formatErrorDescription takes a string in this format: %field% is required
-// and converts it to a string with replacements. The fields come from
-// the ErrorDetails struct and vary for each type of error.
+// 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 {
- for name, val := range details {
- s = strings.Replace(s, "%"+strings.ToLower(name)+"%", fmt.Sprintf("%v", val), -1)
+
+ var tpl *template.Template
+ var descrAsBuffer bytes.Buffer
+ var err error
+
+ if errorTemplates == nil {
+ errorTemplates = template.New("all-errors")
}
- return s
+ tpl = errorTemplates.Lookup(s)
+ if tpl == nil {
+ tpl = errorTemplates.New(s)
+ tpl, err = tpl.Parse(s)
+ if err != nil {
+ return err.Error()
+ }
+ }
+
+ err = tpl.Execute(&descrAsBuffer, details)
+ if err != nil {
+ return err.Error()
+ }
+
+ return descrAsBuffer.String()
}
diff --git a/locales.go b/locales.go
index ae42ae7..f5698f0 100644
--- a/locales.go
+++ b/locales.go
@@ -84,11 +84,11 @@
)
func (l DefaultLocale) Required() string {
- return `%property% is required`
+ return `{{.property}} is required`
}
func (l DefaultLocale) InvalidType() string {
- return `Invalid type. Expected: %expected%, given: %given%`
+ return `Invalid type. Expected: {{.expected}}, given: {{.given}}`
}
func (l DefaultLocale) NumberAnyOf() string {
@@ -108,15 +108,15 @@
}
func (l DefaultLocale) MissingDependency() string {
- return `Has a dependency on %dependency%`
+ return `Has a dependency on {{.dependency}}`
}
func (l DefaultLocale) Internal() string {
- return `Internal Error %error%`
+ return `Internal Error {{.error}}`
}
func (l DefaultLocale) Enum() string {
- return `%field% must be one of the following: %allowed%`
+ return `{{.field}} must be one of the following: {{.allowed}}`
}
func (l DefaultLocale) ArrayNoAdditionalItems() string {
@@ -128,141 +128,141 @@
}
func (l DefaultLocale) ArrayMinItems() string {
- return `Array must have at least %min% items`
+ return `Array must have at least {{.min}} items`
}
func (l DefaultLocale) ArrayMaxItems() string {
- return `Array must have at most %max% items`
+ return `Array must have at most {{.max}} items`
}
func (l DefaultLocale) Unique() string {
- return `%type% items must be unique`
+ return `{{.type}} items must be unique`
}
func (l DefaultLocale) ArrayMinProperties() string {
- return `Must have at least %min% properties`
+ return `Must have at least {{.min}} properties`
}
func (l DefaultLocale) ArrayMaxProperties() string {
- return `Must have at most %max% properties`
+ return `Must have at most {{.max}} properties`
}
func (l DefaultLocale) AdditionalPropertyNotAllowed() string {
- return `Additional property %property% is not allowed`
+ return `Additional property {{.property}} is not allowed`
}
func (l DefaultLocale) InvalidPropertyPattern() string {
- return `Property "%property%" does not match pattern %pattern%`
+ return `Property "{{.property}}" does not match pattern {{.pattern}}`
}
func (l DefaultLocale) StringGTE() string {
- return `String length must be greater than or equal to %min%`
+ return `String length must be greater than or equal to {{.min}}`
}
func (l DefaultLocale) StringLTE() string {
- return `String length must be less than or equal to %max%`
+ return `String length must be less than or equal to {{.max}}`
}
func (l DefaultLocale) DoesNotMatchPattern() string {
- return `Does not match pattern '%pattern%'`
+ return `Does not match pattern '{{.pattern}}'`
}
func (l DefaultLocale) DoesNotMatchFormat() string {
- return `Does not match format '%format%'`
+ return `Does not match format '{{.format}}'`
}
func (l DefaultLocale) MultipleOf() string {
- return `Must be a multiple of %multiple%`
+ return `Must be a multiple of {{.multiple}}`
}
func (l DefaultLocale) NumberGTE() string {
- return `Must be greater than or equal to %min%`
+ return `Must be greater than or equal to {{.min}}`
}
func (l DefaultLocale) NumberGT() string {
- return `Must be greater than %min%`
+ return `Must be greater than {{.min}}`
}
func (l DefaultLocale) NumberLTE() string {
- return `Must be less than or equal to %max%`
+ return `Must be less than or equal to {{.max}}`
}
func (l DefaultLocale) NumberLT() string {
- return `Must be less than %max%`
+ return `Must be less than {{.max}}`
}
// Schema validators
func (l DefaultLocale) RegexPattern() string {
- return `Invalid regex pattern '%pattern%'`
+ return `Invalid regex pattern '{{.pattern}}'`
}
func (l DefaultLocale) GreaterThanZero() string {
- return `%number% must be strictly greater than 0`
+ return `{{.number}} must be strictly greater than 0`
}
func (l DefaultLocale) MustBeOfA() string {
- return `%x% must be of a %y%`
+ return `{{.x}} must be of a {{.y}}`
}
func (l DefaultLocale) MustBeOfAn() string {
- return `%x% must be of an %y%`
+ return `{{.x}} must be of an {{.y}}`
}
func (l DefaultLocale) CannotBeUsedWithout() string {
- return `%x% cannot be used without %y%`
+ return `{{.x}} cannot be used without {{.y}}`
}
func (l DefaultLocale) CannotBeGT() string {
- return `%x% cannot be greater than %y%`
+ return `{{.x}} cannot be greater than {{.y}}`
}
func (l DefaultLocale) MustBeOfType() string {
- return `%key% must be of type %type%`
+ return `{{.key}} must be of type {{.type}}`
}
func (l DefaultLocale) MustBeValidRegex() string {
- return `%key% must be a valid regex`
+ return `{{.key}} must be a valid regex`
}
func (l DefaultLocale) MustBeValidFormat() string {
- return `%key% must be a valid format %given%`
+ return `{{.key}} must be a valid format {{.given}}`
}
func (l DefaultLocale) MustBeGTEZero() string {
- return `%key% must be greater than or equal to 0`
+ return `{{.key}} must be greater than or equal to 0`
}
func (l DefaultLocale) KeyCannotBeGreaterThan() string {
- return `%key% cannot be greater than %y%`
+ return `{{.key}} cannot be greater than {{.y}}`
}
func (l DefaultLocale) KeyItemsMustBeOfType() string {
- return `%key% items must be %type%`
+ return `{{.key}} items must be {{.type}}`
}
func (l DefaultLocale) KeyItemsMustBeUnique() string {
- return `%key% items must be unique`
+ return `{{.key}} items must be unique`
}
func (l DefaultLocale) ReferenceMustBeCanonical() string {
- return `Reference %reference% must be canonical`
+ return `Reference {{.reference}} must be canonical`
}
func (l DefaultLocale) NotAValidType() string {
- return `%type% is not a valid type -- `
+ return `{{.type}} is not a valid type -- `
}
func (l DefaultLocale) Duplicated() string {
- return `%type% type is duplicated`
+ return `{{.type}} type is duplicated`
}
func (l DefaultLocale) httpBadStatus() string {
- return `Could not read schema from HTTP, response status is %status%`
+ return `Could not read schema from HTTP, response status is {{.status}}`
}
// Replacement options: field, description, context, value
func (l DefaultLocale) ErrorFormat() string {
- return `%field%: %description%`
+ return `{{.field}}: {{.description}}`
}
const (