use std::path::Path;
pub(crate) fn stem(path: impl AsRef<Path>, fallback: &str) -> String {
path.as_ref()
.file_stem()
.map(|s| s.to_string_lossy().into_owned())
.unwrap_or_else(|| fallback.to_string())
}
const COMPACT_HEAD: usize = 4;
const COMPACT_TAIL: usize = 4;
const COMPACT_ELLIPSIS: &str = "...";
pub(crate) fn compact_long_strings(text: &str) -> String {
compact_long_strings_mapped(text).0
}
pub(crate) fn compact_long_strings_mapped(text: &str) -> (String, Vec<Vec<usize>>) {
let threshold = COMPACT_HEAD + COMPACT_TAIL + COMPACT_ELLIPSIS.chars().count();
let ellipsis_len = COMPACT_ELLIPSIS.chars().count();
let mut out = String::with_capacity(text.len());
let mut maps: Vec<Vec<usize>> = Vec::new();
let mut cur: Vec<usize> = Vec::new();
let mut full_col: usize = 0;
let mut chars = text.chars().peekable();
while let Some(c) = chars.next() {
if c == '\n' {
cur.push(full_col);
maps.push(std::mem::take(&mut cur));
out.push('\n');
full_col = 0;
continue;
}
if c != '"' {
cur.push(full_col);
out.push(c);
full_col += 1;
continue;
}
cur.push(full_col);
out.push('"');
full_col += 1;
let content_start = full_col;
let mut content: Vec<char> = Vec::new();
let mut closed = false;
while let Some(nc) = chars.next() {
if nc == '\\' {
content.push('\\');
if let Some(esc) = chars.next() {
content.push(esc);
}
continue;
}
if nc == '"' {
closed = true;
break;
}
content.push(nc);
}
if content.len() > threshold {
for k in 0..COMPACT_HEAD {
cur.push(content_start + k);
}
out.extend(&content[..COMPACT_HEAD]);
for _ in 0..ellipsis_len {
cur.push(content_start + COMPACT_HEAD);
}
out.push_str(COMPACT_ELLIPSIS);
let tail_start = content.len() - COMPACT_TAIL;
for k in 0..COMPACT_TAIL {
cur.push(content_start + tail_start + k);
}
out.extend(&content[tail_start..]);
} else {
for k in 0..content.len() {
cur.push(content_start + k);
}
out.extend(&content);
}
full_col = content_start + content.len();
if closed {
cur.push(full_col);
out.push('"');
full_col += 1;
}
}
cur.push(full_col);
maps.push(cur);
(out, maps)
}
#[cfg(test)]
mod tests {
use super::compact_long_strings;
#[test]
fn long_values_are_shortened_but_keys_and_short_values_are_not() {
let src = "{\n \"Key1\": \"anehusenhugroegureolegkregulregurcgeolrgulrecgulrogeulrcgeolrucg\",\n \"Key2\": \"short\"\n}";
let out = compact_long_strings(src);
assert!(out.contains("\"aneh...rucg\""));
assert!(out.contains("\"Key2\": \"short\""));
assert!(out.contains("\"Key1\""));
}
#[test]
fn head_and_tail_are_four_chars_each() {
let src = "\"0123456789abcdef\"";
assert_eq!(compact_long_strings(src), "\"0123...cdef\"");
}
#[test]
fn a_value_at_the_threshold_is_left_intact() {
let src = "\"12345678901\"";
assert_eq!(compact_long_strings(src), src);
}
#[test]
fn escaped_quotes_do_not_end_the_literal_early() {
let src = "\"aaaa\\\"bbbbbbbbbbbbbbbb\"";
let out = compact_long_strings(src);
assert!(out.starts_with('"') && out.ends_with('"'));
assert!(out.contains("..."));
assert_eq!(out.matches('"').count(), 2);
}
#[test]
fn non_string_structure_is_preserved() {
let src = "[1, 2, 3, true, null]";
assert_eq!(compact_long_strings(src), src);
}
#[test]
fn multibyte_content_does_not_panic_and_counts_by_char() {
let src = "\"ééééééééééééééééé\"";
let out = compact_long_strings(src);
assert!(out.contains("..."));
}
#[test]
fn the_map_expands_a_selected_compacted_literal_to_its_full_text() {
let full = "{\n \"k\": \"0123456789abcdef\"\n}";
let (compact, maps) = super::compact_long_strings_mapped(full);
assert_eq!(compact, compact_long_strings(full));
let comp_lines: Vec<&str> = compact.split('\n').collect();
assert_eq!(maps.len(), comp_lines.len());
let line = 1;
let comp_line = comp_lines[line];
let full_line: Vec<char> = full.split('\n').nth(line).unwrap().chars().collect();
let open = comp_line.find("\"0123").unwrap(); let close = comp_line.chars().count(); let (full_open, full_close) = (maps[line][open], maps[line][close]);
let extracted: String = full_line[full_open..full_close].iter().collect();
assert_eq!(extracted, "\"0123456789abcdef\"");
}
#[test]
fn per_line_maps_are_monotonic_and_sentinel_terminated() {
let full = "{\n \"k\": \"0123456789abcdef\",\n \"n\": 12\n}";
let (compact, maps) = super::compact_long_strings_mapped(full);
let comp_lines: Vec<&str> = compact.split('\n').collect();
let full_lines: Vec<&str> = full.split('\n').collect();
assert_eq!(maps.len(), comp_lines.len());
for (i, line_map) in maps.iter().enumerate() {
assert_eq!(line_map.len(), comp_lines[i].chars().count() + 1);
assert!(line_map.windows(2).all(|w| w[0] <= w[1]));
assert_eq!(*line_map.last().unwrap(), full_lines[i].chars().count());
}
}
}