scale_rb 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/console +0 -0
- data/examples/http_client_3.rb +84 -0
- data/exe/metadata +1 -1
- data/lib/scale_rb/call_helper.rb +5 -5
- data/lib/scale_rb/client/{client_ext.rb → client_share.rb} +12 -6
- data/lib/scale_rb/client/http_client.rb +2 -2
- data/lib/scale_rb/client/ws_client.rb +2 -2
- data/lib/scale_rb/decode.rb +17 -0
- data/lib/scale_rb/extrinsic_helper.rb +50 -0
- data/lib/scale_rb/metadata/metadata.rb +213 -64
- data/lib/scale_rb/metadata/metadata_v10.rb +40 -40
- data/lib/scale_rb/metadata/metadata_v11.rb +44 -44
- data/lib/scale_rb/metadata/metadata_v12.rb +22 -22
- data/lib/scale_rb/metadata/metadata_v13.rb +45 -45
- data/lib/scale_rb/metadata/metadata_v14.rb +123 -117
- data/lib/scale_rb/metadata/metadata_v9.rb +56 -56
- data/lib/scale_rb/old_registry.rb +552 -0
- data/lib/scale_rb/portable_registry.rb +30 -12
- data/lib/scale_rb/storage_helper.rb +9 -5
- data/lib/scale_rb/types.rb +6 -0
- data/lib/scale_rb/version.rb +1 -1
- data/lib/scale_rb.rb +3 -1
- metadata +7 -6
- data/lib/scale_rb/metadata/registry.rb +0 -269
- data/lib/scale_rb/metadata/type_exp.rb +0 -286
| @@ -0,0 +1,552 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative './types'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # rubocop:disable all
         | 
| 6 | 
            +
            module ScaleRb
         | 
| 7 | 
            +
              class OldRegistry
         | 
| 8 | 
            +
                include Types
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                # Map name to index of type in `types` array
         | 
| 11 | 
            +
                # % lookup :: String -> Integer
         | 
| 12 | 
            +
                attr_reader :lookup
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                # % keys :: Integer -> String
         | 
| 15 | 
            +
                attr_reader :keys
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                # % types :: Array<PortableType>
         | 
| 18 | 
            +
                attr_reader :types
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                attr_reader :old_types
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                # % initialize :: Hash<String, Any> -> void
         | 
| 23 | 
            +
                def initialize(old_types)
         | 
| 24 | 
            +
                  @old_types = old_types
         | 
| 25 | 
            +
                  @lookup = {}
         | 
| 26 | 
            +
                  @keys = {}
         | 
| 27 | 
            +
                  @types = []
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  build()
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def build()
         | 
| 33 | 
            +
                  @old_types.keys.each do |name|
         | 
| 34 | 
            +
                    use(name)
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def [](identifier)
         | 
| 39 | 
            +
                  if identifier.is_a?(::Integer)
         | 
| 40 | 
            +
                    @types[identifier]
         | 
| 41 | 
            +
                  elsif identifier.is_a?(::String)
         | 
| 42 | 
            +
                    @types[use(identifier)]
         | 
| 43 | 
            +
                  else
         | 
| 44 | 
            +
                    raise "Unknown identifier type: #{identifier.class}"
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def inspect
         | 
| 49 | 
            +
                  "registry(#{@types.length} types)"
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def to_s
         | 
| 53 | 
            +
                  @types.map.with_index do |type, index|
         | 
| 54 | 
            +
                    "#{@keys[index]} => #{type.to_s}"
         | 
| 55 | 
            +
                  end.join("\n")
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                # % use :: String -> Integer
         | 
| 59 | 
            +
                def use(old_type_exp)
         | 
| 60 | 
            +
                  raise "Empty old_type_exp: #{old_type_exp}" if old_type_exp.nil? || old_type_exp.strip == ''
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  ast_type = TypeExp.parse(old_type_exp)
         | 
| 63 | 
            +
                  raise "No AST type for #{old_type_exp}" if ast_type.nil?
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  key = ast_type.to_s
         | 
| 66 | 
            +
                  ti = lookup[key]
         | 
| 67 | 
            +
                  return ti if ti
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  ti = @types.length
         | 
| 70 | 
            +
                  @types[ti] = "Placeholder"
         | 
| 71 | 
            +
                  @lookup[key] = ti
         | 
| 72 | 
            +
                  @keys[ti] = key
         | 
| 73 | 
            +
                  @types[ti] = build_portable_type(ast_type)
         | 
| 74 | 
            +
                  ti
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                # % build_portable_type :: NamedType | ArrayType | TupleType -> PortableType
         | 
| 78 | 
            +
                # __ :build_portable_type, { ast_type: TypedArray[TypeExp::ArrayType | TypeExp::TupleType | TypeExp::NamedType] } => PortableType
         | 
| 79 | 
            +
                def build_portable_type(ast_type)
         | 
| 80 | 
            +
                  case ast_type
         | 
| 81 | 
            +
                  when TypeExp::ArrayType
         | 
| 82 | 
            +
                    ArrayType.new(use(ast_type.item), ast_type.len, registry: self)
         | 
| 83 | 
            +
                  when TypeExp::TupleType
         | 
| 84 | 
            +
                    TupleType.new(ast_type.params.map { |param| use(param) })
         | 
| 85 | 
            +
                  when TypeExp::NamedType
         | 
| 86 | 
            +
                    build_portable_type_from_named_type(ast_type)
         | 
