igniter-contracts 0.5.2

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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +239 -0
  3. data/lib/igniter/contracts/api.rb +92 -0
  4. data/lib/igniter/contracts/assembly/baseline_pack.rb +141 -0
  5. data/lib/igniter/contracts/assembly/const_pack.rb +29 -0
  6. data/lib/igniter/contracts/assembly/dsl_keyword.rb +21 -0
  7. data/lib/igniter/contracts/assembly/hook_result_policies.rb +47 -0
  8. data/lib/igniter/contracts/assembly/hook_spec.rb +73 -0
  9. data/lib/igniter/contracts/assembly/hook_specs.rb +74 -0
  10. data/lib/igniter/contracts/assembly/kernel.rb +220 -0
  11. data/lib/igniter/contracts/assembly/node_type.rb +26 -0
  12. data/lib/igniter/contracts/assembly/ordered_registry.rb +55 -0
  13. data/lib/igniter/contracts/assembly/pack.rb +13 -0
  14. data/lib/igniter/contracts/assembly/pack_manifest.rb +131 -0
  15. data/lib/igniter/contracts/assembly/path_access.rb +76 -0
  16. data/lib/igniter/contracts/assembly/profile.rb +133 -0
  17. data/lib/igniter/contracts/assembly/project_pack.rb +42 -0
  18. data/lib/igniter/contracts/assembly/registry.rb +57 -0
  19. data/lib/igniter/contracts/assembly/step_result_pack.rb +42 -0
  20. data/lib/igniter/contracts/assembly.rb +18 -0
  21. data/lib/igniter/contracts/contract.rb +135 -0
  22. data/lib/igniter/contracts/contractable.rb +288 -0
  23. data/lib/igniter/contracts/environment.rb +51 -0
  24. data/lib/igniter/contracts/errors.rb +47 -0
  25. data/lib/igniter/contracts/execution/baseline_normalizers.rb +23 -0
  26. data/lib/igniter/contracts/execution/baseline_runtime.rb +55 -0
  27. data/lib/igniter/contracts/execution/baseline_validators.rb +113 -0
  28. data/lib/igniter/contracts/execution/builder.rb +43 -0
  29. data/lib/igniter/contracts/execution/compilation_report.rb +46 -0
  30. data/lib/igniter/contracts/execution/compiled_graph.rb +21 -0
  31. data/lib/igniter/contracts/execution/compiler.rb +66 -0
  32. data/lib/igniter/contracts/execution/const_runtime.rb +15 -0
  33. data/lib/igniter/contracts/execution/diagnostics.rb +24 -0
  34. data/lib/igniter/contracts/execution/diagnostics_report.rb +40 -0
  35. data/lib/igniter/contracts/execution/diagnostics_section.rb +37 -0
  36. data/lib/igniter/contracts/execution/effect_invocation.rb +26 -0
  37. data/lib/igniter/contracts/execution/execution_request.rb +28 -0
  38. data/lib/igniter/contracts/execution/execution_result.rb +32 -0
  39. data/lib/igniter/contracts/execution/inline_executor.rb +19 -0
  40. data/lib/igniter/contracts/execution/mutable_named_values.rb +52 -0
  41. data/lib/igniter/contracts/execution/named_values.rb +48 -0
  42. data/lib/igniter/contracts/execution/operation.rb +42 -0
  43. data/lib/igniter/contracts/execution/runtime.rb +43 -0
  44. data/lib/igniter/contracts/execution/step_result.rb +51 -0
  45. data/lib/igniter/contracts/execution/step_result_diagnostics.rb +35 -0
  46. data/lib/igniter/contracts/execution/step_result_runtime.rb +51 -0
  47. data/lib/igniter/contracts/execution/step_result_validators.rb +44 -0
  48. data/lib/igniter/contracts/execution/structured_dump.rb +49 -0
  49. data/lib/igniter/contracts/execution/validation_finding.rb +28 -0
  50. data/lib/igniter/contracts/execution/validation_report.rb +46 -0
  51. data/lib/igniter/contracts/execution.rb +28 -0
  52. data/lib/igniter/contracts.rb +54 -0
  53. data/lib/igniter/lang/backend.rb +19 -0
  54. data/lib/igniter/lang/backends/ruby.rb +42 -0
  55. data/lib/igniter/lang/diagnostic_payload.rb +174 -0
  56. data/lib/igniter/lang/metadata_carrier_manifest.rb +112 -0
  57. data/lib/igniter/lang/metadata_manifest.rb +128 -0
  58. data/lib/igniter/lang/receipt_payload.rb +152 -0
  59. data/lib/igniter/lang/schema_compatibility_diagnostic.rb +300 -0
  60. data/lib/igniter/lang/types.rb +84 -0
  61. data/lib/igniter/lang/verification_report.rb +226 -0
  62. data/lib/igniter/lang.rb +27 -0
  63. data/lib/igniter-contracts.rb +3 -0
  64. metadata +103 -0
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Contracts
5
+ module Execution
6
+ module StructuredDump
7
+ module_function
8
+
9
+ def dump(value)
10
+ case value
11
+ when Array
12
+ value.map { |entry| dump(entry) }
13
+ when Hash
14
+ value.to_h { |key, entry| [normalize_key(key), dump(entry)] }
15
+ else
16
+ dump_object(value)
17
+ end
18
+ end
19
+
20
+ def dump_object(value)
21
+ if serializable_contract_object?(value)
22
+ value.to_h
23
+ else
24
+ value
25
+ end
26
+ end
27
+
28
+ def serializable_contract_object?(value)
29
+ value.is_a?(NamedValues) ||
30
+ value.is_a?(Operation) ||
31
+ value.is_a?(CompiledGraph) ||
32
+ value.is_a?(ExecutionResult) ||
33
+ value.is_a?(EffectInvocation) ||
34
+ value.is_a?(ExecutionRequest) ||
35
+ value.is_a?(DiagnosticsSection) ||
36
+ value.is_a?(DiagnosticsReport) ||
37
+ value.is_a?(ValidationFinding) ||
38
+ value.is_a?(ValidationReport) ||
39
+ value.is_a?(CompilationReport) ||
40
+ (defined?(StepResult) && value.is_a?(StepResult))
41
+ end
42
+
43
+ def normalize_key(key)
44
+ key.respond_to?(:to_sym) ? key.to_sym : key
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Contracts
5
+ module Execution
6
+ class ValidationFinding
7
+ attr_reader :code, :message, :subjects, :metadata
8
+
9
+ def initialize(code:, message:, subjects: [], metadata: {})
10
+ @code = code.to_sym
11
+ @message = message
12
+ @subjects = Array(subjects).map(&:to_sym).freeze
13
+ @metadata = metadata.freeze
14
+ freeze
15
+ end
16
+
17
+ def to_h
18
+ {
19
+ code: code,
20
+ message: message,
21
+ subjects: subjects,
22
+ metadata: StructuredDump.dump(metadata)
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Contracts
5
+ module Execution
6
+ class ValidationReport
7
+ attr_reader :operations, :findings, :profile_fingerprint
8
+
9
+ def initialize(operations:, findings:, profile_fingerprint:)
10
+ @operations = operations.freeze
11
+ @findings = findings.freeze
12
+ @profile_fingerprint = profile_fingerprint
13
+ freeze
14
+ end
15
+
16
+ def ok?
17
+ findings.empty?
18
+ end
19
+
20
+ def invalid?
21
+ !ok?
22
+ end
23
+
24
+ def raise_if_invalid!
25
+ return self if ok?
26
+
27
+ raise ValidationError.new(findings: findings)
28
+ end
29
+
30
+ def to_compiled_graph
31
+ raise_if_invalid!
32
+ CompiledGraph.new(operations: operations, profile_fingerprint: profile_fingerprint)
33
+ end
34
+
35
+ def to_h
36
+ {
37
+ operations: StructuredDump.dump(operations),
38
+ findings: StructuredDump.dump(findings),
39
+ profile_fingerprint: profile_fingerprint,
40
+ ok: ok?
41
+ }
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "execution/baseline_normalizers"
4
+ require_relative "execution/baseline_validators"
5
+ require_relative "execution/baseline_runtime"
6
+ require_relative "execution/const_runtime"
7
+ require_relative "execution/inline_executor"
8
+ require_relative "execution/operation"
9
+ require_relative "execution/named_values"
10
+ require_relative "execution/mutable_named_values"
11
+ require_relative "execution/effect_invocation"
12
+ require_relative "execution/execution_request"
13
+ require_relative "execution/diagnostics_section"
14
+ require_relative "execution/validation_finding"
15
+ require_relative "execution/validation_report"
16
+ require_relative "execution/compilation_report"
17
+ require_relative "execution/structured_dump"
18
+ require_relative "execution/step_result"
19
+ require_relative "execution/step_result_runtime"
20
+ require_relative "execution/step_result_validators"
21
+ require_relative "execution/step_result_diagnostics"
22
+ require_relative "execution/compiled_graph"
23
+ require_relative "execution/builder"
24
+ require_relative "execution/compiler"
25
+ require_relative "execution/execution_result"
26
+ require_relative "execution/runtime"
27
+ require_relative "execution/diagnostics_report"
28
+ require_relative "execution/diagnostics"
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "contracts/errors"
4
+ require_relative "contracts/environment"
5
+ require_relative "contracts/assembly"
6
+ require_relative "contracts/execution"
7
+ require_relative "contracts/contractable"
8
+ require_relative "contracts/contract"
9
+
10
+ module Igniter
11
+ module Contracts
12
+ Registry = Assembly::Registry
13
+ OrderedRegistry = Assembly::OrderedRegistry
14
+ Pack = Assembly::Pack
15
+ PackManifest = Assembly::PackManifest
16
+ NodeType = Assembly::NodeType
17
+ DslKeyword = Assembly::DslKeyword
18
+ HookResultPolicies = Assembly::HookResultPolicies
19
+ HookSpec = Assembly::HookSpec
20
+ HookSpecs = Assembly::HookSpecs
21
+ Profile = Assembly::Profile
22
+ Kernel = Assembly::Kernel
23
+ PathAccess = Assembly::PathAccess
24
+ BaselinePack = Assembly::BaselinePack
25
+ ConstPack = Assembly::ConstPack
26
+ ProjectPack = Assembly::ProjectPack
27
+ StepResultPack = Assembly::StepResultPack
28
+
29
+ CompiledGraph = Execution::CompiledGraph
30
+ Builder = Execution::Builder
31
+ Compiler = Execution::Compiler
32
+ ExecutionResult = Execution::ExecutionResult
33
+ Runtime = Execution::Runtime
34
+ DiagnosticsReport = Execution::DiagnosticsReport
35
+ Diagnostics = Execution::Diagnostics
36
+ BaselineNormalizers = Execution::BaselineNormalizers
37
+ BaselineValidators = Execution::BaselineValidators
38
+ BaselineRuntime = Execution::BaselineRuntime
39
+ ConstRuntime = Execution::ConstRuntime
40
+ InlineExecutor = Execution::InlineExecutor
41
+ Operation = Execution::Operation
42
+ NamedValues = Execution::NamedValues
43
+ EffectInvocation = Execution::EffectInvocation
44
+ ExecutionRequest = Execution::ExecutionRequest
45
+ DiagnosticsSection = Execution::DiagnosticsSection
46
+ ValidationFinding = Execution::ValidationFinding
47
+ ValidationReport = Execution::ValidationReport
48
+ CompilationReport = Execution::CompilationReport
49
+ MutableNamedValues = Execution::MutableNamedValues
50
+ StepResult = Execution::StepResult
51
+ end
52
+ end
53
+
54
+ require_relative "contracts/api"
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Lang
5
+ module Backend
6
+ def compile(...)
7
+ raise NotImplementedError, "#{self.class} must implement #compile"
8
+ end
9
+
10
+ def execute(...)
11
+ raise NotImplementedError, "#{self.class} must implement #execute"
12
+ end
13
+
14
+ def verify(...)
15
+ raise NotImplementedError, "#{self.class} must implement #verify"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Lang
5
+ module Backends
6
+ class Ruby
7
+ include Backend
8
+
9
+ attr_reader :profile
10
+
11
+ def initialize(profile: Igniter::Contracts.default_profile)
12
+ @profile = profile
13
+ end
14
+
15
+ def compile(profile: self.profile, &block)
16
+ Igniter::Contracts.compile(profile: profile, &block)
17
+ end
18
+
19
+ def compilation_report(profile: self.profile, &block)
20
+ Igniter::Contracts.compilation_report(profile: profile, &block)
21
+ end
22
+
23
+ def execute(artifact, inputs:, profile: self.profile)
24
+ Igniter::Contracts.execute(artifact, inputs: inputs, profile: profile)
25
+ end
26
+
27
+ def diagnose(result, profile: self.profile)
28
+ Igniter::Contracts.diagnose(result, profile: profile)
29
+ end
30
+
31
+ def verify(artifact = nil, profile: self.profile, &block)
32
+ if block
33
+ report = compilation_report(profile: profile, &block)
34
+ return VerificationReport.from_compilation_report(report)
35
+ end
36
+
37
+ VerificationReport.from_artifact(artifact, profile_fingerprint: profile.fingerprint)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Lang
5
+ class DiagnosticPayload
6
+ SEMANTICS = {
7
+ report_only: true,
8
+ runtime_enforced: false,
9
+ package_adapter_authorized: false,
10
+ real_data_export_authorized: false,
11
+ readiness_enforced: false,
12
+ ledger_core: false
13
+ }.freeze
14
+
15
+ DEFAULT_REDACTION_POLICY = {
16
+ raw_ref_export: false,
17
+ hash_source_refs: true,
18
+ redacted_ref_kinds: []
19
+ }.freeze
20
+
21
+ STATUSES = %i[ok warning blocked unknown].freeze
22
+ DECISIONS = %i[trusted provisional blocked unknown].freeze
23
+ RAW_REF_KEYS = %i[raw_ref raw_refs raw_source_ref raw_source_refs].freeze
24
+ RAW_REF_PREFIX = "raw:"
25
+
26
+ attr_reader :diagnostic_id,
27
+ :profile,
28
+ :status,
29
+ :decision,
30
+ :payload,
31
+ :evidence_links,
32
+ :redaction_policy,
33
+ :metadata
34
+
35
+ def initialize(
36
+ diagnostic_id:,
37
+ profile:,
38
+ status:,
39
+ decision:,
40
+ payload: {},
41
+ evidence_links: {},
42
+ redaction_policy: {},
43
+ metadata: {}
44
+ )
45
+ @diagnostic_id = require_value(:diagnostic_id, diagnostic_id)
46
+ @profile = require_value(:profile, profile)
47
+ @status = normalize_status(status)
48
+ @decision = normalize_decision(decision)
49
+ @payload = deep_freeze(normalize_hash(payload, :payload))
50
+ @evidence_links = deep_freeze(normalize_hash(evidence_links, :evidence_links))
51
+ @redaction_policy = deep_freeze(normalize_redaction_policy(redaction_policy))
52
+ @metadata = deep_freeze(normalize_hash(metadata, :metadata))
53
+ reject_raw_refs!
54
+ freeze
55
+ end
56
+
57
+ def report_only?
58
+ true
59
+ end
60
+
61
+ def runtime_enforced?
62
+ false
63
+ end
64
+
65
+ def to_h
66
+ {
67
+ diagnostic_id: diagnostic_id,
68
+ profile: profile,
69
+ status: status,
70
+ decision: decision,
71
+ payload: payload,
72
+ evidence_links: evidence_links,
73
+ redaction_policy: redaction_policy,
74
+ semantics: SEMANTICS,
75
+ metadata: metadata
76
+ }
77
+ end
78
+
79
+ private
80
+
81
+ def require_value(name, value)
82
+ raise ArgumentError, "#{name} is required" if value.nil?
83
+
84
+ value
85
+ end
86
+
87
+ def normalize_status(value)
88
+ status_value = require_value(:status, value).to_sym
89
+ return status_value if STATUSES.include?(status_value)
90
+
91
+ raise ArgumentError, "status must be one of #{STATUSES.join(", ")}"
92
+ end
93
+
94
+ def normalize_decision(value)
95
+ decision_value = require_value(:decision, value).to_sym
96
+ return decision_value if DECISIONS.include?(decision_value)
97
+
98
+ raise ArgumentError, "decision must be one of #{DECISIONS.join(", ")}"
99
+ end
100
+
101
+ def normalize_redaction_policy(value)
102
+ policy = DEFAULT_REDACTION_POLICY.merge(normalize_hash(value, :redaction_policy))
103
+ raise ArgumentError, "redaction_policy.raw_ref_export must be true or false" unless [true, false].include?(policy[:raw_ref_export])
104
+
105
+ raise ArgumentError, "redaction_policy.raw_ref_export true is not supported in v0" if policy[:raw_ref_export]
106
+
107
+ policy[:redacted_ref_kinds] = Array(policy[:redacted_ref_kinds]).map(&:to_s)
108
+ policy
109
+ end
110
+
111
+ def reject_raw_refs!
112
+ raw_path = find_raw_ref(
113
+ payload: payload,
114
+ evidence_links: evidence_links,
115
+ metadata: metadata
116
+ )
117
+ raise ArgumentError, "raw refs are not allowed in diagnostic payloads at #{raw_path}" if raw_path
118
+ end
119
+
120
+ def find_raw_ref(value, path = [])
121
+ case value
122
+ when Hash
123
+ value.each do |key, entry|
124
+ normalized_key = key.respond_to?(:to_sym) ? key.to_sym : key
125
+ return (path + [normalized_key]).join(".") if RAW_REF_KEYS.include?(normalized_key)
126
+
127
+ nested = find_raw_ref(entry, path + [normalized_key])
128
+ return nested if nested
129
+ end
130
+ when Array
131
+ value.each_with_index do |entry, index|
132
+ nested = find_raw_ref(entry, path + [index])
133
+ return nested if nested
134
+ end
135
+ when String
136
+ return path.join(".") if value.start_with?(RAW_REF_PREFIX)
137
+ end
138
+
139
+ nil
140
+ end
141
+
142
+ def normalize_hash(value, name)
143
+ raise ArgumentError, "#{name} must be a hash" unless value.respond_to?(:to_h)
144
+
145
+ value.to_h.each_with_object({}) do |(key, entry), hash|
146
+ normalized_key = key.respond_to?(:to_sym) ? key.to_sym : key
147
+ hash[normalized_key] = normalize_value(entry)
148
+ end
149
+ end
150
+
151
+ def normalize_value(value)
152
+ case value
153
+ when Hash
154
+ normalize_hash(value, :value)
155
+ when Array
156
+ value.map { |entry| normalize_value(entry) }
157
+ else
158
+ value
159
+ end
160
+ end
161
+
162
+ def deep_freeze(value)
163
+ case value
164
+ when Array
165
+ value.map { |entry| deep_freeze(entry) }.freeze
166
+ when Hash
167
+ value.transform_values { |entry| deep_freeze(entry) }.freeze
168
+ else
169
+ value.freeze
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Lang
5
+ class MetadataCarrierManifest
6
+ KNOWN_SECTIONS = %i[
7
+ diagnostics
8
+ receipts
9
+ model_validity_reports
10
+ scenario_comparison_reports
11
+ review_receipts
12
+ ].freeze
13
+
14
+ CUSTOM_SECTIONS_KEY = :custom_sections
15
+
16
+ ENTRY_SEMANTICS = {
17
+ report_only: true,
18
+ runtime_enforced: false,
19
+ raw_ref_export: false
20
+ }.freeze
21
+
22
+ attr_reader :sections
23
+
24
+ def self.from_metadata(metadata)
25
+ new(sections: build_sections(metadata))
26
+ end
27
+
28
+ def initialize(sections: [])
29
+ @sections = sections.map { |entry| deep_freeze(entry) }.freeze
30
+ freeze
31
+ end
32
+
33
+ def empty?
34
+ sections.empty?
35
+ end
36
+
37
+ def to_h
38
+ {
39
+ sections: sections
40
+ }
41
+ end
42
+
43
+ class << self
44
+ private
45
+
46
+ def build_sections(metadata)
47
+ metadata_hash = metadata.to_h
48
+ known_sections = KNOWN_SECTIONS.filter_map do |section_name|
49
+ next unless metadata_hash.key?(section_name)
50
+
51
+ build_section(section_name, metadata_hash.fetch(section_name), custom: false)
52
+ end
53
+
54
+ custom_sections = build_custom_sections(metadata_hash.fetch(CUSTOM_SECTIONS_KEY, {}))
55
+ known_sections + custom_sections
56
+ end
57
+
58
+ def build_custom_sections(value)
59
+ return [] if value.nil? || value == {}
60
+ raise ArgumentError, "metadata.custom_sections must be a hash" unless value.is_a?(Hash)
61
+
62
+ value.to_h.map do |section_name, entries|
63
+ normalized_name = section_name.respond_to?(:to_sym) ? section_name.to_sym : section_name
64
+ raise ArgumentError, "metadata.custom_sections.#{normalized_name} duplicates a known section" if KNOWN_SECTIONS.include?(normalized_name)
65
+
66
+ build_section(normalized_name, entries, custom: true)
67
+ end
68
+ end
69
+
70
+ def build_section(section_name, value, custom:)
71
+ entries = normalize_section_entries(section_name, value)
72
+ {
73
+ section_name: section_name,
74
+ count: entries.length,
75
+ profile_names: profile_names(entries),
76
+ custom: custom
77
+ }.merge(ENTRY_SEMANTICS)
78
+ end
79
+
80
+ def normalize_section_entries(section_name, value)
81
+ raise ArgumentError, "metadata.#{section_name} must be an array of hashes" unless value.is_a?(Array)
82
+
83
+ value.each_with_index.map do |entry, index|
84
+ raise ArgumentError, "metadata.#{section_name}[#{index}] must be a hash" unless entry.respond_to?(:to_h)
85
+
86
+ entry.to_h
87
+ end
88
+ end
89
+
90
+ def profile_names(entries)
91
+ entries.filter_map do |entry|
92
+ profile = entry[:profile] || entry["profile"] || entry[:profile_name] || entry["profile_name"]
93
+ profile.to_s unless profile.nil?
94
+ end.uniq.freeze
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ def deep_freeze(value)
101
+ case value
102
+ when Array
103
+ value.map { |entry| deep_freeze(entry) }.freeze
104
+ when Hash
105
+ value.transform_values { |entry| deep_freeze(entry) }.freeze
106
+ else
107
+ value.freeze
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Lang
5
+ class MetadataManifest
6
+ SEMANTICS = {
7
+ report_only: true,
8
+ runtime_enforced: false
9
+ }.freeze
10
+
11
+ attr_reader :descriptors, :return_types, :budgets
12
+
13
+ def self.from_operations(operations)
14
+ new(
15
+ descriptors: extract_descriptors(operations),
16
+ return_types: extract_return_types(operations),
17
+ budgets: extract_budgets(operations)
18
+ )
19
+ end
20
+
21
+ def initialize(descriptors: [], return_types: [], budgets: [])
22
+ @descriptors = freeze_entries(descriptors)
23
+ @return_types = freeze_entries(return_types)
24
+ @budgets = freeze_entries(budgets)
25
+ freeze
26
+ end
27
+
28
+ def report_only?
29
+ true
30
+ end
31
+
32
+ def runtime_enforced?
33
+ false
34
+ end
35
+
36
+ def to_h
37
+ {
38
+ descriptors: descriptors,
39
+ return_types: return_types,
40
+ budgets: budgets,
41
+ stores: [],
42
+ invariants: [],
43
+ semantics: SEMANTICS
44
+ }
45
+ end
46
+
47
+ class << self
48
+ private
49
+
50
+ def extract_descriptors(operations)
51
+ operations.filter_map do |operation|
52
+ type = operation.attributes[:type]
53
+ next unless type.is_a?(Types::Descriptor)
54
+
55
+ {
56
+ node: operation.name,
57
+ kind: operation.kind,
58
+ type: serialize_value(type),
59
+ enforced: false
60
+ }
61
+ end
62
+ end
63
+
64
+ def extract_return_types(operations)
65
+ operations.filter_map do |operation|
66
+ next unless operation.attribute?(:return_type)
67
+
68
+ {
69
+ node: operation.name,
70
+ kind: operation.kind,
71
+ return_type: serialize_value(operation.attributes[:return_type]),
72
+ enforced: false
73
+ }
74
+ end
75
+ end
76
+
77
+ def extract_budgets(operations)
78
+ operations.filter_map do |operation|
79
+ deadline = operation.attributes[:deadline]
80
+ wcet = operation.attributes[:wcet]
81
+ next unless deadline || wcet
82
+
83
+ entry = {
84
+ node: operation.name,
85
+ kind: operation.kind,
86
+ enforced: false
87
+ }
88
+ entry[:deadline] = serialize_value(deadline) if deadline
89
+ entry[:wcet] = serialize_value(wcet) if wcet
90
+ entry
91
+ end
92
+ end
93
+
94
+ def serialize_value(value)
95
+ case value
96
+ when Types::Descriptor
97
+ value.to_h
98
+ when Module
99
+ value.name
100
+ when Array
101
+ value.map { |entry| serialize_value(entry) }
102
+ when Hash
103
+ value.to_h { |key, entry| [key.respond_to?(:to_sym) ? key.to_sym : key, serialize_value(entry)] }
104
+ else
105
+ value.respond_to?(:to_h) && !value.is_a?(Numeric) ? value.to_h : value
106
+ end
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def freeze_entries(entries)
113
+ entries.map { |entry| deep_freeze(entry) }.freeze
114
+ end
115
+
116
+ def deep_freeze(value)
117
+ case value
118
+ when Array
119
+ value.map { |entry| deep_freeze(entry) }.freeze
120
+ when Hash
121
+ value.transform_values { |entry| deep_freeze(entry) }.freeze
122
+ else
123
+ value.freeze
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end