Module: ToolSchema::SorbetTypeMapper

Extended by:
T::Sig
Defined in:
app/lib/tool_schema/sorbet_type_mapper.rb

Constant Summary collapse

TypeAst =
T.type_alias do
  T::Hash[Symbol, T.untyped]
end

Class Method Summary collapse

Class Method Details

.map_array(type) ⇒ Object



76
77
78
79
80
81
# File 'app/lib/tool_schema/sorbet_type_mapper.rb', line 76

def self.map_array(type)
  {
    type: :array,
    item_type: map_type(type.type)
  }
end

.map_fixed_hash(type) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
# File 'app/lib/tool_schema/sorbet_type_mapper.rb', line 94

def self.map_fixed_hash(type)
  children = type.keys.map do |key, value|
    mapped = map_type(value.type)
    mapped.merge(name: key.to_sym, required: value.required?)
  end

  {
    type: :object,
    children: children
  }
end

.map_hash(type) ⇒ Object



84
85
86
87
88
89
90
91
# File 'app/lib/tool_schema/sorbet_type_mapper.rb', line 84

def self.map_hash(type)
  {
    type: :object,
    children: [],
    key_type: map_type(type.keys),
    value_type: map_type(type.values)
  }
end

.map_param(name, type) ⇒ Object



34
35
36
37
# File 'app/lib/tool_schema/sorbet_type_mapper.rb', line 34

def self.map_param(name, type)
  details = map_type(type)
  details.merge(name: name.to_sym)
end

.map_signature(method) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'app/lib/tool_schema/sorbet_type_mapper.rb', line 15

def self.map_signature(method)
  signature = T::Private::Methods.signature_for_method(method)
  raise ToolMeta::MissingSignatureError, "Missing Sorbet signature for #{method.name}" if signature.nil?

  params_ast = signature.arg_types.map do |name, type|
    map_param(name, type)
  end

  signature.kwarg_types.each do |name, type|
    params_ast << map_param(name, type)
  end

  {
    params: params_ast,
    return_type: map_type(signature.return_type)
  }
end

.map_simple(type) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'app/lib/tool_schema/sorbet_type_mapper.rb', line 107

def self.map_simple(type)
  raw = type.raw_type
  case raw.name
  when 'String'
    { type: :string }
  when 'Integer'
    { type: :integer }
  when 'Float'
    { type: :float }
  when 'TrueClass', 'FalseClass'
    { type: :boolean }
  else
    if raw == T::Boolean
      { type: :boolean }
    else
      { type: :any }
    end
  end
end

.map_type(type) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'app/lib/tool_schema/sorbet_type_mapper.rb', line 40

def self.map_type(type)
  nilable, inner_type = unwrap_nilable(type)
  mapped = case inner_type
           when T::Types::FixedHash
             map_fixed_hash(inner_type)
           when T::Types::TypedArray
             map_array(inner_type)
           when T::Types::TypedHash
             map_hash(inner_type)
           when T::Types::Simple
             map_simple(inner_type)
           when T::Types::Union
             map_union(inner_type)
           else
             { type: :any }
           end

  mapped.merge(required: !nilable)
end

.map_union(type) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'app/lib/tool_schema/sorbet_type_mapper.rb', line 61

def self.map_union(type)
  non_nil_types = type.types.reject { |t| t.is_a?(T::Types::Simple) && t.raw_type == NilClass }

  # Check for Boolean (TrueClass | FalseClass)
  is_boolean = non_nil_types.length == 2 && non_nil_types.all? do |t|
    t.is_a?(T::Types::Simple) && [TrueClass, FalseClass].include?(t.raw_type)
  end
  return { type: :boolean } if is_boolean

  return { type: :any } if non_nil_types.length != 1

  map_type(non_nil_types.first).merge(required: true)
end

.unwrap_nilable(type) ⇒ Object



128
129
130
131
132
133
134
135
# File 'app/lib/tool_schema/sorbet_type_mapper.rb', line 128

def self.unwrap_nilable(type)
  return [false, type] unless type.is_a?(T::Types::Union)

  nilable = type.types.any? { |t| t.is_a?(T::Types::Simple) && t.raw_type == NilClass }
  non_nil = type.types.reject { |t| t.is_a?(T::Types::Simple) && t.raw_type == NilClass }
  target = non_nil.length == 1 ? non_nil.first : type
  [nilable, target]
end