use std::fmt::Write;
pub(super) const MAX_ENV_CHANGES_SUMMARY_LEN: usize = 512;
pub(super) fn truncate_env_changes_summary(
changes: &str,
max_len: usize,
change_count: usize,
) -> String {
if changes.len() <= max_len {
return changes.to_owned();
}
let preferred_cut = find_preferred_cut_point(changes, max_len);
let mut truncated: String = changes
.char_indices()
.take_while(|(idx, _)| *idx < preferred_cut)
.map(|(_, ch)| ch)
.collect();
let trimmed = truncated.trim_end();
truncated.truncate(trimmed.len());
let trimmed_no_trailing_comma = truncated.strip_suffix(',').unwrap_or(truncated.as_str());
let shown_changes = trimmed_no_trailing_comma
.chars()
.filter(|&c| c == ',')
.count()
+ 1;
append_truncation_suffix(&mut truncated, shown_changes, change_count);
truncated
}
fn find_preferred_cut_point(changes: &str, max_len: usize) -> usize {
let mut cut = 0usize;
let mut last_comma_before_cut = None;
for (idx, ch) in changes.char_indices() {
if idx >= max_len {
break;
}
cut = idx + ch.len_utf8();
if ch == ',' {
last_comma_before_cut = Some(cut);
}
}
last_comma_before_cut
.filter(|pos| pos.saturating_mul(2) > max_len)
.unwrap_or(cut)
}
fn append_truncation_suffix(truncated: &mut String, shown_changes: usize, change_count: usize) {
if change_count > shown_changes {
let remaining = change_count - shown_changes;
if !truncated.is_empty() && !truncated.ends_with(',') {
truncated.push_str(", ");
}
write_truncation_suffix(truncated, remaining);
} else if !truncated.ends_with("...") {
truncated.push_str("...");
}
}
fn write_truncation_suffix(truncated: &mut String, remaining: usize) {
let write_result = write!(truncated, "+ {remaining} more");
debug_assert!(
write_result.is_ok(),
"writing truncated summary should be infallible"
);
}