-
Notifications
You must be signed in to change notification settings - Fork 0
/
validation.lua
381 lines (349 loc) · 9.24 KB
/
validation.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
-- @file validation.lua
-- @author Théo Brigitte <[email protected]>
-- @contributor Henrique Silva <[email protected]>
-- @date Thu May 28 16:05:15 2015
--
-- @brief Lua schema validation library.
--
-- Validation is achieved by matching data against a schema.
--
-- A schema is a representation of the expected structure of the data. It is
-- a combination of what we call "validators".
-- Validators are clojures which build accurante validation function for each
-- element of the schema.
-- Meta-validators allow to extend the logic of the schema by providing an
-- additional logic layer around validators.
-- e.g. optional()
--
-- Import from global environment.
local type = type
local pairs = pairs
local print = print
local format = string.format
local floor = math.floor
local insert = table.insert
local next = next
-- Disable global environment.
if _G.setfenv then
setfenv(1, {})
else -- Lua 5.2.
_ENV = {}
end
local M = { _NAME = 'validation' }
--- Generate error message for validators.
--
-- @param data mixed
-- Value that failed validation.
-- @param expected_type string
-- Expected type for data
--
-- @return
-- String describing the error.
---
local function error_message(data, expected_type)
if data then
return format('is not %s.', expected_type)
end
return format('is missing and should be %s.', expected_type)
end
--- Create a readable string output from the validation errors output.
--
-- @param error_list table
-- Nested table identifying where the error occured.
-- e.g. { price = { rule_value = 'error message' } }
-- @param parents string
-- String of dot separated parents keys
--
-- @return string
-- Message describing where the error occured. e.g. price.rule_value = "error message"
---
function M.print_err(error_list, parents)
-- Makes prefix not nil, for posterior concatenation.
local error_output = ''
local parents = parents or ''
if not error_list then return false end
-- Iterates over the list of messages.
for key, err in pairs(error_list) do
-- If it is a node, print it.
if type(err) == 'string' then
error_output = format('%s\n%s%s %s', error_output, parents ,key, err)
else
-- If it is a table, recurse it.
error_output = format('%s%s', error_output, M.print_err(err, format('%s%s.', parents, key)))
end
end
return error_output
end
--- Validators.
--
-- A validator is a function in charge of verifying data compliance.
--
-- Prototype:
-- @key
-- Key of data being validated.
-- @data
-- Current data tree level. Meta-validator might need to verify other keys. e.g. assert()
--
-- @return
-- true on success, false and message describing the error
---
--- Generates string validator.
--
-- @return
-- String validator function.
---
function M.is_string()
return function(value)
if type(value) ~= 'string' then
return false, error_message(value, 'a string')
end
return true
end
end
--- Generates integer validator.
--
-- @return
-- Integer validator function.
---
function M.is_integer()
return function(value)
if type(value) ~= 'number' or value%1 ~= 0 then
return false, error_message(value, 'an integer')
end
return true
end
end
--- Generates number validator.
--
-- @return
-- Number validator function.
---
function M.is_number()
return function(value)
if type(value) ~= 'number' then
return false, error_message(value, 'a number')
end
return true
end
end
--- Generates boolean validator.
--
-- @return
-- Boolean validator function.
---
function M.is_boolean()
return function(value)
if type(value) ~= 'boolean' then
return false, error_message(value, 'a boolean')
end
return true
end
end
--- Generates an array validator.
--
-- Validate an array by applying same validator to all elements.
--
-- @param validator function
-- Function used to validate the values.
-- @param is_object boolean (optional)
-- When evaluted to false (default), it enforce all key to be of type number.
--
-- @return
-- Array validator function.
-- This validator return value is either true on success or false and
-- a table holding child_validator errors.
---
function M.is_array(child_validator, is_object)
return function(value, key, data)
local result, err = nil
local err_array = {}
-- Iterate the array and validate them.
if type(value) == 'table' then
for index in pairs(value) do
if not is_object and type(index) ~= 'number' then
insert(err_array, error_message(value, 'an array') )
else
result, err = child_validator(value[index], index, value)
if not result then
err_array[index] = err
end
end
end
else
insert(err_array, error_message(value, 'an array') )
end
if next(err_array) == nil then
return true
else
return false, err_array
end
end
end
--- Generates optional validator.
--
-- When data is present apply the given validator on data.
--
-- @param validator function
-- Function used to validate value.
--
-- @return
-- Optional validator function.
-- This validator return true or the result from the given validator.
---
function M.optional(validator)
return function(value, key, data)
if not value then return true
else
return validator(value, key, data)
end
end
end
--- Generates or meta validator.
--
-- Allow data validation using two different validators and applying
-- or condition between results.
--
-- @param validator_a function
-- Function used to validate value.
-- @param validator_b function
-- Function used to validate value.
--
-- @return
-- Or validator function.
-- This validator return true or the result from the given validator.
---
function M.or_op(validator_a, validator_b)
return function(value, key, data)
if not value then return true
else
local valid, err_a = validator_a(value, key, data)
if not valid then
valid, err_b = validator_b(value, key, data)
end
if not valid then
return valid, err_a .. " OR " .. err_b
else
return valid, nil
end
end
end
end
--- Generates assert validator.
--
-- This function enforces the existence of key/value with the
-- verification of the key_check.
--
-- @param key_check mixed
-- Key used to check the optionality of the asserted key.
-- @param match mixed
-- Comparation value.
-- @param validator function
-- Function that validates the type of the data.
--
-- @return
-- Assert validator function.
-- This validator return true, the result from the given validator or false
-- when the assertion fails.
---
function M.assert(key_check, match, validator)
return function(value, key, data)
if data[key_check] == match then
return validator(value, key, data)
else
return true
end
end
end
--- Generates list validator.
--
-- Ensure the value is contained in the given list.
--
-- @param list table
-- Set of allowed values.
-- @param value mixed
-- Comparation value.
-- @param validator function
-- Function that validates the type of the data.
--
-- @return
-- In list validator function.
---
function M.in_list(list)
return function(value)
local printed_list = "["
for _, word in pairs(list) do
if word == value then
return true
end
printed_list = printed_list .. " '" .. word .. "'"
end
printed_list = printed_list .. " ]"
return false, { error_message(value, 'in list ' .. printed_list) }
end
end
--- Generates table validator.
--
-- Validate table data by using appropriate schema.
--
-- @param schema table
-- Schema used to validate the table.
--
-- @return
-- Table validator function.
-- This validator return value is either true on success or false and
-- a nested table holding all errors.
---
function M.is_table(schema, tolerant)
return function(value)
local result, err = nil
if type(value) ~= 'table' then
-- Enforce errors of childs value.
_, err = validate_table({}, schema, tolerant)
if not err then err = {} end
result = false
insert(err, error_message(value, 'a table') )
else
result, err = validate_table(value, schema, tolerant)
end
return result, err
end
end
--- Validate function.
--
-- @param data
-- Table containing the pairs to be validated.
-- @param schema
-- Schema against which the data will be validated.
--
-- @return
-- String describing the error or true.
---
function validate_table(data, schema, tolerant)
-- Array of error messages.
local errs = {}
-- Check if the data is empty.
-- Check if all data keys are present in the schema.
if not tolerant then
for key in pairs(data) do
if schema[key] == nil then
errs[key] = 'is not allowed.'
end
end
end
-- Iterates over the keys of the data table.
for key in pairs(schema) do
-- Calls a function in the table and validates it.
local result, err = schema[key](data[key], key, data)
-- If validation fails, print the result and return it.
if not result then
errs[key] = err
end
end
-- Lua does not give size of table holding only string as keys.
-- Despite the use of #table we have to manually loop over it.
for _ in pairs(errs) do
return false, errs
end
return true
end
return M