2022-09-20 17:01:17 +03:00
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
package cli
import (
"fmt"
"strings"
)
var _ = fmt . Print
func ( self * Command ) parse_args ( ctx * Context , args [ ] string ) error {
2022-09-23 10:00:41 +03:00
args_to_parse := make ( [ ] string , len ( args ) )
2022-09-20 17:01:17 +03:00
copy ( args_to_parse , args )
ctx . SeenCommands = append ( ctx . SeenCommands , self )
2023-02-19 18:07:17 +03:00
if self . IgnoreAllArgs {
self . Args = args
return nil
}
2022-09-20 17:01:17 +03:00
var expecting_arg_for * Option
options_allowed := true
consume_arg := func ( ) string { ans := args_to_parse [ 0 ] ; args_to_parse = args_to_parse [ 1 : ] ; return ans }
2022-09-23 14:47:32 +03:00
handle_option := func ( opt_str string , has_val bool , opt_val string , val_not_allowed bool ) error {
2022-09-30 10:59:06 +03:00
possible_options := self . FindOptions ( opt_str )
var opt * Option
if len ( possible_options ) == 1 {
opt = possible_options [ 0 ]
opt_str = opt . MatchingAlias ( NormalizeOptionName ( opt_str ) , ! strings . HasPrefix ( opt_str , "--" ) )
} else if len ( possible_options ) == 0 {
2022-09-30 11:48:21 +03:00
possibles := self . SuggestionsForOption ( opt_str , 2 )
if len ( possibles ) > 0 {
return & ParseError { Message : fmt . Sprintf ( "Unknown option: :yellow:`%s`. Did you mean:\n\t%s" , opt_str , strings . Join ( possibles , "\n\t" ) ) }
}
2022-09-20 17:01:17 +03:00
return & ParseError { Message : fmt . Sprintf ( "Unknown option: :yellow:`%s`" , opt_str ) }
2022-09-30 10:59:06 +03:00
} else {
ambi := make ( [ ] string , len ( possible_options ) )
for i , o := range possible_options {
ambi [ i ] = o . MatchingAlias ( NormalizeOptionName ( opt_str ) , ! strings . HasPrefix ( opt_str , "--" ) )
2022-12-14 13:15:43 +03:00
if ambi [ i ] == opt_str {
opt = o
break
}
}
if opt == nil {
return & ParseError { Message : fmt . Sprintf ( "Ambiguous option: :yellow:`%s` could be any of: %s" , opt_str , strings . Join ( ambi , ", " ) ) }
2022-09-30 10:59:06 +03:00
}
2022-09-20 17:01:17 +03:00
}
opt . seen_option = opt_str
2022-09-21 02:37:40 +03:00
needs_arg := opt . needs_argument ( )
2022-09-23 14:47:32 +03:00
if needs_arg && val_not_allowed {
return & ParseError { Message : fmt . Sprintf ( "The option : :yellow:`%s` must be followed by a value not another option" , opt_str ) }
}
2022-09-20 17:01:17 +03:00
if has_val {
2022-09-21 02:37:40 +03:00
if ! needs_arg {
2022-09-20 17:01:17 +03:00
return & ParseError { Message : fmt . Sprintf ( "The option: :yellow:`%s` does not take values" , opt_str ) }
}
return opt . add_value ( opt_val )
2022-09-21 02:37:40 +03:00
} else if needs_arg {
2022-09-20 17:01:17 +03:00
expecting_arg_for = opt
2022-09-21 02:37:40 +03:00
} else {
opt . add_value ( "" )
2022-09-20 17:01:17 +03:00
}
return nil
}
2022-09-23 10:00:41 +03:00
for len ( args_to_parse ) > 0 {
2022-09-20 17:01:17 +03:00
arg := consume_arg ( )
if expecting_arg_for == nil {
if options_allowed && strings . HasPrefix ( arg , "-" ) && arg != "-" {
// handle option arg
if arg == "--" {
options_allowed = false
continue
}
2022-09-23 10:00:41 +03:00
opt_str := arg
2022-09-20 17:01:17 +03:00
opt_val := ""
has_val := false
2022-09-23 10:00:41 +03:00
if strings . HasPrefix ( opt_str , "--" ) {
2022-09-20 17:01:17 +03:00
parts := strings . SplitN ( arg , "=" , 2 )
if len ( parts ) > 1 {
has_val = true
opt_val = parts [ 1 ]
}
opt_str = parts [ 0 ]
2022-09-23 14:47:32 +03:00
err := handle_option ( opt_str , has_val , opt_val , false )
if err != nil {
return err
}
2022-09-20 17:01:17 +03:00
} else {
2022-09-23 14:47:32 +03:00
runes := [ ] rune ( opt_str [ 1 : ] )
for i , sl := range runes {
err := handle_option ( "-" + string ( sl ) , false , "" , i < len ( runes ) - 1 )
2022-09-20 17:01:17 +03:00
if err != nil {
return err
}
}
}
} else {
// handle non option arg
2022-09-20 18:14:16 +03:00
if self . AllowOptionsAfterArgs <= len ( self . Args ) {
2022-09-20 17:01:17 +03:00
options_allowed = false
}
if self . HasSubCommands ( ) {
2022-09-30 13:07:03 +03:00
possible_cmds := self . FindSubCommands ( arg )
if len ( possible_cmds ) == 1 {
return possible_cmds [ 0 ] . parse_args ( ctx , args_to_parse )
}
if ! self . SubCommandIsOptional {
if len ( possible_cmds ) == 0 {
2022-09-30 17:07:59 +03:00
possibles := self . SuggestionsForCommand ( arg , 2 )
if len ( possibles ) > 0 {
return & ParseError { Message : fmt . Sprintf ( "Unknown subcommand: :yellow:`%s`. Did you mean:\n\t%s" , arg , strings . Join ( possibles , "\n\t" ) ) }
}
2022-09-20 17:01:17 +03:00
return & ParseError { Message : fmt . Sprintf ( ":yellow:`%s` is not a known subcommand for :emph:`%s`. Use --help to get a list of valid subcommands." , arg , self . Name ) }
}
2022-09-30 13:07:03 +03:00
cn := make ( [ ] string , len ( possible_cmds ) )
for i , x := range possible_cmds {
cn [ i ] = x . Name
}
return & ParseError { Message : fmt . Sprintf (
":yellow:`%s` is not a known subcommand for :emph:`%s`. Did you mean:\n\t%s" , arg , self . Name , strings . Join ( cn , "\n\t" ) ) }
2022-09-20 17:01:17 +03:00
}
}
2022-09-20 18:14:16 +03:00
self . Args = append ( self . Args , arg )
2022-09-20 17:01:17 +03:00
}
} else {
// handle option value
err := expecting_arg_for . add_value ( arg )
if err != nil {
return err
}
expecting_arg_for = nil
}
}
return nil
}