types 0.1.3 → 0.3.0

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.
data/lib/types/hash.rb ADDED
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+
8
+ module Types
9
+ # Represents a hash type with key and value types.
10
+ #
11
+ # ```ruby
12
+ # type = Types::Hash(Types::String, Types::Integer)
13
+ # type.parse({"foo" => "42"}) # => {"foo" => 42}
14
+ # ```
15
+ class Hash
16
+ include Generic
17
+
18
+ # @parameter key_type [Type] The type of the hash keys.
19
+ # @parameter value_type [Type] The type of the hash values.
20
+ def initialize(key_type, value_type)
21
+ @key_type = key_type
22
+ @value_type = value_type
23
+ end
24
+
25
+ # @returns [Boolean] true if this is a composite type.
26
+ def composite?
27
+ true
28
+ end
29
+
30
+ # Parses the input as a hash with the specified key and value types.
31
+ # @parameter input [Object] The value to parse.
32
+ # @returns [Hash] The parsed hash.
33
+ # @raises [ArgumentError] if the input cannot be converted to a hash.
34
+ def parse(input)
35
+ case input
36
+ when ::String
37
+ return parse_values(parse_string(input))
38
+ when ::Hash
39
+ return parse_values(input)
40
+ else
41
+ raise ArgumentError, "Cannot coerce #{input.inspect} into Hash!"
42
+ end
43
+ end
44
+
45
+ # @returns [String] the string representation of the hash type.
46
+ def to_s
47
+ "Hash(#{@key_type}, #{@value_type})"
48
+ end
49
+
50
+ # @returns [String] the RBS type string, e.g. `{ String => Integer }`.
51
+ def to_rbs
52
+ "{ #{@key_type.to_rbs} => #{@value_type.to_rbs} }"
53
+ end
54
+
55
+ private
56
+
57
+ def parse_string(input)
58
+ ::JSON.parse("{#{input}}")
59
+ end
60
+
61
+ def parse_values(input)
62
+ input.map{|key, value| [@key_type.parse(key), @value_type.parse(value)]}.to_h
63
+ end
64
+ end
65
+
66
+ # Constructs a {Hash} type from the given key and value types.
67
+ # @parameter key_type [Type] The type of the hash keys.
68
+ # @parameter value_type [Type] The type of the hash values.
69
+ # @returns [Hash] a new {Hash} type.
70
+ def self.Hash(key_type, value_type)
71
+ Hash.new(key_type, value_type)
72
+ end
73
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+
8
+ module Types
9
+ # Represents an integer type.
10
+ #
11
+ # ```ruby
12
+ # type = Types::Integer
13
+ # type.parse("42") # => 42
14
+ # ```
15
+ module Integer
16
+ extend Generic
17
+
18
+ # Parses the input as an integer.
19
+ # @parameter input [Object] The value to parse.
20
+ # @returns [Integer] The parsed integer value.
21
+ # @raises [ArgumentError] if the input cannot be converted to an integer.
22
+ def self.parse(input)
23
+ Integer(input)
24
+ end
25
+
26
+ # @returns [String] the RBS type string, e.g. `Integer`.
27
+ def self.to_rbs
28
+ "Integer"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+ require_relative "method"
8
+ require_relative "any"
9
+
10
+ module Types
11
+ # Represents an interface type, defined by required method names.
12
+ #
13
+ # ```ruby
14
+ # type = Types::Interface(
15
+ # foo: Method.new(Any, returns: Any),
16
+ # bar: Method.new(Any, returns: Any)
17
+ # )
18
+ # type.valid?(obj) # => true if obj responds to :foo and :bar
19
+ # ```
20
+ class Interface
21
+ extend Generic
22
+
23
+ # @parameter methods [Array(Symbol)] The required method names.
24
+ def initialize(methods)
25
+ @methods = methods
26
+ end
27
+
28
+ # @returns [Array(Symbol)] The required method names.
29
+ attr :methods
30
+
31
+ # @returns [Boolean] true if this is a composite type.
32
+ def composite?
33
+ true
34
+ end
35
+
36
+ # @returns [String] the string representation of the interface type.
37
+ def to_s
38
+ buffer = ::String.new
39
+
40
+ buffer << "Interface("
41
+
42
+ if @methods&.any?
43
+ first = true
44
+ @methods.each do |name, method|
45
+ if first
46
+ first = false
47
+ else
48
+ buffer << ", "
49
+ end
50
+
51
+ buffer << "#{name}: #{method}"
52
+ end
53
+ end
54
+
55
+ buffer << ")"
56
+
57
+ return buffer
58
+ end
59
+
60
+ # Checks if the given instance responds to all required methods.
61
+ # @parameter instance [Object] The object to check.
62
+ # @returns [Boolean] true if the instance responds to all methods.
63
+ def valid?(instance)
64
+ @methods.all? do |name, method|
65
+ instance.respond_to?(name)
66
+ end
67
+ end
68
+
69
+ # Parses the input as an object and checks if it matches the interface.
70
+ # @parameter input [Object] The value to parse.
71
+ # @returns [Object] the input if it matches the interface.
72
+ # @raises [ArgumentError] if the input does not match the interface.
73
+ def parse(input)
74
+ case input
75
+ when ::String
76
+ instance = eval(input)
77
+ else
78
+ instance = input
79
+ end
80
+
81
+ if valid?(instance)
82
+ return instance
83
+ else
84
+ raise ArgumentError, "Cannot coerce #{input.inspect} into #{self}!"
85
+ end
86
+ end
87
+ end
88
+
89
+ # Constructs an {Interface} type from the given method names.
90
+ # @parameter methods [Array(Symbol)] The required method names.
91
+ # @returns [Interface] a new {Interface} type.
92
+ def self.Interface(*names, **methods)
93
+ # This is a deprecated way to create an interfaces with names only. Ideally, we actually need to know the methods.
94
+ names.each do |name|
95
+ methods[name.to_sym] = Method.new(Any(), [], Any())
96
+ end
97
+
98
+ Interface.new(methods)
99
+ end
100
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+
8
+ module Types
9
+ # Represents a lambda (function) type with argument and return types.
10
+ #
11
+ # ```ruby
12
+ # type = Types::Lambda(Types::String, Types::Integer, returns: Types::String)
13
+ # type.to_s # => "Lambda(String, Integer, returns: String)"
14
+ # ```
15
+ class Lambda
16
+ include Generic
17
+
18
+ # @parameter argument_types [Array(Type)] The types of the lambda arguments.
19
+ # @parameter return_type [Type | Nil] The return type of the lambda.
20
+ def initialize(argument_types, return_type)
21
+ @argument_types = argument_types
22
+ @return_type = return_type
23
+ end
24
+
25
+ # @returns [Array(Type)] The types of the lambda arguments.
26
+ attr :argument_types
27
+ # @returns [Type | Nil] The return type of the lambda.
28
+ attr :return_type
29
+
30
+ # @returns [Boolean] true if this is a composite type.
31
+ def composite?
32
+ true
33
+ end
34
+
35
+ # @returns [String] the string representation of the lambda type.
36
+ def to_s
37
+ if @return_type
38
+ "Lambda(#{@argument_types.join(', ')}, returns: #{@return_type})"
39
+ else
40
+ "Lambda(#{@argument_types.join(', ')})"
41
+ end
42
+ end
43
+
44
+ # Parses the input as a lambda/proc.
45
+ # @parameter input [Object] The value to parse.
46
+ # @returns [Proc] the parsed lambda/proc.
47
+ # @raises [ArgumentError] if the input cannot be converted to a lambda.
48
+ def parse(input)
49
+ case input
50
+ when ::String
51
+ eval("lambda{#{input}}", TOPLEVEL_BINDING)
52
+ when ::Proc
53
+ input
54
+ else
55
+ raise ArgumentError, "Cannot coerce #{input.inpsect} into Lambda!"
56
+ end
57
+ end
58
+ end
59
+
60
+ # Constructs a {Lambda} type from the given argument and return types.
61
+ # @parameter argument_types [Array(Type)] The types of the lambda arguments.
62
+ # @parameter returns [Type | Nil] The return type of the lambda.
63
+ # @returns [Lambda] a new {Lambda} type.
64
+ def self.Lambda(*argument_types, returns: nil)
65
+ Lambda.new(argument_types, returns)
66
+ end
67
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+
8
+ module Types
9
+ # Represents a method type attached to a receiver type.
10
+ #
11
+ # ```ruby
12
+ # type = Types::Method(Types::String, Types::Integer, returns: Types::String)
13
+ # type.to_s # => "Method(String, Integer, returns: String)"
14
+ # ```
15
+ class Method
16
+ include Generic
17
+
18
+ # @parameter receiver_type [Type] The type of the receiver.
19
+ # @parameter argument_types [Array(Type)] The types of the method arguments.
20
+ # @parameter return_type [Type | Nil] The return type of the method.
21
+ def initialize(receiver_type, argument_types, return_type)
22
+ @receiver_type = receiver_type
23
+ @argument_types = argument_types
24
+ @return_type = return_type
25
+ end
26
+
27
+ # @returns [Type] The type of the receiver.
28
+ attr :receiver_type
29
+ # @returns [Array(Type)] The types of the method arguments.
30
+ attr :argument_types
31
+ # @returns [Type | Nil] The return type of the method.
32
+ attr :return_type
33
+
34
+ # @returns [Boolean] true if this is a composite type.
35
+ def composite?
36
+ true
37
+ end
38
+
39
+ # @returns [String] the string representation of the method type.
40
+ def to_s
41
+ buffer = ::String.new
42
+
43
+ if @argument_types&.any?
44
+ buffer << "Method(#{@receiver_type}, #{@argument_types.join(', ')}"
45
+ else
46
+ buffer << "Method(#{@receiver_type}, "
47
+ end
48
+
49
+ if @return_type
50
+ buffer << "returns: #{@return_type})"
51
+ else
52
+ buffer << ")"
53
+ end
54
+
55
+ return buffer
56
+ end
57
+
58
+ # @returns [String] the RBS type string, e.g. `Method[Receiver, (Args) -> Return]`.
59
+ def to_rbs
60
+ argument_types = @argument_types.map {|t| t.to_rbs}.join(", ")
61
+ return_type = @return_type ? @return_type.to_rbs : "void"
62
+
63
+ return "Method[#{@receiver_type}, (#{argument_types}) -> #{return_type}]"
64
+ end
65
+
66
+ # Parses the input as a method or proc.
67
+ # @parameter input [Object] The value to parse.
68
+ # @returns [UnboundMethod, Proc] the parsed method or proc.
69
+ # @raises [ArgumentError] if the input cannot be converted to a method.
70
+ def parse(input)
71
+ case input
72
+ when ::String
73
+ receiver_type.instance_method(input)
74
+ when ::Proc
75
+ input
76
+ else
77
+ raise ArgumentError, "Cannot coerce #{input.inpsect} into Method!"
78
+ end
79
+ end
80
+ end
81
+
82
+ # Constructs a {Method} type from the given receiver, argument, and return types.
83
+ # @parameter receiver_type [Type] The type of the receiver.
84
+ # @parameter argument_types [Array(Type)] The types of the method arguments.
85
+ # @parameter returns [Type | Nil] The return type of the method.
86
+ # @returns [Method] a new {Method} type.
87
+ def self.Method(receiver_type, *argument_types, returns: nil)
88
+ Method.new(receiver_type, argument_types, returns)
89
+ end
90
+ end
data/lib/types/nil.rb ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+
8
+ module Types
9
+ # Represents the nil type.
10
+ #
11
+ # ```ruby
12
+ # type = Types::Nil
13
+ # type.parse("nil") # => nil
14
+ # ```
15
+ module Nil
16
+ extend Generic
17
+
18
+ # Parses the input as nil if it matches.
19
+ # @parameter input [Object] The value to parse.
20
+ # @returns [NilClass] nil if the input matches.
21
+ # @raises [ArgumentError] if the input cannot be converted to nil.
22
+ def self.parse(input)
23
+ if input =~ /nil|null/i
24
+ return nil
25
+ else
26
+ raise ArgumentError, "Cannot coerce #{input.inspect} into Nil!"
27
+ end
28
+ end
29
+
30
+ # @returns [String] the RBS type string, e.g. `nil`.
31
+ def self.to_rbs
32
+ "nil"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+
8
+ module Types
9
+ # Represents a numeric type (integer or float).
10
+ #
11
+ # ```ruby
12
+ # type = Types::Numeric
13
+ # type.parse("42") # => 42
14
+ # type.parse("3.14") # => 3.14
15
+ # ```
16
+ module Numeric
17
+ extend Generic
18
+
19
+ # Parses the input as a numeric value (integer or float).
20
+ # @parameter input [Object] The value to parse.
21
+ # @returns [Numeric] The parsed numeric value.
22
+ def self.parse(input)
23
+ case input
24
+ when Numeric then input
25
+ when /\./ then Float(input)
26
+ else Integer(input)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+
8
+ module Types
9
+ # Represents a string type.
10
+ #
11
+ # ```ruby
12
+ # type = Types::String
13
+ # type.parse(42) # => "42"
14
+ # ```
15
+ module String
16
+ extend Generic
17
+
18
+ # Parses the input as a string.
19
+ # @parameter input [Object] The value to parse.
20
+ # @returns [String] The parsed string value.
21
+ def self.parse(input)
22
+ input.to_s
23
+ end
24
+
25
+ # @returns [String] the RBS type string, e.g. `String`.
26
+ def self.to_rbs
27
+ "String"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+
8
+ module Types
9
+ # Represents a symbol type.
10
+ #
11
+ # ```ruby
12
+ # type = Types::Symbol
13
+ # type.parse("foo") # => :foo
14
+ # ```
15
+ module Symbol
16
+ extend Generic
17
+
18
+ # Parses the input as a symbol.
19
+ # @parameter input [Object] The value to parse.
20
+ # @returns [Symbol] The parsed symbol value.
21
+ def self.parse(input)
22
+ input.to_sym
23
+ end
24
+
25
+ # @returns [String] the RBS type string, e.g. `Symbol`.
26
+ def self.to_rbs
27
+ "Symbol"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+ require "json"
8
+
9
+ module Types
10
+ # Represents a tuple type with specific item types.
11
+ #
12
+ # ```ruby
13
+ # type = Types::Tuple(Types::String, Types::Integer)
14
+ # type.parse(["foo", "42"]) # => ["foo", 42]
15
+ # ```
16
+ class Tuple
17
+ include Generic
18
+
19
+ # @parameter item_types [Array(Type)] The types of the tuple elements.
20
+ def initialize(item_types)
21
+ @item_types = item_types
22
+ end
23
+
24
+ # @returns [Array(Type)] The types of the tuple elements.
25
+ attr :item_types
26
+
27
+ # @returns [Boolean] true if this is a composite type.
28
+ def composite?
29
+ true
30
+ end
31
+
32
+ # Parses the input as a tuple with the specified item types.
33
+ # @parameter input [Object] The value to parse.
34
+ # @returns [Array] The parsed tuple.
35
+ # @raises [ArgumentError] if the input cannot be converted to a tuple.
36
+ def parse(input)
37
+ case input
38
+ when ::String
39
+ return parse_values(parse_string(input))
40
+ when ::Array
41
+ return parse_values(input)
42
+ else
43
+ raise ArgumentError, "Cannot coerce #{input.inspect} into tuple!"
44
+ end
45
+ end
46
+
47
+ # @returns [String] the string representation of the tuple type.
48
+ def to_s
49
+ "Tuple(#{@item_types.join(', ')})"
50
+ end
51
+
52
+ # @returns [String] the RBS type string, e.g. `[String, Integer]`.
53
+ def to_rbs
54
+ "[#{@item_types.map {|t| t.to_rbs}.join(', ')}]"
55
+ end
56
+
57
+ private
58
+
59
+ def parse_string(input)
60
+ ::JSON.parse("[#{input}]")
61
+ end
62
+
63
+ def parse_values(input)
64
+ input.map.with_index{|value, index| @item_types[index].parse(value)}
65
+ end
66
+ end
67
+
68
+ # Constructs a {Tuple} type from the given item types.
69
+ # @parameter item_types [Array(Type)] The types of the tuple elements.
70
+ # @returns [Tuple] a new {Tuple} type.
71
+ def self.Tuple(*item_types)
72
+ Tuple.new(item_types)
73
+ end
74
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2024, by Samuel Williams.
5
+
6
+ module Types
7
+ VERSION = "0.3.0"
8
+ end