1package gojsonschema
2
3import (
4	"bytes"
5	"sync"
6	"text/template"
7)
8
9var errorTemplates = errorTemplate{template.New("errors-new"), sync.RWMutex{}}
10
11// template.Template is not thread-safe for writing, so some locking is done
12// sync.RWMutex is used for efficiently locking when new templates are created
13type errorTemplate struct {
14	*template.Template
15	sync.RWMutex
16}
17
18type (
19
20	// FalseError. ErrorDetails: -
21	FalseError struct {
22		ResultErrorFields
23	}
24
25	// RequiredError indicates that a required field is missing
26	// ErrorDetails: property string
27	RequiredError struct {
28		ResultErrorFields
29	}
30
31	// InvalidTypeError indicates that a field has the incorrect type
32	// ErrorDetails: expected, given
33	InvalidTypeError struct {
34		ResultErrorFields
35	}
36
37	// NumberAnyOfError is produced in case of a failing "anyOf" validation
38	// ErrorDetails: -
39	NumberAnyOfError struct {
40		ResultErrorFields
41	}
42
43	// NumberOneOfError is produced in case of a failing "oneOf" validation
44	// ErrorDetails: -
45	NumberOneOfError struct {
46		ResultErrorFields
47	}
48
49	// NumberAllOfError is produced in case of a failing "allOf" validation
50	// ErrorDetails: -
51	NumberAllOfError struct {
52		ResultErrorFields
53	}
54
55	// NumberNotError is produced if a "not" validation failed
56	// ErrorDetails: -
57	NumberNotError struct {
58		ResultErrorFields
59	}
60
61	// MissingDependencyError is produced in case of a "missing dependency" problem
62	// ErrorDetails: dependency
63	MissingDependencyError struct {
64		ResultErrorFields
65	}
66
67	// InternalError indicates an internal error
68	// ErrorDetails: error
69	InternalError struct {
70		ResultErrorFields
71	}
72
73	// ConstError indicates a const error
74	// ErrorDetails: allowed
75	ConstError struct {
76		ResultErrorFields
77	}
78
79	// EnumError indicates an enum error
80	// ErrorDetails: allowed
81	EnumError struct {
82		ResultErrorFields
83	}
84
85	// ArrayNoAdditionalItemsError is produced if additional items were found, but not allowed
86	// ErrorDetails: -
87	ArrayNoAdditionalItemsError struct {
88		ResultErrorFields
89	}
90
91	// ArrayMinItemsError is produced if an array contains less items than the allowed minimum
92	// ErrorDetails: min
93	ArrayMinItemsError struct {
94		ResultErrorFields
95	}
96
97	// ArrayMaxItemsError is produced if an array contains more items than the allowed maximum
98	// ErrorDetails: max
99	ArrayMaxItemsError struct {
100		ResultErrorFields
101	}
102
103	// ItemsMustBeUniqueError is produced if an array requires unique items, but contains non-unique items
104	// ErrorDetails: type, i, j
105	ItemsMustBeUniqueError struct {
106		ResultErrorFields
107	}
108
109	// ArrayContainsError is produced if an array contains invalid items
110	// ErrorDetails:
111	ArrayContainsError struct {
112		ResultErrorFields
113	}
114
115	// ArrayMinPropertiesError is produced if an object contains less properties than the allowed minimum
116	// ErrorDetails: min
117	ArrayMinPropertiesError struct {
118		ResultErrorFields
119	}
120
121	// ArrayMaxPropertiesError is produced if an object contains more properties than the allowed maximum
122	// ErrorDetails: max
123	ArrayMaxPropertiesError struct {
124		ResultErrorFields
125	}
126
127	// AdditionalPropertyNotAllowedError is produced if an object has additional properties, but not allowed
128	// ErrorDetails: property
129	AdditionalPropertyNotAllowedError struct {
130		ResultErrorFields
131	}
132
133	// InvalidPropertyPatternError is produced if an pattern was found
134	// ErrorDetails: property, pattern
135	InvalidPropertyPatternError struct {
136		ResultErrorFields
137	}
138
139	// InvalidPropertyNameError is produced if an invalid-named property was found
140	// ErrorDetails: property
141	InvalidPropertyNameError struct {
142		ResultErrorFields
143	}
144
145	// StringLengthGTEError is produced if a string is shorter than the minimum required length
146	// ErrorDetails: min
147	StringLengthGTEError struct {
148		ResultErrorFields
149	}
150
151	// StringLengthLTEError is produced if a string is longer than the maximum allowed length
152	// ErrorDetails: max
153	StringLengthLTEError struct {
154		ResultErrorFields
155	}
156
157	// DoesNotMatchPatternError is produced if a string does not match the defined pattern
158	// ErrorDetails: pattern
159	DoesNotMatchPatternError struct {
160		ResultErrorFields
161	}
162
163	// DoesNotMatchFormatError is produced if a string does not match the defined format
164	// ErrorDetails: format
165	DoesNotMatchFormatError struct {
166		ResultErrorFields
167	}
168
169	// MultipleOfError is produced if a number is not a multiple of the defined multipleOf
170	// ErrorDetails: multiple
171	MultipleOfError struct {
172		ResultErrorFields
173	}
174
175	// NumberGTEError is produced if a number is lower than the allowed minimum
176	// ErrorDetails: min
177	NumberGTEError struct {
178		ResultErrorFields
179	}
180
181	// NumberGTError is produced if a number is lower than, or equal to the specified minimum, and exclusiveMinimum is set
182	// ErrorDetails: min
183	NumberGTError struct {
184		ResultErrorFields
185	}
186
187	// NumberLTEError is produced if a number is higher than the allowed maximum
188	// ErrorDetails: max
189	NumberLTEError struct {
190		ResultErrorFields
191	}
192
193	// NumberLTError is produced if a number is higher than, or equal to the specified maximum, and exclusiveMaximum is set
194	// ErrorDetails: max
195	NumberLTError struct {
196		ResultErrorFields
197	}
198
199	// ConditionThenError is produced if a condition's "then" validation is invalid
200	// ErrorDetails: -
201	ConditionThenError struct {
202		ResultErrorFields
203	}
204
205	// ConditionElseError is produced if a condition's "else" condition is invalid
206	// ErrorDetails: -
207	ConditionElseError struct {
208		ResultErrorFields
209	}
210)
211
212// newError takes a ResultError type and sets the type, context, description, details, value, and field
213func newError(err ResultError, context *JsonContext, value interface{}, locale locale, details ErrorDetails) {
214	var t string
215	var d string
216	switch err.(type) {
217	case *FalseError:
218		t = "false"
219		d = locale.False()
220	case *RequiredError:
221		t = "required"
222		d = locale.Required()
223	case *InvalidTypeError:
224		t = "invalid_type"
225		d = locale.InvalidType()
226	case *NumberAnyOfError:
227		t = "number_any_of"
228		d = locale.NumberAnyOf()
229	case *NumberOneOfError:
230		t = "number_one_of"
231		d = locale.NumberOneOf()
232	case *NumberAllOfError:
233		t = "number_all_of"
234		d = locale.NumberAllOf()
235	case *NumberNotError:
236		t = "number_not"
237		d = locale.NumberNot()
238	case *MissingDependencyError:
239		t = "missing_dependency"
240		d = locale.MissingDependency()
241	case *InternalError:
242		t = "internal"
243		d = locale.Internal()
244	case *ConstError:
245		t = "const"
246		d = locale.Const()
247	case *EnumError:
248		t = "enum"
249		d = locale.Enum()
250	case *ArrayNoAdditionalItemsError:
251		t = "array_no_additional_items"
252		d = locale.ArrayNoAdditionalItems()
253	case *ArrayMinItemsError:
254		t = "array_min_items"
255		d = locale.ArrayMinItems()
256	case *ArrayMaxItemsError:
257		t = "array_max_items"
258		d = locale.ArrayMaxItems()
259	case *ItemsMustBeUniqueError:
260		t = "unique"
261		d = locale.Unique()
262	case *ArrayContainsError:
263		t = "contains"
264		d = locale.ArrayContains()
265	case *ArrayMinPropertiesError:
266		t = "array_min_properties"
267		d = locale.ArrayMinProperties()
268	case *ArrayMaxPropertiesError:
269		t = "array_max_properties"
270		d = locale.ArrayMaxProperties()
271	case *AdditionalPropertyNotAllowedError:
272		t = "additional_property_not_allowed"
273		d = locale.AdditionalPropertyNotAllowed()
274	case *InvalidPropertyPatternError:
275		t = "invalid_property_pattern"
276		d = locale.InvalidPropertyPattern()
277	case *InvalidPropertyNameError:
278		t = "invalid_property_name"
279		d = locale.InvalidPropertyName()
280	case *StringLengthGTEError:
281		t = "string_gte"
282		d = locale.StringGTE()
283	case *StringLengthLTEError:
284		t = "string_lte"
285		d = locale.StringLTE()
286	case *DoesNotMatchPatternError:
287		t = "pattern"
288		d = locale.DoesNotMatchPattern()
289	case *DoesNotMatchFormatError:
290		t = "format"
291		d = locale.DoesNotMatchFormat()
292	case *MultipleOfError:
293		t = "multiple_of"
294		d = locale.MultipleOf()
295	case *NumberGTEError:
296		t = "number_gte"
297		d = locale.NumberGTE()
298	case *NumberGTError:
299		t = "number_gt"
300		d = locale.NumberGT()
301	case *NumberLTEError:
302		t = "number_lte"
303		d = locale.NumberLTE()
304	case *NumberLTError:
305		t = "number_lt"
306		d = locale.NumberLT()
307	case *ConditionThenError:
308		t = "condition_then"
309		d = locale.ConditionThen()
310	case *ConditionElseError:
311		t = "condition_else"
312		d = locale.ConditionElse()
313	}
314
315	err.SetType(t)
316	err.SetContext(context)
317	err.SetValue(value)
318	err.SetDetails(details)
319	err.SetDescriptionFormat(d)
320	details["field"] = err.Field()
321
322	if _, exists := details["context"]; !exists && context != nil {
323		details["context"] = context.String()
324	}
325
326	err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details))
327}
328
329// formatErrorDescription takes a string in the default text/template
330// format and converts it to a string with replacements. The fields come
331// from the ErrorDetails struct and vary for each type of error.
332func formatErrorDescription(s string, details ErrorDetails) string {
333
334	var tpl *template.Template
335	var descrAsBuffer bytes.Buffer
336	var err error
337
338	errorTemplates.RLock()
339	tpl = errorTemplates.Lookup(s)
340	errorTemplates.RUnlock()
341
342	if tpl == nil {
343		errorTemplates.Lock()
344		tpl = errorTemplates.New(s)
345
346		if ErrorTemplateFuncs != nil {
347			tpl.Funcs(ErrorTemplateFuncs)
348		}
349
350		tpl, err = tpl.Parse(s)
351		errorTemplates.Unlock()
352
353		if err != nil {
354			return err.Error()
355		}
356	}
357
358	err = tpl.Execute(&descrAsBuffer, details)
359	if err != nil {
360		return err.Error()
361	}
362
363	return descrAsBuffer.String()
364}
365