-- source taken from https://github.com/leafo/tableshape
-- tableshape 2.6.0
-- Library for verifying the shape (schema, structure, etc.) of a table,
-- and transforming it if necessary

-- The MIT License (MIT)

-- Copyright (c) 2018 by Leaf Corcoran

-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-- the Software, and to permit persons to whom the Software is furnished to do so,
-- subject to the following conditions:

-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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
clone_state = function(state_obj)
  if type(state_obj) ~= "table" then
    return { }
  end
  local out
  do
    local _tbl_0 = { }
    for k, v in pairs(state_obj) do
      _tbl_0[k] = v
    end
    out = _tbl_0
  end
  do
    local mt = getmetatable(state_obj)
    if mt then
      setmetatable(out, mt)
    end
  end
  return out
end
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
      return "'" .. tostring(val) .. "'"
    else
      return "`" .. tostring(val) .. "`"
    end
  elseif BaseType:is_base_type(val) then
    return val:_describe()
  else
    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)
  if sep == nil then
    sep = ", "
  end
  local count = #items
  local chunks = { }
  for idx, name in ipairs(items) do
    if idx > 1 then
      local current_sep
      if idx == count then
        current_sep = last_sep or sep
      else
        current_sep = sep
      end
      table.insert(chunks, current_sep)
    end
    table.insert(chunks, name)
  end
  return table.concat(chunks)
end
do
  local _class_0
  local _base_0 = {
    __div = function(self, fn)
      return TransformNode(self, fn)
    end,
    __mod = function(self, fn)
      do
        local _with_0 = TransformNode(self, fn)
        _with_0.with_state = true
        return _with_0
      end
    end,
    __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(_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(left.options)
        }
        table.insert(options, right)
        return FirstOfNode(unpack(options))
      elseif right.__class == FirstOfNode then
        return FirstOfNode(left, unpack(right.options))
      else
        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,
    check_value = function(self, ...)
      local value, state_or_err = self:_transform(...)
      if value == FailedTransform then
        return nil, state_or_err
      end
      if type(state_or_err) == "table" then
        return state_or_err
      else
        return true
      end
    end,
    transform = function(self, ...)
      local value, state_or_err = self:_transform(...)
      if value == FailedTransform then
        return nil, state_or_err
      end
      if type(state_or_err) == "table" then
        return value, state_or_err
      else
        return value
      end
    end,
    repair = function(self, ...)
      return self:transform(...)
    end,
    on_repair = function(self, fn)
      return (self + types.any / fn * self):describe(function()
        return self:_describe()
      end)
    end,
    is_optional = function(self)
      return OptionalType(self)
    end,
    describe = function(self, ...)
      return DescribeNode(self, ...)
    end,
    tag = function(self, name)
      return TaggedType(self, {
        tag = name
      })
    end,
    clone_opts = function(self)
      return error("clone_opts is not longer supported")
    end,
    __call = function(self, ...)
      return self:check_value(...)
    end
  }
  _base_0.__index = _base_0
  _class_0 = setmetatable({
    __init = function(self, opts) end,
    __base = _base_0,
    __name = "BaseType"
  }, {
    __index = _base_0,
    __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.is_base_type = function(self, val)
    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 false
  end
  self.__inherited = function(self, cls)
    cls.__base.__call = cls.__call
    cls.__base.__div = self.__div
    cls.__base.__mod = self.__mod
    cls.__base.__mul = self.__mul
    cls.__base.__add = self.__add
    cls.__base.__unm = self.__unm
    cls.__base.__tostring = self.__tostring
  end
  BaseType = _class_0
end
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _describe = function(self)
      return self.node:_describe()
    end,
    _transform = function(self, value, state)
      local state_or_err
      value, state_or_err = self.node:_transform(value, state)
      if value == FailedTransform then
        return FailedTransform, state_or_err
      else
        local out
        local _exp_0 = type(self.t_fn)
        if "function" == _exp_0 then
          if self.with_state then
            out = self.t_fn(value, state_or_err)
          else
            out = self.t_fn(value)
          end
        else
          out = self.t_fn
        end
        return out, state_or_err
      end
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, node, t_fn)
      self.node, self.t_fn = node, t_fn
      return assert(self.node, "missing node for transform")
    end,
    __base = _base_0,
    __name = "TransformNode",
    __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
  TransformNode = _class_0
end
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _describe = function(self)
      local item_names
      do
        local _accum_0 = { }
        local _len_0 = 1
        local _list_0 = self.sequence
        for _index_0 = 1, #_list_0 do
          local i = _list_0[_index_0]
          _accum_0[_len_0] = describe_type(i)
          _len_0 = _len_0 + 1
        end
        item_names = _accum_0
      end
      return join_names(item_names, " then ")
    end,
    _transform = function(self, value, state)
      local _list_0 = self.sequence
      for _index_0 = 1, #_list_0 do
        local node = _list_0[_index_0]
        value, state = node:_transform(value, state)
        if value == FailedTransform then
          break
        end
      end
      return value, state
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, ...)
      self.sequence = {
        ...
      }
    end,
    __base = _base_0,
    __name = "SequenceNode",
    __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
  SequenceNode = _class_0
