use super::*;
pub(super) fn read_preview(path: &Path, is_dir: bool) -> Preview {
if path.symlink_metadata().is_err()
&& let Some(preview) = read_archive_preview(path)
{
return preview;
}
if !is_dir {
return crate::viewer::load_preview(path, crate::constants::PREVIEW_MAX_BYTES, crate::constants::PREVIEW_MAX_LINES);
}
Preview::Lines(vec![match std::fs::read_dir(path) {
Ok(entries) => format!("{} items", entries.count()),
Err(e) => e.to_string(),
}])
}
fn read_archive_preview(path: &Path) -> Option<Preview> {
use crate::archive::{EntryKind, Next};
let (index, at) = crate::archive::entry_at(path)?;
let entry = index.entry(at);
if entry.kind == EntryKind::Dir {
return Some(Preview::Lines(vec![format!("{} items", index.children(&entry.path).count())]));
}
let (EntryKind::File, Some(position)) = (entry.kind, entry.position) else {
return Some(Preview::Lines(vec![t!("Not a regular file").to_string()]));
};
let located = crate::archive::locate(path)?;
let mut head = None;
let _ = crate::archive::read_entries(&located.archive, index.kind, &[position], &mut |_, contents| {
let mut buffer = Vec::new();
if let Ok(reader) = contents
&& std::io::Read::read_to_end(&mut std::io::Read::take(reader, crate::constants::PREVIEW_MAX_BYTES), &mut buffer).is_ok()
{
head = Some(buffer);
}
Next::Go
});
Some(match head {
Some(head) => crate::viewer::preview_of(head, crate::constants::PREVIEW_MAX_LINES),
None => crate::viewer::unreadable_preview(),
})
}
pub struct CursorDetail {
pub path: Option<PathBuf>,
pub name: String,
pub size: String,
pub modified: String,
pub owner: String,
pub attributes: String,
pub link: Option<String>,
pub(super) awaiting: bool,
}
pub struct PreviewState {
pub path: Option<PathBuf>,
pub label: String,
pub lines: Vec<String>,
pub bytes: Vec<u8>,
pub(super) awaiting: bool,
}
impl AppState {
pub(super) fn cursor_target(&self) -> Option<(PathBuf, String, bool)> {
let (children, state, dir) = if self.is_left_active {
(&self.children_left, &self.state_left, &self.dir_left)
} else {
(&self.children_right, &self.state_right, &self.dir_right)
};
let item = children.get(state.selected()?)?;
if item.is_parent() {
return None;
}
Some((item.path_in(dir), item.name_full.clone(), item.is_dir))
}
pub fn refresh_cursor_detail(&mut self) {
let target = self.cursor_target();
let path = target.as_ref().map(|(path, _, _)| path.clone());
let mut wait = Duration::ZERO;
if self.cursor_detail.as_ref().is_none_or(|detail| detail.path != path) {
let (name, awaiting) = match target {
Some((path, name, is_dir)) => {
self.detail_worker.ask((path, is_dir));
wait = self.detail_wait;
(name, true)
}
None => (String::new(), false),
};
self.cursor_detail = Some(CursorDetail {
path,
name,
size: String::new(),
modified: String::new(),
owner: String::new(),
attributes: String::new(),
link: None,
awaiting,
});
}
if !self.cursor_detail.as_ref().is_some_and(|detail| detail.awaiting) {
return;
}
let Some(((path, _), described)) = self.detail_worker.answer(wait) else {
return;
};
let date_format = self.options.date_format;
let Some(detail) = self.cursor_detail.as_mut().filter(|detail| detail.path.as_ref() == Some(&path)) else {
return;
};
detail.awaiting = false;
detail.size = described.as_ref().and_then(|described| described.size_bytes).map(|bytes| format!("{} bytes", crate::utils::grouped(bytes))).unwrap_or_default();
detail.modified = described.as_ref().and_then(|described| described.modified).map(|at| crate::utils::format_exact(at, date_format)).unwrap_or_default();
detail.owner = described.as_ref().map(|described| described.owner.clone()).unwrap_or_default();
detail.attributes = described.as_ref().map(|described| described.attributes.clone()).unwrap_or_default();
detail.link = described.and_then(|described| described.link);
}
pub fn refresh_preview(&mut self) {
if !self.show_preview {
self.preview = None;
return;
}
let target = self.cursor_target();
let path = target.as_ref().map(|(path, _, _)| path.clone());
let mut wait = Duration::ZERO;
if self.preview.as_ref().is_none_or(|preview| preview.path != path) {
let (label, awaiting) = match target {
Some((path, name, is_dir)) => {
self.preview_worker.ask((path, is_dir));
wait = self.detail_wait;
(name, true)
}
None => (String::new(), false),
};
self.preview = Some(PreviewState {
path,
label,
lines: Vec::new(),
bytes: Vec::new(),
awaiting,
});
}
if !self.preview.as_ref().is_some_and(|preview| preview.awaiting) {
return;
}
let Some(((path, _), content)) = self.preview_worker.answer(wait) else {
return;
};
let Some(preview) = self.preview.as_mut().filter(|preview| preview.path.as_ref() == Some(&path)) else {
return;
};
preview.awaiting = false;
match content {
Preview::Lines(lines) => preview.lines = lines,
Preview::Bytes(bytes) => preview.bytes = bytes,
}
}
}