diff --git a/README.md b/README.md index d406f71..877cdb0 100644 --- a/README.md +++ b/README.md @@ -54,11 +54,10 @@ Just unzip/untar the `phetch` program into your $PATH and get going! - [ ] confirm() helper (downloads, other questions) - [ ] tests for scrolling -- [ ] consolidate views into one struct -- [ ] download to ~/Downloads gopher://zaibatsu.circumlunar.space/1/~cardboard64/ - [ ] telnet - [ ] ipv6 +- [ ] cancel download - [ ] flesh out help - [ ] new screenshots - [ ] little GIF screencast in README @@ -67,8 +66,9 @@ Just unzip/untar the `phetch` program into your $PATH and get going! - [ ] stop 'page down' at a reasonable place - [ ] stop 'down arrow' at a reasonable place +- [ ] `open` failure mode for irc links - [ ] `open` for irc links -- [ ] gopher lawn is truncate +- [ ] gopher lawn is truncated - [ ] gopher://1436.ninja/1/twit.cgi ("iWritten and performed by Nathaniel" weirdness) ## future features diff --git a/src/gopher.rs b/src/gopher.rs index 4644013..78eea26 100644 --- a/src/gopher.rs +++ b/src/gopher.rs @@ -142,7 +142,7 @@ pub fn download_url(url: &str) -> io::Result { .mode(0o770) .open(path) .and_then(|mut file| { - let mut buf = [0; 1024]; + let mut buf = [0 as u8; 8]; // read 8 bytes at a time while let Ok(count) = stream.read(&mut buf) { if count == 0 { break; diff --git a/src/ui.rs b/src/ui.rs index b1f56a9..68a57a3 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -134,44 +134,13 @@ impl UI { } fn download(&mut self, url: &str) -> io::Result<()> { - // request thread - let download_url = url.to_string(); - let req = thread::spawn(move || gopher::download_url(&download_url)); - - // spinner thead - let download_url = url.to_string(); - let (spintx, spinrx) = mpsc::channel(); - thread::spawn(move || loop { - for i in 0..=3 { - if spinrx.try_recv().is_ok() { - return; - } - print!( - "\r{}{}Downloading {}{}{}", - termion::cursor::Hide, - color::Fg(color::LightBlack), - download_url, - ".".repeat(i), - termion::clear::AfterCursor - ); - stdout().flush(); - thread::sleep(Duration::from_millis(350)); - } - }); - - let work = req.join(); - spintx.send(true); // stop spinner - self.dirty = true; - let res = match work { - Ok(opt) => match opt { - Ok(body) => body, - Err(e) => return Err(e), - }, - Err(_) => return Err(io_error("Connection error".into())), - }; - - self.set_status(format!("Download complete! {}", res)); - Ok(()) + let url = url.to_string(); + self.spinner("", move || gopher::download_url(&url)) + .and_then(|res| res) + .and_then(|res| { + self.set_status(format!("Download complete! {}", res)); + Ok(()) + }) } fn fetch(&mut self, url: &str) -> io::Result { @@ -182,37 +151,7 @@ impl UI { // request thread let thread_url = url.to_string(); - let req = thread::spawn(move || gopher::fetch_url(&thread_url)); - - // spinner thead - let (spintx, spinrx) = mpsc::channel(); - thread::spawn(move || loop { - for i in 0..=3 { - if spinrx.try_recv().is_ok() { - return; - } - print!( - "\r{}{}{}{}", - termion::cursor::Hide, - color::Fg(color::LightBlack), - ".".repeat(i), - termion::clear::AfterCursor - ); - stdout().flush(); - thread::sleep(Duration::from_millis(350)); - } - }); - - let work = req.join(); - spintx.send(true); // stop spinner - self.dirty = true; - let res = match work { - Ok(opt) => match opt { - Ok(body) => body, - Err(e) => return Err(e), - }, - Err(_) => return Err(io_error("Connection error".into())), - }; + let res = self.spinner("", move || gopher::fetch_url(&thread_url))??; let (typ, _, _, _) = gopher::parse_url(&url); match typ { Type::Menu | Type::Search => Ok(Box::new(Menu::from(url.to_string(), res))), @@ -233,6 +172,42 @@ impl UI { } } + // Show a spinner while running a thread. Used to make gopher requests or + // download files. + fn spinner T>( + &mut self, + label: &str, + work: F, + ) -> io::Result { + let req = thread::spawn(work); + + let (tx, rx) = mpsc::channel(); + let label = label.to_string(); + thread::spawn(move || loop { + for i in 0..=3 { + if rx.try_recv().is_ok() { + return; + } + print!( + "\r{}{}{}{}{}{}", + termion::cursor::Hide, + color::Fg(color::LightBlack), + label, + ".".repeat(i), + termion::clear::AfterCursor, + color::Fg(color::Reset) + ); + stdout().flush(); + thread::sleep(Duration::from_millis(350)); + } + }); + + let result = req.join(); + tx.send(true); // stop spinner + self.dirty = true; + result.map_err(|e| io_error(format!("Spinner error: {:?}", e))) + } + pub fn render(&mut self) -> String { if let Ok((cols, rows)) = terminal_size() { self.term_size(cols as usize, rows as usize);