use alloc::vec::Vec as StdVec;
use vize_l0::id::NodeId;
use vize_l0::{Span, String};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TextPart {
pub text: String,
pub span: Span,
pub dynamic: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TextParts {
pub parts: StdVec<TextPart>,
}
impl TextParts {
#[doc(hidden)]
#[must_use]
pub fn dynamic_count(&self) -> usize {
self.parts.iter().filter(|part| part.dynamic).count()
}
#[doc(hidden)]
pub fn assert_compound_laws(&self, id: NodeId, span: Span, source: &str) {
let dynamic = self.dynamic_count();
assert!(
self.parts.len() >= 2 && dynamic >= 1,
"compound law broken: op {id} records {} parts ({dynamic} dynamic) — a lone node never merges",
self.parts.len(),
);
assert!(
self.parts
.windows(2)
.all(|pair| pair.iter().any(|part| part.dynamic)),
"compound law broken: op {id} holds adjacent static parts — the lowering fuses them",
);
let mut cursor = span.start;
for part in &self.parts {
assert!(
part.span.start == cursor,
"compound law broken: op {id}'s parts do not tile its span (gap at {cursor})",
);
cursor = part.span.end;
}
assert!(
cursor == span.end,
"compound law broken: op {id}'s parts end at {cursor}, its span at {}",
span.end,
);
let rebuilt = rebuild_source(&self.parts);
assert!(
rebuilt.as_str() == source,
"compound law broken: op {id}'s source {:?} does not rebuild from its parts {:?}",
source,
rebuilt,
);
}
}
const _: () = {
const fn assert_owned<T: 'static>() {}
assert_owned::<TextPart>();
assert_owned::<TextParts>();
};
#[cfg(target_pointer_width = "64")]
const _: () = {
assert!(core::mem::size_of::<TextPart>() == 40);
assert!(core::mem::size_of::<TextParts>() == 24);
};
pub fn rebuild_source(parts: &[TextPart]) -> String {
let capacity = (parts.iter())
.map(|part| part.text.len() + if part.dynamic { 6 } else { 0 })
.sum();
let mut out = String::with_capacity(capacity);
for part in parts {
if part.dynamic {
out.push_str("{{ ");
out.push_str(part.text.as_str());
out.push_str(" }}");
} else {
out.push_str(part.text.as_str());
}
}
out
}