diff options
-rw-r--r-- | contrib/DEPENDENCY_INFO.md | 2 | ||||
-rw-r--r-- | contrib/lua-tableshape/tableshape.lua | 828 |
2 files changed, 642 insertions, 188 deletions
diff --git a/contrib/DEPENDENCY_INFO.md b/contrib/DEPENDENCY_INFO.md index 49eca0992..745b60d76 100644 --- a/contrib/DEPENDENCY_INFO.md +++ b/contrib/DEPENDENCY_INFO.md @@ -17,7 +17,7 @@ | lua-lpeg | 1.0 | MIT | YES | rspamd text + alloc| | lua-moses | ? | MIT | NO | | | lua-lupa | ? | MIT | NO | | -| lua-tableshape | ae67256 | MIT | NO | | +| lua-tableshape | 2.6.0 | MIT | NO | | | mumhash | ? | MIT | NO | | | ngx-http-parser | 2.2.0 | MIT | YES | spamc support | | Mozilla-PublicSuffix | ? | MIT | NO | | diff --git a/contrib/lua-tableshape/tableshape.lua b/contrib/lua-tableshape/tableshape.lua index 0f1f710f6..ccc735519 100644 --- a/contrib/lua-tableshape/tableshape.lua +++ b/contrib/lua-tableshape/tableshape.lua @@ -1,4 +1,5 @@ -local OptionalType, TaggedType, types +local OptionalType, TaggedType, types, is_type +local BaseType, TransformNode, SequenceNode, FirstOfNode, DescribeNode, NotType, Literal local FailedTransform = { } local unpack = unpack or table.unpack local clone_state @@ -22,11 +23,9 @@ clone_state = function(state_obj) end return out end -local BaseType, TransformNode, SequenceNode, FirstOfNode, DescribeNode -local describe_literal -describe_literal = function(val) - local _exp_0 = type(val) - if "string" == _exp_0 then +local describe_type +describe_type = function(val) + if type(val) == "string" then if not val:match('"') then return "\"" .. tostring(val) .. "\"" elseif not val:match("'") then @@ -34,13 +33,23 @@ describe_literal = function(val) else return "`" .. tostring(val) .. "`" end + elseif BaseType:is_base_type(val) then + return val:_describe() else - if BaseType:is_base_type(val) then - return val:_describe() - else - return tostring(val) + return tostring(val) + end +end +local coerce_literal +coerce_literal = function(value) + local _exp_0 = type(value) + if "string" == _exp_0 or "number" == _exp_0 or "boolean" == _exp_0 then + return Literal(value) + elseif "table" == _exp_0 then + if BaseType:is_base_type(value) then + return value end end + return nil, "failed to coerce literal into type, use types.literal() to test for literal value" end local join_names join_names = function(items, sep, last_sep) @@ -66,13 +75,6 @@ end do local _class_0 local _base_0 = { - __eq = function(self, other) - if BaseType:is_base_type(other) then - return other(self) - else - return self(other[1]) - end - end, __div = function(self, fn) return TransformNode(self, fn) end, @@ -83,20 +85,46 @@ do return _with_0 end end, - __mul = function(self, right) - return SequenceNode(self, right) + __mul = function(_left, _right) + local left, err = coerce_literal(_left) + if not (left) then + error("left hand side of multiplication: " .. tostring(_left) .. ": " .. tostring(err)) + end + local right + right, err = coerce_literal(_right) + if not (right) then + error("right hand side of multiplication: " .. tostring(_right) .. ": " .. tostring(err)) + end + return SequenceNode(left, right) end, - __add = function(self, right) - if self.__class == FirstOfNode then + __add = function(_left, _right) + local left, err = coerce_literal(_left) + if not (left) then + error("left hand side of addition: " .. tostring(_left) .. ": " .. tostring(err)) + end + local right + right, err = coerce_literal(_right) + if not (right) then + error("right hand side of addition: " .. tostring(_right) .. ": " .. tostring(err)) + end + if left.__class == FirstOfNode then local options = { - unpack(self.options) + unpack(left.options) } table.insert(options, right) return FirstOfNode(unpack(options)) + elseif right.__class == FirstOfNode then + return FirstOfNode(left, unpack(right.options)) else - return FirstOfNode(self, right) + return FirstOfNode(left, right) end end, + __unm = function(self, right) + return NotType(right) + end, + __tostring = function(self) + return self:_describe() + end, _describe = function(self) return error("Node missing _describe: " .. tostring(self.__class.__name)) end, @@ -141,25 +169,8 @@ do tag = name }) end, - clone_opts = function(self, merge) - local opts - if self.opts then - do - local _tbl_0 = { } - for k, v in pairs(self.opts) do - _tbl_0[k] = v - end - opts = _tbl_0 - end - else - opts = { } - end - if merge then - for k, v in pairs(merge) do - opts[k] = v - end - end - return opts + clone_opts = function(self) + return error("clone_opts is not longer supported") end, __call = function(self, ...) return self:check_value(...) @@ -167,11 +178,7 @@ do } _base_0.__index = _base_0 _class_0 = setmetatable({ - __init = function(self) - if self.opts then - self._describe = self.opts.describe - end - end, + __init = function(self, opts) end, __base = _base_0, __name = "BaseType" }, { @@ -185,35 +192,24 @@ do _base_0.__class = _class_0 local self = _class_0 self.is_base_type = function(self, val) - if not (type(val) == "table") then - return false - end - local cls = val and val.__class - if not (cls) then - return false - end - if BaseType == cls then - return true + do + local mt = type(val) == "table" and getmetatable(val) + if mt then + if mt.__class then + return mt.__class.is_base_type == BaseType.is_base_type + end + end end - return self:is_base_type(cls.__parent) + return false end self.__inherited = function(self, cls) cls.__base.__call = cls.__call - cls.__base.__eq = self.__eq cls.__base.__div = self.__div cls.__base.__mod = self.__mod cls.__base.__mul = self.__mul cls.__base.__add = self.__add - local mt = getmetatable(cls) - local create = mt.__call - mt.__call = function(cls, ...) - local ret = create(cls, ...) - if ret.opts and ret.opts.optional then - return ret:is_optional() - else - return ret - end - end + cls.__base.__unm = self.__unm + cls.__base.__tostring = self.__tostring end BaseType = _class_0 end @@ -274,8 +270,6 @@ do end }) _base_0.__class = _class_0 - local self = _class_0 - self.transformer = true if _parent_0.__inherited then _parent_0.__inherited(_parent_0, _class_0) end @@ -293,11 +287,7 @@ do local _list_0 = self.sequence for _index_0 = 1, #_list_0 do local i = _list_0[_index_0] - if type(i) == "table" and i._describe then - _accum_0[_len_0] = i:_describe() - else - _accum_0[_len_0] = describe_literal(i) - end + _accum_0[_len_0] = describe_type(i) _len_0 = _len_0 + 1 end item_names = _accum_0 @@ -346,8 +336,6 @@ do end }) _base_0.__class = _class_0 - local self = _class_0 - self.transformer = true if _parent_0.__inherited then _parent_0.__inherited(_parent_0, _class_0) end @@ -365,11 +353,7 @@ do local _list_0 = self.options for _index_0 = 1, #_list_0 do local i = _list_0[_index_0] - if type(i) == "table" and i._describe then - _accum_0[_len_0] = i:_describe() - else - _accum_0[_len_0] = describe_literal(i) - end + _accum_0[_len_0] = describe_type(i) _len_0 = _len_0 + 1 end item_names = _accum_0 @@ -421,8 +405,6 @@ do end }) _base_0.__class = _class_0 - local self = _class_0 - self.transformer = true if _parent_0.__inherited then _parent_0.__inherited(_parent_0, _class_0) end @@ -502,6 +484,66 @@ do end DescribeNode = _class_0 end +local AnnotateNode +do + local _class_0 + local _parent_0 = BaseType + local _base_0 = { + format_error = function(self, value, err) + return tostring(tostring(value)) .. ": " .. tostring(err) + end, + _transform = function(self, value, state) + local new_value, state_or_err = self.base_type:_transform(value, state) + if new_value == FailedTransform then + return FailedTransform, self:format_error(value, state_or_err) + else + return new_value, state_or_err + end + end, + _describe = function(self) + if self.base_type._describe then + return self.base_type:_describe() + end + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, base_type, opts) + self.base_type = assert(coerce_literal(base_type)) + if opts then + if opts.format_error then + self.format_error = assert(types.func:transform(opts.format_error)) + end + end + end, + __base = _base_0, + __name = "AnnotateNode", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + AnnotateNode = _class_0 +end do local _class_0 local _parent_0 = BaseType @@ -549,13 +591,16 @@ do end, _describe = function(self) local base_description = self.base_type:_describe() - return tostring(base_description) .. " tagged " .. tostring(describe_literal(self.tag)) + return tostring(base_description) .. " tagged " .. tostring(describe_type(self.tag_name)) end } _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ __init = function(self, base_type, opts) + if opts == nil then + opts = { } + end self.base_type = base_type self.tag_name = assert(opts.tag, "tagged type missing tag") self.tag_type = type(self.tag_name) @@ -673,10 +718,9 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, base_type, opts) - self.base_type, self.opts = base_type, opts - _class_0.__parent.__init(self) - return assert(BaseType:is_base_type(base_type), "expected a type checker") + __init = function(self, base_type) + self.base_type = base_type + return assert(BaseType:is_base_type(self.base_type), "expected a type checker") end, __base = _base_0, __name = "OptionalType", @@ -761,7 +805,7 @@ do _transform = function(self, value, state) local got = type(value) if self.t ~= got then - return FailedTransform, "expected type " .. tostring(describe_literal(self.t)) .. ", got " .. tostring(describe_literal(got)) + return FailedTransform, "expected type " .. tostring(describe_type(self.t)) .. ", got " .. tostring(describe_type(got)) end if self.length_type then local len = #value @@ -780,12 +824,12 @@ do else l = types.range(left, right) end - return Type(self.t, self:clone_opts({ + return Type(self.t, { length = l - })) + }) end, _describe = function(self) - local t = "type " .. tostring(describe_literal(self.t)) + local t = "type " .. tostring(describe_type(self.t)) if self.length_type then t = t .. " length_type " .. tostring(self.length_type:_describe()) end @@ -796,11 +840,12 @@ do setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ __init = function(self, t, opts) - self.t, self.opts = t, opts - if self.opts then - self.length_type = self.opts.length + self.t = t + if opts then + if opts.length then + self.length_type = assert(coerce_literal(opts.length)) + end end - return _class_0.__parent.__init(self) end, __base = _base_0, __name = "Type", @@ -847,7 +892,7 @@ do return FailedTransform, "non number field: " .. tostring(i) end if not (i == k) then - return FailedTransform, "non array index, got " .. tostring(describe_literal(i)) .. " but expected " .. tostring(describe_literal(k)) + return FailedTransform, "non array index, got " .. tostring(describe_type(i)) .. " but expected " .. tostring(describe_type(k)) end k = k + 1 end @@ -857,9 +902,8 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, opts) - self.opts = opts - return _class_0.__parent.__init(self) + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) end, __base = _base_0, __name = "ArrayType", @@ -904,7 +948,7 @@ do if type(i) == "table" and i._describe then _accum_0[_len_0] = i:_describe() else - _accum_0[_len_0] = describe_literal(i) + _accum_0[_len_0] = describe_type(i) end _len_0 = _len_0 + 1 end @@ -947,9 +991,8 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, options, opts) - self.options, self.opts = options, opts - _class_0.__parent.__init(self) + __init = function(self, options) + self.options = options assert(type(self.options) == "table", "expected table for options in one_of") local fast_opts = types.array_of(types.number + types.string) if fast_opts(self.options) then @@ -1004,11 +1047,7 @@ do local _list_0 = self.types for _index_0 = 1, #_list_0 do local i = _list_0[_index_0] - if type(i) == "table" and i._describe then - _accum_0[_len_0] = i:_describe() - else - _accum_0[_len_0] = describe_literal(i) - end + _accum_0[_len_0] = describe_type(i) _len_0 = _len_0 + 1 end item_names = _accum_0 @@ -1030,9 +1069,8 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, types, opts) - self.types, self.opts = types, opts - _class_0.__parent.__init(self) + __init = function(self, types) + self.types = types assert(type(self.types) == "table", "expected table for first argument") local _list_0 = self.types for _index_0 = 1, #_list_0 do @@ -1073,7 +1111,7 @@ do local _parent_0 = BaseType local _base_0 = { _describe = function(self) - return "array of " .. tostring(describe_literal(self.expected)) + return "array of " .. tostring(describe_type(self.expected)) end, _transform = function(self, value, state) local pass, err = types.table(value) @@ -1095,7 +1133,7 @@ do local transformed_item if is_literal then if self.expected ~= item then - return FailedTransform, "array item " .. tostring(idx) .. ": expected " .. tostring(describe_literal(self.expected)) + return FailedTransform, "array item " .. tostring(idx) .. ": expected " .. tostring(describe_type(self.expected)) else transformed_item = item end @@ -1139,12 +1177,13 @@ do setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ __init = function(self, expected, opts) - self.expected, self.opts = expected, opts - if self.opts then - self.keep_nils = self.opts.keep_nils - self.length_type = self.opts.length + self.expected = expected + if opts then + self.keep_nils = opts.keep_nils and true + if opts.length then + self.length_type = assert(coerce_literal(opts.length)) + end end - return _class_0.__parent.__init(self) end, __base = _base_0, __name = "ArrayOf", @@ -1175,11 +1214,130 @@ do end ArrayOf = _class_0 end +local ArrayContains +do + local _class_0 + local _parent_0 = BaseType + local _base_0 = { + short_circuit = true, + keep_nils = false, + _describe = function(self) + return "array containing " .. tostring(describe_type(self.contains)) + end, + _transform = function(self, value, state) + local pass, err = types.table(value) + if not (pass) then + return FailedTransform, err + end + local is_literal = not BaseType:is_base_type(self.contains) + local contains = false + local copy, k + for idx, item in ipairs(value) do + local skip_item = false + local transformed_item + if is_literal then + if self.contains == item then + contains = true + end + transformed_item = item + else + local item_val, new_state = self.contains:_transform(item, state) + if item_val == FailedTransform then + transformed_item = item + else + state = new_state + contains = true + if item_val == nil and not self.keep_nils then + skip_item = true + else + transformed_item = item_val + end + end + end + if transformed_item ~= item or skip_item then + if not (copy) then + do + local _accum_0 = { } + local _len_0 = 1 + local _max_0 = idx - 1 + for _index_0 = 1, _max_0 < 0 and #value + _max_0 or _max_0 do + local i = value[_index_0] + _accum_0[_len_0] = i + _len_0 = _len_0 + 1 + end + copy = _accum_0 + end + k = idx + end + end + if copy and not skip_item then + copy[k] = transformed_item + k = k + 1 + end + if contains and self.short_circuit then + if copy then + for kdx = idx + 1, #value do + copy[k] = value[kdx] + k = k + 1 + end + end + break + end + end + if not (contains) then + return FailedTransform, "expected " .. tostring(self:_describe()) + end + return copy or value, state + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, contains, opts) + self.contains = contains + assert(self.contains, "missing contains") + if opts then + self.short_circuit = opts.short_circuit and true + self.keep_nils = opts.keep_nils and true + end + end, + __base = _base_0, + __name = "ArrayContains", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + self.type_err_message = "expecting table" + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + ArrayContains = _class_0 +end local MapOf do local _class_0 local _parent_0 = BaseType local _base_0 = { + _describe = function(self) + return "map of " .. tostring(self.expected_key:_describe()) .. " -> " .. tostring(self.expected_value:_describe()) + end, _transform = function(self, value, state) local pass, err = types.table(value) if not (pass) then @@ -1196,7 +1354,7 @@ do local new_v = v if key_literal then if k ~= self.expected_key then - return FailedTransform, "map key expected " .. tostring(describe_literal(self.expected_key)) + return FailedTransform, "map key expected " .. tostring(describe_type(self.expected_key)) end else new_k, state = self.expected_key:_transform(k, state) @@ -1206,7 +1364,7 @@ do end if value_literal then if v ~= self.expected_value then - return FailedTransform, "map value expected " .. tostring(describe_literal(self.expected_value)) + return FailedTransform, "map value expected " .. tostring(describe_type(self.expected_value)) end else new_v, state = self.expected_value:_transform(v, state) @@ -1234,9 +1392,9 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, expected_key, expected_value, opts) - self.expected_key, self.expected_value, self.opts = expected_key, expected_value, opts - return _class_0.__parent.__init(self) + __init = function(self, expected_key, expected_value) + self.expected_key = coerce_literal(expected_key) + self.expected_value = coerce_literal(expected_value) end, __base = _base_0, __name = "MapOf", @@ -1270,10 +1428,13 @@ do local _class_0 local _parent_0 = BaseType local _base_0 = { + open = false, + check_all = false, is_open = function(self) - return Shape(self.shape, self:clone_opts({ - open = true - })) + return Shape(self.shape, { + open = true, + check_all = self.check_all or nil + }) end, _describe = function(self) local parts @@ -1281,7 +1442,7 @@ do local _accum_0 = { } local _len_0 = 1 for k, v in pairs(self.shape) do - _accum_0[_len_0] = tostring(describe_literal(k)) .. " = " .. tostring(describe_literal(v)) + _accum_0[_len_0] = tostring(describe_type(k)) .. " = " .. tostring(describe_type(v)) _len_0 = _len_0 + 1 end parts = _accum_0 @@ -1317,11 +1478,11 @@ do if shape_val == item_value then new_val, state = item_value, state else - new_val, state = FailedTransform, "expected " .. tostring(describe_literal(shape_val)) + new_val, state = FailedTransform, "expected " .. tostring(describe_type(shape_val)) end end if new_val == FailedTransform then - err = "field " .. tostring(describe_literal(shape_key)) .. ": " .. tostring(state) + err = "field " .. tostring(describe_type(shape_key)) .. ": " .. tostring(state) if check_all then if errors then table.insert(errors, err) @@ -1353,7 +1514,7 @@ do [k] = item_value }, state) if tuple == FailedTransform then - err = "field " .. tostring(describe_literal(k)) .. ": " .. tostring(state) + err = "field " .. tostring(describe_type(k)) .. ": " .. tostring(state) if check_all then if errors then table.insert(errors, err) @@ -1387,7 +1548,7 @@ do local _accum_0 = { } local _len_0 = 1 for key in pairs(remaining_keys) do - _accum_0[_len_0] = describe_literal(key) + _accum_0[_len_0] = describe_type(key) _len_0 = _len_0 + 1 end names = _accum_0 @@ -1416,13 +1577,15 @@ do setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ __init = function(self, shape, opts) - self.shape, self.opts = shape, opts - _class_0.__parent.__init(self) + self.shape = shape assert(type(self.shape) == "table", "expected table for shape") - if self.opts then - self.extra_fields_type = self.opts.extra_fields - self.open = self.opts.open - self.check_all = self.opts.check_all + if opts then + if opts.extra_fields then + assert(BaseType:is_base_type(opts.extra_fields), "extra_fields_type must be type checker") + self.extra_fields_type = opts.extra_fields + end + self.open = opts.open and true + self.check_all = opts.check_all and true if self.open then assert(not self.extra_fields_type, "open can not be combined with extra_fields") end @@ -1460,31 +1623,77 @@ do end Shape = _class_0 end +local Partial +do + local _class_0 + local _parent_0 = Shape + local _base_0 = { + open = true, + is_open = function(self) + return error("is_open has no effect on Partial") + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "Partial", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Partial = _class_0 +end local Pattern do local _class_0 local _parent_0 = BaseType local _base_0 = { _describe = function(self) - return "pattern " .. tostring(describe_literal(self.pattern)) + return "pattern " .. tostring(describe_type(self.pattern)) end, _transform = function(self, value, state) - do - local initial = self.opts and self.opts.initial_type - if initial then - if not (type(value) == initial) then - return FailedTransform, "expected " .. tostring(describe_literal(initial)) + local test_value + if self.coerce then + if BaseType:is_base_type(self.coerce) then + local c_res, err = self.coerce:_transform(value) + if c_res == FailedTransform then + return FailedTransform, err end + test_value = c_res + else + test_value = tostring(value) end + else + test_value = value end - if self.opts and self.opts.coerce then - value = tostring(value) - end - local t_res, err = types.string(value) + local t_res, err = types.string(test_value) if not (t_res) then return FailedTransform, err end - if value:match(self.pattern) then + if test_value:match(self.pattern) then return value, state else return FailedTransform, "doesn't match " .. tostring(self:_describe()) @@ -1495,8 +1704,12 @@ do setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ __init = function(self, pattern, opts) - self.pattern, self.opts = pattern, opts - return _class_0.__parent.__init(self) + self.pattern = pattern + assert(type(self.pattern) == "string", "Pattern must be a string") + if opts then + self.coerce = opts.coerce + return assert(opts.initial_type == nil, "initial_type has been removed from types.pattern (got: " .. tostring(opts.initial_type) .. ")") + end end, __base = _base_0, __name = "Pattern", @@ -1525,13 +1738,12 @@ do end Pattern = _class_0 end -local Literal do local _class_0 local _parent_0 = BaseType local _base_0 = { _describe = function(self) - return describe_literal(self.value) + return describe_type(self.value) end, _transform = function(self, value, state) if self.value ~= value then @@ -1543,9 +1755,8 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, value, opts) - self.value, self.opts = value, opts - return _class_0.__parent.__init(self) + __init = function(self, value) + self.value = value end, __base = _base_0, __name = "Literal", @@ -1580,7 +1791,7 @@ do local _parent_0 = BaseType local _base_0 = { _describe = function(self) - return self.opts and self.opts.describe or "custom checker " .. tostring(self.fn) + return "custom checker " .. tostring(self.fn) end, _transform = function(self, value, state) local pass, err = self.fn(value, state) @@ -1593,9 +1804,9 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, fn, opts) - self.fn, self.opts = fn, opts - return _class_0.__parent.__init(self) + __init = function(self, fn) + self.fn = fn + return assert(type(self.fn) == "function", "custom checker must be a function") end, __base = _base_0, __name = "Custom", @@ -1630,6 +1841,9 @@ do local values_equivalent local _parent_0 = BaseType local _base_0 = { + _describe = function(self) + return "equivalent to " .. tostring(describe_type(self.val)) + end, _transform = function(self, value, state) if values_equivalent(self.val, value) then return value, state @@ -1641,9 +1855,8 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, val, opts) - self.val, self.opts = val, opts - return _class_0.__parent.__init(self) + __init = function(self, val) + self.val = val end, __base = _base_0, __name = "Equivalent", @@ -1732,9 +1945,8 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, left, right, opts) - self.left, self.right, self.opts = left, right, opts - _class_0.__parent.__init(self) + __init = function(self, left, right) + self.left, self.right = left, right assert(self.left <= self.right, "left range value should be less than right range value") self.value_type = assert(types[type(self.left)], "couldn't figure out type of range boundary") end, @@ -1780,8 +1992,8 @@ do _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, fn, opts) - self.fn, self.opts = fn, opts + __init = function(self, fn) + self.fn = fn end, __base = _base_0, __name = "Proxy", @@ -1810,33 +2022,285 @@ do end Proxy = _class_0 end +local AssertType +do + local _class_0 + local _parent_0 = BaseType + local _base_0 = { + assert = assert, + _transform = function(self, value, state) + local state_or_err + value, state_or_err = self.base_type:_transform(value, state) + self.assert(value ~= FailedTransform, state_or_err) + return value, state_or_err + end, + _describe = function(self) + if self.base_type._describe then + local base_description = self.base_type:_describe() + return "assert " .. tostring(base_description) + end + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, base_type) + self.base_type = base_type + return assert(BaseType:is_base_type(self.base_type), "expected a type checker") + end, + __base = _base_0, + __name = "AssertType", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + AssertType = _class_0 +end +do + local _class_0 + local _parent_0 = BaseType + local _base_0 = { + _transform = function(self, value, state) + local out, _ = self.base_type:_transform(value, state) + if out == FailedTransform then + return value, state + else + return FailedTransform, "expected " .. tostring(self:_describe()) + end + end, + _describe = function(self) + if self.base_type._describe then + local base_description = self.base_type:_describe() + return "not " .. tostring(base_description) + end + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, base_type) + self.base_type = base_type + return assert(BaseType:is_base_type(self.base_type), "expected a type checker") + end, + __base = _base_0, + __name = "NotType", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + NotType = _class_0 +end +local CloneType +do + local _class_0 + local _parent_0 = BaseType + local _base_0 = { + _transform = function(self, value, state) + local _exp_0 = type(value) + if "nil" == _exp_0 or "string" == _exp_0 or "number" == _exp_0 or "boolean" == _exp_0 then + return value, state + elseif "table" == _exp_0 then + local clone_value + do + local _tbl_0 = { } + for k, v in pairs(value) do + _tbl_0[k] = v + end + clone_value = _tbl_0 + end + do + local mt = getmetatable(value) + if mt then + setmetatable(clone_value, mt) + end + end + return clone_value, state + else + return FailedTransform, tostring(describe_type(value)) .. " is not cloneable" + end + end, + _describe = function(self) + return "cloneable value" + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "CloneType", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + CloneType = _class_0 +end +local MetatableIsType +do + local _class_0 + local _parent_0 = BaseType + local _base_0 = { + allow_metatable_update = false, + _transform = function(self, value, state) + local state_or_err + value, state_or_err = types.table:_transform(value, state) + if value == FailedTransform then + return FailedTransform, state_or_err + end + local mt = getmetatable(value) + local new_mt + new_mt, state_or_err = self.metatable_type:_transform(mt, state_or_err) + if new_mt == FailedTransform then + return FailedTransform, "metatable expected: " .. tostring(state_or_err) + end + if new_mt ~= mt then + if self.allow_metatable_update then + setmetatable(value, new_mt) + else + return FailedTransform, "metatable was modified by a type but { allow_metatable_update = true } is not enabled" + end + end + return value, state_or_err + end, + _describe = function(self) + return "has metatable " .. tostring(describe_type(self.metatable_type)) + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, metatable_type, opts) + if BaseType:is_base_type(metatable_type) then + self.metatable_type = metatable_type + else + self.metatable_type = Literal(metatable_type) + end + if opts then + self.allow_metatable_update = opts.allow_metatable_update and true + end + end, + __base = _base_0, + __name = "MetatableIsType", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + MetatableIsType = _class_0 +end +local type_nil = Type("nil") +local type_function = Type("function") +local type_number = Type("number") types = setmetatable({ any = AnyType(), string = Type("string"), - number = Type("number"), - ["function"] = Type("function"), - func = Type("function"), + number = type_number, + ["function"] = type_function, + func = type_function, boolean = Type("boolean"), userdata = Type("userdata"), - ["nil"] = Type("nil"), + ["nil"] = type_nil, + null = type_nil, table = Type("table"), array = ArrayType(), + clone = CloneType(), integer = Pattern("^%d+$", { - coerce = true, - initial_type = "number" + coerce = type_number / tostring }), one_of = OneOf, all_of = AllOf, shape = Shape, + partial = Partial, pattern = Pattern, array_of = ArrayOf, + array_contains = ArrayContains, map_of = MapOf, literal = Literal, range = Range, equivalent = Equivalent, custom = Custom, scope = TagScopeType, - proxy = Proxy + proxy = Proxy, + assert = AssertType, + annotate = AnnotateNode, + metatable_is = MetatableIsType }, { __index = function(self, fn_name) return error("Type checker does not exist: `" .. tostring(fn_name) .. "`") @@ -1847,24 +2311,14 @@ check_shape = function(value, shape) assert(shape.check_value, "missing check_value method from shape") return shape:check_value(value) end -local is_type is_type = function(val) return BaseType:is_base_type(val) end -local type_switch -type_switch = function(val) - return setmetatable({ - val - }, { - __eq = BaseType.__eq - }) -end return { check_shape = check_shape, types = types, is_type = is_type, - type_switch = type_switch, BaseType = BaseType, FailedTransform = FailedTransform, - VERSION = "2.0.0" + VERSION = "2.6.0" } |