From deef4361cd4a7fb3e102d6f349c8b91664d449a1 Mon Sep 17 00:00:00 2001 From: eatradish Date: Thu, 28 Mar 2024 13:38:18 +0800 Subject: [PATCH] feat: use own argument parser to replace structopt structopt has been replaced by clap, whose dependency libc does not build on newer architectures (e.g., loongarch64). My previous attempts to port choose to clap v4 resulted in an incompatible change in allow_hyphen_values's behavior - I have reported this issue to the upstream: - https://github.com/clap-rs/clap/discussions/5434 - https://github.com/clap-rs/clap/pull/4187 To simplify things, I wrote my own argument parser for clap.. This change passes `cargo test` . --- Cargo.lock | 227 +--------------------- Cargo.toml | 1 - src/choice/test/get_negative_start_end.rs | 34 ++-- src/choice/test/is_reverse_range.rs | 10 +- src/choice/test/mod.rs | 9 +- src/choice/test/print_choice.rs | 6 +- src/config.rs | 2 +- src/main.rs | 3 +- src/opt.rs | 193 ++++++++++++++++-- src/parse.rs | 8 +- src/parse_error.rs | 10 +- 11 files changed, 221 insertions(+), 282 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd1cc25..4d7f881 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,38 +11,12 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "backslash" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35a89ea09f2c7f3c81711c0db7d389d86a9d66fa15a7067e6fd6dbef863ef786" -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - [[package]] name = "choose" version = "1.3.4" @@ -50,40 +24,6 @@ dependencies = [ "backslash", "lazy_static", "regex", - "structopt", -] - -[[package]] -name = "clap" -version = "2.33.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "heck" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909" -dependencies = [ - "libc", ] [[package]] @@ -92,61 +32,11 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "libc" -version = "0.2.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" - [[package]] name = "memchr" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" - -[[package]] -name = "proc-macro-error" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc175e9777c3116627248584e8f8b3e2987405cabe1c0adf7d1dd28f09dc7880" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc9795ca17eb581285ec44936da7fc2335a3f34f2ddd13118b6f4d515435c50" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "syn-mid", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.18" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -dependencies = [ - "proc-macro2", -] +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "regex" @@ -166,67 +56,6 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "syn" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "syn-mid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thread_local" version = "1.0.1" @@ -235,55 +64,3 @@ checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ "lazy_static", ] - -[[package]] -name = "unicode-segmentation" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" - -[[package]] -name = "unicode-width" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" - -[[package]] -name = "unicode-xid" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 03bb52f..31ee06f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ exclude = [ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -structopt = "0.3" regex = "1" lazy_static = "1" backslash = "0" diff --git a/src/choice/test/get_negative_start_end.rs b/src/choice/test/get_negative_start_end.rs index edbc5bb..e753d52 100644 --- a/src/choice/test/get_negative_start_end.rs +++ b/src/choice/test/get_negative_start_end.rs @@ -3,7 +3,7 @@ use crate::Error; #[test] fn positive_negative_1() { - let config = Config::from_iter(vec!["choose", "2:-1"]); + let config = Config::from_vec(vec!["choose", "2:-1"]); let slice = &[1, 2, 3, 4, 5]; assert_eq!( Some((2, 4)), @@ -13,7 +13,7 @@ fn positive_negative_1() { #[test] fn positive_negative_gt1() { - let config = Config::from_iter(vec!["choose", "1:-3"]); + let config = Config::from_vec(vec!["choose", "1:-3"]); let slice = &[1, 2, 3, 4, 5]; assert_eq!( Some((1, 2)), @@ -23,7 +23,7 @@ fn positive_negative_gt1() { #[test] fn negative_positive() { - let config = Config::from_iter(vec!["choose", "-3:4"]); + let config = Config::from_vec(vec!["choose", "-3:4"]); let slice = &[1, 2, 3, 4, 5]; assert_eq!( Some((2, 4)), @@ -33,7 +33,7 @@ fn negative_positive() { #[test] fn negative_negative() { - let config = Config::from_iter(vec!["choose", "-3:-4"]); + let config = Config::from_vec(vec!["choose", "-3:-4"]); let slice = &[1, 2, 3, 4, 5]; assert_eq!( Some((2, 1)), @@ -43,7 +43,7 @@ fn negative_negative() { #[test] fn negative1_negative1() { - let config = Config::from_iter(vec!["choose", "-1:-1"]); + let config = Config::from_vec(vec!["choose", "-1:-1"]); let slice = &[1, 2, 3, 4, 5]; assert_eq!( Some((4, 4)), @@ -53,7 +53,7 @@ fn negative1_negative1() { #[test] fn negative_nonexisting_positive() { - let config = Config::from_iter(vec!["choose", "-3:9"]); + let config = Config::from_vec(vec!["choose", "-3:9"]); let slice = &[1, 2, 3, 4, 5]; assert_eq!( Some((2, 4)), @@ -63,7 +63,7 @@ fn negative_nonexisting_positive() { #[test] fn negative_negative_on_empty() { - let config = Config::from_iter(vec!["choose", "-3:-1"]); + let config = Config::from_vec(vec!["choose", "-3:-1"]); let slice = &[0u8; 0]; assert_eq!( None, @@ -73,7 +73,7 @@ fn negative_negative_on_empty() { #[test] fn negative_positive_on_empty() { - let config = Config::from_iter(vec!["choose", "-3:5"]); + let config = Config::from_vec(vec!["choose", "-3:5"]); let slice = &[0u8; 0]; assert_eq!( None, @@ -83,7 +83,7 @@ fn negative_positive_on_empty() { #[test] fn positive_negative_on_empty() { - let config = Config::from_iter(vec!["choose", "2:-1"]); + let config = Config::from_vec(vec!["choose", "2:-1"]); let slice = &[0u8; 0]; assert_eq!( None, @@ -93,7 +93,7 @@ fn positive_negative_on_empty() { #[test] fn negative_positive_all() { - let config = Config::from_iter(vec!["choose", "-5:9"]); + let config = Config::from_vec(vec!["choose", "-5:9"]); let slice = &[0, 1, 2, 3]; assert_eq!( Some((0, 3)), @@ -103,7 +103,7 @@ fn negative_positive_all() { #[test] fn negative_positive_some() { - let config = Config::from_iter(vec!["choose", "-5:2"]); + let config = Config::from_vec(vec!["choose", "-5:2"]); let slice = &[0, 1, 2, 3]; assert_eq!( Some((0, 2)), @@ -113,7 +113,7 @@ fn negative_positive_some() { #[test] fn positive_negative_all() { - let config = Config::from_iter(vec!["choose", "9:-5"]); + let config = Config::from_vec(vec!["choose", "9:-5"]); let slice = &[0, 1, 2, 3]; assert_eq!( Some((3, 0)), @@ -123,7 +123,7 @@ fn positive_negative_all() { #[test] fn positive_negative_some() { - let config = Config::from_iter(vec!["choose", "9:-2"]); + let config = Config::from_vec(vec!["choose", "9:-2"]); let slice = &[0, 1, 2, 3]; assert_eq!( Some((3, 2)), @@ -133,7 +133,7 @@ fn positive_negative_some() { #[test] fn positive_negative_same() { - let config = Config::from_iter(vec!["choose", "1:-3"]); + let config = Config::from_vec(vec!["choose", "1:-3"]); let slice = &[0, 1, 2, 3]; assert_eq!( Some((1, 1)), @@ -144,7 +144,7 @@ fn positive_negative_same() { #[test] fn error_when_choice_is_isize_min() { let isize_min = format!("{}", isize::MIN); - let config = Config::from_iter(vec!["choose", &isize_min]); + let config = Config::from_vec(vec!["choose", &isize_min]); let slice = &[0, 1, 2, 3]; let err = config.opt.choices[0] @@ -161,7 +161,7 @@ fn error_when_choice_is_isize_min() { #[test] fn error_when_choice_start_is_isize_min() { let choice = format!("{}:4", isize::MIN); - let config = Config::from_iter(vec!["choose", &choice]); + let config = Config::from_vec(vec!["choose", &choice]); let slice = &[0, 1, 2, 3]; let err = config.opt.choices[0] @@ -178,7 +178,7 @@ fn error_when_choice_start_is_isize_min() { #[test] fn error_when_choice_end_is_isize_min() { let choice = format!("4:{}", isize::MIN); - let config = Config::from_iter(vec!["choose", &choice]); + let config = Config::from_vec(vec!["choose", &choice]); let slice = &[0, 1, 2, 3]; let err = config.opt.choices[0] diff --git a/src/choice/test/is_reverse_range.rs b/src/choice/test/is_reverse_range.rs index ae4d64b..cb7a7f5 100644 --- a/src/choice/test/is_reverse_range.rs +++ b/src/choice/test/is_reverse_range.rs @@ -2,30 +2,30 @@ use super::*; #[test] fn is_field_reversed() { - let config = Config::from_iter(vec!["choose", "0"]); + let config = Config::from_vec(vec!["choose", "0"]); assert_eq!(false, config.opt.choices[0].is_reverse_range()); } #[test] fn is_field_range_no_start_reversed() { - let config = Config::from_iter(vec!["choose", ":2"]); + let config = Config::from_vec(vec!["choose", ":2"]); assert_eq!(false, config.opt.choices[0].is_reverse_range()); } #[test] fn is_field_range_no_end_reversed() { - let config = Config::from_iter(vec!["choose", "2:"]); + let config = Config::from_vec(vec!["choose", "2:"]); assert_eq!(false, config.opt.choices[0].is_reverse_range()); } #[test] fn is_field_range_no_start_or_end_reversed() { - let config = Config::from_iter(vec!["choose", ":"]); + let config = Config::from_vec(vec!["choose", ":"]); assert_eq!(false, config.opt.choices[0].is_reverse_range()); } #[test] fn is_reversed_field_range_reversed() { - let config = Config::from_iter(vec!["choose", "4:2"]); + let config = Config::from_vec(vec!["choose", "4:2"]); assert_eq!(true, config.opt.choices[0].is_reverse_range()); } diff --git a/src/choice/test/mod.rs b/src/choice/test/mod.rs index 762804c..ffdda6d 100644 --- a/src/choice/test/mod.rs +++ b/src/choice/test/mod.rs @@ -1,20 +1,15 @@ use crate::config::Config; use crate::opt::Opt; -use std::ffi::OsString; use std::io::{self, BufWriter, Write}; -use structopt::StructOpt; mod get_negative_start_end; mod is_reverse_range; mod print_choice; impl Config { - pub fn from_iter(iter: I) -> Self - where - I: IntoIterator, - I::Item: Into + Clone, + pub fn from_vec(v: Vec<&str>) -> Self { - Config::new(Opt::from_iter(iter)) + Config::new(Opt::new(v)) } } diff --git a/src/choice/test/print_choice.rs b/src/choice/test/print_choice.rs index 63a63b5..42c3782 100644 --- a/src/choice/test/print_choice.rs +++ b/src/choice/test/print_choice.rs @@ -1,7 +1,7 @@ use super::*; fn test_fn(vec: Vec<&str>, input: &str, output: &str) { - let config = Config::from_iter(vec); + let config = Config::from_vec(vec); let mut handle = BufWriter::new(MockStdout::new()); config.opt.choices[0] @@ -26,7 +26,7 @@ fn print_after_end() { #[test] fn print_out_of_order() { - let config = Config::from_iter(vec!["choose", "3", "1"]); + let config = Config::from_vec(vec!["choose", "3", "1"]); let mut handle = BufWriter::new(MockStdout::new()); let mut handle1 = BufWriter::new(MockStdout::new()); @@ -513,7 +513,7 @@ fn print_1_to_3_with_output_field_separator_rust_syntax_inclusive() { #[test] fn print_1_and_3_with_output_field_separator_rust_syntax_inclusive() { - let config = Config::from_iter(vec!["choose", "1", "3", "-o", "#"]); + let config = Config::from_vec(vec!["choose", "1", "3", "-o", "#"]); let mut handle = BufWriter::new(MockStdout::new()); config.opt.choices[0] .print_choice(&String::from("a b c d"), &config, &mut handle) diff --git a/src/config.rs b/src/config.rs index 97acaa3..c2455c5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,7 +12,7 @@ pub struct Config { impl Config { pub fn new(mut opt: Opt) -> Self { - for mut choice in &mut opt.choices { + for choice in &mut opt.choices { if (opt.exclusive && choice.kind == ChoiceKind::ColonRange) || choice.kind == ChoiceKind::RustExclusiveRange { diff --git a/src/main.rs b/src/main.rs index 07f66d1..4e9c955 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ use std::fs::File; use std::io::{self, Read}; use std::process; -use structopt::StructOpt; #[macro_use] extern crate lazy_static; @@ -25,7 +24,7 @@ use result::Result; use writer::WriteReceiver; fn main() { - let opt = Opt::from_args(); + let opt = Opt::parse(); let stdout = io::stdout(); let lock = stdout.lock(); diff --git a/src/opt.rs b/src/opt.rs index 94f1236..c6603aa 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -1,49 +1,214 @@ use std::path::PathBuf; -use structopt::StructOpt; +use std::process; use crate::choice::Choice; -use crate::parse; +use crate::parse::{self, choice, PARSE_CHOICE_RE}; -#[derive(Debug, StructOpt)] -#[structopt(name = "choose", about = "`choose` sections from each line of files")] -#[structopt(setting = structopt::clap::AppSettings::AllowLeadingHyphen)] pub struct Opt { /// Choose fields by character number - #[structopt(short, long)] pub character_wise: bool, /// Activate debug mode - #[structopt(short, long)] pub debug: bool, /// Use exclusive ranges, similar to array indexing in many programming languages - #[structopt(short = "x", long)] pub exclusive: bool, /// Specify field separator other than whitespace, using Rust `regex` syntax - #[structopt(short, long)] pub field_separator: Option, /// Input file - #[structopt(short, long, parse(from_os_str))] pub input: Option, /// Use non-greedy field separators - #[structopt(short, long)] pub non_greedy: bool, /// Index from 1 instead of 0 - #[structopt(long)] pub one_indexed: bool, /// Specify output field separator - #[structopt(short, long, parse(from_str = parse::output_field_separator))] pub output_field_separator: Option, /// Fields to print. Either a, a:b, a..b, or a..=b, where a and b are integers. The beginning /// or end of a range can be omitted, resulting in including the beginning or end of the line, /// respectively. a:b is inclusive of b (unless overridden by -x). a..b is /// exclusive of b and a..=b is inclusive of b. - #[structopt(required = true, min_values = 1, parse(try_from_str = parse::choice))] pub choices: Vec, } + +const LONG_ARGS: &[&str] = &[ + "character-wise", + "debug", + "exclusive", + "field-separator", + "input", + "non-greedy", + "one-indexed", + "output-field-separator", +]; + +const SHORT_ARGS: &[&str] = &["c", "d", "x", "f", "i", "n", "", "o"]; + +macro_rules! HELP { + () => { +r"{} {} +`choose` sections from each line of files + +USAGE: + choose [FLAGS] [OPTIONS] ... + +FLAGS: + -c, --character-wise Choose fields by character number + -d, --debug Activate debug mode + -x, --exclusive Use exclusive ranges, similar to array indexing in many programming languages + -h, --help Prints help information + -n, --non-greedy Use non-greedy field separators + --one-indexed Index from 1 instead of 0 + -V, --version Prints version information + +OPTIONS: + -f, --field-separator + Specify field separator other than whitespace, using Rust `regex` syntax + + -i, --input Input file + -o, --output-field-separator Specify output field separator + +ARGS: + ... Fields to print. Either a, a:b, a..b, or a..=b, where a and b are integers. The beginning or end + of a range can be omitted, resulting in including the beginning or end of the line, + respectively. a:b is inclusive of b (unless overridden by -x). a..b is exclusive of b and a..=b + is inclusive of b + " + }; +} + +impl Opt { + pub fn new(args: Vec<&str>) -> Self { + let mut choices = vec![]; + let mut character_wise = false; + let mut debug = false; + let mut exclusive = false; + let mut field_separator = None; + let mut input = None; + let mut non_greedy = false; + let mut one_indexed = false; + let mut output_field_separator = None; + + let mut argument_value = false; + + for (i, c) in args.iter().enumerate() { + if argument_value { + if c.starts_with("-") { + panic!("Argument {} has no value", args[i - 1]); + } + + argument_value = false; + continue; + } + + if is_choice(c) { + let choice = choice(c).expect(&format!("Failed to parse choice {}", c)); + choices.push(choice); + continue; + } + + match c.strip_prefix("--") { + Some(c) if !LONG_ARGS.contains(&c) => panic!("Arg {} does not support.", c), + Some(c) => { + match c { + "character-wise" => character_wise = true, + "debug" => debug = true, + "exclusive" => exclusive = true, + "field-separator" => { + argument_value = true; + let value = args.get(i + 1).expect(&format!("Arg {} has no value", c)); + field_separator = Some(value.to_string()); + } + "input" => { + argument_value = true; + let value = args.get(i + 1).expect(&format!("Arg {} has no value", c)); + input = Some(PathBuf::from(value)); + } + "non-greedy" => non_greedy = true, + "one-indexed" => one_indexed = true, + "output-field-separator" => { + argument_value = true; + let value = args.get(i + 1).expect(&format!("Arg {} has no value", c)); + output_field_separator = Some(parse::output_field_separator(value).unwrap()); + } + _ => unreachable!(), + } + continue; + } + None => {} + } + + match c.strip_prefix("-") { + Some(c) if !SHORT_ARGS.contains(&c) => panic!("Arg {} does not support.", c), + Some(c) => { + match c { + "c" => character_wise = true, + "d" => debug = true, + "x" => exclusive = true, + "f" => { + argument_value = true; + let value = args.get(i + 1).expect(&format!("Arg {} has no value", c)); + field_separator = Some(value.to_string()); + } + "i" => { + argument_value = true; + let value = args.get(i + 1).expect(&format!("Arg {} has no value", c)); + input = Some(PathBuf::from(value)); + } + "n" => non_greedy = true, + "o" => { + argument_value = true; + let value = args.get(i + 1).expect(&format!("Arg {} has no value", c)); + output_field_separator = Some(parse::output_field_separator(value).unwrap()); + } + _ => unreachable!(), + } + continue; + } + None => {} + } + } + + if choices.is_empty() { + println!(HELP!(), env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + process::exit(1); + } + + Self { + character_wise, + debug, + exclusive, + field_separator, + input, + non_greedy, + one_indexed, + output_field_separator, + choices, + } + } + + pub fn parse() -> Self { + let args = std::env::args().collect::>(); + + if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { + println!(HELP!(), env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + process::exit(0); + } + + if args.contains(&"--version".to_string()) || args.contains(&"-V".to_string()) { + println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + process::exit(0); + } + + Self::new(args.iter().map(|x| x.as_str()).collect::>()) + } +} + +fn is_choice(arg: &str) -> bool { + PARSE_CHOICE_RE.captures_iter(arg).next().is_some() || arg.parse::().is_ok() +} diff --git a/src/parse.rs b/src/parse.rs index 520d09c..bb38156 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,3 +1,5 @@ +use std::string::FromUtf8Error; + use backslash::escape_ascii; use regex::Regex; @@ -6,7 +8,7 @@ use crate::error::ParseRangeError; use crate::parse_error::ParseError; lazy_static! { - static ref PARSE_CHOICE_RE: Regex = Regex::new(r"^(-?\d*)(:|\.\.=?)(-?\d*)$").unwrap(); + pub static ref PARSE_CHOICE_RE: Regex = Regex::new(r"^(-?\d*)(:|\.\.=?)(-?\d*)$").unwrap(); } pub fn choice(src: &str) -> Result { @@ -61,8 +63,8 @@ pub fn choice(src: &str) -> Result { Ok(Choice::new(start, end, kind)) } -pub fn output_field_separator(src: &str) -> String { - escape_ascii(src).unwrap() +pub fn output_field_separator(src: &str) -> Result { + escape_ascii(src) } #[cfg(test)] diff --git a/src/parse_error.rs b/src/parse_error.rs index b49b443..797d0cd 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -1,14 +1,16 @@ +use std::fmt::Display; + #[derive(Debug)] pub enum ParseError { ParseIntError(std::num::ParseIntError), ParseRangeError(crate::error::ParseRangeError), } -impl ToString for ParseError { - fn to_string(&self) -> String { +impl Display for ParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ParseError::ParseIntError(e) => e.to_string(), - ParseError::ParseRangeError(e) => e.to_string(), + ParseError::ParseIntError(e) => write!(f, "{}", e), + ParseError::ParseRangeError(e) => write!(f, "{}", e), } } }