vetra 0.1.0.pre.alpha.4

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.
@@ -0,0 +1,50 @@
1
+ module Vetra
2
+ module Graph
3
+ class Registry
4
+ attr_reader :declarations, :bindings, :application
5
+
6
+ def initialize
7
+ @declarations = []
8
+ @bindings = {}
9
+ @application = nil
10
+ @sealed = false
11
+ end
12
+
13
+ def app(attributes)
14
+ ensure_open!
15
+ raise Error.single("duplicate_app", "Declare Vetra.app uma única vez.", source: attributes['source']) if @application
16
+ @application = attributes
17
+ end
18
+
19
+ def add(declaration, block: nil)
20
+ ensure_open!
21
+ key = declaration.values_at("kind", "id")
22
+ if declarations.any? { |entry| entry.values_at("kind", "id") == key }
23
+ raise Error.single("duplicate_id", "Identidade repetida: #{key.join(' ')}.", source: declaration['source'])
24
+ end
25
+ if block
26
+ reference = "#{declaration.fetch('id')}#call"
27
+ raise Error.single("duplicate_implementation", "Implementação repetida: #{reference}.", source: declaration['source']) if bindings.key?(reference)
28
+ @bindings[reference] = block
29
+ end
30
+ @declarations << declaration
31
+ end
32
+
33
+ def seal!
34
+ ensure_open!
35
+ @sealed = true
36
+ Data.freeze(@application)
37
+ Data.freeze(@declarations)
38
+ @bindings.each_value(&:freeze)
39
+ @bindings.freeze
40
+ freeze
41
+ end
42
+
43
+ private
44
+
45
+ def ensure_open!
46
+ raise Error.single("registry_frozen", "O registro já está congelado; inicie outro build.") if @sealed
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,78 @@
1
+ require "json_schemer"
2
+
3
+ module Vetra
4
+ module Graph
5
+ class Schema
6
+ VERSION = "0.3"
7
+ LEGACY_VERSION = "0.1"
8
+ PREVIOUS_VERSION = "0.2"
9
+ FEATURES = %w[vetra.core vetra.timestamps vetra.structured_outputs vetra.live].freeze
10
+ CRUD_ACTIONS = %w[create read update delete].freeze
11
+ PATH = File.expand_path("../../../docs/ir/v0.3.schema.json", __dir__)
12
+ PREVIOUS_PATH = File.expand_path("../../../docs/ir/v0.2.schema.json", __dir__)
13
+ LEGACY_PATH = File.expand_path("../../../docs/ir/v0.1.schema.json", __dir__)
14
+
15
+ def initialize
16
+ @schemas = {}
17
+ end
18
+
19
+ def validate!(document)
20
+ unless document.is_a?(Hash) && [LEGACY_VERSION, PREVIOUS_VERSION, VERSION].include?(document["ir_version"])
21
+ raise Error.single("ir_version", "Versão de IR não suportada; este compilador aceita #{LEGACY_VERSION}, #{PREVIOUS_VERSION} e #{VERSION}.", pointer: "/ir_version")
22
+ end
23
+ version = document.fetch('ir_version')
24
+ supported = case version
25
+ when LEGACY_VERSION then ['vetra.core']
26
+ when PREVIOUS_VERSION then %w[vetra.core vetra.timestamps]
27
+ else FEATURES
28
+ end
29
+ unknown = Array(document["requires"]) - supported
30
+ unless unknown.empty?
31
+ raise Error.single("ir_feature", "Recursos não implementados: #{unknown.join(', ')}.", pointer: "/requires")
32
+ end
33
+ envelope, nodes = validators(version)
34
+ diagnostics = errors(envelope, document)
35
+ raise Error.new(diagnostics) unless diagnostics.empty?
36
+ document.fetch("nodes").each_with_index do |node, index|
37
+ validator = nodes[node["kind"]]
38
+ if validator
39
+ diagnostics.concat(errors(validator, node, "/nodes/#{index}", node["source"]))
40
+ else
41
+ diagnostics << Diagnostic.new(code: "node_kind", message: "Tipo de nó desconhecido.", pointer: "/nodes/#{index}/kind", source: node["source"], severity: :error)
42
+ end
43
+ end
44
+ raise Error.new(diagnostics) unless diagnostics.empty?
45
+ end
46
+
47
+ private
48
+
49
+ def validators(version)
50
+ @schemas[version] ||= begin
51
+ path = case version
52
+ when LEGACY_VERSION then LEGACY_PATH
53
+ when PREVIOUS_VERSION then PREVIOUS_PATH
54
+ else PATH
55
+ end
56
+ definition = JSON.parse(File.read(path, encoding: Encoding::UTF_8))
57
+ properties = definition.fetch('properties')
58
+ envelope = definition.merge('properties' => properties.merge('nodes' => properties.fetch('nodes').merge('items' => { 'type' => 'object' })))
59
+ nodes = properties.fetch('nodes').fetch('items').fetch('oneOf').to_h do |branch|
60
+ kind = branch.fetch('$ref').split('/').last
61
+ [kind, schemer(definition.merge('$ref' => branch.fetch('$ref')).reject { |key, _| %w[type properties required additionalProperties].include?(key) })]
62
+ end
63
+ [schemer(envelope), nodes]
64
+ end
65
+ end
66
+
67
+ def schemer(schema)
68
+ JSONSchemer.schema(schema, ref_resolver: proc { |uri| raise Error.single("schema_reference", "Referência externa proibida: #{uri}.") }, regexp_resolver: proc { |pattern| Regexp.new(pattern.sub(/\A\^/, '\\A').sub(/\$\z/, '\\z')) })
69
+ end
70
+
71
+ def errors(validator, value, prefix = "", source = nil)
72
+ validator.validate(value).map do |error|
73
+ Diagnostic.new(code: "schema", message: error.fetch("error"), pointer: "#{prefix}#{error.fetch('data_pointer')}", source: source, severity: :error)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end