-
Notifications
You must be signed in to change notification settings - Fork 242
/
types.lua
195 lines (179 loc) · 5.95 KB
/
types.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
---- Dealing with Detailed Type Information
-- Dependencies `pl.utils`
-- @module pl.types
local utils = require 'pl.utils'
local math_ceil = math.ceil
local assert_arg = utils.assert_arg
local types = {}
do
-- we prefer debug.getmetatable, but only if available
local gmt = (debug or {}).getmetatable or getmetatable
--- is the object either a function or a callable object?.
-- @param obj Object to check.
function types.is_callable (obj)
if type(obj) == 'function' then
return true
end
local mt = gmt(obj)
if not mt then
return false
end
return type(rawget(mt, "__call")) == "function"
end
end
--- is the object of the specified type?.
-- If the type is a string, then use type, otherwise compare with metatable.
--
-- NOTE: this function is imported from `utils.is_type`.
-- @param obj An object to check
-- @param tp The expected type
-- @function is_type
-- @see utils.is_type
types.is_type = utils.is_type
local fileMT = getmetatable(io.stdout)
--- a string representation of a type.
-- For tables and userdata with metatables, we assume that the metatable has a `_name`
-- field. If the field is not present it will return 'unknown table' or
-- 'unknown userdata'.
-- Lua file objects return the type 'file'.
-- @param obj an object
-- @return a string like 'number', 'table', 'file' or 'List'
function types.type (obj)
local t = type(obj)
if t == 'table' or t == 'userdata' then
local mt = getmetatable(obj)
if mt == fileMT then
return 'file'
elseif mt == nil then
return t
else
-- TODO: the "unknown" is weird, it should just return the type
return mt._name or "unknown "..t
end
else
return t
end
end
--- is this number an integer?
-- @param x a number
-- @raise error if x is not a number
-- @return boolean
function types.is_integer (x)
return math_ceil(x)==x
end
--- Check if the object is "empty".
-- An object is considered empty if it is:
--
-- - `nil`
-- - a table without any items (key-value pairs or indexes)
-- - a string with no content ("")
-- - not a nil/table/string
-- @param o The object to check if it is empty.
-- @param ignore_spaces If the object is a string and this is true the string is
-- considered empty if it only contains spaces.
-- @return `true` if the object is empty, otherwise a falsy value.
function types.is_empty(o, ignore_spaces)
if o == nil then
return true
elseif type(o) == "table" then
return next(o) == nil
elseif type(o) == "string" then
return o == "" or (not not ignore_spaces and (not not o:find("^%s+$")))
else
return true
end
end
local function check_meta (val)
if type(val) == 'table' then return true end
return getmetatable(val)
end
--- is an object 'array-like'?
-- An object is array like if:
--
-- - it is a table, or
-- - it has a metatable with `__len` and `__index` methods
--
-- NOTE: since `__len` is 5.2+, on 5.1 is usually returns `false` for userdata
-- @param val any value.
-- @return `true` if the object is array-like, otherwise a falsy value.
function types.is_indexable (val)
local mt = check_meta(val)
if mt == true then return true end
return mt and mt.__len and mt.__index and true
end
--- can an object be iterated over with `pairs`?
-- An object is iterable if:
--
-- - it is a table, or
-- - it has a metatable with a `__pairs` meta method
--
-- NOTE: since `__pairs` is 5.2+, on 5.1 is usually returns `false` for userdata
-- @param val any value.
-- @return `true` if the object is iterable, otherwise a falsy value.
function types.is_iterable (val)
local mt = check_meta(val)
if mt == true then return true end
return mt and mt.__pairs and true
end
--- can an object accept new key/pair values?
-- An object is iterable if:
--
-- - it is a table, or
-- - it has a metatable with a `__newindex` meta method
--
-- @param val any value.
-- @return `true` if the object is writeable, otherwise a falsy value.
function types.is_writeable (val)
local mt = check_meta(val)
if mt == true then return true end
return mt and mt.__newindex and true
end
-- Strings that should evaluate to true. -- TODO: add on/off ???
local trues = { yes=true, y=true, ["true"]=true, t=true, ["1"]=true }
-- Conditions types should evaluate to true.
local true_types = {
boolean=function(o, true_strs, check_objs) return o end,
string=function(o, true_strs, check_objs)
o = o:lower()
if trues[o] then
return true
end
-- Check alternative user provided strings.
for _,v in ipairs(true_strs or {}) do
if type(v) == "string" and o == v:lower() then
return true
end
end
return false
end,
number=function(o, true_strs, check_objs) return o ~= 0 end,
table=function(o, true_strs, check_objs) if check_objs and next(o) ~= nil then return true end return false end
}
--- Convert to a boolean value.
-- True values are:
--
-- * boolean: true.
-- * string: 'yes', 'y', 'true', 't', '1' or additional strings specified by `true_strs`.
-- * number: Any non-zero value.
-- * table: Is not empty and `check_objs` is true.
-- * everything else: Is not `nil` and `check_objs` is true.
--
-- @param o The object to evaluate.
-- @param[opt] true_strs optional Additional strings that when matched should evaluate to true. Comparison is case insensitive.
-- This should be a List of strings. E.g. "ja" to support German.
-- @param[opt] check_objs True if objects should be evaluated.
-- @return true if the input evaluates to true, otherwise false.
function types.to_bool(o, true_strs, check_objs)
local true_func
if true_strs then
assert_arg(2, true_strs, "table")
end
true_func = true_types[type(o)]
if true_func then
return true_func(o, true_strs, check_objs)
elseif check_objs and o ~= nil then
return true
end
return false
end
return types