94 lines
3.0 KiB
Rust
94 lines
3.0 KiB
Rust
/// The default bound on a verbatim block -- a tool call's input or its
|
|
/// output. Short, because this text is a machine's and the reader is
|
|
/// looking for one line of it.
|
|
pub const VERBATIM_LINES: usize = 80;
|
|
pub const VERBATIM_BYTES: usize = 4096;
|
|
|
|
pub const MESSAGE_LINES: usize = 200;
|
|
pub const MESSAGE_BYTES: usize = 16 * 1024;
|
|
|
|
const _: () = assert!(VERBATIM_LINES > 0 && VERBATIM_BYTES > 0);
|
|
const _: () = assert!(MESSAGE_LINES > 0 && MESSAGE_BYTES > 0);
|
|
|
|
/// `text` cut to `max_lines` lines and `max_bytes` bytes, with the line
|
|
/// count it was cut *from*; `None` when the whole of it fits.
|
|
pub fn cut(text: &str, max_lines: usize, max_bytes: usize) -> Option<(&str, usize)> {
|
|
debug_assert!(
|
|
max_lines > 0 && max_bytes > 0,
|
|
"a cap of nothing shows an empty block and a 'Show all' for every value there is",
|
|
);
|
|
let by_lines = text
|
|
.char_indices()
|
|
.filter(|(_, c)| *c == '\n')
|
|
.nth(max_lines - 1)
|
|
.map(|(i, _)| i);
|
|
let by_bytes = (text.len() > max_bytes).then(|| {
|
|
let mut end = max_bytes;
|
|
// Back up to a character boundary: a cut inside a multi-byte
|
|
// character panics on the slice below, and a transcript is full of
|
|
// them.
|
|
while !text.is_char_boundary(end) {
|
|
end -= 1;
|
|
}
|
|
end
|
|
});
|
|
let cut = match (by_lines, by_bytes) {
|
|
(Some(a), Some(b)) => a.min(b),
|
|
(a, b) => a.or(b)?,
|
|
};
|
|
Some((&text[..cut], text.lines().count()))
|
|
}
|
|
|
|
pub fn show_all_label(lines: usize) -> String {
|
|
format!("Show all {lines} lines")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn text_under_both_bounds_is_not_cut() {
|
|
assert_eq!(cut("one\ntwo\nthree", 80, 4096), None);
|
|
}
|
|
|
|
#[test]
|
|
fn the_line_bound_cuts_at_a_line_boundary() {
|
|
let text = "a\nb\nc\nd\n";
|
|
let (shown, lines) = cut(text, 2, 4096).expect("four lines is over a bound of two");
|
|
assert_eq!(shown, "a\nb");
|
|
assert_eq!(
|
|
lines, 4,
|
|
"the count is the whole text's, not the shown part's"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn the_byte_bound_cuts_one_long_line() {
|
|
let text = "x".repeat(5000);
|
|
let (shown, lines) = cut(&text, 80, 4096).expect("5000 bytes is over a bound of 4096");
|
|
assert_eq!(shown.len(), 4096);
|
|
assert_eq!(lines, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn the_tighter_of_the_two_bounds_wins() {
|
|
let text = "aaaa\n".repeat(100);
|
|
let (shown, _) = cut(&text, 80, 100).expect("over both");
|
|
assert_eq!(shown.len(), 100, "the byte bound is the tighter one here");
|
|
let (shown, _) = cut(&text, 4, 4096).expect("over the line bound");
|
|
assert_eq!(shown, "aaaa\naaaa\naaaa\naaaa");
|
|
}
|
|
|
|
#[test]
|
|
fn a_cut_inside_a_multibyte_character_backs_up_to_the_boundary() {
|
|
let text = "é".repeat(100);
|
|
let (shown, _) = cut(&text, 80, 11).expect("200 bytes is over a bound of 11");
|
|
assert_eq!(
|
|
shown,
|
|
"é".repeat(5),
|
|
"11 bytes lands mid-character; 10 is the cut"
|
|
);
|
|
}
|
|
}
|