| 87 | 
            +
                  else
         | 
| 88 | 
            +
                    raise "Unknown type: #{ast_type.class}"
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                # % build_portable_type_from_named_type :: NamedType -> PortableType
         | 
| 93 | 
            +
                def build_portable_type_from_named_type(named_type)
         | 
| 94 | 
            +
                  name = named_type.name
         | 
| 95 | 
            +
                  params = named_type.params
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  definition = @old_types[name]
         | 
| 98 | 
            +
                  return build_from_definition(name, definition) if definition
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  primitive = as_primitive(name)
         | 
| 101 | 
            +
                  return primitive if primitive
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  case name
         | 
| 104 | 
            +
                  when 'Vec'
         | 
| 105 | 
            +
                    item_index = use(params[0].to_s)
         | 
| 106 | 
            +
                    SequenceType.new(type: item_index, registry: self)
         | 
| 107 | 
            +
                  when 'Option'
         | 
| 108 | 
            +
                    item_index = use(params[0].to_s)
         | 
| 109 | 
            +
                    VariantType.option(item_index, self)
         | 
| 110 | 
            +
                  when 'Result'
         | 
| 111 | 
            +
                    ok_index = use(params[0].to_s)
         | 
| 112 | 
            +
                    err_index = use(params[1].to_s)
         | 
| 113 | 
            +
                    VariantType.result(ok_index, err_index, self)
         | 
| 114 | 
            +
                  when 'Compact'
         | 
| 115 | 
            +
                    # item_index = use(params[0].to_s)
         | 
| 116 | 
            +
                    # CompactType.new(type: item_index, registry: self)
         | 
| 117 | 
            +
                    CompactType.new
         | 
| 118 | 
            +
                  when 'Null'
         | 
| 119 | 
            +
                    UnitType.new
         | 
| 120 | 
            +
                  else
         | 
| 121 | 
            +
                    raise "Unknown type: #{name}"
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
                end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                # % as_primitive :: String -> PrimitiveType | nil
         | 
| 126 | 
            +
                def as_primitive(name)
         | 
| 127 | 
            +
                  case name.downcase
         | 
| 128 | 
            +
                  when /^i\d+$/
         | 
| 129 | 
            +
                    PrimitiveType.new(primitive: "I#{name[1..]}".to_sym)
         | 
| 130 | 
            +
                  when /^u\d+$/
         | 
| 131 | 
            +
                    PrimitiveType.new(primitive: "U#{name[1..]}".to_sym)
         | 
| 132 | 
            +
                  when /^bool$/
         | 
| 133 | 
            +
                    PrimitiveType.new(primitive: :Bool)
         | 
| 134 | 
            +
                  when /^str$/, /^text$/
         | 
| 135 | 
            +
                    PrimitiveType.new(primitive: :Str)
         | 
| 136 | 
            +
                  else
         | 
| 137 | 
            +
                    nil
         | 
| 138 | 
            +
                  end
         | 
| 139 | 
            +
                end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                # % build_from_definition :: String -> OldTypeDefinition -> PortableType | TypeAlias
         | 
| 142 | 
            +
                #
         | 
| 143 | 
            +
                # type OldTypeDefinition = String | OldEnumDefinition | OldStructDefinition
         | 
| 144 | 
            +
                # type OldEnumDefinition = {
         | 
| 145 | 
            +
                #   _enum: String[] | Hash<Symbol, Any>,
         | 
| 146 | 
            +
                # }
         | 
| 147 | 
            +
                # type OldStructDefinition = {
         | 
| 148 | 
            +
                #   _struct: Hash<Symbol, Any>
         | 
| 149 | 
            +
                # }
         | 
| 150 | 
            +
                def build_from_definition(name, definition) # rubocop:disable Metrics/MethodLength
         | 
| 151 | 
            +
                  case definition
         | 
| 152 | 
            +
                  when String
         | 
| 153 | 
            +
                    # TypeAlias.new(name, use(definition))
         | 
| 154 | 
            +
                    alias_type_id = use(definition)
         | 
| 155 | 
            +
                    # p "alias_type_id: #{alias_type_id}"
         | 
| 156 | 
            +
                    types[alias_type_id]
         | 
| 157 | 
            +
                  when Hash
         | 
| 158 | 
            +
                    if definition['_enum']
         | 
| 159 | 
            +
                      _build_portable_type_from_enum_definition(definition)
         | 
| 160 | 
            +
                    elsif definition['_set']
         | 
| 161 | 
            +
                      raise 'Sets are not supported'
         | 
| 162 | 
            +
                    else
         | 
| 163 | 
            +
                      _build_portable_type_from_struct_definition(definition)
         | 
| 164 | 
            +
                    end
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
                end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                private
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                def _indexed_enum?(definition)
         | 
| 171 | 
            +
                  definition['_enum'].is_a?(::Hash) && definition['_enum'].values.all? { |value| value.is_a?(::Integer) }
         | 
| 172 | 
            +
                end
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                # % _build_portable_type_from_enum_definition :: Hash<Symbol, Any> -> VariantType
         | 
| 175 | 
            +
                # { '_enum' => ['A', 'B', 'C'] }
         | 
| 176 | 
            +
                def _build_portable_type_from_enum_definition(definition)
         | 
| 177 | 
            +
                  variants =
         | 
| 178 | 
            +
                    if definition['_enum'].is_a?(::Array)
         | 