end
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _describe = function(self)
      local item_names
      do
        local _accum_0 = { }
        local _len_0 = 1
        local _list_0 = self.options
        for _index_0 = 1, #_list_0 do
          local i = _list_0[_index_0]
          _accum_0[_len_0] = describe_type(i)
          _len_0 = _len_0 + 1
        end
        item_names = _accum_0
      end
      return join_names(item_names, ", ", ", or ")
    end,
    _transform = function(self, value, state)
      if not (self.options[1]) then
        return FailedTransform, "no options for node"
      end
      local _list_0 = self.options
      for _index_0 = 1, #_list_0 do
        local node = _list_0[_index_0]
        local new_val, new_state = node:_transform(value, state)
        if not (new_val == FailedTransform) then
          return new_val, new_state
        end
      end
      return FailedTransform, "expected " .. tostring(self:_describe())
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, ...)
      self.options = {
        ...
      }
    end,
    __base = _base_0,
    __name = "FirstOfNode",
    __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
  FirstOfNode = _class_0
end
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _transform = function(self, input, ...)
      local value, state = self.node:_transform(input, ...)
      if value == FailedTransform then
        local err
        if self.err_handler then
          err = self.err_handler(input, state)
        else
          err = "expected " .. tostring(self:_describe())
        end
        return FailedTransform, err
      end
      return value, state
    end,
    describe = function(self, ...)
      return DescribeNode(self.node, ...)
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, node, describe)
      self.node = node
      local err_message
      if type(describe) == "table" then
        describe, err_message = describe.type, describe.error
      end
      if type(describe) == "string" then
        self._describe = function()
          return describe
        end
      else
        self._describe = describe
      end
      if err_message then
        if type(err_message) == "string" then
          self.err_handler = function()
            return err_message
          end
        else
          self.err_handler = err_message
        end
      end
    end,
    __base = _base_0,
    __name = "DescribeNode",
    __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
  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
  local _base_0 = {
    update_state = function(self, state, value, ...)
      local out = clone_state(state)
      if self.tag_type == "function" then
        if select("#", ...) > 0 then
          self.tag_name(out, ..., value)
        else
          self.tag_name(out, value)
        end
      else
        if self.tag_array then
          local existing = out[self.tag_name]
          if type(existing) == "table" then
            local copy
            do
              local _tbl_0 = { }
              for k, v in pairs(existing) do
                _tbl_0[k] = v
              end
              copy = _tbl_0
            end
            table.insert(copy, value)
            out[self.tag_name] = copy
          else
            out[self.tag_name] = {
              value
            }
          end
        else
          out[self.tag_name] = value
        end
      end
      return out
    end,
    _transform = function(self, value, state)
      value, state = self.base_type:_transform(value, state)
      if value == FailedTransform then
        return FailedTransform, state
      end
      state = self:update_state(state, value)
      return value, state
    end,
    _describe = function(self)
      local base_description = self.base_type:_describe()
      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)
      if self.tag_type == "string" then
        if self.tag_name:match("%[%]$") then
          self.tag_name = self.tag_name:sub(1, -3)
          self.tag_array = true
        end
      end
    end,
    __base = _base_0,
    __name = "TaggedType",
    __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
  TaggedType = _class_0
