feat(lua): add support for multiple optional types in vim.validate (#16864)

This commit is contained in:
Shadman 2022-01-02 01:35:15 +06:00 committed by GitHub
parent f86039de1e
commit 55c4393e9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 56 additions and 22 deletions

View File

@ -1656,16 +1656,25 @@ validate({opt}) *vim.validate()*
=> error('arg1: expected even number, got 3') => error('arg1: expected even number, got 3')
< <
If multiple types are valid they can be given as a list. >
vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
=> NOP (success)
vim.validate{arg1={1, {'string', table'}}}
=> error('arg1: expected string|table, got number')
<
Parameters: ~ Parameters: ~
{opt} Map of parameter names to validations. Each key is {opt} table of parameter names to validations. Each key
a parameter name; each value is a tuple in one of is a parameter name; each value is a tuple in one
these forms: of these forms:
1. (arg_value, type_name, optional) 1. (arg_value, type_name, optional)
• arg_value: argument value • arg_value: argument value
• type_name: string type name, one of: ("table", • type_name: string|table type name, one of:
"t", "string", "s", "number", "n", "boolean", ("table", "t", "string", "s", "number", "n",
"b", "function", "f", "nil", "thread", "boolean", "b", "function", "f", "nil",
"userdata") "thread", "userdata") or list of them.
• optional: (optional) boolean, if true, `nil` • optional: (optional) boolean, if true, `nil`
is valid is valid

View File

@ -526,13 +526,23 @@ end
--- => error('arg1: expected even number, got 3') --- => error('arg1: expected even number, got 3')
--- </pre> --- </pre>
--- ---
---@param opt Map of parameter names to validations. Each key is a parameter --- If multiple types are valid they can be given as a list.
--- <pre>
--- vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
--- => NOP (success)
---
--- vim.validate{arg1={1, {'string', table'}}}
--- => error('arg1: expected string|table, got number')
---
--- </pre>
---
---@param opt table of parameter names to validations. Each key is a parameter
--- name; each value is a tuple in one of these forms: --- name; each value is a tuple in one of these forms:
--- 1. (arg_value, type_name, optional) --- 1. (arg_value, type_name, optional)
--- - arg_value: argument value --- - arg_value: argument value
--- - type_name: string type name, one of: ("table", "t", "string", --- - type_name: string|table type name, one of: ("table", "t", "string",
--- "s", "number", "n", "boolean", "b", "function", "f", "nil", --- "s", "number", "n", "boolean", "b", "function", "f", "nil",
--- "thread", "userdata") --- "thread", "userdata") or list of them.
--- - optional: (optional) boolean, if true, `nil` is valid --- - optional: (optional) boolean, if true, `nil` is valid
--- 2. (arg_value, fn, msg) --- 2. (arg_value, fn, msg)
--- - arg_value: argument value --- - arg_value: argument value
@ -571,21 +581,16 @@ do
end end
local val = spec[1] -- Argument value. local val = spec[1] -- Argument value.
local t = spec[2] -- Type name, or callable. local types = spec[2] -- Type name, or callable.
local optional = (true == spec[3]) local optional = (true == spec[3])
if type(t) == 'string' then if type(types) == 'string' then
local t_name = type_names[t] types = {types}
if not t_name then end
return false, string.format('invalid type name: %s', t)
end
if (not optional or val ~= nil) and not _is_type(val, t_name) then if vim.is_callable(types) then
return false, string.format("%s: expected %s, got %s", param_name, t_name, type(val))
end
elseif vim.is_callable(t) then
-- Check user-provided validation function. -- Check user-provided validation function.
local valid, optional_message = t(val) local valid, optional_message = types(val)
if not valid then if not valid then
local error_message = string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), val) local error_message = string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), val)
if optional_message ~= nil then if optional_message ~= nil then
@ -594,8 +599,25 @@ do
return false, error_message return false, error_message
end end
elseif type(types) == 'table' then
local success = false
for i, t in ipairs(types) do
local t_name = type_names[t]
if not t_name then
return false, string.format('invalid type name: %s', t)
end
types[i] = t_name
if (optional and val == nil) or _is_type(val, t_name) then
success = true
break
end
end
if not success then
return false, string.format("%s: expected %s, got %s", param_name, table.concat(types, '|'), type(val))
end
else else
return false, string.format("invalid type name: %s", tostring(t)) return false, string.format("invalid type name: %s", tostring(types))
end end
end end

View File

@ -906,6 +906,7 @@ describe('lua stdlib', function()
exec_lua("vim.validate{arg1={nil, 'thread', true }}") exec_lua("vim.validate{arg1={nil, 'thread', true }}")
exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}")
exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}") exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}")
exec_lua("vim.validate{arg1={5, {'n', 's'} }, arg2={ 'foo', {'n', 's'} }}")
matches('expected table, got number', matches('expected table, got number',
pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) pcall_err(exec_lua, "vim.validate{ 1, 'x' }"))
@ -935,6 +936,8 @@ describe('lua stdlib', function()
pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}")) pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}"))
matches('arg1: expected %?, got 3', matches('arg1: expected %?, got 3',
pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}")) pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}"))
matches('arg1: expected number|string, got nil',
pcall_err(exec_lua, "vim.validate{ arg1={ nil, {'n', 's'} }}"))
-- Pass an additional message back. -- Pass an additional message back.
matches('arg1: expected %?, got 3. Info: TEST_MSG', matches('arg1: expected %?, got 3. Info: TEST_MSG',