| 179 | 
            +
                      # Simple array enum:
         | 
| 180 | 
            +
                      # {
         | 
| 181 | 
            +
                      #   '_enum' => ['A', 'B', 'C']
         | 
| 182 | 
            +
                      # }
         | 
| 183 | 
            +
                      definition['_enum'].map.with_index do |variant_name, index|
         | 
| 184 | 
            +
                        SimpleVariant.new(name: variant_name.to_sym, index:)
         | 
| 185 | 
            +
                      end
         | 
| 186 | 
            +
                    elsif definition['_enum'].is_a?(::Hash)
         | 
| 187 | 
            +
                      if _indexed_enum?(definition)
         | 
| 188 | 
            +
                        # Indexed enum:
         | 
| 189 | 
            +
                        # {
         | 
| 190 | 
            +
                        #   '_enum' => {
         | 
| 191 | 
            +
                        #     'Variant1' => 0,
         | 
| 192 | 
            +
                        #     'Variant2' => 1,
         | 
| 193 | 
            +
                        #     'Variant3' => 2
         | 
| 194 | 
            +
                        #   }
         | 
| 195 | 
            +
                        # }
         | 
| 196 | 
            +
                        definition['_enum'].map do |variant_name, index|
         | 
| 197 | 
            +
                          SimpleVariant.new(name: variant_name.to_sym, index:)
         | 
| 198 | 
            +
                        end
         | 
| 199 | 
            +
                      else
         | 
| 200 | 
            +
                        # Mixed enum:
         | 
| 201 | 
            +
                        # {
         | 
| 202 | 
            +
                        #   '_enum' => {
         | 
| 203 | 
            +
                        #     'A' => 'u32',
         | 
| 204 | 
            +
                        #     'B' => {a: 'u32', b: 'u32'},
         | 
| 205 | 
            +
                        #     'C' => null,
         | 
| 206 | 
            +
                        #     'D' => ['u32', 'u32']
         | 
| 207 | 
            +
                        #   }
         | 
| 208 | 
            +
                        # }
         | 
| 209 | 
            +
                        definition['_enum'].map.with_index do |(variant_name, variant_def), index|
         | 
| 210 | 
            +
                          case variant_def
         | 
| 211 | 
            +
                          when ::String
         | 
| 212 | 
            +
                            TupleVariant.new(
         | 
| 213 | 
            +
                              name: variant_name.to_sym,
         | 
| 214 | 
            +
                              index:,
         | 
| 215 | 
            +
                              tuple: TupleType.new(
         | 
| 216 | 
            +
                                tuple: [use(variant_def)],
         | 
| 217 | 
            +
                                registry: self
         | 
| 218 | 
            +
                              ),
         | 
| 219 | 
            +
                            )
         | 
| 220 | 
            +
                          when ::Array
         | 
| 221 | 
            +
                            TupleVariant.new(
         | 
| 222 | 
            +
                              name: variant_name.to_sym,
         | 
| 223 | 
            +
                              index:,
         | 
| 224 | 
            +
                              tuple: TupleType.new(
         | 
| 225 | 
            +
                                tuple: variant_def.map { |field_type| use(field_type) },
         | 
| 226 | 
            +
                                registry: self
         | 
| 227 | 
            +
                              )
         | 
| 228 | 
            +
                            )
         | 
| 229 | 
            +
                          when ::Hash
         | 
| 230 | 
            +
                            StructVariant.new(
         | 
| 231 | 
            +
                              name: variant_name.to_sym,
         | 
| 232 | 
            +
                              index:,
         | 
| 233 | 
            +
                              struct: StructType.new(
         | 
| 234 | 
            +
                                fields: variant_def.map do |field_name, field_type|
         | 
| 235 | 
            +
                                  Field.new(name: field_name.to_s, type: use(field_type))
         | 
| 236 | 
            +
                                end,
         | 
| 237 | 
            +
                                registry: self
         | 
| 238 | 
            +
                              )
         | 
| 239 | 
            +
                            )
         | 
| 240 | 
            +
                          else
         | 
| 241 | 
            +
                            raise "Unknown variant type for #{variant_name}: #{variant_def.class}"
         | 
| 242 | 
            +
                          end
         | 
| 243 | 
            +
                        end
         | 
| 244 | 
            +
                      end
         | 
| 245 | 
            +
                    end
         | 
| 246 | 
            +
                  VariantType.new(variants:, registry: self)
         | 
| 247 | 
            +
                end
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                # % _build_portable_type_from_struct_definition :: Hash<String, Any> -> StructType
         | 
| 250 | 
            +
                def _build_portable_type_from_struct_definition(definition)
         | 
| 251 | 
            +
                  fields = definition.map do |field_name, field_type|
         | 
| 252 | 
            +
                    Field.new(name: field_name.to_s, type: use(field_type))
         | 
| 253 | 
            +
                  end
         | 
| 254 | 
            +
                  StructType.new(fields:, registry: self)
         | 
| 255 | 
            +
                end
         | 
| 256 | 
            +
              end
         | 
| 257 | 
            +
            end
         | 
| 258 | 
            +
             | 
| 259 | 
            +
            module ScaleRb
         | 
| 260 | 
            +
              class OldRegistry
         | 
| 261 | 
            +
                module TypeExp
         | 
| 262 | 
            +
                  class Tokenizer
         | 
| 263 | 
            +
                    attr_reader :tokens, :index
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                    # % tokenize :: String -> [String]
         | 
