Skip to main content

binocular/preview/binary/
strings.rs

1use crate::preview::doc::PreviewDoc;
2use std::fs;
3use std::path::Path;
4
5pub fn append_printable_strings(path: &Path, doc: &mut PreviewDoc) {
6    let Ok(mut file) = fs::File::open(path) else {
7        return;
8    };
9
10    let sample = super::read_prefix(&mut file, super::STRINGS_SAMPLE_BYTES);
11    let strings = extract_printable_strings(&sample, super::MIN_PRINTABLE_STRING_LEN);
12    if strings.is_empty() {
13        return;
14    }
15
16    doc.push_section(super::SECTION_PRINTABLE_STRINGS);
17
18    for (index, s) in strings
19        .iter()
20        .take(super::MAX_PRINTABLE_STRINGS_SHOWN)
21        .enumerate()
22    {
23        let text = truncate_for_preview(s, super::MAX_STRING_PREVIEW_LEN);
24        doc.push_indexed(index + 1, text);
25    }
26
27    if strings.len() > super::MAX_PRINTABLE_STRINGS_SHOWN {
28        doc.push_muted_italic(format!(
29            "   ... and {} more strings",
30            strings.len() - super::MAX_PRINTABLE_STRINGS_SHOWN
31        ));
32    }
33}
34
35pub fn extract_printable_strings(data: &[u8], min_length: usize) -> Vec<String> {
36    let mut strings = Vec::new();
37    let mut current = String::new();
38
39    for &byte in data {
40        if byte.is_ascii_graphic() || byte == b' ' {
41            current.push(byte as char);
42        } else {
43            if current.len() >= min_length {
44                strings.push(std::mem::take(&mut current));
45            }
46            current.clear();
47        }
48    }
49
50    if current.len() >= min_length {
51        strings.push(current);
52    }
53
54    strings
55}
56
57fn truncate_for_preview(text: &str, max_len: usize) -> String {
58    if text.len() > max_len {
59        format!("{}...", &text[..max_len])
60    } else {
61        text.to_string()
62    }
63}