tsjson 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/lib/errors/cant_distinguish_type_error.rb +17 -0
  3. data/lib/errors/index.rb +12 -0
  4. data/lib/errors/list_validation_error.rb +34 -0
  5. data/lib/errors/literal_union_validation_error.rb +18 -0
  6. data/lib/errors/literal_validation_error.rb +16 -0
  7. data/lib/errors/not_enough_discriminators.rb +7 -0
  8. data/lib/errors/object_validation_error.rb +56 -0
  9. data/lib/errors/required_field_error.rb +7 -0
  10. data/lib/errors/scalar_union_validation_error.rb +18 -0
  11. data/lib/errors/scalar_validation_error.rb +16 -0
  12. data/lib/errors/unexpected_field_error.rb +7 -0
  13. data/lib/errors/unexpected_value_error.rb +16 -0
  14. data/lib/errors/validation_error.rb +16 -0
  15. data/lib/language/ast/kind.rb +25 -0
  16. data/lib/language/lexer/lexer.rb +452 -0
  17. data/lib/language/lexer/location.rb +20 -0
  18. data/lib/language/lexer/syntax_error.rb +89 -0
  19. data/lib/language/lexer/token.rb +34 -0
  20. data/lib/language/lexer/token_kind.rb +37 -0
  21. data/lib/language/lexer/utils.rb +32 -0
  22. data/lib/language/parser/parser.rb +437 -0
  23. data/lib/language/source.rb +109 -0
  24. data/lib/schema/schema.rb +48 -0
  25. data/lib/schema/schema_builder.rb +148 -0
  26. data/lib/tsjson.rb +1 -0
  27. data/lib/types/any.rb +15 -0
  28. data/lib/types/base.rb +19 -0
  29. data/lib/types/boolean.rb +17 -0
  30. data/lib/types/discriminator_map.rb +116 -0
  31. data/lib/types/enum.rb +47 -0
  32. data/lib/types/float.rb +17 -0
  33. data/lib/types/index.rb +27 -0
  34. data/lib/types/int.rb +17 -0
  35. data/lib/types/intersection.rb +72 -0
  36. data/lib/types/list.rb +33 -0
  37. data/lib/types/literal.rb +25 -0
  38. data/lib/types/literal_union.rb +48 -0
  39. data/lib/types/merge.rb +21 -0
  40. data/lib/types/null.rb +17 -0
  41. data/lib/types/object.rb +87 -0
  42. data/lib/types/scalar.rb +24 -0
  43. data/lib/types/scalar_union.rb +25 -0
  44. data/lib/types/string.rb +17 -0
  45. data/lib/types/union.rb +61 -0
  46. metadata +85 -0
