1 package gojsonschema
2
3 import (
4 "net"
5 "net/mail"
6 "net/url"
7 "regexp"
8 "strings"
9 "sync"
10 "time"
11 )
12
13 type (
14
15 FormatChecker interface {
16
17 IsFormat(input interface{}) bool
18 }
19
20
21 FormatCheckerChain struct {
22 formatters map[string]FormatChecker
23 }
24
25
26 EmailFormatChecker struct{}
27
28
29 IPV4FormatChecker struct{}
30
31
32 IPV6FormatChecker struct{}
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 DateTimeFormatChecker struct{}
56
57
58
59
60
61
62
63
64
65
66 DateFormatChecker struct{}
67
68
69
70
71
72
73
74
75
76
77
78
79
80 TimeFormatChecker struct{}
81
82
83 URIFormatChecker struct{}
84
85
86 URIReferenceFormatChecker struct{}
87
88
89 URITemplateFormatChecker struct{}
90
91
92 HostnameFormatChecker struct{}
93
94
95 UUIDFormatChecker struct{}
96
97
98 RegexFormatChecker struct{}
99
100
101 JSONPointerFormatChecker struct{}
102
103
104 RelativeJSONPointerFormatChecker struct{}
105 )
106
107 var (
108
109
110 FormatCheckers = FormatCheckerChain{
111 formatters: map[string]FormatChecker{
112 "date": DateFormatChecker{},
113 "time": TimeFormatChecker{},
114 "date-time": DateTimeFormatChecker{},
115 "hostname": HostnameFormatChecker{},
116 "email": EmailFormatChecker{},
117 "idn-email": EmailFormatChecker{},
118 "ipv4": IPV4FormatChecker{},
119 "ipv6": IPV6FormatChecker{},
120 "uri": URIFormatChecker{},
121 "uri-reference": URIReferenceFormatChecker{},
122 "iri": URIFormatChecker{},
123 "iri-reference": URIReferenceFormatChecker{},
124 "uri-template": URITemplateFormatChecker{},
125 "uuid": UUIDFormatChecker{},
126 "regex": RegexFormatChecker{},
127 "json-pointer": JSONPointerFormatChecker{},
128 "relative-json-pointer": RelativeJSONPointerFormatChecker{},
129 },
130 }
131
132
133 rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`)
134
135
136 rxURITemplate = regexp.MustCompile("^([^{]*({[^}]*})?)*$")
137
138 rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
139
140 rxJSONPointer = regexp.MustCompile("^(?:/(?:[^~/]|~0|~1)*)*$")
141
142 rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$")
143
144 lock = new(sync.RWMutex)
145 )
146
147
148
149 func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain {
150 lock.Lock()
151 c.formatters[name] = f
152 lock.Unlock()
153
154 return c
155 }
156
157
158 func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain {
159 lock.Lock()
160 delete(c.formatters, name)
161 lock.Unlock()
162
163 return c
164 }
165
166
167 func (c *FormatCheckerChain) Has(name string) bool {
168 lock.RLock()
169 _, ok := c.formatters[name]
170 lock.RUnlock()
171
172 return ok
173 }
174
175
176
177 func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool {
178 lock.RLock()
179 f, ok := c.formatters[name]
180 lock.RUnlock()
181
182
183 if !ok {
184 return true
185 }
186
187 return f.IsFormat(input)
188 }
189
190
191 func (f EmailFormatChecker) IsFormat(input interface{}) bool {
192 asString, ok := input.(string)
193 if !ok {
194 return false
195 }
196
197 _, err := mail.ParseAddress(asString)
198 return err == nil
199 }
200
201
202 func (f IPV4FormatChecker) IsFormat(input interface{}) bool {
203 asString, ok := input.(string)
204 if !ok {
205 return false
206 }
207
208
209 ip := net.ParseIP(asString)
210 return ip != nil && strings.Contains(asString, ".")
211 }
212
213
214 func (f IPV6FormatChecker) IsFormat(input interface{}) bool {
215 asString, ok := input.(string)
216 if !ok {
217 return false
218 }
219
220
221 ip := net.ParseIP(asString)
222 return ip != nil && strings.Contains(asString, ":")
223 }
224
225
226 func (f DateTimeFormatChecker) IsFormat(input interface{}) bool {
227 asString, ok := input.(string)
228 if !ok {
229 return false
230 }
231
232 formats := []string{
233 "15:04:05",
234 "15:04:05Z07:00",
235 "2006-01-02",
236 time.RFC3339,
237 time.RFC3339Nano,
238 }
239
240 for _, format := range formats {
241 if _, err := time.Parse(format, asString); err == nil {
242 return true
243 }
244 }
245
246 return false
247 }
248
249
250 func (f DateFormatChecker) IsFormat(input interface{}) bool {
251 asString, ok := input.(string)
252 if !ok {
253 return false
254 }
255 _, err := time.Parse("2006-01-02", asString)
256 return err == nil
257 }
258
259
260 func (f TimeFormatChecker) IsFormat(input interface{}) bool {
261 asString, ok := input.(string)
262 if !ok {
263 return false
264 }
265
266 if _, err := time.Parse("15:04:05Z07:00", asString); err == nil {
267 return true
268 }
269
270 _, err := time.Parse("15:04:05", asString)
271 return err == nil
272 }
273
274
275 func (f URIFormatChecker) IsFormat(input interface{}) bool {
276 asString, ok := input.(string)
277 if !ok {
278 return false
279 }
280
281 u, err := url.Parse(asString)
282
283 if err != nil || u.Scheme == "" {
284 return false
285 }
286
287 return !strings.Contains(asString, `\`)
288 }
289
290
291 func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
292 asString, ok := input.(string)
293 if !ok {
294 return false
295 }
296
297 _, err := url.Parse(asString)
298 return err == nil && !strings.Contains(asString, `\`)
299 }
300
301
302 func (f URITemplateFormatChecker) IsFormat(input interface{}) bool {
303 asString, ok := input.(string)
304 if !ok {
305 return false
306 }
307
308 u, err := url.Parse(asString)
309 if err != nil || strings.Contains(asString, `\`) {
310 return false
311 }
312
313 return rxURITemplate.MatchString(u.Path)
314 }
315
316
317 func (f HostnameFormatChecker) IsFormat(input interface{}) bool {
318 asString, ok := input.(string)
319 if !ok {
320 return false
321 }
322
323 return rxHostname.MatchString(asString) && len(asString) < 256
324 }
325
326
327 func (f UUIDFormatChecker) IsFormat(input interface{}) bool {
328 asString, ok := input.(string)
329 if !ok {
330 return false
331 }
332
333 return rxUUID.MatchString(asString)
334 }
335
336
337 func (f RegexFormatChecker) IsFormat(input interface{}) bool {
338 asString, ok := input.(string)
339 if !ok {
340 return false
341 }
342
343 if asString == "" {
344 return true
345 }
346 _, err := regexp.Compile(asString)
347 return err == nil
348 }
349
350
351 func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool {
352 asString, ok := input.(string)
353 if !ok {
354 return false
355 }
356
357 return rxJSONPointer.MatchString(asString)
358 }
359
360
361 func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool {
362 asString, ok := input.(string)
363 if !ok {
364 return false
365 }
366
367 return rxRelJSONPointer.MatchString(asString)
368 }
369
View as plain text