end
local TagScopeType
do
  local _class_0
  local _parent_0 = TaggedType
  local _base_0 = {
    create_scope_state = function(self, state)
      return nil
    end,
    _transform = function(self, value, state)
      local scope
      value, scope = self.base_type:_transform(value, self:create_scope_state(state))
      if value == FailedTransform then
        return FailedTransform, scope
      end
      if self.tag_name then
        state = self:update_state(state, scope, value)
      end
      return value, state
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, base_type, opts)
      if opts then
        return _class_0.__parent.__init(self, base_type, opts)
      else
        self.base_type = base_type
      end
    end,
    __base = _base_0,
    __name = "TagScopeType",
    __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
  TagScopeType = _class_0
end
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _transform = function(self, value, state)
      if value == nil then
        return value, state
      end
      return self.base_type:_transform(value, state)
    end,
    is_optional = function(self)
      return self
    end,
    _describe = function(self)
      if self.base_type._describe then
        local base_description = self.base_type:_describe()
        return "optional " .. 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 = "OptionalType",
    __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
  OptionalType = _class_0
end
local AnyType
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _transform = function(self, v, state)
      return v, state
    end,
    _describe = function(self)
      return "anything"
    end,
    is_optional = function(self)
      return self
    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 = "AnyType",
    __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
  AnyType = _class_0
end
local Type
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _transform = function(self, value, state)
      local got = type(value)
      if self.t ~= got then
        return FailedTransform, "expected type " .. tostring(describe_type(self.t)) .. ", got " .. tostring(describe_type(got))
      end
      if self.length_type then
        local len = #value
        local res
        res, state = self.length_type:_transform(len, state)
        if res == FailedTransform then
          return FailedTransform, tostring(self.t) .. " length " .. tostring(state) .. ", got " .. tostring(len)
        end
      end
      return value, state
    end,
    length = function(self, left, right)
      local l
      if BaseType:is_base_type(left) then
        l = left
      else
        l = types.range(left, right)
      end
      return Type(self.t, {
        length = l
      })
    end,
    _describe = function(self)
      local t = "type " .. tostring(describe_type(self.t))
      if self.length_type then
        t = t .. " length_type " .. tostring(self.length_type:_describe())
      end
      return t
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, t, opts)
      self.t = t
      if opts then
        if opts.length then
          self.length_type = assert(coerce_literal(opts.length))
        end
      end
    end,
    __base = _base_0,
    __name = "Type",
    __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
  Type = _class_0
end
local ArrayType
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _describe = function(self)
      return "an array"
    end,
    _transform = function(self, value, state)
      if not (type(value) == "table") then
        return FailedTransform, "expecting table"
      end
      local k = 1
      for i, v in pairs(value) do
        if not (type(i) == "number") then
          return FailedTransform, "non number field: " .. tostring(i)
        end
        if not (i == k) then
          return FailedTransform, "non array index, got " .. tostring(describe_type(i)) .. " but expected " .. tostring(describe_type(k))
        end
        k = k + 1
      end
      return value, state
    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 = "ArrayType",
    __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
  ArrayType = _class_0