| 266 | 
            +
                    def initialize(type_exp)
         | 
| 267 | 
            +
                      @tokens = tokenize(type_exp)
         | 
| 268 | 
            +
                      @index = 0
         | 
| 269 | 
            +
                    end
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                    # % next_token :: -> String
         | 
| 272 | 
            +
                    def next_token
         | 
| 273 | 
            +
                      token = @tokens[@index]
         | 
| 274 | 
            +
                      @index += 1
         | 
| 275 | 
            +
                      token
         | 
| 276 | 
            +
                    end
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                    # % peek_token :: -> String
         | 
| 279 | 
            +
                    def peek_token
         | 
| 280 | 
            +
                      @tokens[@index]
         | 
| 281 | 
            +
                    end
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                    # % eof? :: -> Bool
         | 
| 284 | 
            +
                    def eof?
         | 
| 285 | 
            +
                      @index >= @tokens.length
         | 
| 286 | 
            +
                    end
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                    private
         | 
| 289 | 
            +
             | 
| 290 | 
            +
                    def tokenize(type_exp)
         | 
| 291 | 
            +
                      tokens = []
         | 
| 292 | 
            +
                      current_token = ''
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                      type_exp.each_char do |char|
         | 
| 295 | 
            +
                        case char
         | 
| 296 | 
            +
                        when /[a-zA-Z0-9_]/
         | 
| 297 | 
            +
                          current_token += char
         | 
| 298 | 
            +
                        when ':', '<', '>', '(', ')', '[', ']', ',', ';', '&', "'"
         | 
| 299 | 
            +
                          tokens << current_token unless current_token.empty?
         | 
| 300 | 
            +
                          if char == ':' && tokens.last == ':'
         | 
| 301 | 
            +
                            tokens[-1] = '::'
         | 
| 302 | 
            +
                          else
         | 
| 303 | 
            +
                            tokens << char
         | 
| 304 | 
            +
                          end
         | 
| 305 | 
            +
                          current_token = ''
         | 
| 306 | 
            +
                        when /\s/
         | 
| 307 | 
            +
                          tokens << current_token unless current_token.empty?
         | 
| 308 | 
            +
                          current_token = ''
         | 
| 309 | 
            +
                        else
         | 
| 310 | 
            +
                          raise abort
         | 
| 311 | 
            +
                        end
         | 
| 312 | 
            +
                      end
         | 
| 313 | 
            +
             | 
| 314 | 
            +
                      tokens << current_token unless current_token.empty?
         | 
| 315 | 
            +
                      tokens
         | 
| 316 | 
            +
                    end
         | 
| 317 | 
            +
                  end
         | 
| 318 | 
            +
             | 
| 319 | 
            +
                  class NamedType
         | 
| 320 | 
            +
                    attr_reader :name, :params
         | 
| 321 | 
            +
             | 
| 322 | 
            +
                    def initialize(name, params)
         | 
| 323 | 
            +
                      @name = name
         | 
| 324 | 
            +
                      @params = params
         | 
| 325 | 
            +
                    end
         | 
| 326 | 
            +
             | 
| 327 | 
            +
                    def to_s
         | 
| 328 | 
            +
                      params.empty? ? name : "#{name}<#{params.map(&:to_s).join(', ')}>"
         | 
| 329 | 
            +
                    end
         | 
| 330 | 
            +
                  end
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                  class ArrayType
         | 
| 333 | 
            +
                    attr_reader :item, :len
         | 
| 334 | 
            +
             | 
| 335 | 
            +
                    def initialize(item, len)
         | 
| 336 | 
            +
                      @item = item
         | 
| 337 | 
            +
                      @len = len
         | 
| 338 | 
            +
                    end
         | 
| 339 | 
            +
             | 
| 340 | 
            +
                    def to_s
         | 
| 341 | 
            +
                      "[#{item}; #{len}]"
         | 
| 342 | 
            +
                    end
         | 
| 343 | 
            +
                  end
         | 
| 344 | 
            +
             | 
| 345 | 
            +
                  class TupleType
         | 
| 346 | 
            +
                    attr_reader :params
         | 
| 347 | 
            +
             | 
| 348 | 
            +
                    def initialize(params)
         | 
| 349 | 
            +
                      @params = params
         | 
| 350 | 
            +
                    end
         | 
| 351 | 
            +
             | 
| 352 | 
            +
                    def to_s
         | 
| 353 | 
            +
                      "(#{params.map(&:to_s).join(', ')})"
         | 
| 354 | 
            +
                    end
         | 
| 355 | 
            +
                  end
         | 
| 356 | 
            +
             | 
| 357 | 
            +
                  # % print :: NamedType | ArrayType | TupleType -> String
         | 
| 358 | 
            +
                  def self.print(type)
         | 
| 359 | 
            +
                    type.to_s
         | 
| 360 | 
            +
                  end
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                  # % parse :: String -> NamedType | ArrayType | TupleType
         | 
| 363 | 
            +
                  def self.parse(type_exp)
         | 
| 364 | 
            +
                    TypeExpParser.new(type_exp).parse
         | 
| 365 | 
            +
                  end
         | 
| 366 | 
            +
             | 
| 367 | 
            +
                  class TypeExpParser
         | 
| 368 | 
            +
                    def initialize(type_exp)
         | 
| 369 | 
            +
                      @type_exp = type_exp
         | 
| 370 | 
            +
                      @tokenizer = Tokenizer.new(type_exp)
         | 
