binocular/preview/binary/
strings.rs1use 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}