use std::fmt::Write;
#[derive(Debug, Default)]
pub(crate) struct ErrorFormatScratch {
pub(crate) buf: String,
}
impl ErrorFormatScratch {
#[cold]
pub(crate) fn finish(&mut self) -> String {
std::mem::take(&mut self.buf)
}
}
#[cold]
pub(crate) fn write_dispatch_byte_len_mismatch(
scratch: &mut ErrorFormatScratch,
name: &str,
got: usize,
required: usize,
) -> String {
scratch.buf.clear();
let _ = write!(
scratch.buf,
"{name} output has malformed byte length: got {got} bytes, expected exactly {required}. Fix: backend must emit the declared u32 output size and no trailing bytes."
);
scratch.finish()
}
#[cold]
pub(crate) fn write_scalar_byte_len_mismatch(
scratch: &mut ErrorFormatScratch,
name: &str,
got: usize,
) -> String {
scratch.buf.clear();
let _ = write!(
scratch.buf,
"{name} output has malformed byte length: got {got} bytes, expected exactly 4. Fix: backend must emit the declared scalar u32 output size and no trailing bytes."
);
scratch.finish()
}
#[cold]
pub(crate) fn write_fixed_point_layout_mismatch(
scratch: &mut ErrorFormatScratch,
consumer: &str,
resident_hash: u64,
prepared_hash: u64,
) -> String {
scratch.buf.clear();
let _ = write!(
scratch.buf,
"{consumer} received resident graph layout hash {resident_hash} but prepared graph layout hash {prepared_hash}. Fix: keep the resident graph and sequence-window plan from the same FixedPointAnalysisPlan."
);
scratch.finish()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scratch_default_is_empty() {
let scratch = ErrorFormatScratch::default();
assert!(scratch.buf.is_empty());
}
#[test]
fn finish_returns_empty_string_when_empty() {
let mut scratch = ErrorFormatScratch::default();
let s = scratch.finish();
assert_eq!(s, "");
}
#[test]
fn finish_clears_buf() {
let mut scratch = ErrorFormatScratch::default();
scratch.buf.push_str("leftover");
let _ = scratch.finish();
assert!(scratch.buf.is_empty());
}
#[test]
fn finish_moves_content() {
let mut scratch = ErrorFormatScratch::default();
scratch.buf.push_str("content");
let s = scratch.finish();
assert_eq!(s, "content");
}
#[test]
fn dispatch_byte_len_basic() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_dispatch_byte_len_mismatch(&mut scratch, "MyOp", 8, 16);
assert!(msg.contains("MyOp"));
assert!(msg.contains("8 bytes"));
assert!(msg.contains("16"));
assert!(msg.contains("malformed byte length"));
}
#[test]
fn dispatch_byte_len_zero_got() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_dispatch_byte_len_mismatch(&mut scratch, "EmptyOp", 0, 4);
assert!(msg.contains("0 bytes"));
assert!(msg.contains("expected exactly 4"));
}
#[test]
fn dispatch_byte_len_zero_required() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_dispatch_byte_len_mismatch(&mut scratch, "NoOutput", 8, 0);
assert!(msg.contains("got 8 bytes"));
assert!(msg.contains("expected exactly 0"));
}
#[test]
fn dispatch_byte_len_large_values() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_dispatch_byte_len_mismatch(&mut scratch, "BigOp", usize::MAX, 1_000_000);
assert!(msg.contains(&usize::MAX.to_string()));
assert!(msg.contains("1000000"));
}
#[test]
fn dispatch_byte_len_empty_name() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_dispatch_byte_len_mismatch(&mut scratch, "", 1, 2);
assert!(msg.contains(" output has malformed byte length"));
}
#[test]
fn dispatch_byte_len_long_name() {
let mut scratch = ErrorFormatScratch::default();
let long = "a".repeat(500);
let msg = write_dispatch_byte_len_mismatch(&mut scratch, &long, 1, 2);
assert!(msg.contains(&long));
assert!(msg.contains("malformed byte length"));
}
#[test]
fn dispatch_byte_len_fix_message_present() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_dispatch_byte_len_mismatch(&mut scratch, "X", 1, 2);
assert!(msg.contains(
"Fix: backend must emit the declared u32 output size and no trailing bytes."
));
}
#[test]
fn dispatch_byte_len_same_got_and_required() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_dispatch_byte_len_mismatch(&mut scratch, "Same", 8, 8);
assert!(msg.contains("got 8 bytes"));
assert!(msg.contains("expected exactly 8"));
}
#[test]
fn scalar_byte_len_basic() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_scalar_byte_len_mismatch(&mut scratch, "ScalarOp", 8);
assert!(msg.contains("ScalarOp"));
assert!(msg.contains("8 bytes"));
assert!(msg.contains("expected exactly 4"));
}
#[test]
fn scalar_byte_len_zero() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_scalar_byte_len_mismatch(&mut scratch, "ZeroOp", 0);
assert!(msg.contains("0 bytes"));
}
#[test]
fn scalar_byte_len_exactly_four() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_scalar_byte_len_mismatch(&mut scratch, "CorrectOp", 4);
assert!(msg.contains("got 4 bytes"));
assert!(msg.contains("expected exactly 4"));
}
#[test]
fn scalar_byte_len_large() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_scalar_byte_len_mismatch(&mut scratch, "HugeOp", 1_000_000_000);
assert!(msg.contains("1000000000 bytes"));
}
#[test]
fn scalar_byte_len_empty_name() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_scalar_byte_len_mismatch(&mut scratch, "", 99);
assert!(msg.contains(" output has malformed byte length"));
}
#[test]
fn scalar_byte_len_fix_message_present() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_scalar_byte_len_mismatch(&mut scratch, "Y", 7);
assert!(msg.contains(
"Fix: backend must emit the declared scalar u32 output size and no trailing bytes."
));
}
#[test]
fn scalar_byte_len_unicode_name() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_scalar_byte_len_mismatch(&mut scratch, "αβγ", 8);
assert!(msg.contains("αβγ"));
}
#[test]
fn layout_mismatch_basic() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_fixed_point_layout_mismatch(&mut scratch, "ConsumerA", 0x1234, 0x5678);
assert!(msg.contains("ConsumerA"));
assert!(msg.contains("4660")); assert!(msg.contains("22136")); }
#[test]
fn layout_mismatch_zero_hashes() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_fixed_point_layout_mismatch(&mut scratch, "ZeroConsumer", 0, 0);
assert!(msg.contains("0"));
assert!(msg.contains("Fix: keep the resident graph"));
}
#[test]
fn layout_mismatch_max_hashes() {
let mut scratch = ErrorFormatScratch::default();
let msg =
write_fixed_point_layout_mismatch(&mut scratch, "MaxConsumer", u64::MAX, u64::MAX);
assert!(msg.contains(&u64::MAX.to_string()));
}
#[test]
fn layout_mismatch_empty_consumer() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_fixed_point_layout_mismatch(&mut scratch, "", 1, 2);
assert!(msg.contains(" received resident graph layout hash "));
}
#[test]
fn layout_mismatch_fix_message_present() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_fixed_point_layout_mismatch(&mut scratch, "C", 1, 2);
assert!(msg.contains("Fix: keep the resident graph and sequence-window plan from the same FixedPointAnalysisPlan."));
}
#[test]
fn layout_mismatch_different_hashes() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_fixed_point_layout_mismatch(&mut scratch, "Diff", 42, 43);
assert!(msg.contains("42"));
assert!(msg.contains("43"));
}
#[test]
fn scratch_reuse_dispatch_then_scalar() {
let mut scratch = ErrorFormatScratch::default();
let msg1 = write_dispatch_byte_len_mismatch(&mut scratch, "Op1", 1, 2);
let msg2 = write_scalar_byte_len_mismatch(&mut scratch, "Op2", 3);
assert!(msg1.contains("Op1"));
assert!(!msg1.contains("Op2"));
assert!(msg2.contains("Op2"));
assert!(!msg2.contains("Op1"));
}
#[test]
fn scratch_reuse_all_three() {
let mut scratch = ErrorFormatScratch::default();
let m1 = write_dispatch_byte_len_mismatch(&mut scratch, "A", 1, 2);
let m2 = write_scalar_byte_len_mismatch(&mut scratch, "B", 3);
let m3 = write_fixed_point_layout_mismatch(&mut scratch, "C", 4, 5);
assert!(m1.contains("A"));
assert!(m2.contains("B"));
assert!(m3.contains("C"));
assert!(m1.contains("malformed byte length"));
assert!(m2.contains("expected exactly 4"));
assert!(m3.contains("layout hash"));
}
#[test]
fn scratch_does_not_leak_previous_content() {
let mut scratch = ErrorFormatScratch::default();
scratch.buf.push_str("pollution");
let msg = write_scalar_byte_len_mismatch(&mut scratch, "Clean", 8);
assert!(!msg.contains("pollution"));
assert!(msg.contains("Clean"));
}
#[test]
fn dispatch_byte_len_snapshot() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_dispatch_byte_len_mismatch(&mut scratch, "TestDispatch", 12, 24);
assert_eq!(
msg,
"TestDispatch output has malformed byte length: got 12 bytes, expected exactly 24. Fix: backend must emit the declared u32 output size and no trailing bytes."
);
}
#[test]
fn scalar_byte_len_snapshot() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_scalar_byte_len_mismatch(&mut scratch, "TestScalar", 7);
assert_eq!(
msg,
"TestScalar output has malformed byte length: got 7 bytes, expected exactly 4. Fix: backend must emit the declared scalar u32 output size and no trailing bytes."
);
}
#[test]
fn layout_mismatch_snapshot() {
let mut scratch = ErrorFormatScratch::default();
let msg = write_fixed_point_layout_mismatch(&mut scratch, "TestConsumer", 12345, 67890);
assert_eq!(
msg,
"TestConsumer received resident graph layout hash 12345 but prepared graph layout hash 67890. Fix: keep the resident graph and sequence-window plan from the same FixedPointAnalysisPlan."
);
}
}