-
-
Notifications
You must be signed in to change notification settings - Fork 110
/
schema_compiler.rb
103 lines (84 loc) · 2.37 KB
/
schema_compiler.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
require 'dry/schema/constants'
module Dry
module Schema
# @api private
module Info
# @api private
class SchemaCompiler
PREDICATE_TO_TYPE = {
array?: 'array',
bool?: 'bool',
date?: 'date',
date_time?: 'date_time',
decimal?: 'float',
float?: 'float',
hash?: 'hash',
int?: 'integer',
nil?: 'nil',
str?: 'string',
time?: 'time'
}.freeze
# @api private
attr_reader :keys
# @api private
def initialize
@keys = EMPTY_HASH.dup
end
# @api private
def to_h
{ keys: keys }
end
# @api private
def call(ast)
visit(ast)
end
# @api private
def visit(node, opts = EMPTY_HASH)
meth, rest = node
public_send(:"visit_#{meth}", rest, opts)
end
# @api private
def visit_set(node, opts = EMPTY_HASH)
target = (key = opts[:key]) ? self.class.new : self
node.map { |child| target.visit(child, opts) }
return unless key
target_info = opts[:member] ? { member: target.to_h } : target.to_h
type = opts[:member] ? 'array' : 'hash'
keys.update(key => { **keys[key], type: type, **target_info })
end
# @api private
def visit_and(node, opts = EMPTY_HASH)
left, right = node
visit(left, opts)
visit(right, opts)
end
# @api private
def visit_implication(node, opts = EMPTY_HASH)
node.each do |el|
visit(el, opts.merge(required: false))
end
end
# @api private
def visit_each(node, opts = EMPTY_HASH)
visit(node, opts.merge(member: true))
end
# @api private
def visit_key(node, opts = EMPTY_HASH)
name, rest = node
visit(rest, opts.merge(key: name, required: true))
end
# @api private
def visit_predicate(node, opts = EMPTY_HASH)
name, rest = node
key = opts[:key]
if name.equal?(:key?)
keys[rest[0][1]] = { required: opts.fetch(:required, true) }
else
type = PREDICATE_TO_TYPE[name]
keys[key][:type] = type if type
end
end
end
end
end
end