From 8b78d5486351ee9f3849ebe59f6f9f46c72de9a3 Mon Sep 17 00:00:00 2001 From: Denis Isidoro Date: Fri, 29 Jul 2022 18:53:18 -0300 Subject: [PATCH] Improve error handling for cheatsh and tldr (#766) Fixes #695 and #703 --- src/clients/cheatsh.rs | 80 +++++++++++++-------------------------- src/clients/tldr.rs | 48 +++++++---------------- src/commands/core/mod.rs | 39 +++++++++++++++++-- src/commands/temp.rs | 5 ++- src/config/mod.rs | 16 -------- src/structures/fetcher.rs | 17 +++++++++ src/welcome.rs | 7 +--- 7 files changed, 98 insertions(+), 114 deletions(-) diff --git a/src/clients/cheatsh.rs b/src/clients/cheatsh.rs index f039b0cc..13ffaf73 100644 --- a/src/clients/cheatsh.rs +++ b/src/clients/cheatsh.rs @@ -1,26 +1,22 @@ -use crate::parser::Parser; use crate::prelude::*; - -use crate::structures::fetcher; -use std::process::{self, Command}; +use std::process::Command; fn map_line(line: &str) -> String { line.trim().trim_end_matches(':').to_string() } -fn lines(query: &str, markdown: &str) -> impl Iterator> { +fn as_lines(query: &str, markdown: &str) -> Vec { format!( "% {}, cheat.sh {}", query, markdown ) .lines() - .map(|line| Ok(map_line(line))) - .collect::>>() - .into_iter() + .map(map_line) + .collect() } -pub fn fetch(query: &str) -> Result { +pub fn call(query: &str) -> Result> { let args = ["-qO-", &format!("cheat.sh/{}", query)]; let child = Command::new("wget") @@ -32,19 +28,34 @@ pub fn fetch(query: &str) -> Result { let child = match child { Ok(x) => x, Err(_) => { - eprintln!( - "navi was unable to call wget. -Make sure wget is correctly installed." - ); - process::exit(34) + let msg = "navi was unable to call wget. +Make sure wget is correctly installed."; + return Err(anyhow!(msg)); } }; let out = child.wait_with_output().context("Failed to wait for wget")?; if let Some(0) = out.status.code() { + let stdout = out.stdout; + let plain_bytes = strip_ansi_escapes::strip(&stdout)?; + + let markdown = String::from_utf8(plain_bytes).context("Output is invalid utf8")?; + if markdown.starts_with("Unknown topic.") { + let msg = format!( + "`{}` not found in cheatsh. +Output: +{} +", + &query, markdown, + ); + return Err(anyhow!(msg)); + } + + let lines = as_lines(query, &markdown); + Ok(lines) } else { - eprintln!( + let msg = format!( "Failed to call: wget {} @@ -58,43 +69,6 @@ Error: String::from_utf8(out.stdout).unwrap_or_else(|_e| "Unable to get output message".to_string()), String::from_utf8(out.stderr).unwrap_or_else(|_e| "Unable to get error message".to_string()) ); - process::exit(35) - } - - let stdout = out.stdout; - let plain_bytes = strip_ansi_escapes::strip(&stdout)?; - - String::from_utf8(plain_bytes).context("Output is invalid utf8") -} - -pub struct Fetcher { - query: String, -} - -impl Fetcher { - pub fn new(query: String) -> Self { - Self { query } - } -} - -impl fetcher::Fetcher for Fetcher { - fn fetch(&self, parser: &mut Parser) -> Result { - let cheat = &fetch(&self.query)?; - - if cheat.starts_with("Unknown topic.") { - eprintln!( - "`{}` not found in cheatsh. - -Output: -{} -", - &self.query, cheat - ); - process::exit(35) - } - - parser.read_lines(lines(&self.query, cheat), "cheat.sh", None)?; - - Ok(true) + Err(anyhow!(msg)) } } diff --git a/src/clients/tldr.rs b/src/clients/tldr.rs index f1645344..6783fdc3 100644 --- a/src/clients/tldr.rs +++ b/src/clients/tldr.rs @@ -1,6 +1,4 @@ -use crate::parser::Parser; use crate::prelude::*; -use crate::structures::fetcher; use std::process::{self, Command, Stdio}; lazy_static! { @@ -42,19 +40,18 @@ fn convert_tldr(line: &str) -> String { } } -fn markdown_lines(query: &str, markdown: &str) -> impl Iterator> { +fn markdown_lines(query: &str, markdown: &str) -> Vec { format!( "% {}, tldr {}", query, markdown ) .lines() - .map(|line| Ok(convert_tldr(line))) - .collect::>>() - .into_iter() + .map(convert_tldr) + .collect() } -pub fn fetch(query: &str) -> Result { +pub fn call(query: &str) -> Result> { let args = [query, "--markdown"]; let child = Command::new("tldr") @@ -67,7 +64,7 @@ pub fn fetch(query: &str) -> Result { let child = match child { Ok(x) => x, Err(_) => { - eprintln!( + let msg = format!( "navi was unable to call tldr. Make sure tldr is correctly installed. Refer to https://github.com/tldr-pages/tldr for more info. @@ -77,15 +74,20 @@ Note: ", VERSION_DISCLAIMER ); - process::exit(34) + return Err(anyhow!(msg)); } }; let out = child.wait_with_output().context("Failed to wait for tldr")?; if let Some(0) = out.status.code() { + let stdout = out.stdout; + + let markdown = String::from_utf8(stdout).context("Output is invalid utf8")?; + let lines = markdown_lines(query, &markdown); + Ok(lines) } else { - eprintln!( + let msg = format!( "Failed to call: tldr {} @@ -103,30 +105,8 @@ If you are already using a supported version you can ignore this message. args.join(" "), String::from_utf8(out.stdout).unwrap_or_else(|_e| "Unable to get output message".to_string()), String::from_utf8(out.stderr).unwrap_or_else(|_e| "Unable to get error message".to_string()), - VERSION_DISCLAIMER + VERSION_DISCLAIMER, ); - process::exit(35) - } - - let stdout = out.stdout; - - String::from_utf8(stdout).context("Output is invalid utf8") -} - -pub struct Fetcher { - query: String, -} - -impl Fetcher { - pub fn new(query: String) -> Self { - Self { query } - } -} - -impl fetcher::Fetcher for Fetcher { - fn fetch(&self, parser: &mut Parser) -> Result { - let markdown = fetch(&self.query)?; - parser.read_lines(markdown_lines(&self.query, &markdown), "markdown", None)?; - Ok(true) + Err(anyhow!(msg)) } } diff --git a/src/commands/core/mod.rs b/src/commands/core/mod.rs index f212880a..74e28527 100644 --- a/src/commands/core/mod.rs +++ b/src/commands/core/mod.rs @@ -1,20 +1,23 @@ mod actor; mod extractor; +use crate::clients::cheatsh; +use crate::config::Source; +use crate::filesystem; use crate::finder::structures::Opts as FinderOpts; use crate::parser::Parser; use crate::prelude::*; +use crate::structures::fetcher::{Fetcher, StaticFetcher}; use crate::welcome; -pub fn main() -> Result<()> { +pub fn init(fetcher: Box) -> Result<()> { let config = &CONFIG; let opts = FinderOpts::snippet_default(); + // let fetcher = config.fetcher(); let (raw_selection, (variables, files)) = config .finder() .call(opts, |writer| { - let fetcher = config.fetcher(); - let mut parser = Parser::new(writer, true); let found_something = fetcher @@ -32,10 +35,38 @@ pub fn main() -> Result<()> { let extractions = extractor::extract_from_selections(&raw_selection, config.best_match()); if extractions.is_err() { - return main(); + return init(fetcher); } actor::act(extractions, files, variables)?; Ok(()) } + +pub fn get_fetcher() -> Result> { + match CONFIG.source() { + Source::Cheats(query) => { + let lines = cheatsh::call(&query)?; + let fetcher = Box::new(StaticFetcher::new(lines)); + Ok(fetcher) + } + Source::Tldr(query) => { + let lines = cheatsh::call(&query)?; + let fetcher = Box::new(StaticFetcher::new(lines)); + Ok(fetcher) + } + Source::Filesystem(path) => { + let fetcher = Box::new(filesystem::Fetcher::new(path)); + Ok(fetcher) + } + Source::Welcome => { + let fetcher = Box::new(welcome::Fetcher::new()); + Ok(fetcher) + } + } +} + +pub fn main() -> Result<()> { + let fetcher = get_fetcher()?; + init(fetcher) +} diff --git a/src/commands/temp.rs b/src/commands/temp.rs index 35a1ac34..efcdd16c 100644 --- a/src/commands/temp.rs +++ b/src/commands/temp.rs @@ -1,3 +1,4 @@ +use crate::commands::core::get_fetcher; use crate::common::shell::{self, ShellSpawnError}; use crate::finder::structures::Opts as FinderOpts; use crate::parser::Parser; @@ -8,7 +9,7 @@ pub fn main() -> Result<()> { let config = &CONFIG; let _opts = FinderOpts::snippet_default(); - let fetcher = config.fetcher(); + let fetcher = get_fetcher()?; let hash: u64 = 2087294461664323320; let mut buf = vec![]; @@ -50,7 +51,7 @@ pub fn main() -> Result<()> { pub fn _main0() -> Result<()> { let config = &CONFIG; - let fetcher = config.fetcher(); + let fetcher = get_fetcher()?; let mut stdout = io::stdout(); let mut writer: Box<&mut dyn Write> = Box::new(&mut stdout); diff --git a/src/config/mod.rs b/src/config/mod.rs index 43f97ca1..1126b051 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -2,15 +2,8 @@ mod cli; mod env; mod yaml; -use crate::clients::cheatsh; -use crate::clients::tldr; - use crate::commands::func::Func; -use crate::config::Source; -use crate::filesystem; use crate::finder::FinderChoice; -use crate::structures::fetcher::Fetcher; -use crate::welcome; pub use cli::*; use crossterm::style::Color; use env::EnvConfig; @@ -62,15 +55,6 @@ impl Config { } } - pub fn fetcher(&self) -> Box { - match self.source() { - Source::Cheats(query) => Box::new(cheatsh::Fetcher::new(query)), - Source::Tldr(query) => Box::new(tldr::Fetcher::new(query)), - Source::Filesystem(path) => Box::new(filesystem::Fetcher::new(path)), - Source::Welcome => Box::new(welcome::Fetcher::new()), - } - } - pub fn path(&self) -> Option { self.clap .path diff --git a/src/structures/fetcher.rs b/src/structures/fetcher.rs index 4488ef79..f6c84370 100644 --- a/src/structures/fetcher.rs +++ b/src/structures/fetcher.rs @@ -8,3 +8,20 @@ pub trait Fetcher { vec![] } } + +pub struct StaticFetcher { + lines: Vec, +} + +impl StaticFetcher { + pub fn new(lines: Vec) -> Self { + Self { lines } + } +} + +impl Fetcher for StaticFetcher { + fn fetch(&self, parser: &mut Parser) -> Result { + parser.read_lines(self.lines.clone().into_iter().map(Ok), "static", None)?; + Ok(true) + } +} diff --git a/src/welcome.rs b/src/welcome.rs index 62e41bcd..bf3e6185 100644 --- a/src/welcome.rs +++ b/src/welcome.rs @@ -4,12 +4,9 @@ use crate::structures::fetcher; pub fn populate_cheatsheet(parser: &mut Parser) -> Result<()> { let cheatsheet = include_str!("../docs/navi.cheat"); + let lines = cheatsheet.split('\n').into_iter().map(|s| Ok(s.to_string())); - parser.read_lines( - cheatsheet.split('\n').into_iter().map(|s| Ok(s.to_string())), - "welcome", - None, - )?; + parser.read_lines(lines, "welcome", None)?; Ok(()) }