# frozen_string_literal: true require 'dry/types/hash/constructor' module Dry module Types # Hash types can be used to define maps and schemas # # @api public class Hash < Nominal NOT_REQUIRED = { required: false }.freeze # @overload schema(type_map, meta = EMPTY_HASH) # @param [{Symbol => Dry::Types::Nominal}] type_map # @param [Hash] meta # @return [Dry::Types::Schema] # # @overload schema(keys) # @param [Array] key List of schema keys # @param [Hash] meta # @return [Dry::Types::Schema] # # @api public def schema(keys_or_map, meta = EMPTY_HASH) if keys_or_map.is_a?(::Array) keys = keys_or_map else keys = build_keys(keys_or_map) end Schema.new(primitive, keys: keys, **options, meta: self.meta.merge(meta)) end # Build a map type # # @param [Type] key_type # @param [Type] value_type # # @return [Map] # # @api public def map(key_type, value_type) Map.new( primitive, key_type: resolve_type(key_type), value_type: resolve_type(value_type), meta: meta ) end # @api private def weak(*) raise 'Support for old hash schemas was removed, please refer to the CHANGELOG '\ 'on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md' end alias_method :permissive, :weak alias_method :strict, :weak alias_method :strict_with_defaults, :weak alias_method :symbolized, :weak # Injects a type transformation function for building schemas # # @param [#call,nil] proc # @param [#call,nil] block # # @return [Hash] # # @api public def with_type_transform(proc = nil, &block) fn = proc || block raise ArgumentError, 'a block or callable argument is required' if fn.nil? handle = Dry::Types::FnContainer.register(fn) with(type_transform_fn: handle) end # @api private def constructor_type ::Dry::Types::Hash::Constructor end # Whether the type transforms types of schemas created by {Dry::Types::Hash#schema} # # @return [Boolean] # # @api public def transform_types? !options[:type_transform_fn].nil? end # @param meta [Boolean] Whether to dump the meta to the AST # # @return [Array] An AST representation # # @api public def to_ast(meta: true) opts = if RUBY_VERSION >= '2.5' options.slice(:type_transform_fn) else options.select { |k, _| k == :type_transform_fn } end [:hash, [opts, meta ? self.meta : EMPTY_HASH]] end private # @api private def build_keys(type_map) type_fn = options.fetch(:type_transform_fn, Schema::NO_TRANSFORM) type_transform = Dry::Types::FnContainer[type_fn] type_map.map do |map_key, type| name, options = key_name(map_key) key = Schema::Key.new(resolve_type(type), name, options) type_transform.(key) end end # @api private def resolve_type(type) case type when String, Class then Types[type] else type end end # @api private def key_name(key) if key.to_s.end_with?('?') [key.to_s.chop.to_sym, NOT_REQUIRED] else [key, EMPTY_HASH] end end end end end require 'dry/types/schema/key' require 'dry/types/schema'