Schema-driven typed decoding of raw fact rows for in-process consumers.
Argus.Pipeline.Emit produces facts as lists of string fields — the right
shape for Souffle .facts files, but stringly for Elixir consumers. This
module decodes those rows against Argus.Schema: each row becomes a map
keyed by the schema's field names, with values decoded by field kind
(numbers and labels to integers, instruction IDs to Argus.InstrId
structs).
Decoding is strict: a row that doesn't match its relation's schema (wrong arity, non-numeric number, malformed instruction ID) raises — such a row can only come from a bug in an emitter or extractor, and surfacing it loudly at the decode boundary beats consumers silently dropping it. Relations not in the schema (custom extractor output) pass through undecoded.
Eventually emission itself may become typed, with stringification pushed to
the .facts writer; this decoder is the compatible first step.
Summary
Types
Interned rows: one tuple per row in schema field order, symbol-kinded
fields as Argus.Symbols ids and numeric fields as integers. Relations
the schema does not know hold their every field as a symbol.
Functions
Decode a raw facts map (relation => [[String.t()]]) into typed rows.
Decode interned rows to the same typed rows decode/1 produces from raw
ones. Instruction IDs come from the table's parse cache instead of being
parsed per row.
Intern raw rows (relation => [[String.t()]]) against symbols.
The raw rows behind interned ones, field for field.
Types
Interned rows: one tuple per row in schema field order, symbol-kinded
fields as Argus.Symbols ids and numeric fields as integers. Relations
the schema does not know hold their every field as a symbol.
Functions
Decode a raw facts map (relation => [[String.t()]]) into typed rows.
@spec decode(interned(), Argus.Symbols.t()) :: t()
Decode interned rows to the same typed rows decode/1 produces from raw
ones. Instruction IDs come from the table's parse cache instead of being
parsed per row.
@spec intern(%{required(atom()) => [[String.t()]]}, Argus.Symbols.t()) :: interned()
Intern raw rows (relation => [[String.t()]]) against symbols.
@spec materialize(interned(), Argus.Symbols.t()) :: %{ required(atom()) => [[String.t()]] }
The raw rows behind interned ones, field for field.