use std::sync::Arc;
use parking_lot::Mutex;
#[derive(Debug, Default, Clone)]
pub(crate) struct BoundedOutput {
pub(crate) bytes: Vec<u8>,
pub(crate) truncated: bool,
}
impl BoundedOutput {
pub(crate) fn push(&mut self, chunk: &[u8], max_bytes: Option<usize>) {
let Some(limit) = max_bytes else {
self.bytes.extend_from_slice(chunk);
return;
};
let remaining = limit.saturating_sub(self.bytes.len());
let kept = remaining.min(chunk.len());
self.bytes.extend_from_slice(&chunk[..kept]);
if kept < chunk.len() {
self.truncated = true;
}
}
}
pub(crate) type SharedOutput = Arc<Mutex<BoundedOutput>>;
pub(crate) fn shared_output() -> SharedOutput {
Arc::new(Mutex::new(BoundedOutput::default()))
}
pub(crate) fn take_shared(shared: &SharedOutput) -> BoundedOutput {
shared.lock().clone()
}
pub(crate) fn append_line_bounded(
dst: &mut Vec<u8>,
extra: &[u8],
max_bytes: Option<usize>,
) -> bool {
let Some(limit) = max_bytes else {
if !dst.is_empty() {
dst.push(b'\n');
}
dst.extend_from_slice(extra);
return false;
};
if dst.len() >= limit {
return true;
}
if !dst.is_empty() && dst.len() + 1 < limit {
dst.push(b'\n');
}
let remaining = limit.saturating_sub(dst.len());
if extra.len() > remaining {
dst.extend_from_slice(&extra[..remaining]);
true
} else {
dst.extend_from_slice(extra);
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_retains_up_to_the_bound_and_flags_truncation() {
let mut output = BoundedOutput::default();
output.push(b"hello", Some(7));
output.push(b" world", Some(7));
assert_eq!(output.bytes, b"hello w");
assert!(output.truncated);
let mut unbounded = BoundedOutput::default();
unbounded.push(b"abcdef", None);
assert_eq!(unbounded.bytes, b"abcdef");
assert!(!unbounded.truncated);
let mut zero = BoundedOutput::default();
zero.push(b"abc", Some(0));
assert!(zero.bytes.is_empty());
assert!(zero.truncated);
}
#[test]
fn append_line_bounded_preserves_separator_and_reports_truncation() {
let mut unbounded = b"err".to_vec();
assert!(!append_line_bounded(&mut unbounded, b"tail", None));
assert_eq!(unbounded, b"err\ntail");
let mut full = b"abc".to_vec();
assert!(append_line_bounded(&mut full, b"tail", Some(3)));
assert_eq!(full, b"abc");
let mut partial = b"a".to_vec();
assert!(append_line_bounded(&mut partial, b"bcdef", Some(4)));
assert_eq!(partial, b"a\nbc");
let mut fits = b"a".to_vec();
assert!(!append_line_bounded(&mut fits, b"b", Some(4)));
assert_eq!(fits, b"a\nb");
}
#[test]
fn take_shared_snapshots_without_consuming() {
let shared = shared_output();
shared.lock().push(b"data", Some(16));
let snapshot = take_shared(&shared);
assert_eq!(snapshot.bytes, b"data");
assert!(!snapshot.truncated);
assert_eq!(shared.lock().bytes, b"data");
}
}