From 88a703c6045a37115d457d091789840b4ed8807d Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sun, 13 Dec 2020 20:26:35 +0100 Subject: [PATCH] Replaced manual cli arg parsing with the seahorse crate --- Cargo.lock | 7 +++ kalk_cli/Cargo.toml | 1 + kalk_cli/src/main.rs | 113 +++++++++++++++++++++---------------------- 3 files changed, 63 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95d3a6c..ef901da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,6 +170,7 @@ dependencies = [ "lazy_static", "regex", "rustyline", + "seahorse", "winres", ] @@ -315,6 +316,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "seahorse" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce7d9440e2865cce0db733bdc530591b37d37a2d32badace34a1fc9ba5686d58" + [[package]] name = "serde" version = "1.0.118" diff --git a/kalk_cli/Cargo.toml b/kalk_cli/Cargo.toml index 1784fea..17e7a2a 100644 --- a/kalk_cli/Cargo.toml +++ b/kalk_cli/Cargo.toml @@ -21,6 +21,7 @@ rustyline = "7.0.0" ansi_term = "0.12" regex = "1" lazy_static = "1.4.0" +seahorse = "1.1.1" [target.'cfg(windows)'.build-dependencies] winres = "0.1" diff --git a/kalk_cli/src/main.rs b/kalk_cli/src/main.rs index 2b3dc6e..a96b33d 100644 --- a/kalk_cli/src/main.rs +++ b/kalk_cli/src/main.rs @@ -2,6 +2,7 @@ mod output; mod repl; use kalk::parser; +use seahorse::{App, Context, Flag, FlagType}; use std::env; use std::fs::File; use std::io::Read; @@ -9,76 +10,72 @@ use std::io::Read; static DEFAULT_PRECISION: u32 = 53; fn main() { - let mut parser_context = parser::Context::new().set_angle_unit(&get_angle_unit()); + let args: Vec = env::args().collect(); + let app = App::new("kalk") + .author(env!("CARGO_PKG_AUTHORS")) + .version(env!("CARGO_PKG_VERSION")) + .usage("kalk [options] [input]") + .action(default_action) + .flag( + Flag::new("input-file", FlagType::String) + .description("Load a file with predefined variables and functions") + .alias("i"), + ) + .flag( + Flag::new("precision", FlagType::Int) + .description("Specify number precision") + .alias("p"), + ) + .flag( + Flag::new("angle-unit", FlagType::String) + .description("Unit used for angles, either rad or deg. This can also be specified using an environment variable with the name 'ANGLE_UNIT'.") + .alias("a"), + ); - // Command line argument input, execute it and exit. - let mut args = env::args().skip(1); - let mut expr_input: Option = None; - let mut precision = DEFAULT_PRECISION; - loop { - // Get the next argument if possible, otherwise break the loop. - let arg = if let Some(arg) = args.next() { - arg - } else { - break; - }; + app.run(args); +} - match arg.as_ref() { - "-h" | "--help" => { - // The indentation... Will have to do something more scalable in the future. - println!( - " -[kalk help] - -kalk [OPTIONS] [INPUT] --h, --help : show this --i : load a file with predefined functions/variables ---precision : specify number precision - -[Environment variables] -ANGLE_UNIT=(deg/rad) : Sets the default unit used for trigonometric functions. - " - ); - return; - } - "-i" => { - let file_name = &args.next().expect("Expected input file."); // The next argument will be the file name. - let mut file_content = String::new(); - File::open(&file_name) - .expect("Couldn't find file.") - .read_to_string(&mut file_content) - .expect("Failed to read input file."); - - // Parse the input file content, resulting in the symbol table being filled out. - // Output is not needed here. - parser::eval(&mut parser_context, &file_content, precision) - .expect("Failed to parse input file."); - } - "--precision" => { - precision = args - .next() - .expect("Expected precision input.") - .parse::() - .expect("Precision value could not be parsed."); - } +fn default_action(context: &Context) { + let angle_unit = if let Ok(angle_unit) = context.string_flag("angle-unit") { + match angle_unit.as_ref() { + "rad" | "deg" => angle_unit, _ => { - // Main argument. This is expected to be a maths expression. - // After the loop is finished, this will be parsed and outputted. - expr_input = Some(arg); + output::print_err("Invalid angle unit. Expected 'rad' or 'deg'."); + std::process::exit(1); } } + } else { + get_env_angle_unit() + }; + let mut parser_context = parser::Context::new().set_angle_unit(&angle_unit); + let precision = context.int_flag("precision").unwrap_or(53isize) as u32; + + if let Ok(input_file_path) = context.string_flag("input-file") { + load_input_file(&input_file_path, precision, &mut parser_context); } - if let Some(input) = expr_input { - // Direct output - output::eval(&mut parser_context, &input, precision); - } else { + if context.args.len() == 0 { // REPL repl::start(&mut parser_context, precision); + } else { + // Direct output + output::eval(&mut parser_context, &context.args.join(" "), precision); } } -fn get_angle_unit() -> String { +fn load_input_file(file_name: &str, precision: u32, parser_context: &mut parser::Context) { + let mut file_content = String::new(); + File::open(&file_name) + .expect("Couldn't find file.") + .read_to_string(&mut file_content) + .expect("Failed to read input file."); + + // Parse the input file content, resulting in the symbol table being filled out. + // Output is not needed here. + parser::eval(parser_context, &file_content, precision).expect("Failed to parse input file."); +} + +fn get_env_angle_unit() -> String { if let Ok(angle_unit_var) = env::var("ANGLE_UNIT") { angle_unit_var } else {