1use std::path::Path;
17
18use crate::app::state::{Entry, EntryKind};
19use crate::preview::provider::{PreviewContent, PreviewCtx, PreviewOutcome, PreviewProvider};
20
21const IMAGE_EXTENSIONS: &[&str] = &[
26 "png", "jpg", "jpeg", "gif", "bmp", "ico", "tiff", "tif", "webp", "avif", "svg",
27];
28
29pub struct BinaryProvider;
35
36impl PreviewProvider for BinaryProvider {
37 fn can_handle(&self, entry: &Entry) -> bool {
38 if entry.kind != EntryKind::File {
39 return false;
40 }
41 if is_image_path(&entry.path) {
43 return false;
44 }
45 entry.is_text == Some(false)
50 }
51
52 fn preview(&self, entry: &Entry, _ctx: &PreviewCtx) -> PreviewOutcome {
53 let content = build_binary_preview(&entry.path, entry.metadata.as_ref());
54 PreviewOutcome::Ready(content)
55 }
56}
57
58fn is_image_path(path: &Path) -> bool {
60 path.extension()
61 .and_then(|e| e.to_str())
62 .map(|ext| IMAGE_EXTENSIONS.contains(&ext.to_lowercase().as_str()))
63 .unwrap_or(false)
64}
65
66pub fn build_binary_preview(path: &Path, metadata: Option<&std::fs::Metadata>) -> PreviewContent {
72 let owned;
73 let meta: &std::fs::Metadata = match metadata {
74 Some(m) => m,
75 None => {
76 owned = match std::fs::metadata(path) {
77 Ok(m) => m,
78 Err(e) => {
79 return PreviewContent::Binary(vec![format!(" Cannot read metadata: {e}")])
80 }
81 };
82 &owned
83 }
84 };
85
86 let size_str = crate::metafmt::size(meta.len());
89
90 let ext = path
91 .extension()
92 .and_then(|e| e.to_str())
93 .unwrap_or("unknown")
94 .to_uppercase();
95
96 let modified = crate::metafmt::modified(meta.modified().ok());
97
98 let mut lines = vec![
99 format!(" Type : {} binary", ext),
100 format!(" Size : {}", size_str),
101 format!(" Modified : {}", modified),
102 ];
103
104 if meta.len() > 0 {
106 lines.push(String::new());
107 lines.push(" (binary content — no text preview)".to_owned());
108 }
109
110 PreviewContent::Binary(lines)
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use std::io::Write;
117 use tempfile::NamedTempFile;
118
119 #[test]
120 fn binary_preview_includes_size() {
121 let mut f = NamedTempFile::new().unwrap();
122 f.write_all(&[0u8; 1024]).unwrap();
123 let meta = std::fs::metadata(f.path()).unwrap();
124 let content = build_binary_preview(f.path(), Some(&meta));
125 if let PreviewContent::Binary(lines) = content {
126 let combined = lines.join("\n");
127 assert!(
128 combined.contains("1.02 kB")
129 || combined.contains("1 kB")
130 || combined.contains("1024"),
131 "expected size info in: {combined}"
132 );
133 } else {
134 panic!("expected Binary variant");
135 }
136 }
137
138 #[test]
139 fn is_image_path_recognises_png() {
140 let p = Path::new("photo.PNG");
141 assert!(is_image_path(p));
142 }
143
144 #[test]
145 fn is_image_path_ignores_rs() {
146 let p = Path::new("main.rs");
147 assert!(!is_image_path(p));
148 }
149}