1package gojsonschema 2 3import ( 4 "bytes" 5 "sync" 6 "text/template" 7) 8 9var errorTemplates errorTemplate = 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 // RequiredError. ErrorDetails: property string 20 RequiredError struct { 21 ResultErrorFields 22 } 23 24 // InvalidTypeError. ErrorDetails: expected, given 25 InvalidTypeError struct { 26 ResultErrorFields 27 } 28 29 // NumberAnyOfError. ErrorDetails: - 30 NumberAnyOfError struct { 31 ResultErrorFields 32 } 33 34 // NumberOneOfError. ErrorDetails: - 35 NumberOneOfError struct { 36 ResultErrorFields 37 } 38 39 // NumberAllOfError. ErrorDetails: - 40 NumberAllOfError struct { 41 ResultErrorFields 42 } 43 44 // NumberNotError. ErrorDetails: - 45 NumberNotError struct { 46 ResultErrorFields 47 } 48 49 // MissingDependencyError. ErrorDetails: dependency 50 MissingDependencyError struct { 51 ResultErrorFields 52 } 53 54 // InternalError. ErrorDetails: error 55 InternalError struct { 56 ResultErrorFields 57 } 58 59 // ConstError. ErrorDetails: allowed 60 ConstError struct { 61 ResultErrorFields 62 } 63 64 // EnumError. ErrorDetails: allowed 65 EnumError struct { 66 ResultErrorFields 67 } 68 69 // ArrayNoAdditionalItemsError. ErrorDetails: - 70 ArrayNoAdditionalItemsError struct { 71 ResultErrorFields 72 } 73 74 // ArrayMinItemsError. ErrorDetails: min 75 ArrayMinItemsError struct { 76 ResultErrorFields 77 } 78 79 // ArrayMaxItemsError. ErrorDetails: max 80 ArrayMaxItemsError struct { 81 ResultErrorFields 82 } 83 84 // ItemsMustBeUniqueError. ErrorDetails: type, i, j 85 ItemsMustBeUniqueError struct { 86 ResultErrorFields 87 } 88 89 // ArrayContainsError. ErrorDetails: 90 ArrayContainsError struct { 91 ResultErrorFields 92 } 93 94 // ArrayMinPropertiesError. ErrorDetails: min 95 ArrayMinPropertiesError struct { 96 ResultErrorFields 97 } 98 99 // ArrayMaxPropertiesError. ErrorDetails: max 100 ArrayMaxPropertiesError struct { 101 ResultErrorFields 102 } 103 104 // AdditionalPropertyNotAllowedError. ErrorDetails: property 105 AdditionalPropertyNotAllowedError struct { 106 ResultErrorFields 107 } 108 109 // InvalidPropertyPatternError. ErrorDetails: property, pattern 110 InvalidPropertyPatternError struct { 111 ResultErrorFields 112 } 113 114 // InvalidPopertyNameError. ErrorDetails: property 115 InvalidPropertyNameError struct { 116 ResultErrorFields 117 } 118 119 // StringLengthGTEError. ErrorDetails: min 120 StringLengthGTEError struct { 121 ResultErrorFields 122 } 123 124 // StringLengthLTEError. ErrorDetails: max 125 StringLengthLTEError struct { 126 ResultErrorFields 127 } 128 129 // DoesNotMatchPatternError. ErrorDetails: pattern 130 DoesNotMatchPatternError struct { 131 ResultErrorFields 132 } 133 134 // DoesNotMatchFormatError. ErrorDetails: format 135 DoesNotMatchFormatError struct { 136 ResultErrorFields 137 } 138 139 // MultipleOfError. ErrorDetails: multiple 140 MultipleOfError struct { 141 ResultErrorFields 142 } 143 144 // NumberGTEError. ErrorDetails: min 145 NumberGTEError struct { 146 ResultErrorFields 147 } 148 149 // NumberGTError. ErrorDetails: min 150 NumberGTError struct { 151 ResultErrorFields 152 } 153 154 // NumberLTEError. ErrorDetails: max 155 NumberLTEError struct { 156 ResultErrorFields 157 } 158 159 // NumberLTError. ErrorDetails: max 160 NumberLTError struct { 161 ResultErrorFields 162 } 163 164 // ConditionThenError. ErrorDetails: - 165 ConditionThenError struct { 166 ResultErrorFields 167 } 168 169 // ConditionElseError. ErrorDetails: - 170 ConditionElseError struct { 171 ResultErrorFields 172 } 173) 174 175// newError takes a ResultError type and sets the type, context, description, details, value, and field 176func newError(err ResultError, context *JsonContext, value interface{}, locale locale, details ErrorDetails) { 177 var t string 178 var d string 179 switch err.(type) { 180 case *RequiredError: 181 t = "required" 182 d = locale.Required() 183 case *InvalidTypeError: 184 t = "invalid_type" 185 d = locale.InvalidType() 186 case *NumberAnyOfError: 187 t = "number_any_of" 188 d = locale.NumberAnyOf() 189 case *NumberOneOfError: 190 t = "number_one_of" 191 d = locale.NumberOneOf() 192 case *NumberAllOfError: 193 t = "number_all_of" 194 d = locale.NumberAllOf() 195 case *NumberNotError: 196 t = "number_not" 197 d = locale.NumberNot() 198 case *MissingDependencyError: 199 t = "missing_dependency" 200 d = locale.MissingDependency() 201 case *InternalError: 202 t = "internal" 203 d = locale.Internal() 204 case *ConstError: 205 t = "const" 206 d = locale.Const() 207 case *EnumError: 208 t = "enum" 209 d = locale.Enum() 210 case *ArrayNoAdditionalItemsError: 211 t = "array_no_additional_items" 212 d = locale.ArrayNoAdditionalItems() 213 case *ArrayMinItemsError: 214 t = "array_min_items" 215 d = locale.ArrayMinItems() 216 case *ArrayMaxItemsError: 217 t = "array_max_items" 218 d = locale.ArrayMaxItems() 219 case *ItemsMustBeUniqueError: 220 t = "unique" 221 d = locale.Unique() 222 case *ArrayContainsError: 223 t = "contains" 224 d = locale.ArrayContains() 225 case *ArrayMinPropertiesError: 226 t = "array_min_properties" 227 d = locale.ArrayMinProperties() 228 case *ArrayMaxPropertiesError: 229 t = "array_max_properties" 230 d = locale.ArrayMaxProperties() 231 case *AdditionalPropertyNotAllowedError: 232 t = "additional_property_not_allowed" 233 d = locale.AdditionalPropertyNotAllowed() 234 case *InvalidPropertyPatternError: 235 t = "invalid_property_pattern" 236 d = locale.InvalidPropertyPattern() 237 case *InvalidPropertyNameError: 238 t = "invalid_property_name" 239 d = locale.InvalidPropertyName() 240 case *StringLengthGTEError: 241 t = "string_gte" 242 d = locale.StringGTE() 243 case *StringLengthLTEError: 244 t = "string_lte" 245 d = locale.StringLTE() 246 case *DoesNotMatchPatternError: 247 t = "pattern" 248 d = locale.DoesNotMatchPattern() 249 case *DoesNotMatchFormatError: 250 t = "format" 251 d = locale.DoesNotMatchFormat() 252 case *MultipleOfError: 253 t = "multiple_of" 254 d = locale.MultipleOf() 255 case *NumberGTEError: 256 t = "number_gte" 257 d = locale.NumberGTE() 258 case *NumberGTError: 259 t = "number_gt" 260 d = locale.NumberGT() 261 case *NumberLTEError: 262 t = "number_lte" 263 d = locale.NumberLTE() 264 case *NumberLTError: 265 t = "number_lt" 266 d = locale.NumberLT() 267 case *ConditionThenError: 268 t = "condition_then" 269 d = locale.ConditionThen() 270 case *ConditionElseError: 271 t = "condition_else" 272 d = locale.ConditionElse() 273 } 274 275 err.SetType(t) 276 err.SetContext(context) 277 err.SetValue(value) 278 err.SetDetails(details) 279 err.SetDescriptionFormat(d) 280 details["field"] = err.Field() 281 282 if _, exists := details["context"]; !exists && context != nil { 283 details["context"] = context.String() 284 } 285 286 err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details)) 287} 288 289// formatErrorDescription takes a string in the default text/template 290// format and converts it to a string with replacements. The fields come 291// from the ErrorDetails struct and vary for each type of error. 292func formatErrorDescription(s string, details ErrorDetails) string { 293 294 var tpl *template.Template 295 var descrAsBuffer bytes.Buffer 296 var err error 297 298 errorTemplates.RLock() 299 tpl = errorTemplates.Lookup(s) 300 errorTemplates.RUnlock() 301 302 if tpl == nil { 303 errorTemplates.Lock() 304 tpl = errorTemplates.New(s) 305 306 if ErrorTemplateFuncs != nil { 307 tpl.Funcs(ErrorTemplateFuncs) 308 } 309 310 tpl, err = tpl.Parse(s) 311 errorTemplates.Unlock() 312 313 if err != nil { 314 return err.Error() 315 } 316 } 317 318 err = tpl.Execute(&descrAsBuffer, details) 319 if err != nil { 320 return err.Error() 321 } 322 323 return descrAsBuffer.String() 324} 325