module Scale::Types

Attributes

debug[RW]
logger[RW]

Public Class Methods

check_types() click to toggle source
# File lib/scale.rb, line 50
def self.check_types
  TypeRegistry.instance.all_types.keys.each do |key|
    begin
      type = self.get(key)
    rescue => ex
      puts "[[ERROR]] #{key}: #{ex}"
    end
  end
  true
end
get(type_info) click to toggle source

type_info: type_string or type_def

type_string: hard coded type name, Compact, H128, Vec<Compact>, (U32, U128), ...
type_def   : struct, enum, set

if type_string start_with Scale::Types::, it is treat as a hard coded type

# File lib/type_builder.rb, line 10
def get(type_info)
  if type_info.class == ::String 
    if type_info.start_with?('Scale::Types::')
      return get_hard_coded_type(type_info)
    end

    # find the final type from registry
    type_info = fix_name(type_info)
    type_info = get_final_type_from_registry(type_info)
  end

  build_type(type_info)
end

Private Class Methods

build_array(type_string) click to toggle source
# File lib/type_builder.rb, line 113
def build_array(type_string)
  scan_result = type_string.scan /\[(.+);\s*(\d+)\]/

  #
  inner_type_str = scan_result[0][0]
  inner_type = get(inner_type_str)

  #
  len = scan_result[0][1].to_i

  type_name = "Array_#{inner_type.name.gsub('Scale::Types::', '')}_#{len}_"

  if !Scale::Types.const_defined?(type_name)
    klass = Class.new do
      include Scale::Types::Array
      inner_type inner_type
      length len
    end
    Scale::Types.const_set type_name, klass
  else
    Scale::Types.const_get type_name
  end
end
build_enum(type_info) click to toggle source

not implemented: [“Compact”, “Hex”]

# File lib/type_builder.rb, line 186
def build_enum(type_info)
  # type_mapping: [["Item1", "Compact"], [["Item2", "Hex"]]
  if type_info.has_key?(:type_mapping)
    # items: {a: Type}
    items = type_info[:type_mapping].map do |item|
      item_name = item[0]
      item_type = get(item[1])
      [item_name.to_sym, item_type]
    end.to_h

    partials = []
    items.each_pair do |item_name, item_type|
      partials << item_name.to_s.camelize2 + 'In' + item_type.name.gsub('Scale::Types::', '') 
    end
    type_name = "Enum_#{partials.join('_')}_"

    if !Scale::Types.const_defined?(type_name)
      klass = Class.new do
        include Scale::Types::Enum
        items(**items)
      end

      return Scale::Types.const_set type_name, klass
    else
      return Scale::Types.const_get type_name
    end
  end

  # value_list: [1, "hello"]
  if type_info.has_key?(:value_list)
    type_name = "Enum#{type_info[:value_list].map {|value| value.to_s.camelize2}.join}"

    if !Scale::Types.const_defined?(type_name)
      klass = Class.new do
        include Scale::Types::Enum
        values *type_info[:value_list]
      end
      return Scale::Types.const_set type_name, klass
    else
      return Scale::Types.const_get type_name
    end
  end
end
build_option(type_string) click to toggle source
# File lib/type_builder.rb, line 91
def build_option(type_string)
  inner_type_str = type_string.scan(/\AOption<(.+)>\z/).first.first

  # an exception
  # https://substrate.dev/docs/en/knowledgebase/advanced/codec#options
  return get("Scale::Types::OptionBool") if inner_type_str.camelize2 == "Bool"

  inner_type = get(inner_type_str)

  type_name = "Option_#{inner_type.name.gsub('Scale::Types::', '')}_"

  if !Scale::Types.const_defined?(type_name)
    klass = Class.new do
      include Scale::Types::Option
      inner_type inner_type
    end
    Scale::Types.const_set type_name, klass
  else
    Scale::Types.const_get type_name
  end
end
build_set(type_info) click to toggle source

{ value_type: u32, value_list: {

"TransactionPayment" => 0b00000001,
"Transfer" => 0b00000010,
"Reserve" => 0b00000100,
...

} }

# File lib/type_builder.rb, line 240
def build_set(type_info)
  type_name = "Set#{type_info[:value_list].keys.map(&:camelize2).join("")}"
  if !Scale::Types.const_defined?(type_name)
    bytes_length = type_info[:value_type][1..].to_i / 8
    klass = Class.new do
      include Scale::Types::Set
      items type_info[:value_list], bytes_length
    end
    return Scale::Types.const_set type_name, klass
  else
    return Scale::Types.const_get type_name
  end
  Scale::Types.const_set fix(name), klass
