///|
fn record(fields : Array[(String, @ion.IonValue)]) -> @ion.IonValue {
@ion.Ion(
@ion.IonValueKind::Struct(fields.map(pair => @ion.field(pair.0, pair.1))),
)
}
///|
fn ion_text(text : String) -> @ion.IonValue {
@ion.Ion(@ion.IonValueKind::String(text))
}
///|
fn texts(items : Array[String]) -> @ion.IonValue {
@ion.Ion(@ion.IonValueKind::List(items.map(ion_text)))
}
///|
fn symbol_metadata(
input : Input,
source_ir : String,
symbols : Array[Symbol],
owned : Map[String, Bool],
names : TargetNames,
error_contract : ErrorContract,
) -> Bytes raise {
let metadata = input.metadata.copy()
metadata.sort_by(fn(a, b) { a.node_id.compare(b.node_id) })
let seen = Map([])
for entry in metadata {
if !owned.contains(entry.node_id) || seen.contains(entry.node_id) {
raise Invalid(
"moonbit.metadata_identity",
entry.node_id,
"Unknown or duplicate source node identity",
)
}
seen[entry.node_id] = true
}
let value = record([
("profile", ion_text("morphir-moonbit-symbols-2026-10-17")),
("generated_error_type", ion_text(error_contract.source())),
(
"naming_policy",
ion_text(
if names.style == Readable {
NAMING_POLICY
} else {
"moonbit-legacy-hex-v1"
},
),
),
("warnings", @ion.IonValue::list(names.warnings.map(w => w.to_ion()))),
(
"naming_allocations",
@ion.IonValue::list(
names.allocations.map(a => {
record([
("owner", ion_text(a.owner)),
("role", ion_text(a.role)),
(
"original",
a.original
.map(ion_text)
.unwrap_or(@ion.Ion(@ion.IonValueKind::Null(@ion.Null))),
),
(
"requested",
a.requested
.map(ion_text)
.unwrap_or(@ion.Ion(@ion.IonValueKind::Null(@ion.Null))),
),
("candidate", ion_text(a.candidate)),
("allocated", ion_text(a.allocated)),
("reason", ion_text(a.reason)),
])
}),
),
),
("unvalidated_predicates", texts(names.facts.unvalidated_predicates)),
(
"consumed_naming_facts",
@ion.IonValue::list(
names.facts.facts.map(f => {
record([
("owner", ion_text(f.owner)),
("provider_revision", ion_text(f.provider_revision)),
(
"assertions",
@ion.IonValue::list(
f.assertions.map(a => {
record([
("holder", ion_text(a.holder)),
("document", ion_text(a.document)),
("carrier", ion_text(a.carrier)),
("subject", ion_text(a.subject)),
("predicate", ion_text(a.predicate)),
("property", ion_text(a.property)),
("ordinal", @ion.IonValue::int(a.ordinal)),
("object_ordinal", @ion.IonValue::int(a.object_ordinal)),
])
}),
),
),
])
}),
),
),
("source_provenance", @ion.IonValue::list(input.provenance)),
("unit_id", ion_text(input.unit_id)),
("source_ir_json", ion_text(source_ir)),
(
"source_annotations",
@ion.Ion(
@ion.IonValueKind::List(
input.annotations.map(token => {
@ion.Ion(@ion.IonValueKind::Symbol(token))
}),
),
),
),
(
"metadata",
@ion.Ion(
@ion.IonValueKind::List(
metadata.map(entry => {
record([("node_id", ion_text(entry.node_id)), ("data", entry.data)])
}),
),
),
),
(
"symbols",
@ion.Ion(
@ion.IonValueKind::List(
symbols.map(symbol => {
record([
("fqname", ion_text(symbol.fqname)),
("generated_name", ion_text(symbol.generated_name)),
("public", @ion.Ion(@ion.IonValueKind::Bool(symbol.public))),
("source_node", ion_text(symbol.source_node)),
("generated_node", ion_text(symbol.generated_node)),
("origins", texts(symbol.origins)),
])
}),
),
),
),
]).with_annotation(@ion.symbol("morphir_moonbit_symbols"))
@ion_binary.write_binary_value(value)
}