errors.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. package gojsonschema
  2. import (
  3. "bytes"
  4. "sync"
  5. "text/template"
  6. )
  7. var errorTemplates errorTemplate = errorTemplate{template.New("errors-new"), sync.RWMutex{}}
  8. // template.Template is not thread-safe for writing, so some locking is done
  9. // sync.RWMutex is used for efficiently locking when new templates are created
  10. type errorTemplate struct {
  11. *template.Template
  12. sync.RWMutex
  13. }
  14. type (
  15. // RequiredError. ErrorDetails: property string
  16. RequiredError struct {
  17. ResultErrorFields
  18. }
  19. // InvalidTypeError. ErrorDetails: expected, given
  20. InvalidTypeError struct {
  21. ResultErrorFields
  22. }
  23. // NumberAnyOfError. ErrorDetails: -
  24. NumberAnyOfError struct {
  25. ResultErrorFields
  26. }
  27. // NumberOneOfError. ErrorDetails: -
  28. NumberOneOfError struct {
  29. ResultErrorFields
  30. }
  31. // NumberAllOfError. ErrorDetails: -
  32. NumberAllOfError struct {
  33. ResultErrorFields
  34. }
  35. // NumberNotError. ErrorDetails: -
  36. NumberNotError struct {
  37. ResultErrorFields
  38. }
  39. // MissingDependencyError. ErrorDetails: dependency
  40. MissingDependencyError struct {
  41. ResultErrorFields
  42. }
  43. // InternalError. ErrorDetails: error
  44. InternalError struct {
  45. ResultErrorFields
  46. }
  47. // EnumError. ErrorDetails: allowed
  48. EnumError struct {
  49. ResultErrorFields
  50. }
  51. // ArrayNoAdditionalItemsError. ErrorDetails: -
  52. ArrayNoAdditionalItemsError struct {
  53. ResultErrorFields
  54. }
  55. // ArrayMinItemsError. ErrorDetails: min
  56. ArrayMinItemsError struct {
  57. ResultErrorFields
  58. }
  59. // ArrayMaxItemsError. ErrorDetails: max
  60. ArrayMaxItemsError struct {
  61. ResultErrorFields
  62. }
  63. // ItemsMustBeUniqueError. ErrorDetails: type
  64. ItemsMustBeUniqueError struct {
  65. ResultErrorFields
  66. }
  67. // ArrayMinPropertiesError. ErrorDetails: min
  68. ArrayMinPropertiesError struct {
  69. ResultErrorFields
  70. }
  71. // ArrayMaxPropertiesError. ErrorDetails: max
  72. ArrayMaxPropertiesError struct {
  73. ResultErrorFields
  74. }
  75. // AdditionalPropertyNotAllowedError. ErrorDetails: property
  76. AdditionalPropertyNotAllowedError struct {
  77. ResultErrorFields
  78. }
  79. // InvalidPropertyPatternError. ErrorDetails: property, pattern
  80. InvalidPropertyPatternError struct {
  81. ResultErrorFields
  82. }
  83. // StringLengthGTEError. ErrorDetails: min
  84. StringLengthGTEError struct {
  85. ResultErrorFields
  86. }
  87. // StringLengthLTEError. ErrorDetails: max
  88. StringLengthLTEError struct {
  89. ResultErrorFields
  90. }
  91. // DoesNotMatchPatternError. ErrorDetails: pattern
  92. DoesNotMatchPatternError struct {
  93. ResultErrorFields
  94. }
  95. // DoesNotMatchFormatError. ErrorDetails: format
  96. DoesNotMatchFormatError struct {
  97. ResultErrorFields
  98. }
  99. // MultipleOfError. ErrorDetails: multiple
  100. MultipleOfError struct {
  101. ResultErrorFields
  102. }
  103. // NumberGTEError. ErrorDetails: min
  104. NumberGTEError struct {
  105. ResultErrorFields
  106. }
  107. // NumberGTError. ErrorDetails: min
  108. NumberGTError struct {
  109. ResultErrorFields
  110. }
  111. // NumberLTEError. ErrorDetails: max
  112. NumberLTEError struct {
  113. ResultErrorFields
  114. }
  115. // NumberLTError. ErrorDetails: max
  116. NumberLTError struct {
  117. ResultErrorFields
  118. }
  119. )
  120. // newError takes a ResultError type and sets the type, context, description, details, value, and field
  121. func newError(err ResultError, context *jsonContext, value interface{}, locale locale, details ErrorDetails) {
  122. var t string
  123. var d string
  124. switch err.(type) {
  125. case *RequiredError:
  126. t = "required"
  127. d = locale.Required()
  128. case *InvalidTypeError:
  129. t = "invalid_type"
  130. d = locale.InvalidType()
  131. case *NumberAnyOfError:
  132. t = "number_any_of"
  133. d = locale.NumberAnyOf()
  134. case *NumberOneOfError:
  135. t = "number_one_of"
  136. d = locale.NumberOneOf()
  137. case *NumberAllOfError:
  138. t = "number_all_of"
  139. d = locale.NumberAllOf()
  140. case *NumberNotError:
  141. t = "number_not"
  142. d = locale.NumberNot()
  143. case *MissingDependencyError:
  144. t = "missing_dependency"
  145. d = locale.MissingDependency()
  146. case *InternalError:
  147. t = "internal"
  148. d = locale.Internal()
  149. case *EnumError:
  150. t = "enum"
  151. d = locale.Enum()
  152. case *ArrayNoAdditionalItemsError:
  153. t = "array_no_additional_items"
  154. d = locale.ArrayNoAdditionalItems()
  155. case *ArrayMinItemsError:
  156. t = "array_min_items"
  157. d = locale.ArrayMinItems()
  158. case *ArrayMaxItemsError:
  159. t = "array_max_items"
  160. d = locale.ArrayMaxItems()
  161. case *ItemsMustBeUniqueError:
  162. t = "unique"
  163. d = locale.Unique()
  164. case *ArrayMinPropertiesError:
  165. t = "array_min_properties"
  166. d = locale.ArrayMinProperties()
  167. case *ArrayMaxPropertiesError:
  168. t = "array_max_properties"
  169. d = locale.ArrayMaxProperties()
  170. case *AdditionalPropertyNotAllowedError:
  171. t = "additional_property_not_allowed"
  172. d = locale.AdditionalPropertyNotAllowed()
  173. case *InvalidPropertyPatternError:
  174. t = "invalid_property_pattern"
  175. d = locale.InvalidPropertyPattern()
  176. case *StringLengthGTEError:
  177. t = "string_gte"
  178. d = locale.StringGTE()
  179. case *StringLengthLTEError:
  180. t = "string_lte"
  181. d = locale.StringLTE()
  182. case *DoesNotMatchPatternError:
  183. t = "pattern"
  184. d = locale.DoesNotMatchPattern()
  185. case *DoesNotMatchFormatError:
  186. t = "format"
  187. d = locale.DoesNotMatchFormat()
  188. case *MultipleOfError:
  189. t = "multiple_of"
  190. d = locale.MultipleOf()
  191. case *NumberGTEError:
  192. t = "number_gte"
  193. d = locale.NumberGTE()
  194. case *NumberGTError:
  195. t = "number_gt"
  196. d = locale.NumberGT()
  197. case *NumberLTEError:
  198. t = "number_lte"
  199. d = locale.NumberLTE()
  200. case *NumberLTError:
  201. t = "number_lt"
  202. d = locale.NumberLT()
  203. }
  204. err.SetType(t)
  205. err.SetContext(context)
  206. err.SetValue(value)
  207. err.SetDetails(details)
  208. details["field"] = err.Field()
  209. if _, exists := details["context"]; !exists && context != nil {
  210. details["context"] = context.String()
  211. }
  212. err.SetDescription(formatErrorDescription(d, details))
  213. }
  214. // formatErrorDescription takes a string in the default text/template
  215. // format and converts it to a string with replacements. The fields come
  216. // from the ErrorDetails struct and vary for each type of error.
  217. func formatErrorDescription(s string, details ErrorDetails) string {
  218. var tpl *template.Template
  219. var descrAsBuffer bytes.Buffer
  220. var err error
  221. errorTemplates.RLock()
  222. tpl = errorTemplates.Lookup(s)
  223. errorTemplates.RUnlock()
  224. if tpl == nil {
  225. errorTemplates.Lock()
  226. tpl = errorTemplates.New(s)
  227. if ErrorTemplateFuncs != nil {
  228. tpl.Funcs(ErrorTemplateFuncs)
  229. }
  230. tpl, err = tpl.Parse(s)
  231. errorTemplates.Unlock()
  232. if err != nil {
  233. return err.Error()
  234. }
  235. }
  236. err = tpl.Execute(&descrAsBuffer, details)
  237. if err != nil {
  238. return err.Error()
  239. }
  240. return descrAsBuffer.String()
  241. }