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.
- checksums.yaml +7 -0
- data/README.md +239 -0
- data/lib/igniter/contracts/api.rb +92 -0
- data/lib/igniter/contracts/assembly/baseline_pack.rb +141 -0
- data/lib/igniter/contracts/assembly/const_pack.rb +29 -0
- data/lib/igniter/contracts/assembly/dsl_keyword.rb +21 -0
- data/lib/igniter/contracts/assembly/hook_result_policies.rb +47 -0
- data/lib/igniter/contracts/assembly/hook_spec.rb +73 -0
- data/lib/igniter/contracts/assembly/hook_specs.rb +74 -0
- data/lib/igniter/contracts/assembly/kernel.rb +220 -0
- data/lib/igniter/contracts/assembly/node_type.rb +26 -0
- data/lib/igniter/contracts/assembly/ordered_registry.rb +55 -0
- data/lib/igniter/contracts/assembly/pack.rb +13 -0
- data/lib/igniter/contracts/assembly/pack_manifest.rb +131 -0
- data/lib/igniter/contracts/assembly/path_access.rb +76 -0
- data/lib/igniter/contracts/assembly/profile.rb +133 -0
- data/lib/igniter/contracts/assembly/project_pack.rb +42 -0
- data/lib/igniter/contracts/assembly/registry.rb +57 -0
- data/lib/igniter/contracts/assembly/step_result_pack.rb +42 -0
- data/lib/igniter/contracts/assembly.rb +18 -0
- data/lib/igniter/contracts/contract.rb +135 -0
- data/lib/igniter/contracts/contractable.rb +288 -0
- data/lib/igniter/contracts/environment.rb +51 -0
- data/lib/igniter/contracts/errors.rb +47 -0
- data/lib/igniter/contracts/execution/baseline_normalizers.rb +23 -0
- data/lib/igniter/contracts/execution/baseline_runtime.rb +55 -0
- data/lib/igniter/contracts/execution/baseline_validators.rb +113 -0
- data/lib/igniter/contracts/execution/builder.rb +43 -0
- data/lib/igniter/contracts/execution/compilation_report.rb +46 -0
- data/lib/igniter/contracts/execution/compiled_graph.rb +21 -0
- data/lib/igniter/contracts/execution/compiler.rb +66 -0
- data/lib/igniter/contracts/execution/const_runtime.rb +15 -0
- data/lib/igniter/contracts/execution/diagnostics.rb +24 -0
- data/lib/igniter/contracts/execution/diagnostics_report.rb +40 -0
- data/lib/igniter/contracts/execution/diagnostics_section.rb +37 -0
- data/lib/igniter/contracts/execution/effect_invocation.rb +26 -0
- data/lib/igniter/contracts/execution/execution_request.rb +28 -0
- data/lib/igniter/contracts/execution/execution_result.rb +32 -0
- data/lib/igniter/contracts/execution/inline_executor.rb +19 -0
- data/lib/igniter/contracts/execution/mutable_named_values.rb +52 -0
- data/lib/igniter/contracts/execution/named_values.rb +48 -0
- data/lib/igniter/contracts/execution/operation.rb +42 -0
- data/lib/igniter/contracts/execution/runtime.rb +43 -0
- data/lib/igniter/contracts/execution/step_result.rb +51 -0
- data/lib/igniter/contracts/execution/step_result_diagnostics.rb +35 -0
- data/lib/igniter/contracts/execution/step_result_runtime.rb +51 -0
- data/lib/igniter/contracts/execution/step_result_validators.rb +44 -0
- data/lib/igniter/contracts/execution/structured_dump.rb +49 -0
- data/lib/igniter/contracts/execution/validation_finding.rb +28 -0
- data/lib/igniter/contracts/execution/validation_report.rb +46 -0
- data/lib/igniter/contracts/execution.rb +28 -0
- data/lib/igniter/contracts.rb +54 -0
- data/lib/igniter/lang/backend.rb +19 -0
- data/lib/igniter/lang/backends/ruby.rb +42 -0
- data/lib/igniter/lang/diagnostic_payload.rb +174 -0
- data/lib/igniter/lang/metadata_carrier_manifest.rb +112 -0
- data/lib/igniter/lang/metadata_manifest.rb +128 -0
- data/lib/igniter/lang/receipt_payload.rb +152 -0
- data/lib/igniter/lang/schema_compatibility_diagnostic.rb +300 -0
- data/lib/igniter/lang/types.rb +84 -0
- data/lib/igniter/lang/verification_report.rb +226 -0
- data/lib/igniter/lang.rb +27 -0
- data/lib/igniter-contracts.rb +3 -0
- 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
|