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