Lua: Use vim.validate() instead of assert()

This commit is contained in:
Justin M. Keyes
2019-11-10 22:18:59 -08:00
parent 7aa4042d3b
commit a0d992785f
3 changed files with 102 additions and 33 deletions

View File

@@ -847,4 +847,63 @@ pesc({s}) *vim.pesc()*
See also: ~ See also: ~
https://github.com/rxi/lume https://github.com/rxi/lume
validate({opt}) *vim.validate()*
Validates a parameter specification (types and values).
Usage example: >
function user.new(name, age, hobbies)
vim.validate{
name={name, 'string'},
age={age, 'number'},
hobbies={hobbies, 'table'},
}
...
end
<
Examples with explicit argument values (can be run directly): >
vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
=> NOP (success)
<
>
vim.validate{arg1={1, 'table'}}
=> error('arg1: expected table, got number')
<
>
vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
=> error('arg1: expected even number, got 3')
<
Parameters: ~
{opt} Map of parameter names to validations. Each key is
a parameter name; each value is a tuple in one of
these forms:
1. (arg_value, type_name, optional)
• arg_value: argument value
• type_name: string type name, one of: ("table",
"t", "string", "s", "number", "n", "boolean",
"b", "function", "f", "nil", "thread",
"userdata")
• optional: (optional) boolean, if true, `nil`
is valid
2. (arg_value, fn, msg)
• arg_value: argument value
• fn: any function accepting one argument,
returns true if and only if the argument is
valid
• msg: (optional) error string if validation
fails
is_callable({f}) *vim.is_callable()*
Returns true if object `f` can be called as a function.
Parameters: ~
{f} Any object
Return: ~
true if `f` is callable, else false
vim:tw=78:ts=8:ft=help:norl: vim:tw=78:ts=8:ft=help:norl:

View File

@@ -47,9 +47,7 @@ end)()
--@param plain If `true` use `sep` literally (passed to String.find) --@param plain If `true` use `sep` literally (passed to String.find)
--@returns Iterator over the split components --@returns Iterator over the split components
function vim.gsplit(s, sep, plain) function vim.gsplit(s, sep, plain)
assert(type(s) == "string", string.format("Expected string, got %s", type(s))) vim.validate{s={s,'s'},sep={sep,'s'},plain={plain,'b',true}}
assert(type(sep) == "string", string.format("Expected string, got %s", type(sep)))
assert(type(plain) == "boolean" or type(plain) == "nil", string.format("Expected boolean or nil, got %s", type(plain)))
local start = 1 local start = 1
local done = false local done = false
@@ -106,7 +104,7 @@ end
--@param value Value to compare --@param value Value to compare
--@returns true if `t` contains `value` --@returns true if `t` contains `value`
function vim.tbl_contains(t, value) function vim.tbl_contains(t, value)
assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) vim.validate{t={t,'t'}}
for _,v in ipairs(t) do for _,v in ipairs(t) do
if v == value then if v == value then
@@ -176,7 +174,7 @@ end
--@param s String to trim --@param s String to trim
--@returns String with whitespace removed from its beginning and end --@returns String with whitespace removed from its beginning and end
function vim.trim(s) function vim.trim(s)
assert(type(s) == 'string', string.format("Expected string, got %s", type(s))) vim.validate{s={s,'s'}}
return s:match('^%s*(.*%S)') or '' return s:match('^%s*(.*%S)') or ''
end end
@@ -186,13 +184,13 @@ end
--@param s String to escape --@param s String to escape
--@returns %-escaped pattern string --@returns %-escaped pattern string
function vim.pesc(s) function vim.pesc(s)
assert(type(s) == 'string', string.format("Expected string, got %s", type(s))) vim.validate{s={s,'s'}}
return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
end end
--- Validates a parameter specification (types and values). --- Validates a parameter specification (types and values).
--- ---
--- Examples: --- Usage example:
--- <pre> --- <pre>
--- function user.new(name, age, hobbies) --- function user.new(name, age, hobbies)
--- vim.validate{ --- vim.validate{
@@ -202,26 +200,29 @@ end
--- } --- }
--- ... --- ...
--- end --- end
-- --- </pre>
--- vim.validate{ arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} ---
--- Examples with explicit argument values (can be run directly):
--- <pre>
--- vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
--- => NOP (success) --- => NOP (success)
---
--- vim.validate{arg1={1, 'table'}} --- vim.validate{arg1={1, 'table'}}
--- => error("arg1: expected table, got number") --- => error('arg1: expected table, got number')
--- vim.validate{arg1={{'foo'}, 'table'}, arg2={1, 'string'}} ---
--- => error("arg2: expected string, got number")
--- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} --- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
--- => 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 --@param opt Map 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 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")
--- - 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
--- - fn: any function accepting one argument, returns true if and --- - fn: any function accepting one argument, returns true if and
--- only if the argument is valid --- only if the argument is valid
@@ -234,14 +235,14 @@ vim.validate = (function()
['boolean']='boolean', ['function']='function', ['callable']='callable', ['boolean']='boolean', ['function']='function', ['callable']='callable',
['nil']='nil', ['thread']='thread', ['userdata']='userdata', ['nil']='nil', ['thread']='thread', ['userdata']='userdata',
} }
local function type_name(t) local function _type_name(t)
local tname = type_names[t] local tname = type_names[t]
if tname == nil then if tname == nil then
error(string.format('invalid type name: %s', tostring(t))) error(string.format('invalid type name: %s', tostring(t)))
end end
return tname return tname
end end
local function is_type(val, t) local function _is_type(val, t)
return t == 'callable' and vim.is_callable(val) or type(val) == t return t == 'callable' and vim.is_callable(val) or type(val) == t
end end
@@ -255,21 +256,21 @@ vim.validate = (function()
local optional = (true == spec[3]) local optional = (true == spec[3])
if not vim.is_callable(t) then -- Check type name. if not vim.is_callable(t) then -- Check type name.
if not (optional or type(val) == 'nil') and not is_type(val, type_name(t)) then if (not optional or val ~= nil) and not _is_type(val, _type_name(t)) then
error(string.format("%s: expected %s, got %s", param_name, type_name(t), type(val))) error(string.format("%s: expected %s, got %s", param_name, _type_name(t), type(val)))
end end
elseif not t(val) then -- Check user-provided validation function. elseif not t(val) then -- Check user-provided validation function.
error(string.format("%s: expected %s, got %s", param_name, spec[3], val)) error(string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), val))
end end
end end
return true return true
end end
end)() end)()
--- Return whether an object can call be used as a function. --- Returns true if object `f` can be called as a function.
--- ---
--@param f Any type of variable --@param f Any object
--@return Boolean --@return true if `f` is callable, else false
function vim.is_callable(f) function vim.is_callable(f)
if type(f) == 'function' then return true end if type(f) == 'function' then return true end
local m = getmetatable(f) local m = getmetatable(f)