| 371 | 
            +
                      @current_token = @tokenizer.next_token
         | 
| 372 | 
            +
                    end
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                    def parse
         | 
| 375 | 
            +
                      build_type
         | 
| 376 | 
            +
                    end
         | 
| 377 | 
            +
             | 
| 378 | 
            +
                    private
         | 
| 379 | 
            +
             | 
| 380 | 
            +
                    # Consume and return the current token, or nil if it doesn't equal the expected token.
         | 
| 381 | 
            +
                    def expect(token)
         | 
| 382 | 
            +
                      return unless @current_token == token
         | 
| 383 | 
            +
             | 
| 384 | 
            +
                      current_token = @current_token
         | 
| 385 | 
            +
                      @current_token = @tokenizer.next_token
         | 
| 386 | 
            +
                      current_token
         | 
| 387 | 
            +
                    end
         | 
| 388 | 
            +
             | 
| 389 | 
            +
                    def expect!(token)
         | 
| 390 | 
            +
                      expect(token) || raise("Expected #{token}, got #{@current_token}")
         | 
| 391 | 
            +
                    end
         | 
| 392 | 
            +
             | 
| 393 | 
            +
                    # Consume and return the current token if it matches the expected regex pattern.
         | 
| 394 | 
            +
                    def expect_regex(pattern)
         | 
| 395 | 
            +
                      return unless pattern.match?(@current_token)
         | 
| 396 | 
            +
             | 
| 397 | 
            +
                      current_token = @current_token
         | 
| 398 | 
            +
                      @current_token = @tokenizer.next_token
         | 
| 399 | 
            +
                      current_token
         | 
| 400 | 
            +
                    end
         | 
| 401 | 
            +
             | 
| 402 | 
            +
                    def expect_regex!(pattern)
         | 
| 403 | 
            +
                      expect_regex(pattern) || raise("Expected current token matching #{pattern.inspect}, got #{@current_token}")
         | 
| 404 | 
            +
                    end
         | 
| 405 | 
            +
             | 
| 406 | 
            +
                    # Consume and return a natural number (integer) if the current token matches.
         | 
| 407 | 
            +
                    def expect_nat
         | 
| 408 | 
            +
                      expect_regex(/^\d+$/)&.to_i
         | 
| 409 | 
            +
                    end
         | 
| 410 | 
            +
             | 
| 411 | 
            +
                    def expect_nat!
         | 
| 412 | 
            +
                      expect_nat || raise("Expected natural number, got #{@current_token}")
         | 
| 413 | 
            +
                    end
         | 
| 414 | 
            +
             | 
| 415 | 
            +
                    def expect_name
         | 
| 416 | 
            +
                      expect_regex(/^[a-zA-Z]\w*$/)
         | 
| 417 | 
            +
                    end
         | 
| 418 | 
            +
             | 
| 419 | 
            +
                    def expect_name!
         | 
| 420 | 
            +
                      expect_name || raise("Expected name, got #{@current_token}")
         | 
| 421 | 
            +
                    end
         | 
| 422 | 
            +
             | 
| 423 | 
            +
                    def list(sep, &block)
         | 
| 424 | 
            +
                      result = []
         | 
| 425 | 
            +
                      item = block.call
         | 
| 426 | 
            +
                      return result if item.nil?
         | 
| 427 | 
            +
             | 
| 428 | 
            +
                      result << item
         | 
| 429 | 
            +
                      while expect(sep)
         | 
| 430 | 
            +
                        item = block.call
         | 
| 431 | 
            +
                        break if item.nil? # (A, B,)
         | 
| 432 | 
            +
             | 
| 433 | 
            +
                        result << item
         | 
| 434 | 
            +
                      end
         | 
| 435 | 
            +
                      result
         | 
| 436 | 
            +
                    end
         | 
| 437 | 
            +
             | 
| 438 | 
            +
                    def build_tuple_type
         | 
| 439 | 
            +
                      return nil unless expect('(')
         | 
| 440 | 
            +
             | 
| 441 | 
            +
                      params = list(',') { build_type }
         | 
| 442 | 
            +
                      expect!(')')
         | 
| 443 | 
            +
             | 
| 444 | 
            +
                      TupleType.new(params)
         | 
| 445 | 
            +
                    end
         | 
| 446 | 
            +
             | 
| 447 | 
            +
                    # [u8; 16; H128]
         | 
| 448 | 
            +
                    # [u8; 16]
         | 
| 449 | 
            +
                    def build_array_type
         | 
| 450 | 
            +
                      return nil unless expect('[')
         | 
| 451 | 
            +
             | 
| 452 | 
            +
                      item = build_type
         | 
| 453 | 
            +
                      raise "Expected array item, got #{@current_token}" if item.nil?
         | 
| 454 | 
            +
             | 
| 455 | 
            +
                      expect!(';')
         | 
| 456 | 
            +
                      len = expect_nat!
         | 
| 457 | 
            +
             | 
| 458 | 
            +
                      # [u8; 16; H128]
         | 
| 459 | 
            +
                      if expect(';')
         | 
| 460 | 
            +
                        expect_name! # Just consume the name
         | 
| 461 | 
            +
                      end
         | 
| 462 | 
            +
             | 
| 463 | 
            +
                      expect!(']')
         | 
| 464 | 
            +
                      ArrayType.new(item, len)
         | 
| 465 | 
            +
                    end
         | 
