use std::path::Path;
use crate::app::state::{Entry, EntryKind};
use crate::preview::provider::{PreviewContent, PreviewCtx, PreviewOutcome, PreviewProvider};
const IMAGE_EXTENSIONS: &[&str] = &[
"png", "jpg", "jpeg", "gif", "bmp", "ico", "tiff", "tif", "webp", "avif", "svg",
];
pub struct BinaryProvider;
impl PreviewProvider for BinaryProvider {
fn can_handle(&self, entry: &Entry) -> bool {
if entry.kind != EntryKind::File {
return false;
}
if is_image_path(&entry.path) {
return false;
}
entry.is_text == Some(false)
}
fn preview(&self, entry: &Entry, _ctx: &PreviewCtx) -> PreviewOutcome {
let content = build_binary_preview(&entry.path, entry.metadata.as_ref());
PreviewOutcome::Ready(content)
}
}
fn is_image_path(path: &Path) -> bool {
path.extension()
.and_then(|e| e.to_str())
.map(|ext| IMAGE_EXTENSIONS.contains(&ext.to_lowercase().as_str()))
.unwrap_or(false)
}
pub fn build_binary_preview(path: &Path, metadata: Option<&std::fs::Metadata>) -> PreviewContent {
let owned;
let meta: &std::fs::Metadata = match metadata {
Some(m) => m,
None => {
owned = match std::fs::metadata(path) {
Ok(m) => m,
Err(e) => {
return PreviewContent::Binary(vec![format!(" Cannot read metadata: {e}")])
}
};
&owned
}
};
let size_str = crate::metafmt::size(meta.len());
let ext = path
.extension()
.and_then(|e| e.to_str())
.unwrap_or("unknown")
.to_uppercase();
let modified = crate::metafmt::modified(meta.modified().ok());
let mut lines = vec![
format!(" Type : {} binary", ext),
format!(" Size : {}", size_str),
format!(" Modified : {}", modified),
];
if meta.len() > 0 {
lines.push(String::new());
lines.push(" (binary content — no text preview)".to_owned());
}
PreviewContent::Binary(lines)
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn binary_preview_includes_size() {
let mut f = NamedTempFile::new().unwrap();
f.write_all(&[0u8; 1024]).unwrap();
let meta = std::fs::metadata(f.path()).unwrap();
let content = build_binary_preview(f.path(), Some(&meta));
if let PreviewContent::Binary(lines) = content {
let combined = lines.join("\n");
assert!(
combined.contains("1.02 kB")
|| combined.contains("1 kB")
|| combined.contains("1024"),
"expected size info in: {combined}"
);
} else {
panic!("expected Binary variant");
}
}
#[test]
fn is_image_path_recognises_png() {
let p = Path::new("photo.PNG");
assert!(is_image_path(p));
}
#[test]
fn is_image_path_ignores_rs() {
let p = Path::new("main.rs");
assert!(!is_image_path(p));
}
}