class Dry::Schema::JSONSchema::SchemaCompiler

@api private

def call(ast)

Other tags:
    Api: - private
def call(ast)
  visit(ast)
end

def fetch_filled_options(type, _target)

Other tags:
    Api: - private
def fetch_filled_options(type, _target)
  case type
  when "string"
    {minLength: 1}
  when "array"
    raise_unknown_conversion_error!(:type, :array) unless loose?
    {not: {type: "null"}}
  else
    {not: {type: "null"}}
  end
end

def fetch_type_opts_for_predicate(name, rest, target)

Other tags:
    Api: - private
def fetch_type_opts_for_predicate(name, rest, target)
  type_opts = PREDICATE_TO_TYPE.fetch(name) do
    raise_unknown_conversion_error!(:predicate, name) unless loose?
    EMPTY_HASH
  end.dup
  type_opts.transform_values! { |v| v.respond_to?(:call) ? v.call(rest[0][1], target) : v }
  type_opts.merge!(fetch_filled_options(target[:type], target)) if name == :filled?
  type_opts
end

def initialize(root: false, loose: false)

Other tags:
    Api: - private
def initialize(root: false, loose: false)
  @keys = EMPTY_HASH.dup
  @required = Set.new
  @root = root
  @loose = loose
end

def loose?

Other tags:
    Api: - private
def loose?
  @loose
end

def merge_opts!(orig_opts, new_opts)

Other tags:
    Api: - private
def merge_opts!(orig_opts, new_opts)
  new_type = new_opts[:type]
  orig_type = orig_opts[:type]
  if orig_type && new_type && orig_type != new_type
    new_opts[:type] = [orig_type, new_type]
  end
  orig_opts.merge!(new_opts)
end

def raise_unknown_conversion_error!(type, name)

def raise_unknown_conversion_error!(type, name)
  message = <<~MSG
    Could not find an equivalent conversion for #{type} #{name.inspect}.
    This means that your generated JSON schema may be missing this validation.
    You can ignore this by generating the schema in "loose" mode, i.e.:
        my_schema.json_schema(loose: true)
  MSG
  raise UnknownConversionError, message.chomp
end

def root?

Other tags:
    Api: - private
def root?
  @root
end

def to_hash

Other tags:
    Api: - private
def to_hash
  result = {}
  result[:$schema] = "http://json-schema.org/draft-06/schema#" if root?
  result.merge!(type: "object", properties: keys, required: required.to_a)
  result
end

def visit(node, opts = EMPTY_HASH)

Other tags:
    Api: - private
def visit(node, opts = EMPTY_HASH)
  meth, rest = node
  public_send(:"visit_#{meth}", rest, opts)
end

def visit_and(node, opts = EMPTY_HASH)

Other tags:
    Api: - private
def visit_and(node, opts = EMPTY_HASH)
  left, right = node
  # We need to know the type first to apply filled macro
  if left[1][0] == :filled?
    visit(right, opts)
    visit(left, opts)
  else
    visit(left, opts)
    visit(right, opts)
  end
end

def visit_each(node, opts = EMPTY_HASH)

Other tags:
    Api: - private
def visit_each(node, opts = EMPTY_HASH)
  visit(node, opts.merge(member: true))
end

def visit_implication(node, opts = EMPTY_HASH)

Other tags:
    Api: - private
def visit_implication(node, opts = EMPTY_HASH)
  node.each do |el|
    visit(el, **opts, required: false)
  end
end

def visit_key(node, opts = EMPTY_HASH)

Other tags:
    Api: - private
def visit_key(node, opts = EMPTY_HASH)
  name, rest = node
  if opts.fetch(:required, :true)
    required << name.to_s
  else
    opts.delete(:required)
  end
  visit(rest, opts.merge(key: name))
end

def visit_not(node, opts = EMPTY_HASH)

Other tags:
    Api: - private
def visit_not(node, opts = EMPTY_HASH)
  _name, rest = node
  visit_predicate(rest, opts)
end

def visit_or(node, opts = EMPTY_HASH)

Other tags:
    Api: - private
def visit_or(node, opts = EMPTY_HASH)
  node.each do |child|
    c = self.class.new(loose: loose?)
    c.keys.update(subschema: {})
    c.visit(child, opts.merge(key: :subschema))
    any_of = (keys[opts[:key]][:anyOf] ||= [])
    any_of << c.keys[:subschema]
  end
end

def visit_predicate(node, opts = EMPTY_HASH)

Other tags:
    Api: - private
def visit_predicate(node, opts = EMPTY_HASH)
  name, rest = node
  if name.equal?(:key?)
    prop_name = rest[0][1]
    keys[prop_name] = {}
  else
    target = keys[opts[:key]]
    type_opts = fetch_type_opts_for_predicate(name, rest, target)
    if target[:type]&.include?("array")
      target[:items] ||= {}
      merge_opts!(target[:items], type_opts)
    else
      merge_opts!(target, type_opts)
    end
  end
end

def visit_set(node, opts = EMPTY_HASH)

Other tags:
    Api: - private
def visit_set(node, opts = EMPTY_HASH)
  target = (key = opts[:key]) ? self.class.new(loose: loose?) : self
  node.map { |child| target.visit(child, opts) }
  return unless key
  target_info = opts[:member] ? {items: target.to_h} : target.to_h
  type = opts[:member] ? "array" : "object"
  keys.update(key => {**keys[key], type: type, **target_info})
end