Resolver.find_type()   B
last analyzed

Complexity

Conditions 5

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 8.5454
cc 5
1
# frozen_string_literal: true
2
3
require_relative '../mixin/errors'
4
require_relative '../type'
5
require_relative 'parameter'
6
7
module AMA
8
  module Entity
9
    class Mapper
10
      class Type
11
        # This class is responsible for resolution of simple type definitions,
12
        # converting definitions like
13
        # [Array, T: [NilClass, [Hash, K: Symbol, V: Integer]]]
14
        # into real type hierarchy
15
        class Resolver
16
          include Mixin::Errors
17
18
          # @param [Registry] registry
19
          def initialize(registry)
20
            @registry = registry
21
          end
22
23
          def resolve(definition)
24
            definition = [definition] unless definition.is_a?(Enumerable)
25
            resolve_definition(definition)
26
          rescue StandardError => parent
27
            message = "Definition #{definition} resolution resulted " \
28
              "in error: #{parent}"
29
            compliance_error(message)
30
          end
31
32
          private
33
34
          def resolve_definitions(definitions)
35
            definitions = [definitions] unless definitions.is_a?(Array)
36
            if definitions.size == 2 && definitions.last.is_a?(Hash)
37
              definitions = [definitions]
38
            end
39
            definitions.map do |definition|
40
              resolve_definition(definition)
41
            end
42
          end
43
44
          def resolve_definition(definition)
45
            definition = [definition] unless definition.is_a?(Array)
46
            type = definition.first
47
            parameters = definition[1] || {}
48
            resolve_type(type, parameters)
49
          rescue StandardError => e
50
            message = "Unexpected error during definition #{definition} " \
51
              "resolution: #{e.message}"
52
            compliance_error(message)
53
          end
54
55
          def resolve_type(type, parameters)
56
            type = find_type(type)
57
            unless parameters.is_a?(Hash)
58
              message = "Type parameters were passed not as hash: #{parameters}"
59
              compliance_error(message)
60
            end
61
            parameters.each do |parameter, replacements|
62
              parameter = resolve_type_parameter(type, parameter)
63
              replacements = resolve_definitions(replacements)
64
              type = type.resolve_parameter(parameter, replacements)
65
            end
66
            type
67
          end
68
69
          def find_type(type)
70
            return type if type.is_a?(Type)
71
            return Type::Any::INSTANCE if [:*, '*'].include?(type)
72
            if type.is_a?(Class) || type.is_a?(Module)
73
              return @registry[type] || Type::Analyzer.analyze(type)
74
            end
75
            message = 'Invalid type provided for resolution, expected Type, ' \
76
              "Class or Module: #{type}"
77
            compliance_error(message)
78
          end
79
80
          def resolve_type_parameter(type, parameter)
81
            unless parameter.is_a?(Parameter)
82
              parameter = find_parameter(type, parameter)
83
            end
84
            return parameter if parameter.owner.type == type.type
85
            message = "Parameter #{parameter} belongs to different type " \
86
              'rather one it is resolved against'
87
            compliance_error(message)
88
          end
89
90
          def find_parameter(type, parameter)
91
            parameter = parameter.to_sym if parameter.respond_to?(:to_sym)
92
            unless parameter.is_a?(Symbol)
93
              message = "#{parameter} is not a valid parameter identifier " \
94
                '(Symbol expected)'
95
              compliance_error(message)
96
            end
97
            return type.parameters[parameter] if type.parameters.key?(parameter)
98
            message = "Type #{type} has no requested parameter #{parameter}"
99
            compliance_error(message)
100
          end
101
        end
102
      end
103
    end
104
  end
105
end
106