use super::*;
pub(super) fn bound_card(card: &mut ToolCard) {
bound_string(
&mut card.title,
256,
OmissionSubject::Title,
&mut card.omissions,
);
for text in &mut card.metadata {
bound_string(text, 512, OmissionSubject::Metadata, &mut card.omissions);
}
if card.metadata.len() > 8 {
record_omission(
&mut card.omissions,
OmissionSubject::Metadata,
OmissionReason::SizeLimit,
Some(card.metadata.len() - 8),
);
card.metadata.truncate(8);
}
if let Some(summary) = &mut card.public_protection.summary {
bound_string(summary, 256, OmissionSubject::Metadata, &mut card.omissions);
}
bound_string(
&mut card.body,
4096,
OmissionSubject::Text,
&mut card.omissions,
);
for block in &mut card.blocks {
match block {
ToolBlock::Text { lines } => {
if lines.len() > 32 {
record_omission(
&mut card.omissions,
OmissionSubject::Rows,
OmissionReason::SizeLimit,
Some(lines.len() - 32),
);
lines.truncate(32);
}
for line in lines {
bound_line(line, &mut card.omissions);
}
}
ToolBlock::Resources { rows } => {
if rows.len() > 32 {
record_omission(
&mut card.omissions,
OmissionSubject::Rows,
OmissionReason::SizeLimit,
Some(rows.len() - 32),
);
rows.truncate(32);
}
for row in rows {
bound_string(
&mut row.location,
512,
OmissionSubject::Metadata,
&mut card.omissions,
);
bound_line(&mut row.line, &mut card.omissions);
}
}
ToolBlock::Diff { lines, .. } => {
for line in lines {
bound_line(line, &mut card.omissions);
}
}
ToolBlock::Command {
command,
working_directory,
..
} => {
if let Some(command) = command {
bound_string(command, 4096, OmissionSubject::Text, &mut card.omissions);
}
if let Some(directory) = working_directory {
bound_string(
directory,
512,
OmissionSubject::Metadata,
&mut card.omissions,
);
}
}
ToolBlock::Results { rows } => {
for row in rows {
bound_string(
&mut row.label,
1024,
OmissionSubject::Text,
&mut card.omissions,
);
if let Some(value) = &mut row.value {
bound_string(value, 1024, OmissionSubject::Text, &mut card.omissions);
if value.contains("<redacted>") {
record_omission(
&mut card.omissions,
OmissionSubject::Text,
OmissionReason::Redacted,
None,
);
}
} else {
record_omission(
&mut card.omissions,
OmissionSubject::Rows,
OmissionReason::MissingSource,
None,
);
}
}
}
ToolBlock::Progress { label, .. } => {
bound_string(label, 1024, OmissionSubject::Text, &mut card.omissions)
}
}
}
if card.blocks.len() > 16 {
record_omission(
&mut card.omissions,
OmissionSubject::Blocks,
OmissionReason::SizeLimit,
Some(card.blocks.len() - 16),
);
card.blocks.truncate(16);
}
for row in &mut card.activity_rows {
bound_string(
&mut row.label,
256,
OmissionSubject::Children,
&mut card.omissions,
);
}
}
fn bound_line(line: &mut TextLine, omissions: &mut Vec<DisplayOmission>) {
let source = line.plain_text();
let mut text = source.clone();
bound_string(&mut text, 1024, OmissionSubject::Text, omissions);
if text != source {
let meaning = line
.spans
.first()
.map(|span| span.meaning)
.filter(|meaning| {
matches!(
meaning,
TextMeaning::Added | TextMeaning::Removed | TextMeaning::Metadata
)
})
.unwrap_or(TextMeaning::Plain);
*line = TextLine::from_span(text, meaning);
}
}
fn bound_string(
text: &mut String,
max_bytes: usize,
subject: OmissionSubject,
omissions: &mut Vec<DisplayOmission>,
) {
let safe = sanitize_display_text(text);
if safe != *text {
record_omission(omissions, subject.clone(), OmissionReason::Redacted, None);
*text = safe;
}
if text.len() <= max_bytes {
return;
}
const MARKER: &str = "… [truncated]";
let mut boundary = max_bytes.saturating_sub(MARKER.len());
while !text.is_char_boundary(boundary) {
boundary -= 1;
}
let omitted = text[boundary..].chars().count();
text.truncate(boundary);
text.push_str(MARKER);
record_omission(omissions, subject, OmissionReason::SizeLimit, Some(omitted));
}
pub(super) fn record_omission(
omissions: &mut Vec<DisplayOmission>,
subject: OmissionSubject,
reason: OmissionReason,
count: Option<usize>,
) {
if let Some(existing) = omissions
.iter_mut()
.find(|omission| omission.subject == subject)
{
existing.omitted_count = None;
if reason < existing.reason {
existing.reason = reason;
}
} else {
omissions.push(DisplayOmission {
subject,
reason,
omitted_count: count,
});
}
}