1
1
#[ cfg( not( feature = "clap" ) ) ]
2
- use eyre:: Report ;
2
+ use eyre:: { Report , WrapErr } ;
3
3
4
4
#[ cfg( feature = "clap" ) ]
5
5
use clap:: {
6
6
builder:: ValueParser , value_parser, Arg , ArgAction , ArgGroup , ArgMatches , Args , Command ,
7
7
FromArgMatches , Parser , Subcommand , ValueEnum ,
8
8
} ;
9
9
10
- #[ cfg( feature = "clap" ) ]
11
- use std:: collections:: VecDeque ;
12
- use std:: { ffi:: OsString , num:: ParseIntError , path:: PathBuf } ;
10
+ use std:: { collections:: VecDeque , ffi:: OsString , num:: ParseIntError , path:: PathBuf } ;
11
+ #[ cfg( not( feature = "clap" ) ) ]
12
+ use std:: {
13
+ io:: { self , Write } ,
14
+ process,
15
+ } ;
13
16
14
17
#[ derive( Debug ) ]
15
18
#[ cfg_attr( feature = "clap" , derive( Parser ) ) ]
@@ -22,9 +25,125 @@ pub struct ZipCli {
22
25
pub command : ZipCommand ,
23
26
}
24
27
28
+ #[ cfg( not( feature = "clap" ) ) ]
29
+ #[ derive( Debug ) ]
30
+ enum SubcommandName {
31
+ Compress ,
32
+ Info ,
33
+ Extract ,
34
+ }
35
+
25
36
#[ cfg( not( feature = "clap" ) ) ]
26
37
impl ZipCli {
27
- pub fn parse_argv ( _argv : impl IntoIterator < Item = OsString > ) -> Result < Self , Report > {
38
+ const VERSION : & ' static str = env ! ( "CARGO_PKG_VERSION" ) ;
39
+ const DESCRIPTION : & ' static str = env ! ( "CARGO_PKG_DESCRIPTION" ) ;
40
+ /* This should really be CARGO_BIN_NAME according to clap and cargo's own docs, but that env
41
+ * var wasn't found for some reason, so we use the crate name since that's the same for zip-cli
42
+ * and zip-clite at least. https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates */
43
+ const BIN_NAME : & ' static str = env ! ( "CARGO_CRATE_NAME" ) ;
44
+ const ARGV_PARSE_FAILED_EXIT_CODE : i32 = 2 ;
45
+ const NON_FAILURE_EXIT_CODE : i32 = 0 ;
46
+
47
+ fn generate_version_text ( ) -> String {
48
+ format ! ( "{} {}\n " , Self :: BIN_NAME , Self :: VERSION )
49
+ }
50
+
51
+ fn generate_full_help_text ( ) -> String {
52
+ format ! (
53
+ r"
54
+ {}
55
+
56
+ Usage: {} [OPTIONS] <COMMAND>
57
+
58
+ Commands:
59
+ compress
60
+ info
61
+ extract
62
+
63
+ Options:
64
+ -v, --verbose
65
+ -h, --help
66
+ -V, --version
67
+
68
+ Build this binary with '--features clap' for more thorough help text.
69
+ " ,
70
+ Self :: DESCRIPTION ,
71
+ Self :: BIN_NAME
72
+ )
73
+ }
74
+
75
+ fn generate_brief_help_text ( context : & str ) -> String {
76
+ format ! (
77
+ r"
78
+ error: {context}
79
+
80
+ Usage: {} [OPTIONS] <COMMAND>
81
+
82
+ For more information, try '--help'.
83
+ " ,
84
+ Self :: BIN_NAME
85
+ )
86
+ }
87
+
88
+ fn parse_up_to_subcommand_name ( argv : & mut VecDeque < OsString > ) -> ( bool , SubcommandName ) {
89
+ let mut verbose: bool = false ;
90
+ let mut subcommand_name: Option < SubcommandName > = None ;
91
+ while subcommand_name. is_none ( ) {
92
+ match argv. pop_front ( ) {
93
+ None => {
94
+ let help_text = Self :: generate_full_help_text ( ) ;
95
+ io:: stderr ( )
96
+ . write_all ( help_text. as_bytes ( ) )
97
+ . wrap_err ( "Failed to write zero-arg help text to stderr" )
98
+ . unwrap ( ) ;
99
+ process:: exit ( Self :: ARGV_PARSE_FAILED_EXIT_CODE ) ;
100
+ }
101
+ Some ( arg) => match arg. as_encoded_bytes ( ) {
102
+ b"-v" | b"--verbose" => verbose = true ,
103
+ b"-V" | b"--version" => {
104
+ let version_text = Self :: generate_version_text ( ) ;
105
+ io:: stdout ( )
106
+ . write_all ( version_text. as_bytes ( ) )
107
+ . wrap_err ( "Failed to write version to stdout" )
108
+ . unwrap ( ) ;
109
+ process:: exit ( Self :: NON_FAILURE_EXIT_CODE )
110
+ }
111
+ b"-h" | b"--help" => {
112
+ let help_text = Self :: generate_full_help_text ( ) ;
113
+ io:: stdout ( )
114
+ . write_all ( help_text. as_bytes ( ) )
115
+ . wrap_err ( "Failed to write -h/--help help text to stdout" )
116
+ . unwrap ( ) ;
117
+ process:: exit ( Self :: NON_FAILURE_EXIT_CODE ) ;
118
+ }
119
+ b"compress" => subcommand_name = Some ( SubcommandName :: Compress ) ,
120
+ b"info" => subcommand_name = Some ( SubcommandName :: Info ) ,
121
+ b"extract" => subcommand_name = Some ( SubcommandName :: Extract ) ,
122
+ arg_bytes => {
123
+ let context = if arg_bytes. starts_with ( b"-" ) {
124
+ format ! ( "unrecognized flag {arg:?}" )
125
+ } else {
126
+ format ! ( "unrecognized subcommand name {arg:?}" )
127
+ } ;
128
+ let help_text = Self :: generate_brief_help_text ( & context) ;
129
+ io:: stderr ( )
130
+ . write_all ( help_text. as_bytes ( ) )
131
+ . wrap_err ( "Failed to write unrecognized arg text to stderr" )
132
+ . unwrap ( ) ;
133
+ process:: exit ( Self :: ARGV_PARSE_FAILED_EXIT_CODE )
134
+ }
135
+ } ,
136
+ }
137
+ }
138
+ ( verbose, subcommand_name. unwrap ( ) )
139
+ }
140
+
141
+ pub fn parse_argv ( argv : impl IntoIterator < Item = OsString > ) -> Result < Self , Report > {
142
+ let mut argv: VecDeque < OsString > = argv. into_iter ( ) . collect ( ) ;
143
+ let _exe_name = argv. pop_front ( ) . unwrap ( ) ;
144
+ let ( verbose, subcommand_name) = Self :: parse_up_to_subcommand_name ( & mut argv) ;
145
+ dbg ! ( verbose) ;
146
+ dbg ! ( subcommand_name) ;
28
147
todo ! ( )
29
148
}
30
149
}
0 commit comments