| 466 | 
            +
             | 
| 467 | 
            +
                    def build_named_type
         | 
| 468 | 
            +
                      name = nil
         | 
| 469 | 
            +
                      trait = nil
         | 
| 470 | 
            +
                      item = nil
         | 
| 471 | 
            +
             | 
| 472 | 
            +
                      if expect('<')
         | 
| 473 | 
            +
                        # Handle trait syntax: <T::Trait as OtherTrait>::Type
         | 
| 474 | 
            +
                        #                          name     trait        item
         | 
| 475 | 
            +
                        # '<T::InherentOfflineReport as InherentOfflineReport>::Inherent' -> 'InherentOfflineReport'
         | 
| 476 | 
            +
                        # '<T::Balance as HasCompact>' -> 'Compact<Balance>'
         | 
| 477 | 
            +
                        # '<T as Trait<I>>::Proposal' -> 'Proposal'
         | 
| 478 | 
            +
                        name = build_named_type.name
         | 
| 479 | 
            +
                        expect!('as')
         | 
| 480 | 
            +
                        trait = build_named_type.name
         | 
| 481 | 
            +
                        expect!('>')
         | 
| 482 | 
            +
                      else
         | 
| 483 | 
            +
                        name = expect_name
         | 
| 484 | 
            +
                        return if name.nil?
         | 
| 485 | 
            +
                      end
         | 
| 486 | 
            +
             | 
| 487 | 
            +
                      # Consume the :: and get the next name
         | 
| 488 | 
            +
                      item = expect_name while expect('::')
         | 
| 489 | 
            +
             | 
| 490 | 
            +
                      # Handle special cases
         | 
| 491 | 
            +
                      # From subsquid's code. But where are these coming from?
         | 
| 492 | 
            +
                      if name == 'InherentOfflineReport' && name == trait && item == 'Inherent'
         | 
| 493 | 
            +
                        # Do nothing
         | 
| 494 | 
            +
                      elsif name == 'exec' && item == 'StorageKey'
         | 
| 495 | 
            +
                        name = 'ContractStorageKey'
         | 
| 496 | 
            +
                      elsif name == 'Lookup' && item == 'Source'
         | 
| 497 | 
            +
                        name = 'LookupSource'
         | 
| 498 | 
            +
                      elsif name == 'Lookup' && item == 'Target'
         | 
| 499 | 
            +
                        name = 'LookupTarget'
         | 
| 500 | 
            +
                      elsif item
         | 
| 501 | 
            +
                        # '<T::Balance as HasCompact>::Item' will raise error
         | 
| 502 | 
            +
                        raise "Expected item, got #{item}" if trait == 'HasCompact'
         | 
| 503 | 
            +
             | 
| 504 | 
            +
                        name = item
         | 
| 505 | 
            +
                      elsif trait == 'HasCompact' # '<T::Balance as HasCompact>'
         | 
| 506 | 
            +
                        return NamedType.new('Compact', [NamedType.new(name, [])])
         | 
| 507 | 
            +
                      end
         | 
| 508 | 
            +
             | 
| 509 | 
            +
                      NamedType.new(name, type_parameters)
         | 
| 510 | 
            +
                    end
         | 
| 511 | 
            +
             | 
| 512 | 
            +
                    def type_parameters
         | 
| 513 | 
            +
                      if expect('<')
         | 
| 514 | 
            +
                        params = list(',') { expect_nat || build_type }
         | 
| 515 | 
            +
                        expect!('>')
         | 
| 516 | 
            +
                      else
         | 
| 517 | 
            +
                        params = []
         | 
| 518 | 
            +
                      end
         | 
| 519 | 
            +
             | 
| 520 | 
            +
                      params
         | 
| 521 | 
            +
                    end
         | 
| 522 | 
            +
             | 
| 523 | 
            +
                    # &[u8]
         | 
| 524 | 
            +
                    # &'static [u8]
         | 
| 525 | 
            +
                    def build_pointer_bytes
         | 
| 526 | 
            +
                      return nil unless expect('&') # &
         | 
| 527 | 
            +
             | 
| 528 | 
            +
                      expect("'") && expect!('static')
         | 
| 529 | 
            +
                      expect!('[')
         | 
| 530 | 
            +
                      expect!('u8')
         | 
| 531 | 
            +
                      expect!(']')
         | 
| 532 | 
            +
                      NamedType.new('Vec', [NamedType.new('u8', [])])
         | 
| 533 | 
            +
                    end
         | 
| 534 | 
            +
             | 
| 535 | 
            +
                    # % build_type :: TupleType | ArrayType | NamedType
         | 
| 536 | 
            +
                    def build_type
         | 
| 537 | 
            +
                      build_tuple_type || build_array_type || build_named_type || build_pointer_bytes
         | 
| 538 | 
            +
                    end
         | 
| 539 | 
            +
                  end
         | 
| 540 | 
            +
                end
         | 
| 541 | 
            +
              end
         | 
| 542 | 
            +
            end
         | 
| 543 | 
            +
             | 
| 544 | 
            +
            # require_relative '../../metadata/metadata'
         | 
| 545 | 
            +
             | 
| 546 | 
            +
            # begin
         | 
| 547 | 
            +
            #   registry = ScaleRb::Metadata::Registry.new ScaleRb::Metadata::TYPES
         | 
| 548 | 
            +
            #   puts registry
         | 
| 549 | 
            +
            # rescue StandardError => e
         | 