end
local OneOf
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _describe = function(self)
      local item_names
      do
        local _accum_0 = { }
        local _len_0 = 1
        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_type(i)
          end
          _len_0 = _len_0 + 1
        end
        item_names = _accum_0
      end
      return tostring(join_names(item_names, ", ", ", or "))
    end,
    _transform = function(self, value, state)
      if self.options_hash then
        if self.options_hash[value] then
          return value, state
        end
      else
        local _list_0 = self.options
        for _index_0 = 1, #_list_0 do
          local _continue_0 = false
          repeat
            local item = _list_0[_index_0]
            if item == value then
              return value, state
            end
            if BaseType:is_base_type(item) then
              local new_value, new_state = item:_transform(value, state)
              if new_value == FailedTransform then
                _continue_0 = true
                break
              end
              return new_value, new_state
            end
            _continue_0 = true
          until true
          if not _continue_0 then
            break
          end
        end
      end
      return FailedTransform, "expected " .. tostring(self:_describe())
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __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
        do
          local _tbl_0 = { }
          local _list_0 = self.options
          for _index_0 = 1, #_list_0 do
            local v = _list_0[_index_0]
            _tbl_0[v] = true
          end
          self.options_hash = _tbl_0
        end
      end
    end,
    __base = _base_0,
    __name = "OneOf",
    __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
  OneOf = _class_0
end
local AllOf
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _describe = function(self)
      local item_names
      do
        local _accum_0 = { }
        local _len_0 = 1
        local _list_0 = self.types
        for _index_0 = 1, #_list_0 do
          local i = _list_0[_index_0]
          _accum_0[_len_0] = describe_type(i)
          _len_0 = _len_0 + 1
        end
        item_names = _accum_0
      end
      return join_names(item_names, " and ")
    end,
    _transform = function(self, value, state)
      local _list_0 = self.types
      for _index_0 = 1, #_list_0 do
        local t = _list_0[_index_0]
        value, state = t:_transform(value, state)
        if value == FailedTransform then
          return FailedTransform, state
        end
      end
      return value, state
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __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
        local checker = _list_0[_index_0]
        assert(BaseType:is_base_type(checker), "all_of expects all type checkers")
      end
    end,
    __base = _base_0,
    __name = "AllOf",
    __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
  AllOf = _class_0
end
local ArrayOf
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _describe = function(self)
      return "array of " .. tostring(describe_type(self.expected))
    end,
    _transform = function(self, value, state)
      local pass, err = types.table(value)
      if not (pass) then
        return FailedTransform, err
      end
      if self.length_type then
        local len = #value
        local res
        res, state = self.length_type:_transform(len, state)
        if res == FailedTransform then
          return FailedTransform, "array length " .. tostring(state) .. ", got " .. tostring(len)
        end
      end
      local is_literal = not BaseType:is_base_type(self.expected)
      local copy, k
      for idx, item in ipairs(value) do
        local skip_item = false
        local transformed_item
        if is_literal then
          if self.expected ~= item then
            return FailedTransform, "array item " .. tostring(idx) .. ": expected " .. tostring(describe_type(self.expected))
          else
            transformed_item = item
          end
        else
          local item_val
          item_val, state = self.expected:_transform(item, state)
          if item_val == FailedTransform then
            return FailedTransform, "array item " .. tostring(idx) .. ": " .. tostring(state)
          end
          if item_val == nil and not self.keep_nils then
            skip_item = true
          else
            transformed_item = item_val
          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
      end
      return copy or value, state
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, expected, opts)
      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
    end,
    __base = _base_0,
    __name = "ArrayOf",
    __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
  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
        return FailedTransform, err
      end
      local key_literal = not BaseType:is_base_type(self.expected_key)
      local value_literal = not BaseType:is_base_type(self.expected_value)
      local transformed = false
      local out = { }
      for k, v in pairs(value) do
        local _continue_0 = false
        repeat
          local new_k = k
          local new_v = v
          if key_literal then
            if k ~= self.expected_key then
              return FailedTransform, "map key expected " .. tostring(describe_type(self.expected_key))
            end
          else
            new_k, state = self.expected_key:_transform(k, state)
            if new_k == FailedTransform then
              return FailedTransform, "map key " .. tostring(state)
            end
          end
          if value_literal then
            if v ~= self.expected_value then
              return FailedTransform, "map value expected " .. tostring(describe_type(self.expected_value))
            end
          else
            new_v, state = self.expected_value:_transform(v, state)
            if new_v == FailedTransform then
              return FailedTransform, "map value " .. tostring(state)
            end
          end
          if new_k ~= k or new_v ~= v then
            transformed = true
          end
          if new_k == nil then
            _continue_0 = true
            break
          end
          out[new_k] = new_v
          _continue_0 = true
        until true
        if not _continue_0 then
          break
        end
      end
      return transformed and out or value, state
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __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",
    __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
  MapOf = _class_0
