@@ -22,9 +22,36 @@ pub fn parse() -> Result<Options, ArgsError> {
2222/// (*all* arguments, including binary name).
2323#[ inline]
2424pub fn parse_from_argv < I , T > ( argv : I ) -> Result < Options , ArgsError >
25- where I : IntoIterator < Item =T > , T : Into < OsString >
25+ where I : IntoIterator < Item =T > , T : Clone + Into < OsString >
2626{
27- let matches = create_parser ( ) . get_matches_from ( argv) ;
27+ let argv: Vec < _ > = argv. into_iter ( ) . collect ( ) ;
28+
29+ // We allow `gisht JohnDoe/foo` to be an alias of `gisht run JohnDoe/foo`.
30+ // To support this, some preprocessing on the arguments has to be done
31+ // in order to pick the parser with or without subcommands.
32+ let parser = {
33+ // Determine whether the first non-flag argument is one of the gist commands.
34+ let first_arg = argv. iter ( ) . skip ( 1 )
35+ . map ( |arg| {
36+ let arg: OsString = arg. clone ( ) . into ( ) ;
37+ arg. into_string ( ) . unwrap_or_else ( |_| String :: new ( ) )
38+ } )
39+ . find ( |arg| !arg. starts_with ( "-" ) )
40+ . unwrap_or_else ( || String :: new ( ) ) ;
41+
42+ // If it is, use the full argument parser which recognizes those commands.
43+ match Command :: from_str ( & first_arg) {
44+ Ok ( _) => create_full_parser ( ) ,
45+ Err ( _) => {
46+ // If it's not, the parser will already have "run" command baked in.
47+ let mut parser = create_parser_base ( ) ;
48+ parser = configure_run_gist_parser ( parser) ;
49+ parser
50+ } ,
51+ }
52+ } ;
53+
54+ let matches = parser. get_matches_from ( argv) ;
2855 Options :: try_from ( matches)
2956}
3057
@@ -37,10 +64,10 @@ pub struct Options {
3764 /// Corresponds to the number of times the -v flag has been passed.
3865 /// If -q has been used instead, this will be negative.
3966 pub verbosity : isize ,
40- /// Gist command that's been issued, if any .
41- pub command : Option < Command > ,
42- /// URI to the gist to operate on, if any .
43- pub gist : Option < gist:: Uri > ,
67+ /// Gist command that's been issued.
68+ pub command : Command ,
69+ /// URI to the gist to operate on.
70+ pub gist : gist:: Uri ,
4471 /// Arguments to the gist, if any.
4572 /// This is only used if command == Some(Command::Run).
4673 pub gist_args : Option < Vec < String > > ,
@@ -60,19 +87,22 @@ impl<'a> TryFrom<ArgMatches<'a>> for Options {
6087 let verbose_count = matches. occurrences_of ( OPT_VERBOSE ) as isize ;
6188 let quiet_count = matches. occurrences_of ( OPT_QUIET ) as isize ;
6289
63- // Command may be optionally provided, alongside the gist argument.
64- let ( subcmd, submatches) = matches. subcommand ( ) ;
65- let command = Command :: from_str ( subcmd) . ok ( ) ;
66- let gist = match submatches. and_then ( |m| m. value_of ( ARG_GIST ) ) {
67- Some ( g) => Some ( try!( gist:: Uri :: from_str ( g) ) ) ,
68- _ => None ,
69- } ;
90+ // Command may be optionally provided.
91+ // If it isn't, it means the "run" default was used, and so all the arguments
92+ // are arguments to `gisht run`.
93+ let ( cmd, cmd_matches) = matches. subcommand ( ) ;
94+ let cmd_matches = cmd_matches. unwrap_or ( & matches) ;
95+ let command = Command :: from_str ( cmd) . unwrap_or ( Command :: Run ) ;
96+
97+ // Parse out the gist URI argument.
98+ let gist = try!( gist:: Uri :: from_str (
99+ cmd_matches. value_of ( ARG_GIST ) . unwrap ( )
100+ ) ) ;
70101
71102 // For the "run" command, arguments may be provided.
72- let mut gist_args = submatches
73- . and_then ( |m| m. values_of ( ARG_GIST_ARGV ) )
103+ let mut gist_args = cmd_matches. values_of ( ARG_GIST_ARGV )
74104 . map ( |argv| argv. map ( |v| v. to_owned ( ) ) . collect ( ) ) ;
75- if command == Some ( Command :: Run ) && gist_args. is_none ( ) {
105+ if command == Command :: Run && gist_args. is_none ( ) {
76106 gist_args = Some ( vec ! [ ] ) ;
77107 }
78108
@@ -159,8 +189,34 @@ const OPT_VERBOSE: &'static str = "verbose";
159189const OPT_QUIET : & ' static str = "quiet" ;
160190
161191
162- /// Create the argument parser.
163- fn create_parser < ' p > ( ) -> Parser < ' p > {
192+ /// Create the full argument parser.
193+ /// This parser accepts the entire gamut of the application's arguments and flags.
194+ fn create_full_parser < ' p > ( ) -> Parser < ' p > {
195+ let parser = create_parser_base ( ) ;
196+
197+ parser
198+ . setting ( AppSettings :: SubcommandRequiredElseHelp )
199+ . setting ( AppSettings :: VersionlessSubcommands )
200+
201+ . subcommand ( configure_run_gist_parser (
202+ SubCommand :: with_name ( Command :: Run . name ( ) )
203+ . about ( "Run the specified gist" ) ) )
204+ . subcommand ( SubCommand :: with_name ( Command :: Which . name ( ) )
205+ . about ( "Output the path to gist's binary" )
206+ . arg ( gist_arg ( "Gist to locate" ) ) )
207+ . subcommand ( SubCommand :: with_name ( Command :: Print . name ( ) )
208+ . about ( "Print the source code of gist's binary" )
209+ . arg ( gist_arg ( "Gist to print" ) ) )
210+ . subcommand ( SubCommand :: with_name ( Command :: Open . name ( ) )
211+ . about ( "Open the gist's webpage" )
212+ . arg ( gist_arg ( "Gist to open" ) ) )
213+ }
214+
215+ /// Create the "base" argument parser object.
216+ ///
217+ /// This base contains all the shared configuration (like the application name)
218+ /// and the flags shared by all gist subcommands.
219+ fn create_parser_base < ' p > ( ) -> Parser < ' p > {
164220 let mut parser = Parser :: new ( APP_NAME ) ;
165221 if let Some ( version) = option_env ! ( "CARGO_PKG_VERSION" ) {
166222 parser = parser. version ( version) ;
@@ -169,8 +225,6 @@ fn create_parser<'p>() -> Parser<'p> {
169225 . about ( APP_DESC )
170226
171227 . setting ( AppSettings :: ArgRequiredElseHelp )
172- . setting ( AppSettings :: SubcommandRequiredElseHelp )
173- . setting ( AppSettings :: VersionlessSubcommands )
174228 . setting ( AppSettings :: UnifiedHelpMessage )
175229 . setting ( AppSettings :: DeriveDisplayOrder )
176230
@@ -187,28 +241,22 @@ fn create_parser<'p>() -> Parser<'p> {
187241 . help ( "Decrease logging verbosity" ) )
188242 . help_short ( "H" )
189243 . version_short ( "V" )
244+ }
190245
191- . subcommand ( SubCommand :: with_name ( Command :: Run . name ( ) )
192- . about ( "Run the specified gist" )
193- . arg ( gist_arg ( "Gist to run" ) )
194- // This argument spec is capturing everything after the gist URI,
195- // allowing for the arguments to be passed to the gist itself.
196- . arg ( Arg :: with_name ( ARG_GIST_ARGV )
197- . required ( false )
198- . multiple ( true )
199- . use_delimiter ( false )
200- . help ( "Optional arguments passed to the gist" )
201- . value_name ( "ARGS" ) )
202- . setting ( AppSettings :: TrailingVarArg ) )
203- . subcommand ( SubCommand :: with_name ( Command :: Which . name ( ) )
204- . about ( "Output the path to gist's binary" )
205- . arg ( gist_arg ( "Gist to locate" ) ) )
206- . subcommand ( SubCommand :: with_name ( Command :: Print . name ( ) )
207- . about ( "Print the source code of gist's binary" )
208- . arg ( gist_arg ( "Gist to print" ) ) )
209- . subcommand ( SubCommand :: with_name ( Command :: Open . name ( ) )
210- . about ( "Open the gist's webpage" )
211- . arg ( gist_arg ( "Gist to open" ) ) )
246+ /// Configure a parser for the "run" command.
247+ /// This is also used when there is no command given.
248+ fn configure_run_gist_parser < ' p > ( parser : Parser < ' p > ) -> Parser < ' p > {
249+ parser
250+ . arg ( gist_arg ( "Gist to run" ) )
251+ // This argument spec is capturing everything after the gist URI,
252+ // allowing for the arguments to be passed to the gist itself.
253+ . arg ( Arg :: with_name ( ARG_GIST_ARGV )
254+ . required ( false )
255+ . multiple ( true )
256+ . use_delimiter ( false )
257+ . help ( "Optional arguments passed to the gist" )
258+ . value_name ( "ARGS" ) )
259+ . setting ( AppSettings :: TrailingVarArg )
212260}
213261
214262/// Create the GIST argument to various gist subcommands.
0 commit comments