| 550 | 
            +
            #   puts e.message
         | 
| 551 | 
            +
            #   puts e.backtrace.join("\n")
         | 
| 552 | 
            +
            # end
         | 
| @@ -24,6 +24,16 @@ module ScaleRb | |
| 24 24 | 
             
                  "a_portable_registry"
         | 
| 25 25 | 
             
                end
         | 
| 26 26 |  | 
| 27 | 
            +
                def to_s
         | 
| 28 | 
            +
                  "a_portable_registry"
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def add_type(type)
         | 
| 32 | 
            +
                  type_id = @types.size
         | 
| 33 | 
            +
                  @types << type
         | 
| 34 | 
            +
                  type_id
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 27 37 | 
             
                private
         | 
| 28 38 |  | 
| 29 39 | 
             
                __ :build_types, {}
         | 
| @@ -36,33 +46,39 @@ module ScaleRb | |
| 36 46 | 
             
                    raise "No 'def' found: #{type}" if def_.nil?
         | 
| 37 47 |  | 
| 38 48 | 
             
                    path = type._get(:type, :path)
         | 
| 49 | 
            +
                    params = type._get(:type, :params).map do |p|
         | 
| 50 | 
            +
                      name = p._get(:name)
         | 
| 51 | 
            +
                      type = p._get(:type)
         | 
| 52 | 
            +
                      type.nil? ? Param.new(name: name) : Param.new(name: name, type: type)
         | 
| 53 | 
            +
                    end
         | 
| 39 54 |  | 
| 40 55 | 
             
                    type_name = def_.keys.first.to_sym
         | 
| 41 56 | 
             
                    type_def = def_._get(type_name)
         | 
| 42 | 
            -
                    @types[id] = _build_type(id, type_name, type_def, path)
         | 
| 57 | 
            +
                    @types[id] = _build_type(id, type_name, type_def, path, params)
         | 
| 43 58 | 
             
                  end
         | 
| 44 59 | 
             
                end
         | 
| 45 60 |  | 
| 46 61 | 
             
                # TODO: type_def better type definition
         | 
| 47 | 
            -
                __ :_build_type, { id: Ti, type_name: Symbol, type_def: Hash | String | Array, path: TypedArray[String] }, PortableType
         | 
| 48 | 
            -
                def _build_type(id, type_name, type_def, path)
         | 
| 62 | 
            +
                __ :_build_type, { id: Ti, type_name: Symbol, type_def: Hash | String | Array, path: TypedArray[String], params: TypedArray[Field] }, PortableType
         | 
| 63 | 
            +
                def _build_type(id, type_name, type_def, path, params)
         | 
| 49 64 | 
             
                  case type_name
         | 
| 50 65 | 
             
                  when :primitive
         | 
| 51 66 | 
             
                    # type_def: 'I32'
         | 
| 52 | 
            -
                    PrimitiveType.new(primitive: type_def.to_sym, path: path)
         | 
| 67 | 
            +
                    PrimitiveType.new(primitive: type_def.to_sym, path: path, params: params)
         | 
| 53 68 | 
             
                  when :compact
         | 
| 54 69 | 
             
                    # type_def: { type: 1 }
         | 
| 55 | 
            -
                    CompactType.new(type: type_def._get(:type), registry: self, path: path)
         | 
| 70 | 
            +
                    CompactType.new(type: type_def._get(:type), registry: self, path: path, params: params)
         | 
| 56 71 | 
             
                  when :sequence
         | 
| 57 72 | 
             
                    # type_def: { type: 9 }
         | 
| 58 | 
            -
                    SequenceType.new(type: type_def._get(:type), registry: self, path: path)
         | 
| 73 | 
            +
                    SequenceType.new(type: type_def._get(:type), registry: self, path: path, params: params)
         | 
| 59 74 | 
             
                  when :bitSequence
         | 
| 60 75 | 
             
                    # type_def: {"bitStoreType"=>2, "bitOrderType"=>502}
         | 
| 61 76 | 
             
                    BitSequenceType.new(
         | 
| 62 77 | 
             
                      bit_store_type: type_def._get(:bitStoreType),
         | 
| 63 78 | 
             
                      bit_order_type: type_def._get(:bitOrderType),
         | 
| 64 79 | 
             
                      registry: self,
         | 
| 65 | 
            -
                      path: path
         | 
| 80 | 
            +
                      path: path,
         | 
| 81 | 
            +
                      params: params
         | 
| 66 82 | 
             
                    )
         | 
| 67 83 | 
             
                  when :array
         | 
| 68 84 | 
             
                    # type_def: { len: 3, type: 1 }
         | 
| @@ -70,11 +86,12 @@ module ScaleRb | |
| 70 86 | 
             
                      len: type_def._get(:len),
         | 
| 71 87 | 
             
                      type: type_def._get(:type),
         | 
| 72 88 | 
             
                      registry: self,
         | 
| 73 | 
            -
                      path: path
         | 
| 89 | 
            +
                      path: path,
         | 
| 90 | 
            +
                      params: params
         | 
| 74 91 | 
             
                    )
         | 
| 75 92 | 
             
                  when :tuple
         | 
| 76 93 | 
             
                    # type_def: [1, 2, 3]
         | 
| 77 | 
            -
                    TupleType.new(tuple: type_def, registry: self, path: path)
         | 
| 94 | 
            +
                    TupleType.new(tuple: type_def, registry: self, path: path, params: params)
         | 
