literal 1.2.0 → 1.4.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.
- checksums.yaml +4 -4
- data/lib/literal/array.rb +183 -1
- data/lib/literal/data.rb +1 -7
- data/lib/literal/deferred_type.rb +23 -0
- data/lib/literal/enum.rb +9 -2
- data/lib/literal/properties/schema.rb +1 -1
- data/lib/literal/property.rb +4 -4
- data/lib/literal/transforms.rb +142 -0
- data/lib/literal/tuple.rb +60 -0
- data/lib/literal/types/any_type.rb +2 -2
- data/lib/literal/types/boolean_type.rb +2 -2
- data/lib/literal/types/constraint_type.rb +4 -2
- data/lib/literal/types/falsy_type.rb +2 -2
- data/lib/literal/types/frozen_type.rb +35 -1
- data/lib/literal/types/hash_type.rb +22 -1
- data/lib/literal/types/interface_type.rb +29 -1
- data/lib/literal/types/intersection_type.rb +25 -0
- data/lib/literal/types/json_data_type.rb +34 -4
- data/lib/literal/types/map_type.rb +18 -1
- data/lib/literal/types/never_type.rb +18 -3
- data/lib/literal/types/nilable_type.rb +18 -1
- data/lib/literal/types/not_type.rb +22 -1
- data/lib/literal/types/range_type.rb +18 -1
- data/lib/literal/types/set_type.rb +18 -1
- data/lib/literal/types/truthy_type.rb +17 -3
- data/lib/literal/types/tuple_type.rb +19 -2
- data/lib/literal/types/union_type.rb +21 -4
- data/lib/literal/types/void_type.rb +13 -3
- data/lib/literal/types.rb +51 -44
- data/lib/literal/version.rb +1 -1
- data/lib/literal.rb +11 -65
- metadata +7 -12
- data/lib/literal/data_property.rb +0 -16
- data/lib/literal/types/callable_type.rb +0 -31
- data/lib/literal/types/float_type.rb +0 -10
- data/lib/literal/types/integer_type.rb +0 -10
- data/lib/literal/types/lambda_type.rb +0 -12
- data/lib/literal/types/procable_type.rb +0 -12
- data/lib/literal/types/string_type.rb +0 -10
- data/lib/literal/types/symbol_type.rb +0 -10
@@ -2,13 +2,47 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::FrozenType
|
5
|
+
ALWAYS_FROZEN = Set[Symbol, Integer, Float, Numeric, true, false, nil].freeze
|
6
|
+
|
7
|
+
include Literal::Type
|
8
|
+
|
5
9
|
def initialize(type)
|
10
|
+
if ALWAYS_FROZEN.include?(type)
|
11
|
+
warn "_Frozen type is redundant for #{type.inspect} since it is always frozen."
|
12
|
+
end
|
13
|
+
|
6
14
|
@type = type
|
7
15
|
end
|
8
16
|
|
9
|
-
|
17
|
+
attr_reader :type
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"_Frozen(#{@type.inspect})"
|
21
|
+
end
|
10
22
|
|
11
23
|
def ===(value)
|
12
24
|
value.frozen? && @type === value
|
13
25
|
end
|
26
|
+
|
27
|
+
def >=(other)
|
28
|
+
case other
|
29
|
+
when Literal::Types::FrozenType
|
30
|
+
@type >= other.type
|
31
|
+
when Literal::Types::ConstraintType
|
32
|
+
type_match = false
|
33
|
+
frozen_match = Literal.subtype?(other.property_constraints[:frozen?], of: true)
|
34
|
+
|
35
|
+
other.object_constraints.each do |constraint|
|
36
|
+
frozen_match ||= ALWAYS_FROZEN.include?(constraint)
|
37
|
+
type_match ||= Literal.subtype?(constraint, of: @type)
|
38
|
+
return true if frozen_match && type_match
|
39
|
+
end
|
40
|
+
|
41
|
+
false
|
42
|
+
else
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
freeze
|
14
48
|
end
|
@@ -2,12 +2,18 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::HashType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(key_type, value_type)
|
6
8
|
@key_type = key_type
|
7
9
|
@value_type = value_type
|
8
10
|
end
|
9
11
|
|
10
|
-
|
12
|
+
attr_reader :key_type, :value_type
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"_Hash(#{@key_type.inspect}, #{@value_type.inspect})"
|
16
|
+
end
|
11
17
|
|
12
18
|
def ===(value)
|
13
19
|
return false unless Hash === value
|
@@ -19,6 +25,19 @@ class Literal::Types::HashType
|
|
19
25
|
true
|
20
26
|
end
|
21
27
|
|
28
|
+
def >=(other)
|
29
|
+
case other
|
30
|
+
when Literal::Types::HashType
|
31
|
+
(
|
32
|
+
Literal.subtype?(other.key_type, of: @key_type)
|
33
|
+
) && (
|
34
|
+
Literal.subtype?(other.value_type, of: @value_type)
|
35
|
+
)
|
36
|
+
else
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
22
41
|
def record_literal_type_errors(context)
|
23
42
|
unless Hash === context.actual
|
24
43
|
return
|
@@ -35,4 +54,6 @@ class Literal::Types::HashType
|
|
35
54
|
end
|
36
55
|
end
|
37
56
|
end
|
57
|
+
|
58
|
+
freeze
|
38
59
|
end
|
@@ -2,6 +2,15 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::InterfaceType
|
5
|
+
# TODO: We can generate this and make it much more extensive.
|
6
|
+
METHOD_TYPE_MAPPINGS = {
|
7
|
+
:call => Set[Proc, Method],
|
8
|
+
:to_proc => Set[Proc, Method],
|
9
|
+
:to_s => Set[String],
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
include Literal::Type
|
13
|
+
|
5
14
|
def initialize(*methods)
|
6
15
|
raise Literal::ArgumentError.new("_Interface type must have at least one method.") if methods.size < 1
|
7
16
|
@methods = methods
|
@@ -9,9 +18,28 @@ class Literal::Types::InterfaceType
|
|
9
18
|
|
10
19
|
attr_reader :methods
|
11
20
|
|
12
|
-
def inspect
|
21
|
+
def inspect
|
22
|
+
"_Interface(#{@methods.map(&:inspect).join(', ')})"
|
23
|
+
end
|
13
24
|
|
14
25
|
def ===(value)
|
15
26
|
@methods.all? { |m| value.respond_to?(m) }
|
16
27
|
end
|
28
|
+
|
29
|
+
def >=(other)
|
30
|
+
case other
|
31
|
+
when Literal::Types::InterfaceType
|
32
|
+
@methods.all? { |m| other.methods.include?(m) }
|
33
|
+
when Module
|
34
|
+
@methods.map { |m| METHOD_TYPE_MAPPINGS[m] }.all? { |types| types&.include?(other) }
|
35
|
+
when Literal::Types::IntersectionType
|
36
|
+
other.types.any? { |type| Literal.subtype?(type, of: self) }
|
37
|
+
when Literal::Types::ConstraintType
|
38
|
+
other.object_constraints.any? { |type| Literal.subtype?(type, of: self) }
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
freeze
|
17
45
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::IntersectionType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(*types)
|
6
8
|
raise Literal::ArgumentError.new("_Intersection type must have at least one type.") if types.size < 1
|
7
9
|
|
@@ -27,4 +29,27 @@ class Literal::Types::IntersectionType
|
|
27
29
|
context.add_child(label: inspect, expected: type, actual: context.actual)
|
28
30
|
end
|
29
31
|
end
|
32
|
+
|
33
|
+
def >=(other)
|
34
|
+
case other
|
35
|
+
when Literal::Types::IntersectionType
|
36
|
+
@types.all? do |type|
|
37
|
+
other.types.any? do |other_type|
|
38
|
+
Literal.subtype?(other_type, of: type)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
when Literal::Types::ConstraintType
|
42
|
+
@types.all? do |type|
|
43
|
+
other.object_constraints.any? do |object_constraint|
|
44
|
+
Literal.subtype?(object_constraint, of: type)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
when Literal::Types::FrozenType
|
48
|
+
@types.all? { |type| Literal.subtype?(other.type, of: type) }
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
freeze
|
30
55
|
end
|
@@ -1,10 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::JSONDataType
|
5
|
+
Instance = new.freeze
|
6
6
|
|
7
|
-
|
7
|
+
include Literal::Type
|
8
|
+
|
9
|
+
COMPATIBLE_TYPES = Set[
|
10
|
+
Integer,
|
11
|
+
Float,
|
12
|
+
String,
|
13
|
+
true,
|
14
|
+
false,
|
15
|
+
nil,
|
16
|
+
Literal::Types::BooleanType::Instance,
|
17
|
+
Instance
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
def inspect = "_JSONData"
|
21
|
+
|
22
|
+
def ===(value)
|
8
23
|
case value
|
9
24
|
when String, Integer, Float, true, false, nil
|
10
25
|
true
|
@@ -19,7 +34,7 @@ module Literal::Types::JSONDataType
|
|
19
34
|
end
|
20
35
|
end
|
21
36
|
|
22
|
-
def
|
37
|
+
def record_literal_type_errors(context)
|
23
38
|
case value = context.actual
|
24
39
|
when String, Integer, Float, true, false, nil
|
25
40
|
# nothing to do
|
@@ -35,5 +50,20 @@ module Literal::Types::JSONDataType
|
|
35
50
|
end
|
36
51
|
end
|
37
52
|
|
53
|
+
def >=(other)
|
54
|
+
return true if COMPATIBLE_TYPES.include?(other)
|
55
|
+
|
56
|
+
case other
|
57
|
+
when Literal::Types::ArrayType
|
58
|
+
self >= other.type
|
59
|
+
when Literal::Types::HashType
|
60
|
+
(self >= other.key_type) && (self >= other.value_type)
|
61
|
+
when Literal::Types::ConstraintType
|
62
|
+
other.object_constraints.any? { |type| self >= type }
|
63
|
+
else
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
38
68
|
freeze
|
39
69
|
end
|
@@ -2,10 +2,14 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::MapType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(**shape)
|
6
8
|
@shape = shape
|
7
9
|
end
|
8
10
|
|
11
|
+
attr_reader :shape
|
12
|
+
|
9
13
|
def inspect
|
10
14
|
"_Map(#{@shape.inspect})"
|
11
15
|
end
|
@@ -23,7 +27,7 @@ class Literal::Types::MapType
|
|
23
27
|
|
24
28
|
@shape.each do |key, expected|
|
25
29
|
unless context.actual.key?(key) || expected === nil
|
26
|
-
context.add_child(label: "[]", expected
|
30
|
+
context.add_child(label: "[#{key.inspect}]", expected:, actual: nil)
|
27
31
|
next
|
28
32
|
end
|
29
33
|
|
@@ -33,4 +37,17 @@ class Literal::Types::MapType
|
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
40
|
+
|
41
|
+
def >=(other)
|
42
|
+
case other
|
43
|
+
when Literal::Types::MapType
|
44
|
+
other_shape = other.shape
|
45
|
+
|
46
|
+
@shape.all? do |k, v|
|
47
|
+
Literal.subtype?(other_shape[k], of: v)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
36
53
|
end
|
@@ -1,12 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::NeverType
|
5
|
+
Instance = new.freeze
|
6
6
|
|
7
|
-
|
7
|
+
include Literal::Type
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"_Never"
|
11
|
+
end
|
12
|
+
|
13
|
+
def ===(value)
|
8
14
|
false
|
9
15
|
end
|
10
16
|
|
17
|
+
def >=(other)
|
18
|
+
case other
|
19
|
+
when Literal::Types::NeverTypeClass
|
20
|
+
true
|
21
|
+
else
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
11
26
|
freeze
|
12
27
|
end
|
@@ -2,11 +2,17 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::NilableType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Nilable(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
nil === value || @type === value
|
@@ -16,5 +22,16 @@ class Literal::Types::NilableType
|
|
16
22
|
@type.record_literal_type_errors(ctx) if @type.respond_to?(:record_literal_type_errors)
|
17
23
|
end
|
18
24
|
|
25
|
+
def >=(other)
|
26
|
+
case other
|
27
|
+
when Literal::Types::NilableType
|
28
|
+
Literal.subtype?(other.type, of: @type)
|
29
|
+
when nil
|
30
|
+
true
|
31
|
+
else
|
32
|
+
Literal.subtype?(other, of: @type)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
19
36
|
freeze
|
20
37
|
end
|
@@ -2,13 +2,34 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::NotType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Not(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
!(@type === value)
|
13
19
|
end
|
20
|
+
|
21
|
+
def >=(other)
|
22
|
+
case other
|
23
|
+
when Literal::Types::NotType
|
24
|
+
Literal.subtype?(other.type, of: @type)
|
25
|
+
when Literal::Types::ConstraintType
|
26
|
+
other.object_constraints.any? { |constraint| Literal.subtype?(constraint, of: self) }
|
27
|
+
when Literal::Types::IntersectionType
|
28
|
+
other.types.any? { |type| Literal.subtype?(type, of: self) }
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
freeze
|
14
35
|
end
|
@@ -2,11 +2,17 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::RangeType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Range(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
Range === value && (
|
@@ -17,4 +23,15 @@ class Literal::Types::RangeType
|
|
17
23
|
)
|
18
24
|
)
|
19
25
|
end
|
26
|
+
|
27
|
+
def >=(other)
|
28
|
+
case other
|
29
|
+
when Literal::Types::RangeType
|
30
|
+
Literal.subtype?(other.type, of: @type)
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
freeze
|
20
37
|
end
|
@@ -2,11 +2,17 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::SetType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Set(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
return false unless Set === value
|
@@ -27,4 +33,15 @@ class Literal::Types::SetType
|
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
36
|
+
|
37
|
+
def >=(other)
|
38
|
+
case other
|
39
|
+
when Literal::Types::SetType
|
40
|
+
Literal.subtype?(other.type, of: @type)
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
freeze
|
30
47
|
end
|
@@ -1,12 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::TruthyType
|
5
|
+
Instance = new.freeze
|
6
|
+
include Literal::Type
|
6
7
|
|
7
|
-
def
|
8
|
+
def inspect
|
9
|
+
"_Truthy"
|
10
|
+
end
|
11
|
+
|
12
|
+
def ===(value)
|
8
13
|
!!value
|
9
14
|
end
|
10
15
|
|
16
|
+
def >=(other)
|
17
|
+
case other
|
18
|
+
when Literal::Types::TruthyType, true
|
19
|
+
true
|
20
|
+
else
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
11
25
|
freeze
|
12
26
|
end
|
@@ -2,13 +2,19 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::TupleType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(*types)
|
6
8
|
raise Literal::ArgumentError.new("_Tuple type must have at least one type.") if types.size < 1
|
7
9
|
|
8
10
|
@types = types
|
9
11
|
end
|
10
12
|
|
11
|
-
|
13
|
+
attr_reader :types
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"_Tuple(#{@types.map(&:inspect).join(', ')})"
|
17
|
+
end
|
12
18
|
|
13
19
|
def ===(value)
|
14
20
|
return false unless Array === value
|
@@ -32,11 +38,22 @@ class Literal::Types::TupleType
|
|
32
38
|
while i < len
|
33
39
|
actual = context.actual[i]
|
34
40
|
if !(expected = @types[i])
|
35
|
-
context.add_child(label: "[#{i}]", expected: Literal::Types::NeverType, actual:)
|
41
|
+
context.add_child(label: "[#{i}]", expected: Literal::Types::NeverType::Instance, actual:)
|
36
42
|
elsif !(expected === actual)
|
37
43
|
context.add_child(label: "[#{i}]", expected:, actual:)
|
38
44
|
end
|
39
45
|
i += 1
|
40
46
|
end
|
41
47
|
end
|
48
|
+
|
49
|
+
def >=(other)
|
50
|
+
case other
|
51
|
+
when Literal::Types::TupleType
|
52
|
+
@types == other.types
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
freeze
|
42
59
|
end
|
@@ -12,7 +12,11 @@ class Literal::Types::UnionType
|
|
12
12
|
@types.freeze
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
attr_reader :types
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
"_Union(#{@types.inspect})"
|
19
|
+
end
|
16
20
|
|
17
21
|
def ===(value)
|
18
22
|
types = @types
|
@@ -46,9 +50,20 @@ class Literal::Types::UnionType
|
|
46
50
|
ctx.children.clear if ctx.children.none? { |c| c.children.any? }
|
47
51
|
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
def >=(other)
|
54
|
+
case other
|
55
|
+
when Literal::Types::UnionType
|
56
|
+
other.types.all? do |other_type|
|
57
|
+
@types.any? do |type|
|
58
|
+
Literal.subtype?(type, of: other_type)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
else
|
62
|
+
@types.any? do |type|
|
63
|
+
Literal.subtype?(other, of: type)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
52
67
|
|
53
68
|
private
|
54
69
|
|
@@ -57,4 +72,6 @@ class Literal::Types::UnionType
|
|
57
72
|
(Literal::Types::UnionType === type) ? load_types(type.types) : @types << type
|
58
73
|
end
|
59
74
|
end
|
75
|
+
|
76
|
+
freeze
|
60
77
|
end
|
@@ -1,10 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::VoidType
|
5
|
+
Instance = new.freeze
|
6
6
|
|
7
|
-
|
7
|
+
include Literal::Type
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"_Void"
|
11
|
+
end
|
12
|
+
|
13
|
+
def ===(_)
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def >=(_)
|
8
18
|
true
|
9
19
|
end
|
10
20
|
|