1
0
mirror of https://github.com/bobwen-dev/hunter synced 2025-04-12 00:55:41 +02:00
hunter/src/preview.rs
2019-03-20 00:29:20 +01:00

633 lines
20 KiB
Rust

use std::sync::{Arc, Mutex};
use failure::Backtrace;
use crate::files::{File, Files, Kind};
use crate::listview::ListView;
use crate::textview::TextView;
use crate::widget::{Widget, WidgetCore};
use crate::coordinates::Coordinates;
use crate::fail::{HResult, HError, ErrorLog};
type HClosure<T> = Box<Fn(Arc<Mutex<bool>>) -> Result<T, HError> + Send>;
type HCClosure<T> = Box<Fn(Arc<Mutex<bool>>, WidgetCore) -> Result<T, HError> + Send>;
type WidgetO = Box<dyn Widget + Send>;
lazy_static! {
static ref SUBPROC: Arc<Mutex<Option<u32>>> = { Arc::new(Mutex::new(None)) };
}
fn kill_proc() -> HResult<()> {
let mut pid = SUBPROC.lock()?;
pid.map(|pid|
unsafe { libc::kill(pid as i32, 15); }
);
*pid = None;
Ok(())
}
pub fn is_stale(stale: &Arc<Mutex<bool>>) -> HResult<bool> {
let stale = *(stale.lock().unwrap());
Ok(stale)
}
enum State {
Is,
Becoming,
Fail(HError)
}
struct WillBe<T: Send> {
pub state: Arc<Mutex<State>>,
pub thing: Arc<Mutex<Option<T>>>,
on_ready: Arc<Mutex<Option<Box<Fn(Arc<Mutex<Option<T>>>) -> HResult<()> + Send>>>>,
stale: Arc<Mutex<bool>>
}
impl<T: Send + 'static> WillBe<T> where {
pub fn new_become(closure: HClosure<T>)
-> WillBe<T> {
let mut willbe = WillBe { state: Arc::new(Mutex::new(State::Becoming)),
thing: Arc::new(Mutex::new(None)),
on_ready: Arc::new(Mutex::new(None)),
stale: Arc::new(Mutex::new(false)) };
willbe.run(closure);
willbe
}
fn run(&mut self, closure: HClosure<T>) {
let state = self.state.clone();
let stale = self.stale.clone();
let thing = self.thing.clone();
let on_ready_fn = self.on_ready.clone();
std::thread::spawn(move|| {
let got_thing = closure(stale);
match got_thing {
Ok(got_thing) => {
*thing.lock().unwrap() = Some(got_thing);
*state.lock().unwrap() = State::Is;
match *on_ready_fn.lock().unwrap() {
Some(ref on_ready) => { on_ready(thing.clone()).ok(); },
None => {}
}
},
Err(err) => *state.lock().unwrap() = State::Fail(err)
}
});
}
pub fn set_stale(&mut self) -> HResult<()> {
*self.stale.lock()? = true;
Ok(())
}
pub fn take(&mut self) -> HResult<T> {
self.check()?;
Ok(self.thing.lock()?.take()?)
}
pub fn check(&self) -> HResult<()> {
match *self.state.lock()? {
State::Is => Ok(()),
_ => Err(HError::WillBeNotReady(Backtrace::new()))
}
}
pub fn on_ready(&mut self,
fun: Box<Fn(Arc<Mutex<Option<T>>>) -> HResult<()> + Send>)
-> HResult<()> {
if self.check().is_ok() {
fun(self.thing.clone())?;
} else {
*self.on_ready.lock()? = Some(fun);
}
Ok(())
}
}
impl<W: Widget + Send + 'static> PartialEq for WillBeWidget<W> {
fn eq(&self, other: &WillBeWidget<W>) -> bool {
if self.get_coordinates().unwrap() ==
other.get_coordinates().unwrap() {
true
} else {
false
}
}
}
pub struct WillBeWidget<T: Widget + Send + 'static> {
willbe: WillBe<T>,
core: WidgetCore
}
impl<T: Widget + Send + 'static> WillBeWidget<T> {
pub fn new(core: &WidgetCore, closure: HClosure<T>) -> WillBeWidget<T> {
let sender = core.get_sender();
let mut willbe = WillBe::new_become(Box::new(move |stale| closure(stale)));
willbe.on_ready(Box::new(move |_| {
sender.send(crate::widget::Events::WidgetReady)?;
Ok(()) })).ok();
WillBeWidget {
willbe: willbe,
core: core.clone()
}
}
pub fn change_to(&mut self, closure: HCClosure<T>) -> HResult<()> {
self.set_stale().log();
let sender = self.get_core()?.get_sender();
let core = self.get_core()?.clone();
let mut willbe = WillBe::new_become(Box::new(move |stale| {
let core = core.clone();
closure(stale, core)
}));
willbe.on_ready(Box::new(move |_| {
sender.send(crate::widget::Events::WidgetReady)?;
Ok(())
}))?;
self.willbe = willbe;
Ok(())
}
pub fn set_stale(&mut self) -> HResult<()> {
self.willbe.set_stale()
}
pub fn widget(&self) -> HResult<Arc<Mutex<Option<T>>>> {
self.willbe.check()?;
Ok(self.willbe.thing.clone())
}
pub fn ready(&self) -> bool {
match self.willbe.check() {
Ok(_) => true,
_ => false
}
}
pub fn take(&mut self) -> HResult<T> {
self.willbe.take()
}
}
// impl<T: Widget + Send> WillBeWidget<T> {
// fn is_widget(&self) -> bool {
// self.willbe.check().is_ok()
// }
// fn take_widget(self) {
// if self.is_widget() {
// let widget = self.willbe.take();
// }
// }
//}
impl<T: Widget + Send + 'static> Widget for WillBeWidget<T> {
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok(&mut self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
self.core.coordinates = coordinates.clone();
if let Ok(widget) = self.widget() {
let mut widget = widget.lock()?;
let widget = widget.as_mut()?;
widget.set_coordinates(&coordinates)?;
}
Ok(())
}
fn refresh(&mut self) -> HResult<()> {
let coords = self.get_coordinates()?;
if let Ok(widget) = self.widget() {
let mut widget = widget.lock()?;
let widget = widget.as_mut()?;
if widget.get_coordinates()? != coords {
widget.set_coordinates(self.get_coordinates()?)?;
widget.refresh()?;
} else {
widget.refresh()?;
}
}
Ok(())
}
fn get_drawlist(&self) -> HResult<String> {
if self.willbe.check().is_err() {
let clear = self.get_clearlist()?;
let (xpos, ypos) = self.get_coordinates()?.u16position();
let pos = crate::term::goto_xy(xpos, ypos);
return Ok(clear + &pos + "...")
}
if is_stale(&self.willbe.stale)? {
return self.get_clearlist()
}
let widget = self.widget()?;
let widget = widget.lock()?;
let widget = widget.as_ref()?;
widget.get_drawlist()
}
fn on_key(&mut self, key: termion::event::Key) -> HResult<()> {
if self.willbe.check().is_err() { return Ok(()) }
let widget = self.widget()?;
let mut widget = widget.lock()?;
let widget = widget.as_mut()?;
widget.on_key(key)
}
}
impl PartialEq for Previewer {
fn eq(&self, other: &Previewer) -> bool {
if self.widget.get_coordinates().unwrap() ==
other.widget.get_coordinates().unwrap() {
true
} else {
false
}
}
}
pub struct Previewer {
widget: WillBeWidget<Box<dyn Widget + Send>>,
core: WidgetCore,
file: Option<File>,
selection: Option<File>,
cached_files: Option<Files>
}
impl Previewer {
pub fn new(core: &WidgetCore) -> Previewer {
let core_ = core.clone();
let willbe = WillBeWidget::new(&core, Box::new(move |_| {
Ok(Box::new(crate::textview::TextView::new_blank(&core_))
as Box<dyn Widget + Send>)
}));
Previewer { widget: willbe,
core: core.clone(),
file: None,
selection: None,
cached_files: None }
}
fn become_preview(&mut self,
widget: HResult<WillBeWidget<WidgetO>>) {
let coordinates = self.get_coordinates().unwrap().clone();
self.widget = widget.unwrap();
self.widget.set_coordinates(&coordinates).ok();
}
pub fn set_file(&mut self,
file: &File,
selection: Option<File>,
cached_files: Option<Files>) {
if Some(file) == self.file.as_ref() { return }
self.file = Some(file.clone());
self.selection = selection.clone();
self.cached_files = cached_files.clone();
let coordinates = self.get_coordinates().unwrap().clone();
let file = file.clone();
let core = self.core.clone();
self.widget.set_stale().ok();
self.become_preview(Ok(WillBeWidget::new(&self.core, Box::new(move |stale| {
kill_proc().unwrap();
let file = file.clone();
let selection = selection.clone();
let cached_files = cached_files.clone();
if file.kind == Kind::Directory {
let preview = Previewer::preview_dir(&file,
selection,
cached_files,
&core,
stale.clone());
return preview;
}
if file.get_mime() == Some("text".to_string()) {
return Previewer::preview_text(&file, &core, stale.clone())
}
let preview = Previewer::preview_external(&file, &core, stale.clone());
if preview.is_ok() { return preview; }
else {
let mut blank = Box::new(TextView::new_blank(&core));
blank.set_coordinates(&coordinates).log();
blank.refresh().log();
blank.animate_slide_up().log();
return Ok(blank)
}
}))));
}
pub fn reload(&mut self) {
if let Some(file) = self.file.clone() {
self.file = None;
let cache = self.cached_files.take();
self.set_file(&file, self.selection.clone(), cache);
}
}
fn preview_failed(file: &File) -> HResult<WidgetO> {
HError::preview_failed(file)
}
fn preview_dir(file: &File,
selection: Option<File>,
cached_files: Option<Files>,
core: &WidgetCore,
stale: Arc<Mutex<bool>>)
-> Result<WidgetO, HError> {
let files = cached_files.or_else(|| {
Files::new_from_path_cancellable(&file.path,
stale.clone()).ok()
})?;
let len = files.len();
if len == 0 || is_stale(&stale)? { return Previewer::preview_failed(&file) }
let mut file_list = ListView::new(&core, files);
if let Some(selection) = selection {
file_list.select_file(&selection);
}
file_list.set_coordinates(&core.coordinates)?;
file_list.refresh()?;
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
file_list.animate_slide_up()?;
Ok(Box::new(file_list) as Box<dyn Widget + Send>)
}
fn preview_text(file: &File, core: &WidgetCore, stale: Arc<Mutex<bool>>)
-> HResult<WidgetO> {
let lines = core.coordinates.ysize() as usize;
let mut textview
= TextView::new_from_file_limit_lines(&core,
&file,
lines)?;
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
textview.set_coordinates(&core.coordinates)?;
textview.refresh()?;
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
textview.animate_slide_up()?;
Ok(Box::new(textview))
}
fn preview_external(file: &File, core: &WidgetCore, stale: Arc<Mutex<bool>>)
-> Result<Box<dyn Widget + Send>, HError> {
let process =
std::process::Command::new("scope.sh")
.arg(&file.path)
.arg("10".to_string())
.arg("10".to_string())
.arg("".to_string())
.arg("false".to_string())
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::null())
.spawn()?;
let pid = process.id();
{
let mut pid_ = SUBPROC.lock()?;
*pid_ = Some(pid);
}
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
let output = process.wait_with_output()?;
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
{
let mut pid_ = SUBPROC.lock()?;
*pid_ = None;
}
let status = output.status.code()
.ok_or(HError::preview_failed(file)?);
if status == Ok(0) || status == Ok(5) && !is_stale(&stale)? {
let output = std::str::from_utf8(&output.stdout)
.unwrap()
.to_string();
let mut textview = TextView {
lines: output.lines().map(|s| s.to_string()).collect(),
core: core.clone(),
follow: false,
offset: 0};
textview.set_coordinates(&core.coordinates).log();
textview.refresh().log();
textview.animate_slide_up().log();
return Ok(Box::new(textview))
}
HError::preview_failed(file)
}
}
impl Widget for Previewer {
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok(&mut self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
self.core.coordinates = coordinates.clone();
self.widget.set_coordinates(&coordinates)
}
fn refresh(&mut self) -> HResult<()> {
self.widget.refresh()
}
fn get_drawlist(&self) -> HResult<String> {
self.widget.get_drawlist()
}
}
// #[derive(PartialEq)]
// pub struct AsyncPreviewer {
// pub file: Option<File>,
// pub buffer: String,
// pub coordinates: Coordinates,
// pub async_plug: AsyncPlug2<Box<dyn Widget + Send + 'static>>
// }
// impl AsyncPreviewer {
// pub fn new() -> AsyncPreviewer {
// let closure = Box::new(|| {
// Box::new(crate::textview::TextView {
// lines: vec![],
// buffer: "".to_string(),
// coordinates: Coordinates::new()
// }) as Box<dyn Widget + Send + 'static>
// });
// AsyncPreviewer {
// file: None,
// buffer: String::new(),
// coordinates: Coordinates::new(),
// async_plug: AsyncPlug2::new_from_closure(closure),
// }
// }
// pub fn set_file(&mut self, file: &File) {
// let coordinates = self.coordinates.clone();
// let file = file.clone();
// let redraw = crate::term::reset() + &self.get_redraw_empty_list(0);
// //let pids = PIDS.clone();
// //kill_procs();
// self.async_plug.replace_widget(Box::new(move || {
// kill_procs();
// let mut bufout = std::io::BufWriter::new(std::io::stdout());
// match &file.kind {
// Kind::Directory => match Files::new_from_path(&file.path) {
// Ok(files) => {
// //if !is_current(&file) { return }
// let len = files.len();
// //if len == 0 { return };
// let mut file_list = ListView::new(files);
// file_list.set_coordinates(&coordinates);
// file_list.refresh();
// //if !is_current(&file) { return }
// file_list.animate_slide_up();
// return Box::new(file_list)
// }
// Err(err) => {
// write!(bufout, "{}", redraw).unwrap();
// let textview = crate::textview::TextView {
// lines: vec![],
// buffer: "".to_string(),
// coordinates: Coordinates::new(),
// };
// return Box::new(textview)
// },
// }
// _ => {
// if file.get_mime() == Some("text".to_string()) {
// let lines = coordinates.ysize() as usize;
// let mut textview
// = TextView::new_from_file_limit_lines(&file,
// lines);
// //if !is_current(&file) { return }
// textview.set_coordinates(&coordinates);
// textview.refresh();
// //if !is_current(&file) { return }
// textview.animate_slide_up();
// return Box::new(textview)
// } else {
// let process =
// std::process::Command::new("scope.sh")
// .arg(&file.name)
// .arg("10".to_string())
// .arg("10".to_string())
// .arg("".to_string())
// .arg("false".to_string())
// .stdin(std::process::Stdio::null())
// .stdout(std::process::Stdio::piped())
// .stderr(std::process::Stdio::null())
// .spawn().unwrap();
// let pid = process.id();
// PIDS.lock().unwrap().push(pid);
// //if !is_current(&file) { return }
// let output = process.wait_with_output();
// match output {
// Ok(output) => {
// let status = output.status.code();
// match status {
// Some(status) => {
// if status == 0 || status == 5 && is_current(&file) {
// let output = std::str::from_utf8(&output.stdout)
// .unwrap()
// .to_string();
// let mut textview = TextView {
// lines: output.lines().map(|s| s.to_string()).collect(),
// buffer: String::new(),
// coordinates: Coordinates::new() };
// textview.set_coordinates(&coordinates);
// textview.refresh();
// textview.animate_slide_up();
// return Box::new(textview)
// }
// }, None => {}
// }
// }, Err(_) => {}
// }
// write!(bufout, "{}", redraw).unwrap();
// //std::io::stdout().flush().unwrap();
// let textview = crate::textview::TextView {
// lines: vec![],
// buffer: "".to_string(),
// coordinates: Coordinates::new(),
// };
// return Box::new(textview)
// }
// }
// }}))
// }
// }
impl<T> Widget for Box<T> where T: Widget + ?Sized {
fn get_core(&self) -> HResult<&WidgetCore> {
Ok((**self).get_core()?)
}
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok((**self).get_core_mut()?)
}
fn render_header(&self) -> HResult<String> {
(**self).render_header()
}
fn refresh(&mut self) -> HResult<()> {
(**self).refresh()
}
fn get_drawlist(&self) -> HResult<String> {
(**self).get_drawlist()
}
}