From 90c43a8320655807254a09319116b328eac185a0 Mon Sep 17 00:00:00 2001 From: ksss Date: Fri, 16 Oct 2026 17:29:36 +0900 Subject: [PATCH] Add types for msgpack v1.8 Cover the basic API: MessagePack.pack/unpack, #to_msgpack on core classes, Packer, Unpacker, Factory (including Pool and register_type), Timestamp, and the unpack error classes. Co-Authored-By: Claude Opus 5.5 --- gems/msgpack/.rubocop.yml | 19 ++ gems/msgpack/1.8/_test/metadata.yaml | 2 + gems/msgpack/1.8/_test/test.rb | 110 ++++++++++++ gems/msgpack/1.8/_test/test.rbs | 11 ++ gems/msgpack/1.8/msgpack.rbs | 256 +++++++++++++++++++++++++++ gems/msgpack/_reviewers.yaml | 2 + 6 files changed, 400 insertions(+) create mode 100644 gems/msgpack/.rubocop.yml create mode 100644 gems/msgpack/1.8/_test/metadata.yaml create mode 100644 gems/msgpack/1.8/_test/test.rb create mode 100644 gems/msgpack/1.8/_test/test.rbs create mode 100644 gems/msgpack/1.8/msgpack.rbs create mode 100644 gems/msgpack/_reviewers.yaml diff --git a/gems/msgpack/.rubocop.yml b/gems/msgpack/.rubocop.yml new file mode 100644 index 000000000..7d0be8ff2 --- /dev/null +++ b/gems/msgpack/.rubocop.yml @@ -0,0 +1,19 @@ +# This configuration inherits from /.rubocop.yml. +# You can configure RBS style of this gem. +# This file is used on CI. It is configured to automatically +# make rubocop suggestions on pull requests for this gem. +# If you do not like the style enforcement, you should remove this file. +inherit_from: ../../.rubocop.yml + +## +# If you want to customize the style, please consult with the gem reviewers. +# You can see the list of cops at https://github.com/ksss/rubocop-on-rbs/blob/main/docs/modules/ROOT/pages/cops.adoc + +RBS/Layout: + Enabled: true + +RBS/Lint: + Enabled: true + +RBS/Style: + Enabled: true diff --git a/gems/msgpack/1.8/_test/metadata.yaml b/gems/msgpack/1.8/_test/metadata.yaml new file mode 100644 index 000000000..3300ba093 --- /dev/null +++ b/gems/msgpack/1.8/_test/metadata.yaml @@ -0,0 +1,2 @@ +additional_gems: + - stringio diff --git a/gems/msgpack/1.8/_test/test.rb b/gems/msgpack/1.8/_test/test.rb new file mode 100644 index 000000000..3d008e2aa --- /dev/null +++ b/gems/msgpack/1.8/_test/test.rb @@ -0,0 +1,110 @@ +# Write Ruby code to test the RBS. +# It is type checked by `steep check` command. + +require "msgpack" +require "stringio" + +# Serializing and deserializing +msg = [1, 2, 3].to_msgpack +MessagePack.unpack(msg) +packed = MessagePack.pack({ "a" => 1 }) +MessagePack.load(packed, symbolize_keys: true) +MessagePack.dump(nil.to_msgpack) +"str".to_msgpack(StringIO.new) + +# Streaming serialization +io = StringIO.new +pk = MessagePack::Packer.new(io) +pk.write_array_header(2).write(1).write("e2").flush +pk.full_pack + +packer = MessagePack::Packer.new(compatibility_mode: true) +packer.write_map_header(1).write_string("key").write_int(1) +packed_string = packer.to_s +packer.reset +packer.write_ext(1, "payload").full_pack + +# Streaming deserialization +u = MessagePack::Unpacker.new(StringIO.new(packed_string), symbolize_keys: true) +u.each do |obj| + obj +end + +u = MessagePack::Unpacker.new +u.feed_each(packed_string) { |obj| obj } +u.feed(packed_string).read_map_header + 1 + +# Extension types +class MyClass + def to_msgpack_ext + "" + end + + def self.from_msgpack_ext(_data) + new + end +end + +pk = MessagePack::Packer.new(io) +pk.register_type(0x01, MyClass, :to_msgpack_ext) +pk.register_type(0x02, MyClass) { |_obj| "" } +pk.type_registered?(MyClass) + +uk = MessagePack::Unpacker.new +uk.register_type(0x01, MyClass, :from_msgpack_ext) +uk.register_type(0x02) { |data| MyClass.from_msgpack_ext(data) } + +MessagePack::DefaultFactory.register_type(0x00, Symbol) +MessagePack::DefaultFactory.register_type( + MessagePack::Timestamp::TYPE, + Time, + packer: MessagePack::Time::Packer, + unpacker: MessagePack::Time::Unpacker +) + +class Point + def x + @x + end + + def y + @y + end + + def initialize(x, y) + @x = x + @y = y + end +end + +factory = MessagePack::Factory.new +# @type var point_packer: ^(Point, MessagePack::Packer) -> void +point_packer = ->(point, packer) { + packer.write(point.x) + packer.write(point.y) +} +# @type var point_unpacker: ^(MessagePack::Unpacker) -> Point +point_unpacker = ->(unpacker) { + Point.new(unpacker.read, unpacker.read) +} +factory.register_type(0x01, Point, packer: point_packer, unpacker: point_unpacker, recursive: true) +factory.load(factory.dump(Point.new(12, 34))) +factory.packer.write(1).full_pack +factory.unpacker(symbolize_keys: true).feed(packed_string).full_unpack + +# Pooling +pool = factory.pool(5) +pool.load(pool.dump(Point.new(12, 34))) +size = pool.packer { |pk| pk.write(1).size } +size + 1 + +# Timestamp +ts = MessagePack::Timestamp.new(1, 2) +MessagePack::Timestamp.from_msgpack_ext(ts.to_msgpack_ext).sec + ts.nsec + +# Errors +begin + MessagePack.unpack("\xc1") +rescue MessagePack::MalformedFormatError, MessagePack::UnpackError => e + e.message +end diff --git a/gems/msgpack/1.8/_test/test.rbs b/gems/msgpack/1.8/_test/test.rbs new file mode 100644 index 000000000..5ab12c0b2 --- /dev/null +++ b/gems/msgpack/1.8/_test/test.rbs @@ -0,0 +1,11 @@ +class MyClass + def to_msgpack_ext: () -> String + def self.from_msgpack_ext: (String data) -> MyClass +end + +class Point + attr_reader x: Integer + attr_reader y: Integer + + def initialize: (Integer x, Integer y) -> void +end diff --git a/gems/msgpack/1.8/msgpack.rbs b/gems/msgpack/1.8/msgpack.rbs new file mode 100644 index 000000000..ed37cb300 --- /dev/null +++ b/gems/msgpack/1.8/msgpack.rbs @@ -0,0 +1,256 @@ +module MessagePack + VERSION: String + + DefaultFactory: Factory + + type packer_options = { + ?compatibility_mode: boolish, + ?read_reference_threshold: Integer, + ?write_reference_threshold: Integer, + ?io_buffer_size: Integer + } + + type unpacker_options = { + ?symbolize_keys: boolish, + ?freeze: boolish, + ?allow_unknown_ext: boolish, + ?key_cache: boolish, + ?read_reference_threshold: Integer, + ?write_reference_threshold: Integer, + ?io_buffer_size: Integer + } + + def self?.pack: (untyped v, ?packer_options? options) -> String + | (untyped v, ::_Writer io, ?packer_options? options) -> nil + alias self.dump self.pack + alias dump pack + + def self?.load: (String src, ?unpacker_options? options) -> untyped + | (::_Reader | ::_ReaderPartial io, ?unpacker_options? options) -> untyped + alias self.unpack self.load + alias unpack load + + module CoreExt + def to_msgpack: () -> String + | (Packer packer) -> Packer + | (::_Writer io) -> nil + end + + class UnpackError < StandardError + end + + class MalformedFormatError < UnpackError + end + + class StackError < UnpackError + end + + module TypeError + end + + class UnexpectedTypeError < UnpackError + include TypeError + end + + class UnknownExtTypeError < UnpackError + end + + class ExtensionValue < Struct[untyped] + include CoreExt + + attr_accessor type: Integer + attr_accessor payload: String + + def self.new: (Integer type, String payload) -> instance + end + + class Buffer + def clear: () -> nil + def size: () -> Integer + def empty?: () -> bool + def io: () -> untyped + def flush: () -> self + def to_str: () -> String + alias to_s to_str + def to_a: () -> Array[String] + end + + class Packer + def initialize: (?packer_options? options) -> void + | (::_Writer? io, ?packer_options? options) -> void + + def compatibility_mode?: () -> bool + def buffer: () -> Buffer + + def write: (untyped v) -> self + alias pack write + def write_nil: () -> self + def write_true: () -> self + def write_false: () -> self + def write_float: (Numeric v) -> self + def write_float32: (Numeric v) -> self + def write_int: (Integer v) -> self + def write_string: (String v) -> self + def write_bin: (String v) -> self + def write_symbol: (Symbol v) -> self + def write_array: (Array[untyped] v) -> self + def write_hash: (Hash[untyped, untyped] v) -> self + def write_extension: (ExtensionValue v) -> self + def write_ext: (Integer type, String payload) -> self + def write_array_header: (Integer n) -> self + def write_map_header: (Integer n) -> self + def write_bin_header: (Integer n) -> self + + def flush: () -> self + def reset: () -> nil + alias clear reset + def size: () -> Integer + def empty?: () -> bool + def write_to: (::_Writer io) -> Integer + def to_str: () -> String + alias to_s to_str + def to_a: () -> Array[String] + + # Returns nil when the packer writes to an IO. + def full_pack: () -> String? + + def register_type: (Integer type, Module klass, Symbol | String method_name) -> nil + | (Integer type, Module klass) { (untyped object) -> String } -> nil + def registered_types: () -> Array[{ type: Integer, class: Module, packer: Proc }] + def type_registered?: (Module | Integer klass_or_type) -> bool + end + + class Unpacker + def initialize: (?unpacker_options? options) -> void + | ((::_Reader | ::_ReaderPartial)? io, ?unpacker_options? options) -> void + + def symbolize_keys?: () -> bool + def freeze?: () -> bool + def allow_unknown_ext?: () -> bool + def buffer: () -> Buffer + + def feed: (String data) -> self + alias feed_reference feed + + def read: () -> untyped + alias unpack read + def skip: () -> nil + def skip_nil: () -> bool + def read_array_header: () -> Integer + def read_map_header: () -> Integer + + def each: () { (untyped object) -> void } -> nil + | () -> Enumerator[untyped, nil] + def feed_each: (String data) { (untyped object) -> void } -> nil + | (String data) -> Enumerator[untyped, nil] + + def reset: () -> nil + def full_unpack: () -> untyped + + def register_type: (Integer type, Module klass, Symbol | String method_name) -> nil + | (Integer type, ?Module? klass) { (String data) -> untyped } -> nil + def registered_types: () -> Array[{ type: Integer, class: Module?, unpacker: Proc }] + def type_registered?: (Module | Integer klass_or_type) -> bool + end + + class Factory + def initialize: () -> void + + def packer: (?packer_options? options) -> Packer + | (::_Writer? io, ?packer_options? options) -> Packer + def unpacker: (?unpacker_options? options) -> Unpacker + | ((::_Reader | ::_ReaderPartial)? io, ?unpacker_options? options) -> Unpacker + + def register_type: ( + Integer type, + Module klass, + ?{ + ?packer: Proc | Method | Symbol | String, + ?unpacker: Proc | Method | Symbol | String, + ?recursive: boolish, + ?optimized_symbols_parsing: boolish, + ?oversized_integer_extension: boolish + }? options + ) -> nil + def registered_types: (?:both | :packer | :unpacker selector) -> Array[Hash[Symbol, untyped]] + def type_registered?: (Module | Integer klass_or_type, ?:both | :packer | :unpacker selector) -> bool + + def load: (String src, ?unpacker_options? options) -> untyped + | (::_Reader | ::_ReaderPartial io, ?unpacker_options? options) -> untyped + alias unpack load + + def dump: (untyped v, ?packer_options? options) -> String + | (untyped v, ::_Writer io, ?packer_options? options) -> nil + alias pack dump + + def freeze: () -> self + def pool: (?Integer size, **untyped options) -> Pool + + class Pool + def load: (String data) -> untyped + def dump: (untyped object) -> String + def packer: [T] () { (Packer packer) -> T } -> T + def unpacker: [T] () { (Unpacker unpacker) -> T } -> T + end + end + + class Timestamp + TYPE: Integer + TIMESTAMP32_MAX_SEC: Integer + TIMESTAMP64_MAX_SEC: Integer + + attr_reader sec: Integer + attr_reader nsec: Integer + + def initialize: (Integer sec, Integer nsec) -> void + + def self.from_msgpack_ext: (String data) -> Timestamp + def self.to_msgpack_ext: (Integer sec, Integer nsec) -> String + def to_msgpack_ext: () -> String + def ==: (untyped other) -> bool + end + + module Time + Packer: ^(::Time time) -> String + Unpacker: ^(String payload) -> ::Time + end +end + +class NilClass + include MessagePack::CoreExt +end + +class TrueClass + include MessagePack::CoreExt +end + +class FalseClass + include MessagePack::CoreExt +end + +class Integer + include MessagePack::CoreExt +end + +class Float + include MessagePack::CoreExt +end + +class String + include MessagePack::CoreExt +end + +class Symbol + include MessagePack::CoreExt + + def self.from_msgpack_ext: (String data) -> Symbol + def to_msgpack_ext: () -> String +end + +class Array[unchecked out Elem] + include MessagePack::CoreExt +end + +class Hash[unchecked out K, unchecked out V] + include MessagePack::CoreExt +end diff --git a/gems/msgpack/_reviewers.yaml b/gems/msgpack/_reviewers.yaml new file mode 100644 index 000000000..0b4d3b7e8 --- /dev/null +++ b/gems/msgpack/_reviewers.yaml @@ -0,0 +1,2 @@ +reviewers: + - ksss