end
local Shape
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    open = false,
    check_all = false,
    is_open = function(self)
      return Shape(self.shape, {
        open = true,
        check_all = self.check_all or nil
      })
    end,
    _describe = function(self)
      local parts
      do
        local _accum_0 = { }
        local _len_0 = 1
        for k, v in pairs(self.shape) do
          _accum_0[_len_0] = tostring(describe_type(k)) .. " = " .. tostring(describe_type(v))
          _len_0 = _len_0 + 1
        end
        parts = _accum_0
      end
      return "{ " .. tostring(table.concat(parts, ", ")) .. " }"
    end,
    _transform = function(self, value, state)
      local pass, err = types.table(value)
      if not (pass) then
        return FailedTransform, err
      end
      local check_all = self.check_all
      local remaining_keys
      do
        local _tbl_0 = { }
        for key in pairs(value) do
          _tbl_0[key] = true
        end
        remaining_keys = _tbl_0
      end
      local errors
      local dirty = false
      local out = { }
      for shape_key, shape_val in pairs(self.shape) do
        local item_value = value[shape_key]
        if remaining_keys then
          remaining_keys[shape_key] = nil
        end
        local new_val
        if BaseType:is_base_type(shape_val) then
          new_val, state = shape_val:_transform(item_value, state)
        else
          if shape_val == item_value then
            new_val, state = item_value, state
          else
            new_val, state = FailedTransform, "expected " .. tostring(describe_type(shape_val))
          end
        end
        if new_val == FailedTransform then
          err = "field " .. tostring(describe_type(shape_key)) .. ": " .. tostring(state)
          if check_all then
            if errors then
              table.insert(errors, err)
            else
              errors = {
                err
              }
            end
          else
            return FailedTransform, err
          end
        else
          if new_val ~= item_value then
            dirty = true
          end
          out[shape_key] = new_val
        end
      end
      if remaining_keys and next(remaining_keys) then
        if self.open then
          for k in pairs(remaining_keys) do
            out[k] = value[k]
          end
        elseif self.extra_fields_type then
          for k in pairs(remaining_keys) do
            local item_value = value[k]
            local tuple
            tuple, state = self.extra_fields_type:_transform({
              [k] = item_value
            }, state)
            if tuple == FailedTransform then
              err = "field " .. tostring(describe_type(k)) .. ": " .. tostring(state)
              if check_all then
                if errors then
                  table.insert(errors, err)
                else
                  errors = {
                    err
                  }
                end
              else
                return FailedTransform, err
              end
            else
              do
                local nk = tuple and next(tuple)
                if nk then
                  if nk ~= k then
                    dirty = true
                  elseif tuple[nk] ~= item_value then
                    dirty = true
                  end
                  out[nk] = tuple[nk]
                else
                  dirty = true
                end
              end
            end
          end
        else
          local names
          do
            local _accum_0 = { }
            local _len_0 = 1
            for key in pairs(remaining_keys) do
              _accum_0[_len_0] = describe_type(key)
              _len_0 = _len_0 + 1
            end
            names = _accum_0
          end
          err = "extra fields: " .. tostring(table.concat(names, ", "))
          if check_all then
            if errors then
              table.insert(errors, err)
            else
              errors = {
                err
              }
            end
          else
            return FailedTransform, err
          end
        end
      end
      if errors and next(errors) then
        return FailedTransform, table.concat(errors, "; ")
      end
      return dirty and out or value, state
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, shape, opts)
      self.shape = shape
      assert(type(self.shape) == "table", "expected table for shape")
      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
        if self.extra_fields_type then
          return assert(not self.open, "extra_fields can not be combined with open")
        end
      end
    end,
    __base = _base_0,
    __name = "Shape",
    __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
  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_type(self.pattern))
    end,
    _transform = function(self, value, state)
      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
      local t_res, err = types.string(test_value)
      if not (t_res) then
        return FailedTransform, err
      end
      if test_value:match(self.pattern) then
        return value, state
      else
        return FailedTransform, "doesn't match " .. tostring(self:_describe())
      end
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, pattern, opts)
      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",
    __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
  Pattern = _class_0
