Skip to content

Commit

Permalink
Initial prototype of positional arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
vsachs committed Jul 27, 2022
1 parent 778261a commit 765b373
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 22 deletions.
12 changes: 8 additions & 4 deletions argparse.go
Expand Up @@ -80,6 +80,9 @@ type Parser struct {
// Options are specific options for every argument. They can be provided if necessary.
// Possible fields are:
//
// Options.Positional - tells Parser that the argument is positional (implies Required)
// Positional arguments do not require the flag name to precede them and must come in a specific order.
//
// Options.Required - tells Parser that this argument is required to be provided.
// useful when specific Command requires some data provided.
//
Expand All @@ -95,10 +98,11 @@ type Parser struct {
// in case if this argument was not supplied on command line. File default value is a string which it will be open with
// provided options. In case if provided value type does not match expected, the error will be returned on run-time.
type Options struct {
Required bool
Validate func(args []string) error
Help string
Default interface{}
Positional bool
Required bool
Validate func(args []string) error
Help string
Default interface{}
}

// NewParser creates new Parser object that will allow to add arguments for parsing
Expand Down
28 changes: 24 additions & 4 deletions argument.go
Expand Up @@ -29,6 +29,14 @@ type Arg interface {
GetSname() string
GetLname() string
GetResult() interface{}
GetPositional() bool
}

func (o arg) GetPositional() bool {
if o.opts != nil {
return o.opts.Positional
}
return false
}

func (o arg) GetOpts() *Options {
Expand Down Expand Up @@ -68,7 +76,7 @@ func (o *arg) checkLongName(argument string) int {
return 0
}

// checkShortName if argumet present.
// checkShortName if argument present.
// checkShortName - returns the argumet's short name number of occurrences and error.
// For shorthand argument - 0 if there is no occurrences, or count of occurrences.
// Shorthand argument with parametr, mast be the only or last in the argument string.
Expand Down Expand Up @@ -101,7 +109,7 @@ func (o *arg) checkShortName(argument string) (int, error) {
return 0, nil
}

// check if argumet present.
// check if argument present.
// check - returns the argumet's number of occurrences and error.
// For long name return value is 0 or 1.
// For shorthand argument - 0 if there is no occurrences, or count of occurrences.
Expand All @@ -115,6 +123,10 @@ func (o *arg) check(argument string) (int, error) {
return o.checkShortName(argument)
}

func (o *arg) reducePositional(position int, args *[]string) {
(*args)[position] = ""
}

func (o *arg) reduceLongName(position int, args *[]string) {
argument := (*args)[position]
// Check for long name only if not empty
Expand Down Expand Up @@ -166,8 +178,12 @@ func (o *arg) reduceShortName(position int, args *[]string) {

// clear out already used argument from args at position
func (o *arg) reduce(position int, args *[]string) {
o.reduceLongName(position, args)
o.reduceShortName(position, args)
if o.GetPositional() {
o.reducePositional(position, args)
} else {
o.reduceLongName(position, args)
o.reduceShortName(position, args)
}
}

func (o *arg) parseInt(args []string, argCount int) error {
Expand Down Expand Up @@ -382,6 +398,10 @@ func (o *arg) parseSomeType(args []string, argCount int) error {
return err
}

func (o *arg) parsePositional(arg string) error {
return o.parse([]string{arg}, 1)
}

func (o *arg) parse(args []string, argCount int) error {
// If unique do not allow more than one time
if o.unique && (o.parsed || argCount > 1) {
Expand Down
59 changes: 45 additions & 14 deletions command.go
Expand Up @@ -51,7 +51,13 @@ func (o *Command) addArg(a *arg) error {
current = current.parent
}
a.parent = o

if a.GetPositional() {
a.opts.Required = true
a.size = 1 // We could allow other sizes in the future
}
o.args = append(o.args, a)

return nil
}

Expand All @@ -73,22 +79,49 @@ func (o *Command) parseSubCommands(args *[]string) error {
return nil
}

func (o *Command) splitArgOnEqual(arg) {

}

//parseArguments - Parses arguments
func (o *Command) parseArguments(args *[]string) error {
func (o *Command) parseArguments(inputArgs *[]string) error {
// Iterate over the args
for i := 0; i < len(o.args); i++ {
oarg := o.args[i]
for j := 0; j < len(*args); j++ {
arg := (*args)[j]
for _, oarg := range o.args {
for j := 0; j < len(*inputArgs); j++ {
arg := (*inputArgs)[j]
if arg == "" {
continue
}
if oarg.GetPositional() {
// Skip any flags
// This has the subtle effect of requiring flags
// to use `=` for their value pairing if any
// positionals are defined AND are not satisfied yet.
// If they don't use `=` then the positional parse
// will unknowingly consume the arg on next iteration.
//
// It would be possible to potentially avoid this
// requirement IF we choose to check whether the
// flag in question has a default. If not then we
// know either:
// it must be for that flag OR
// the user made an error
// However this is highly ambiguous so best avoided.
if strings.HasPrefix(arg, "-") {
continue
}
if err := oarg.parsePositional(arg); err != nil {
return err
}
oarg.reduce(j, inputArgs)
break // Positionals can only occur once
}
if strings.Contains(arg, "=") {
splitInd := strings.LastIndex(arg, "=")
equalArg := []string{arg[:splitInd], arg[splitInd+1:]}
if cnt, err := oarg.check(equalArg[0]); err != nil {
return err
} else if cnt > 0 {
} else if cnt > 0 { // No args implies we supply default
if equalArg[1] == "" {
return fmt.Errorf("not enough arguments for %s", oarg.name())
}
Expand All @@ -99,32 +132,30 @@ func (o *Command) parseArguments(args *[]string) error {
if err != nil {
return err
}
oarg.reduce(j, args)
oarg.reduce(j, inputArgs)
continue
}
}
if cnt, err := oarg.check(arg); err != nil {
return err
} else if cnt > 0 {
if len(*args) < j+oarg.size {
if len(*inputArgs) < j+oarg.size {
return fmt.Errorf("not enough arguments for %s", oarg.name())
}
err := oarg.parse((*args)[j+1:j+oarg.size], cnt)
err := oarg.parse((*inputArgs)[j+1:j+oarg.size], cnt)
if err != nil {
return err
}
oarg.reduce(j, args)
oarg.reduce(j, inputArgs)
continue
}
}

// Check if arg is required and not provided
if oarg.opts != nil && oarg.opts.Required && !oarg.parsed {
return fmt.Errorf("[%s] is required", oarg.name())
}

// Check for argument default value and if provided try to type cast and assign
if oarg.opts != nil && oarg.opts.Default != nil && !oarg.parsed {
} else if oarg.opts != nil && oarg.opts.Default != nil && !oarg.parsed {
// Check for argument default value and if provided try to type cast and assign
err := oarg.setDefault()
if err != nil {
return err
Expand Down

0 comments on commit 765b373

Please sign in to comment.