Completed
Push — master ( 308a2c...1aebdd )
by Fike
01:00
created

Resolver.resolve()   A

Complexity

Conditions 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 3
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
            if type.is_a?(Class) || type.is_a?(Module)
72
              return @registry[type] || Type::Analyzer.analyze(type)
73
            end
74
            message = 'Invalid type provided for resolution, expected Type, ' \
75
              "Class or Module: #{type}"
76
            compliance_error(message)
77
          end
78
79
          def resolve_type_parameter(type, parameter)
80
            unless parameter.is_a?(Parameter)
81
              parameter = find_parameter(type, parameter)
82
            end
83
            return parameter if parameter.owner.type == type.type
84
            message = "Parameter #{parameter} belongs to different type " \
85
              'rather one it is resolved against'
86
            compliance_error(message)
87
          end
88
89
          def find_parameter(type, parameter)
90
            parameter = parameter.to_sym if parameter.respond_to?(:to_sym)
91
            unless parameter.is_a?(Symbol)
92
              message = "#{parameter} is not a valid parameter identifier " \
93
                '(Symbol expected)'
94
              compliance_error(message)
95
            end
96
            return type.parameters[parameter] if type.parameters.key?(parameter)
97
            message = "Type #{type} has no requested parameter #{parameter}"
98
            compliance_error(message)
99
          end
100
        end
101
      end
102
    end
103
  end
104
end
105