end
build_struct(type_info) click to toggle source
# File lib/type_builder.rb, line 160
def build_struct(type_info)
  # items: {"a" => Type}
  items = type_info[:type_mapping].map do |item|
    item_name = item[0]
    item_type = get(item[1])
    [item_name, item_type]
  end.to_h

  partials = []
  items.each_pair do |item_name, item_type|
    partials << item_name.camelize2 + 'In' + item_type.name.gsub('Scale::Types::', '') 
  end
  type_name = "Struct_#{partials.join('_')}_"

  if !Scale::Types.const_defined?(type_name)
    klass = Class.new do
      include Scale::Types::Struct
      items(**items)
    end
    Scale::Types.const_set type_name, klass
  else
    Scale::Types.const_get type_name
  end
end
build_tuple(type_string) click to toggle source
# File lib/type_builder.rb, line 137
def build_tuple(type_string)
  scan_result = type_string.scan /\A\((.+)\)\z/
  inner_types_str = scan_result[0][0]
  inner_type_strs = inner_types_str.split(",").map do |inner_type_str| 
    inner_type_str.strip
  end

  inner_types = inner_type_strs.map do |inner_type_str|
    get(inner_type_str)
  end

  type_name = "Tuple_#{inner_types.map {|inner_type| inner_type.name.gsub('Scale::Types::', '')}.join("_")}_"
  if !Scale::Types.const_defined?(type_name)
    klass = Class.new do
      include Scale::Types::Tuple
      inner_types(*inner_types)
    end
    Scale::Types.const_set type_name, klass
  else
    Scale::Types.const_get type_name
  end
end
build_type(type_info) click to toggle source
# File lib/type_builder.rb, line 25
def build_type(type_info)
  # 1. hard coded types, 2. Vec<...>, 3. Option<...>, 4. [xx; x], 5. (x, y)
  if type_info.class == ::String 
    type_string = fix_name(type_info)
    if type_string =~ /\AVec<.+>\z/ 
      build_vec(type_string)
    elsif type_info =~ /\AOption<.+>\z/
      build_option(type_string)
    elsif type_info =~ /\A\[.+;\s*\d+\]\z/
      build_array(type_string)
    elsif type_info =~ /\A\(.+\)\z/
      build_tuple(type_string)
    else
      get_hard_coded_type(type_string)
    end

  # 5. Struct, 6. Enum, 7. Set
  else 

    type_info.transform_keys!(&:to_sym)
    if type_info[:type] == "struct"
      build_struct(type_info)
    elsif type_info[:type] == "enum"
      build_enum(type_info)
    elsif type_info[:type] == "set"
      build_set(type_info)
    else
      raise Scale::TypeBuildError.new("Failed to build a type from #{type_info}")
    end

  end
end
build_vec(type_string) click to toggle source
# File lib/type_builder.rb, line 74
def build_vec(type_string)
  inner_type_str = type_string.scan(/\AVec<(.+)>\z/).first.first
  inner_type = get(inner_type_str)

  type_name = "Vec_#{inner_type.name.gsub('Scale::Types::', '')}_"

  if !Scale::Types.const_defined?(type_name)
    klass = Class.new do
      include Scale::Types::Vec
      inner_type inner_type
    end
    Scale::Types.const_set type_name, klass
  else
    Scale::Types.const_get type_name
  end
end
fix_name(type) click to toggle source
# File lib/type_builder.rb, line 255
def fix_name(type)
  type = type.gsub("T::", "")
    .gsub("<T>", "")
    .gsub("<T as Trait>::", "")
    .delete("\n")
    .gsub(/(u)(\d+)/, 'U\2')
  return "Bool" if type == "bool"
  return "Null" if type == "()"
  return "String" if type == "Vec<u8>"
  return "Compact" if type == "Compact<u32>" || type == "Compact<U32>"
  return "Address" if type == "<Lookup as StaticLookup>::Source"
  return "Compact" if type == "<Balance as HasCompact>::Type"
  return "Compact" if type == "<BlockNumber as HasCompact>::Type"
  return "Compact" if type =~ /\ACompact<[a-zA-Z0-9\s]*>\z/
  return "CompactMoment" if type == "<Moment as HasCompact>::Type"
  return "CompactMoment" if type == "Compact<Moment>"
  return "InherentOfflineReport" if type == "<InherentOfflineReport as InherentOfflineReport>::Inherent"
  return "AccountData" if type == "AccountData<Balance>"
  return "EventRecord" if type == "EventRecord<Event, Hash>"

  type
end
get_final_type_from_registry(type_info) click to toggle source
# File lib/type_builder.rb, line 58
def get_final_type_from_registry(type_info)
  type_registry = TypeRegistry.instance
  if type_registry.types.nil?
    raise TypeRegistryNotLoadYet
  end
  TypeRegistry.instance.get(type_info)
end
get_hard_coded_type(type_name) click to toggle source
# File lib/type_builder.rb, line 66
def get_hard_coded_type(type_name)
  # type_name = rename(type_name)
  type_name = (type_name.start_with?("Scale::Types::") ? type_name : "Scale::Types::#{type_name}")
  type_name.constantize2
rescue => e
  raise Scale::TypeBuildError.new("Failed to get the hard coded type named `#{type_name}`")
end