| 78 95 | 
             
                  when :composite
         | 
| 79 96 | 
             
                    fields = type_def._get(:fields)
         | 
| 80 97 | 
             
                    first_field = fields.first
         | 
| @@ -83,7 +100,7 @@ module ScaleRb | |
| 83 100 | 
             
                    return UnitType.new(path: path) if first_field.nil?
         | 
| 84 101 |  | 
| 85 102 | 
             
                    # type_def: {"fields"=>[{"name"=>nil, "type"=>1}, {"name"=>nil, "type"=>2}]}
         | 
| 86 | 
            -
                    return TupleType.new(tuple: fields.map { |f| f._get(:type) }, registry: self, path: path) unless first_field._get(:name)
         | 
| 103 | 
            +
                    return TupleType.new(tuple: fields.map { |f| f._get(:type) }, registry: self, path: path, params: params) unless first_field._get(:name)
         | 
| 87 104 |  | 
| 88 105 | 
             
                    # type_def: { fields: [{ name: 'name', type: 1 }, { name: 'age', type: 2 }] }
         | 
| 89 106 | 
             
                    StructType.new(
         | 
| @@ -91,7 +108,8 @@ module ScaleRb | |
| 91 108 | 
             
                        Field.new(name: field._get(:name), type: field._get(:type))
         | 
| 92 109 | 
             
                      end,
         | 
| 93 110 | 
             
                      registry: self,
         | 
| 94 | 
            -
                      path: path
         | 
| 111 | 
            +
                      path: path,
         | 
| 112 | 
            +
                      params: params
         | 
| 95 113 | 
             
                    )
         | 
| 96 114 | 
             
                  when :variant
         | 
| 97 115 | 
             
                    variants = type_def._get(:variants)
         | 
| @@ -126,7 +144,7 @@ module ScaleRb | |
| 126 144 | 
             
                        )
         | 
| 127 145 | 
             
                      end
         | 
| 128 146 | 
             
                    end
         | 
| 129 | 
            -
                    VariantType.new(variants: variant_list, path: path)
         | 
| 147 | 
            +
                    VariantType.new(variants: variant_list, path: path, params: params)
         | 
| 130 148 | 
             
                  end
         | 
| 131 149 | 
             
                end
         | 
| 132 150 | 
             
              end
         | 
| @@ -53,11 +53,15 @@ module ScaleRb | |
| 53 53 | 
             
                  # data: hex string
         | 
| 54 54 | 
             
                  # type: portable type id
         | 
| 55 55 | 
             
                  # optional: boolean
         | 
| 56 | 
            -
                  # fallback: hex string
         | 
| 56 | 
            +
                  # fallback: hex string or u8array
         | 
| 57 57 | 
             
                  # returns nil or data
         | 
| 58 58 | 
             
                  def decode_storage(data, type, optional, fallback, registry)
         | 
| 59 | 
            -
                     | 
| 60 | 
            -
                     | 
| 59 | 
            +
                    bytes = data.nil? ? nil : Utils.hex_to_u8a(data)
         | 
| 60 | 
            +
                    bytes = bytes.nil? ?
         | 
| 61 | 
            +
                      (optional ? nil : (fallback.is_a?(Array) ? fallback : Utils.hex_to_u8a(fallback))) :
         | 
| 62 | 
            +
                      bytes
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    ScaleRb::Codec.decode(type, bytes, registry)[0] if bytes
         | 
| 61 65 | 
             
                  end
         | 
| 62 66 |  | 
| 63 67 | 
             
                  # storage_item: the storage item from metadata
         | 
| @@ -69,8 +73,8 @@ module ScaleRb | |
| 69 73 | 
             
                  end
         | 
| 70 74 |  | 
| 71 75 | 
             
                  def decode_storage3(data, pallet_name, item_name, metadata)
         | 
| 72 | 
            -
                    registry =  | 
| 73 | 
            -
                    storage_item =  | 
| 76 | 
            +
                    registry = metadata.build_registry
         | 
| 77 | 
            +
                    storage_item = metadata.storage(pallet_name, item_name)
         | 
| 74 78 | 
             
                    decode_storage2(data, storage_item, registry)
         | 
| 75 79 | 
             
                  end
         | 
| 76 80 | 
             
                end
         | 
    
        data/lib/scale_rb/types.rb
    CHANGED
    
    | @@ -24,9 +24,15 @@ module ScaleRb | |
| 24 24 | 
             
                UnsignedInteger = Types::Strict::Integer.constrained(gteq: 0)
         | 
| 25 25 | 
             
                TypedArray = ->(type) { Types::Array.of(type) }
         | 
| 26 26 |  | 
| 27 | 
            +
                class Param < Dry::Struct
         | 
| 28 | 
            +
                  attribute :name, Types::Strict::String
         | 
| 29 | 
            +
                  attribute? :type, Ti
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 27 32 | 
             
                class Base < Dry::Struct
         | 
| 28 33 | 
             
                  attribute? :registry, Registry
         | 
| 29 34 | 
             
                  attribute? :path, Types::Strict::Array.of(Types::Strict::String)
         | 
| 35 | 
            +
                  attribute? :params, Types::Strict::Array.of(Param)
         | 
| 30 36 |  | 
| 31 37 | 
             
                  def t(type_id)
         | 
| 32 38 | 
             
                    raise 'No registry' unless registry
         | 
    
        data/lib/scale_rb/version.rb
    CHANGED