end
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _describe = function(self)
      return describe_type(self.value)
    end,
    _transform = function(self, value, state)
      if self.value ~= value then
        return FailedTransform, "expected " .. tostring(self:_describe())
      end
      return value, state
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, value)
      self.value = value
    end,
    __base = _base_0,
    __name = "Literal",
    __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
  Literal = _class_0
end
local Custom
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _describe = function(self)
      return "custom checker " .. tostring(self.fn)
    end,
    _transform = function(self, value, state)
      local pass, err = self.fn(value, state)
      if not (pass) then
        return FailedTransform, err or "failed custom check"
      end
      return value, state
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __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",
    __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
  Custom = _class_0
end
local Equivalent
do
  local _class_0
  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
      else
        return FailedTransform, "not equivalent to " .. tostring(self.val)
      end
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, val)
      self.val = val
    end,
    __base = _base_0,
    __name = "Equivalent",
    __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
  values_equivalent = function(a, b)
    if a == b then
      return true
    end
    if type(a) == "table" and type(b) == "table" then
      local seen_keys = { }
      for k, v in pairs(a) do
        seen_keys[k] = true
        if not (values_equivalent(v, b[k])) then
          return false
        end
      end
      for k, v in pairs(b) do
        local _continue_0 = false
        repeat
          if seen_keys[k] then
            _continue_0 = true
            break
          end
          if not (values_equivalent(v, a[k])) then
            return false
          end
          _continue_0 = true
        until true
        if not _continue_0 then
          break
        end
      end
      return true
    else
      return false
    end
  end
  if _parent_0.__inherited then
    _parent_0.__inherited(_parent_0, _class_0)
  end
  Equivalent = _class_0
end
local Range
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _transform = function(self, value, state)
      local res
      res, state = self.value_type:_transform(value, state)
      if res == FailedTransform then
        return FailedTransform, "range " .. tostring(state)
      end
      if value < self.left then
        return FailedTransform, "not in " .. tostring(self:_describe())
      end
      if value > self.right then
        return FailedTransform, "not in " .. tostring(self:_describe())
      end
      return value, state
    end,
    _describe = function(self)
      return "range from " .. tostring(self.left) .. " to " .. tostring(self.right)
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __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,
    __base = _base_0,
    __name = "Range",
    __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
  Range = _class_0
end
local Proxy
do
  local _class_0
  local _parent_0 = BaseType
  local _base_0 = {
    _transform = function(self, ...)
      return assert(self.fn(), "proxy missing transformer"):_transform(...)
    end,
    _describe = function(self, ...)
      return assert(self.fn(), "proxy missing transformer"):_describe(...)
    end
  }
  _base_0.__index = _base_0
  setmetatable(_base_0, _parent_0.__base)
  _class_0 = setmetatable({
    __init = function(self, fn)
      self.fn = fn
    end,
    __base = _base_0,
    __name = "Proxy",
    __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
  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,
  boolean = Type("boolean"),
  userdata = Type("userdata"),
  ["nil"] = type_nil,
  null = type_nil,
  table = Type("table"),
  array = ArrayType(),
  clone = CloneType(),
  integer = Pattern("^%d+$", {
    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,
  assert = AssertType,
  annotate = AnnotateNode,
  metatable_is = MetatableIsType
}, {
  __index = function(self, fn_name)
    return error("Type checker does not exist: `" .. tostring(fn_name) .. "`")
  end
})
local check_shape
check_shape = function(value, shape)
  assert(shape.check_value, "missing check_value method from shape")
  return shape:check_value(value)
end
is_type = function(val)
  return BaseType:is_base_type(val)
end
return {
  check_shape = check_shape,
  types = types,
  is_type = is_type,
  BaseType = BaseType,
  FailedTransform = FailedTransform,
  VERSION = "2.6.0"
}
