use alloc::vec::Vec;
use core::fmt;
use vize_l0::dump::value::DumpValue;
use vize_l0::dump::{Dump, Error as DumpError};
use vize_l0::{Span, String, cstr};
use crate::dump::parse::{tail_span_value, take_quoted_value};
use crate::dump::print::quoted;
use crate::provenance::ProvenanceRecord;
#[derive(Debug, Clone, Default, PartialEq, Eq, Dump)]
#[dump(name = "l2-provenance-dump-v2")]
pub struct Page {
pub records: Vec<Record>,
}
impl Page {
#[must_use]
pub fn of(records: &[ProvenanceRecord]) -> Self {
Self {
records: records.iter().map(Record::from).collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Record {
pub rule: String,
pub node: Option<u32>,
pub before: String,
pub after: String,
pub span: Span,
}
impl From<&ProvenanceRecord> for Record {
fn from(record: &ProvenanceRecord) -> Self {
Self {
rule: record.rule.clone(),
node: record.node.map(|node| node.index()),
before: record.before.clone(),
after: record.after.clone(),
span: record.span,
}
}
}
impl DumpValue for Record {
fn print_value<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
write!(w, "rule={} node=", self.rule)?;
match self.node {
Some(node) => write!(w, "{node}")?,
None => w.write_char('-')?,
}
w.write_str(" before=")?;
quoted(w, &self.before)?;
w.write_str(" after=")?;
quoted(w, &self.after)?;
write!(w, " @{}:{}", self.span.start, self.span.end)
}
fn parse_value(text: &str, line: usize) -> Result<Self, DumpError> {
let rest = expect(text, "rule=", line)?;
let (rule, rest) = rest
.split_once(' ')
.filter(|(rule, _)| !rule.is_empty())
.ok_or_else(|| DumpError::new(line, cstr!("expected `rule=<name> `")))?;
let rest = expect(rest, "node=", line)?;
let (node, rest) = rest
.split_once(' ')
.ok_or_else(|| DumpError::new(line, cstr!("expected `node=<id|-> `")))?;
let node = match node {
"-" => None,
id => Some(
id.parse()
.map_err(|_| DumpError::new(line, cstr!("invalid node id `{id}`")))?,
),
};
let (before, rest) = take_quoted_value(expect(rest, "before=", line)?, line)?;
let (after, rest) = take_quoted_value(expect(rest, " after=", line)?, line)?;
let span = tail_span_value(rest, line)?;
Ok(Self {
rule: String::from(rule),
node,
before,
after,
span,
})
}
}
fn expect<'a>(text: &'a str, prefix: &str, line: usize) -> Result<&'a str, DumpError> {
text.strip_prefix(prefix)
.ok_or_else(|| DumpError::new(line, cstr!("expected `{prefix}`")))
}