///|
fn string_dict(tpe : @ir.Type) -> Bool {
guard tpe is Reference(_, name, args) &&
name.to_canonical() == "morphir/SDK:dict#dict" &&
args.length() == 2 else {
return false
}
args.all(t => {
t is Reference(_, n, ps) &&
n.to_canonical() == "morphir/SDK:string#string" &&
ps.is_empty()
})
}
///|
fn validate_provider(
predicate : String,
restored : RestoredLibrary,
capabilities : Capabilities,
) -> String raise {
if !digest(restored.revision) || restored.format != "4.1.0" {
raise Invalid(
"metadata.invalid_provider_identity", predicate, "Restore the exact authenticated morphir/naming 4.1.0 Library and supply its SHA-256 revision",
)
}
let native = address(restored.document)
if native.package_name.to_canonical() != "morphir/naming" ||
native.format != restored.format ||
native.fragment != "/distribution" ||
native.revision != Some("sha256:" + restored.revision) {
raise Invalid(
"metadata.invalid_provider_identity", predicate, "Provider document must identify the freshly restored, revision-pinned Library",
)
}
let (_, requested) = declaration(predicate)
if requested.revision is Some(r) && r != "sha256:" + restored.revision {
raise Invalid(
"metadata.provider_revision_mismatch", predicate, "Restore the revision requested by this predicate URI",
)
}
guard restored.core.distribution is Library(package_name, _, definition) &&
package_name.to_canonical() == "morphir/naming" else {
raise Invalid(
"metadata.invalid_provider_declaration", predicate, "The provider must be a Library with public value definitions, not Specs",
)
}
guard definition.modules.get(@ir.Path::parse("naming")) is Some(module_def) &&
module_def.access == @ir.Access::Public else {
raise Invalid(
"metadata.invalid_provider_declaration", predicate, "Expose public module naming",
)
}
guard module_def.value.values.get(@ir.Name::parse("target-names"))
is Some(entry) &&
entry.access == @ir.Access::Public else {
raise Invalid(
"metadata.invalid_provider_declaration", predicate, "Expose a public target-names value definition",
)
}
guard entry.value.value is ExpressionBody(_, output, _) &&
output is Record(_, members) &&
members.length() == 2 else {
raise Invalid(
"metadata.invalid_provider_type", predicate, "Declare the closed frontend/backend output record",
)
}
for name in ["frontend", "backend"] {
guard members.get(@ir.Name::parse(name)) is Some(tpe) && string_dict(tpe) else {
raise Invalid(
"metadata.invalid_provider_type", predicate, "Both fields must be Dict String String",
)
}
}
let context = scoped_context(
restored.document_metadata,
Context::new(),
capabilities,
restored.document,
)
let facts = graph(
restored.document_metadata,
context,
"provider",
restored.document,
)
let declarations = facts.filter(f => {
f.assertion.subject == TARGET_NAMES_PREDICATE ||
f.assertion.subject ==
TARGET_NAMES_PREDICATE.split("#").collect()[0].to_owned() +
"&rev=sha256:" +
restored.revision +
"#/module/naming/value/target-names"
})
let stem = "morphir://ir/pkg/morphir/metadata?format=4.1.0#/module/schema/value/"
fn literals(predicate : String) -> Array[String] raise {
declarations
.filter(f => f.assertion.predicate == stem + predicate)
.map(f => {
guard f.data is Some(v) && !f.is_json else {
raise Invalid(
"metadata.invalid_provider_declaration", predicate, "Schema declarations use literal strings",
)
}
text(v, predicate)
})
}
let roles = literals("subject-role")
let forms = literals("object-form")
let interpreters = literals("interpreter")
if roles.is_empty() ||
!roles.all(r => r == "ValueDefinition") ||
forms.is_empty() ||
!forms.all(f => f == "data") ||
interpreters.is_empty() ||
!interpreters.all(i => i == "target-name-language-ids") {
raise Invalid(
"metadata.invalid_provider_declaration", predicate, "Declare ValueDefinition, data and target-name-language-ids in the restored provider's own native facts",
)
}
restored.revision
}