View File

@@ -230,12 +230,12 @@ describe('lua stdlib', function()
end end
-- Validates args. -- Validates args.
eq(true, pcall(split, 'string', 'string', nil)) eq(true, pcall(split, 'string', 'string'))
eq('Error executing lua: .../shared.lua: Expected string, got number', eq('Error executing lua: .../shared.lua: s: expected string, got number',
pcall_err(split, 1, 'string', nil)) pcall_err(split, 1, 'string'))
eq('Error executing lua: .../shared.lua: Expected string, got number', eq('Error executing lua: .../shared.lua: sep: expected string, got number',
pcall_err(split, 'string', 1, nil)) pcall_err(split, 'string', 1))
eq('Error executing lua: .../shared.lua: Expected boolean or nil, got number', eq('Error executing lua: .../shared.lua: plain: expected boolean, got number',
pcall_err(split, 'string', 'string', 1)) pcall_err(split, 'string', 'string', 1))
end) end)
@@ -256,7 +256,7 @@ describe('lua stdlib', function()
end end
-- Validates args. -- Validates args.
eq('Error executing lua: .../shared.lua: Expected string, got number', eq('Error executing lua: .../shared.lua: s: expected string, got number',
pcall_err(trim, 2)) pcall_err(trim, 2))
end) end)
@@ -301,7 +301,7 @@ describe('lua stdlib', function()
eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]]))
-- Validates args. -- Validates args.
eq("Error executing lua: .../shared.lua: Expected string, got number", eq('Error executing lua: .../shared.lua: s: expected string, got number',
pcall_err(exec_lua, [[return vim.pesc(2)]])) pcall_err(exec_lua, [[return vim.pesc(2)]]))
end) end)
@@ -441,16 +441,25 @@ describe('lua stdlib', function()
eq("Error executing lua: .../shared.lua: invalid type name: nil", eq("Error executing lua: .../shared.lua: invalid type name: nil",
pcall_err(exec_lua, "vim.validate{ arg1={ 1 }}")) pcall_err(exec_lua, "vim.validate{ arg1={ 1 }}"))
-- Validated parameters are required by default.
eq("Error executing lua: .../shared.lua: arg1: expected string, got nil",
pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's' }}"))
-- Explicitly required.
eq("Error executing lua: .../shared.lua: arg1: expected string, got nil",
pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's', false }}"))
eq("Error executing lua: .../shared.lua: arg1: expected table, got number", eq("Error executing lua: .../shared.lua: arg1: expected table, got number",
pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}")) pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}"))
eq("Error executing lua: .../shared.lua: arg2: expected string, got number", eq("Error executing lua: .../shared.lua: arg2: expected string, got number",
pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}")) pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}"))
eq("Error executing lua: .../shared.lua: arg2: expected string, got nil", eq("Error executing lua: .../shared.lua: arg2: expected string, got nil",
pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}"))
eq("Error executing lua: .../shared.lua: arg2: expected string, got nil", eq("Error executing lua: .../shared.lua: arg2: expected string, got nil",
pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}"))
eq("Error executing lua: .../shared.lua: arg1: expected even number, got 3", eq("Error executing lua: .../shared.lua: arg1: expected even number, got 3",
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'}}"))
eq("Error executing lua: .../shared.lua: arg1: expected ?, got 3",
pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}"))
end) end)
it('vim.is_callable', function() it('vim.is_callable', function()