diff --git a/Cargo.lock b/Cargo.lock index 30355749..722462af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1323,6 +1323,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "stork-search" version = "1.0.4" dependencies = [ + "atty", "bincode", "console_error_panic_hook", "criterion", diff --git a/Cargo.toml b/Cargo.toml index 613504c9..7ed00423 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ scraper = "0.12.0" frontmatter = "0.4.0" markdown = "0.3.0" once_cell = "1" +atty = "0.2" futures = {version = "0.3", optional = true} hyper = {version = "0.13", optional = true} tokio = { version = "0.2", features = ["full"], optional = true } diff --git a/src/bin/stork/argparse.rs b/src/bin/stork/argparse.rs index 7bb0faf1..c056103f 100644 --- a/src/bin/stork/argparse.rs +++ b/src/bin/stork/argparse.rs @@ -1,5 +1,5 @@ use super::{ExitCode, EXIT_FAILURE, EXIT_SUCCESS}; -use std::fmt; +use std::{convert::TryInto, fmt, ops::Range}; pub struct Argparse { commands: Vec, @@ -14,15 +14,14 @@ struct Command { enum ValueOrRange { Value(u8), - Range(u8, u8), + Range(Range), } impl fmt::Display for ValueOrRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ValueOrRange::Value(val) => write!(f, "{}", val), - - ValueOrRange::Range(min, max) => write!(f, "between {} and {}", min, max), + ValueOrRange::Range(range) => write!(f, "between {} and {}", range.start, range.end), } } } @@ -44,19 +43,11 @@ impl Argparse { } #[allow(dead_code)] - pub fn register_range(&mut self, cmd_name: &str, action: fn(&[String]), args_range: (u8, u8)) { - let min = std::cmp::min(args_range.0, args_range.1); - let max = std::cmp::max(args_range.0, args_range.1); - let number_of_args = if min == max { - ValueOrRange::Value(min) - } else { - ValueOrRange::Range(min, max) - }; - + pub fn register_range(&mut self, cmd_name: &str, action: fn(&[String]), args_range: Range) { self.commands.push(Command { name: cmd_name.to_string(), action, - number_of_args, + number_of_args: ValueOrRange::Range(args_range), }) } @@ -74,13 +65,10 @@ impl Argparse { for command in &self.commands { if args[1] == ["--", &command.name].concat() { - let number_of_args = args.len() - 2; - let valid = match command.number_of_args { - ValueOrRange::Value(val) => (number_of_args as u8) == val, - - ValueOrRange::Range(min, max) => { - (number_of_args as u8) >= min && (number_of_args as u8) <= max - } + let number_of_args: u8 = (args.len() - 2).try_into().unwrap(); + let valid = match &command.number_of_args { + ValueOrRange::Value(val) => number_of_args == val.to_owned(), + ValueOrRange::Range(range) => range.contains(&number_of_args), }; if !valid { diff --git a/src/bin/stork/main.rs b/src/bin/stork/main.rs index c36c4d01..3909b8a2 100644 --- a/src/bin/stork/main.rs +++ b/src/bin/stork/main.rs @@ -4,15 +4,16 @@ mod argparse; use argparse::Argparse; mod test_server; +use atty::Stream; use test_server::serve; mod display_timings; use display_timings::*; -use std::fs::File; +use std::env; use std::io::{BufReader, Read}; use std::time::Instant; -use std::{env, error::Error}; +use std::{fs::File, io}; use stork::config::Config; use stork::LatestVersion::structs::Index; @@ -51,26 +52,48 @@ USAGE: fn main() { let mut a = Argparse::new(); - a.register("build", build_handler, 1); + a.register_range("build", build_handler, 0..2); a.register("test", test_handler, 1); a.register("search", search_handler, 2); a.register_help(&help_text()); std::process::exit(a.exec(env::args().collect())); } -pub fn build_index(config_path: &str) -> Result<(Config, Index), Box> { - let config = Config::from_file(std::path::PathBuf::from(config_path))?; - let index = stork::build(&config)?; - Ok((config, index)) +pub fn build_index(optional_config_path: Option<&String>) -> (Config, Index) { + // Potential refactor: this method could return a result instead of + // std::process::exiting when there's a failure. + + let config = { + match optional_config_path { + Some(config_path) => Config::from_file(std::path::PathBuf::from(config_path)), + None => { + let mut stdin_buffer = String::new(); + if atty::isnt(Stream::Stdin) { + let _ = io::stdin().read_to_string(&mut stdin_buffer); + } else { + eprintln!("stork --build doesn't support interactive stdin! Pipe in a stream instead.") + } + Config::from_string(stdin_buffer) + } + } + } + .unwrap_or_else(|error| { + eprintln!("Could not read configuration: {}", error.to_string()); + std::process::exit(EXIT_FAILURE); + }); + + let index = stork::build(&config).unwrap_or_else(|error| { + eprintln!("Could not generate index: {}", error.to_string()); + std::process::exit(EXIT_FAILURE); + }); + + (config, index) } fn build_handler(args: &[String]) { let start_time = Instant::now(); - let (config, index) = build_index(&args[2]).unwrap_or_else(|e| { - eprintln!("Could not generate index: {}", e.to_string()); - std::process::exit(EXIT_FAILURE); - }); + let (config, index) = build_index(args.get(2)); let build_time = Instant::now(); let bytes_written = match index.write(&config) { @@ -107,11 +130,7 @@ fn build_handler(args: &[String]) { } fn test_handler(args: &[String]) { - let (_, index) = build_index(&args[2]).unwrap_or_else(|e| { - eprintln!("Could not generate index: {}", e.to_string()); - std::process::exit(EXIT_FAILURE); - }); - + let (_, index) = build_index(args.get(2)); let _r = serve(index); } diff --git a/src/config/config_read_err.rs b/src/config/config_read_err.rs index 7527671c..6544e43b 100644 --- a/src/config/config_read_err.rs +++ b/src/config/config_read_err.rs @@ -1,6 +1,7 @@ use std::{error::Error, fmt, path::PathBuf}; #[derive(Debug)] pub enum ConfigReadErr { + EmptyString, UnreadableFile(PathBuf), UnparseableInput(toml::de::Error), } @@ -10,6 +11,7 @@ impl Error for ConfigReadErr {} impl fmt::Display for ConfigReadErr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let desc: String = match self { + ConfigReadErr::EmptyString => "Received empty configuration string".to_string(), ConfigReadErr::UnreadableFile(s) => format!("File {} not found", s.to_string_lossy()), ConfigReadErr::UnparseableInput(e) => e.to_string(), }; diff --git a/src/config/mod.rs b/src/config/mod.rs index 7c693d82..2735f3cd 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -41,7 +41,14 @@ impl Config { pub fn from_file(path: std::path::PathBuf) -> Result { let contents = fs::read_to_string(&path).map_err(|_| ConfigReadErr::UnreadableFile(path))?; - toml::from_str(&contents).map_err(ConfigReadErr::UnparseableInput) + Config::from_string(contents) + } + + pub fn from_string(str: String) -> Result { + if str.is_empty() { + return Err(ConfigReadErr::EmptyString); + } + toml::from_str(&str).map_err(ConfigReadErr::UnparseableInput) } }