@@ -0,0 +1,47 @@
1
+ module TSJSON
2
+ class Enum < Base
3
+ def initialize(members = {})
4
+ super()
5
+
6
+ @members = members
7
+ @members.each { |name, type| validate_type!(type) }
8
+ end
9
+
10
+ def member(name)
11
+ m = @members[name]
12
+ raise "Can't access property #{name} of #{self.class.name}" unless m
13
+
14
+ m
15
+ end
16
+ alias property member
17
+
18
+ def push_type(type)
19
+ validate_type!(type)
20
+ return self if types.include?(type)
21
+ return Enum.new(@types.dup.push(type))
22
+ end
23
+
24
+ def types
25
+ @types ||= @members.values
26
+ end
27
+
28
+ def has?(type)
29
+ types.include?(type)
30
+ end
31
+
32
+ def validate_type!(type)
33
+ unless type.is_a?(Literal)
34
+ raise "Enum can contain only literal values received #{type.class}"
35
+ end
36
+ end
37
+
38
+ def validate(value)
39
+ types.each { |type| return true if type.valid?(value) }
40
+
41
+ raise LiteralUnionValidationError.new(
42
+ expected_values: types.map(&:value),
43
+ received_value: value
44
+ )
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ require_relative './scalar.rb'
2
+
3
+ module TSJSON
4
+ class FloatType < ScalarType
5
+ def initialize
6
+ super('Float')
7
+ end
8
+
9
+ def validate(value)
10
+ super(value) unless value.is_a?(::Numeric)
11
+
12
+ true
13
+ end
14
+ end
15
+
16
+ TSJSONFloat = FloatType.new
17
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../errors/index.rb'
2
+
3
+ #
4
+
5
+ require_relative './string.rb'
6
+ require_relative './int.rb'
7
+ require_relative './float.rb'
8
+ require_relative './boolean.rb'
9
+ require_relative './null.rb'
10
+ require_relative './any.rb'
11
+
12
+ #
13
+
14
+ require_relative './literal.rb'
15
+ require_relative './scalar_union.rb'
16
+ require_relative './literal_union.rb'
17
+ require_relative './list.rb'
18
+ require_relative './object.rb'
19
+
20
+ #
21
+
22
+ require_relative './enum.rb'
23
+
24
+ #
25
+
26
+ require_relative './union.rb'
27
+ require_relative './intersection.rb'
@@ -0,0 +1,17 @@
1
+ require_relative './scalar.rb'
2
+
3
+ module TSJSON
4
+ class IntType < ScalarType
5
+ def initialize
6
+ super('Int')
7
+ end
8
+
9
+ def validate(value)
10
+ super(value) unless value.is_a?(::Integer)
11
+
12
+ true
13
+ end
14
+ end
15
+
16
+ TSJSONInt = IntType.new
17
+ end
@@ -0,0 +1,72 @@
1
+ require_relative './merge.rb'
2
+
3
+ module TSJSON
4
+ class Intersection < Merge
5
+ def normal_form
6
+ unless @normalized
7
+ @normalized =
8
+ @types.reduce(Intersection.new) do |buffer, type|
9
+ if type.is_a?(Merge)
10
+ buffer.intersect_normal(type.normal_form)
11
+ else
12
+ buffer.intersect_normal(type)
13
+ end
14
+ end
15
+ end
16
+ @normalized
17
+ end
18
+
19
+ def intersect_normal(other)
20
+ # raise_if_not_composite(other)
21
+
22
+ if other.is_a?(Intersection)
23
+ Intersection.new(@types.dup.concat(other.types), true)
24
+ elsif other.is_a?(Union)
25
+ other.types.reduce(Union.new) do |u, t|
26
+ u.union_normal(intersect_normal(t))
27
+ end
28
+ else
29
+ Intersection.new(@types.dup.push(other), true)
30
+ end
31
+ end
32
+
33
+ def union_normal(other)
34
+ # raise_if_not_composite(other)
35
+
36
+ Union.new([self, other], true)
37
+ end
38
+
39
+ def validate(object)
40
+ return normal_form.validate(object) unless @is_normal
41
+
42
+ merged_object.validate(object)
43
+ end
44
+
45
+ def fields
46
+ merged_object.fields
47
+ end
48
+
49
+ def to_s
50
+ @types.map(&:to_s).join(' & ')
51
+ end
52
+
53
+ def compile
54
+ return if @compiled
55
+ @compiled = true
56
+
57
+ return normal_form.compile unless @is_normal
58
+ merged_object
59
+ end
60
+
61
+ private
62
+
63
+ def merged_object
64
+ raise 'not a normal form' unless @is_normal
65
+
66
+ @merged_object ||=
67
+ ObjectType.new(
68
+ @types.reduce({}) { |obj, type| obj.merge(type.fields_map) }
69
+ )
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,33 @@
1
+ module TSJSON
2
+ class List < Base
3
+ def initialize(type)
4
+ super()
5
+ @type = type
6
+ end
7
+
8
+ def of_type
9
+ @type
10
+ end
11
+
12
+ def validate(value)
13
+ unless value.is_a?(::Array)
14
+ raise ScalarValidationError.new(
15
+ expected_type: 'Array',
16
+ received_type: value.class.name,
17
+ received_value: value
18
+ )
19
+ end
20
+
21
+ errors = []
22
+ value.each_with_index do |item, index|
23
+ of_type.validate(item)
24
+ rescue ValidationError => e
25
+ errors.push({ index: index, error: e })
26
+ end
27
+
28
+ raise ListValidationError.new(errors: errors) if errors.length > 0
29
+
30
+ true
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ module TSJSON
2
+ class Literal < Base
3
+ attr_reader :value
4
+
5
+ def initialize(value)
6
+ @value = value
7
+ end
8
+
9
+ def ==(other)
10
+ return value == other unless other.is_a?(self.class)
11
+
12
+ value == other.value
13
+ end
14
+
15
+ def validate(other)
16
+ unless @value == other
17
+ raise LiteralValidationError.new(
18
+ expected_value: @value, received_value: other
19
+ )
20
+ end
21
+
22
+ true
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ module TSJSON
2
+ class LiteralUnion < Base
3
+ attr_reader :types
4
+
5
+ def initialize(types = [])
6
+ types.each { |t| validate_type!(t) }
7
+
8
+ @types = types
9
+ end
10
+
11
+ def push_type(type)
12
+ validate_type!(type)
13
+ return self if types.include?(type)
14
+ return LiteralUnion.new(@types.dup.push(type))
15
+ end
16
+
17
+ def has?(type)
18
+ @types.include?(type)
19
+ end
20
+
21
+ def size
22
+ @types.size
23
+ end
24
+
25
+ def validate_type!(type)
26
+ unless type.is_a?(Literal)
27
+ raise 'LiteralUnion may contain only Literal types'
28
+ end
29
+ end
30
+
31
+ def validate(value)
32
+ @types.each { |type| return true if type.valid?(value) }
33
+
34
+ raise LiteralUnionValidationError.new(
35
+ expected_values: @types.map(&:value),
36
+ received_value: value
37
+ )
38
+ end
39
+
40
+ def to_s
41
+ @types.map(&:to_s).join(' | ')
42
+ end
43
+
44
+ def to_json
45
+ @types.map(&:value)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ module TSJSON
2
+ class Merge < Base
3
+ attr_reader :types
4
+ attr_accessor :is_normal
5
+
6
+ def initialize(types = [], is_normal = false)
7
+ super()
8
+
9
+ types.each do |t|
10
+ unless t.is_a?(ObjectType) || t.is_a?(Union) || t.is_a?(Intersection)
11
+ raise "#{
12
+ self.class.name
13
+ } may contain only ObjectType, Union or Intersection types"
14
+ end
15
+ end
16
+
17
+ @types = types
18
+ @is_normal = types.length == 0 || is_normal
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ require_relative './scalar.rb'
2
+
3
+ module TSJSON
4
+ class NullType < ScalarType
5
+ def initialize
6
+ super('Null')
7
+ end
8
+
9
+ def validate(value)
10
+ super(value) unless value == nil
11
+
12
+ true
13
+ end
14
+ end
15
+
16
+ TSJSONNull = NullType.new
17
+ end
@@ -0,0 +1,87 @@
1
+ module TSJSON
2
+ class ObjectType < Base
3
+ def initialize(fields_map = nil, &block)
4
+ super()
5
+ @fields_map = fields_map&.transform_keys!(&:to_s)
6
+ @resolve_fields = block
7
+ end
8
+
9
+ def fields
10
+ @fields = fields_map.map { |k, v| v.merge({ name: k }) } unless @fields
11
+
12
+ @fields
13
+ end
14
+
15
+ def field(name)
16
+ f = fields_map.dig(name, :type)
17
+ raise "Can't access index #{name} of #{self.class.name}" unless f
18
+
19
+ f
20
+ end
21
+ alias index field
22
+
23
+ def fields_map
24
+ unless @fields_map
25
+ @fields_map = @resolve_fields.call&.transform_keys!(&:to_s)
26
+ end
27
+ @fields_map
28
+ end
29
+
30
+ def compile
31
+ return if @compiled
32
+ @compiled = true
33
+
34
+ fields.each { |f| f[:type].compile }
35
+ end
36
+
37
+ def validate(object)
38
+ unless object.is_a?(::Hash)
39
+ raise ScalarValidationError.new(
40
+ expected_type: 'Object',
41
+ received_type: object.class.name,
42
+ received_value: object
43
+ )
44
+ end
45
+
46
+ object = object&.transform_keys!(&:to_s)
47
+
48
+ keys = object.keys
49
+ errors = []
50
+
51
+ fields.each do |f|
52
+ name = f[:name]
53
+ type = f[:type]
54
+ optional = f[:optional]
55
+ value = object[name]
56
+
57
+ keys.delete(name)
58
+ has_field = object.key?(name)
59
+ next if optional && !has_field
60
+ raise RequiredFieldError.new if !optional && !has_field
61
+
62
+ type.validate(value)
63
+ rescue ValidationError => e
64
+ errors.push({ field: name, error: e })
65
+ end
66
+ keys.each do |name|
67
+ errors.push({ field: name, error: UnexpectedFieldError.new })
68
+ end
69
+
70
+ raise ObjectValidationError.new(errors: errors) if errors.length > 0
71
+
72
+ true
73
+ end
74
+
75
+ def to_s
76
+ "{\n#{
77
+ fields.map do |f|
78
+ name = f[:name]
79
+ type = f[:type].to_s
80
+ optional = f[:optional]
81
+
82
+ " #{name}#{optional ? '?' : ''}: #{type}"
83
+ end.join("\n")
84
+ }\n}"
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,24 @@
1
+ require_relative './base.rb'
2
+
3
+ module TSJSON
4
+ class ScalarType < Base
5
+ attr_reader :name
6
+
7
+ def initialize(name)
8
+ super()
9
+ @name = name
10
+ end
11
+
12
+ def validate(value)
13
+ raise ScalarValidationError.new(
14
+ expected_type: @name,
15
+ received_type: value.class.name,
16
+ received_value: value
17
+ )
18
+ end
19
+
20
+ def to_s
21
+ @name
22
+ end
23
+ end
24
+ end