-
Notifications
You must be signed in to change notification settings - Fork 1
Positionals
The model for declaring and using positional arguments is one of the places where this library shines the most, and where lies one of the -relative- weaknesses of cobra. In cobra, positional arguments are loosely typed: always passed as string arrays. On top, library users must write a function either for converting or analyzing what is being fed to their commands.
This library changes that for the best, with mostly three features:
- A mechanism for specifying various positional slots with their own requirements, on native types.
- Almost for-free validation on each of them (see here).
- A correspondingly exact, automatically generated
Args
function and its associated completion handler.
For any given command, only one embedded struct can be used to store the entire array of positional arguments 'slots':
type Create struct {
Args struct {
// Positional slots will come here
} `positional-args:"yes"`
}
The snippet above declares a struct with no global requirements (eg. a required:"yes"
tag on the struct).
That is, the various fields we will declare are not required to be provided at least one argument each.
We can now declare various slots, meant to receive our positional arguments. The following is an example where all of the positionals are required to be given at least one word each:
type Create struct {
Args struct {
Host string `desc:"Host where to create the resource"`
Files []string `desc:"Files to create on the remote host"`
} `positional-args:"yes" required:"true"`
}
This command would then be invoked this way, supposing our Root
CLI from the commands page:
./program create 10.10.10.10 file.go config.yml
Any arguments after the 10.10.10.10
host would then be contained by the Files
slot, since
it is a list placed in the last position, with no maximum requirement specified on the field.
You would then access the fields like this in your Execute(args []string)
method:
func (c *Create) Execute(args []string) error {
conn, err := net.Dial(c.Host)
for _, file := range c.Files {
// Use the connection to write/create the files however you want.
}
}
Suppose the following command:
type Scan struct {
Args struct {
Host string `desc:"A host to scan"`
Protocols []string `desc:"Protocols scans to run"`
Port uint16 `desc:"A port to scan on the host`
} `positional-args:"yes" required:"true"`
}
As it is, the command above will require at least 3 arguments to the command: one for each field.
If more than 3 arguments are passed, Host
will get the first, Port
will get the last, and all
those in the middle will be put into Protocols
:
./program scan host@domain.com ssh https imap 443
*Note that parsing this positional struct would panic it there wasn't this global requirement
tag: otherwise, the Protocols
slice, having no maximum, would shadow Port
and prevent any
argument word from ever being put into it.
We can also use more complex and per-field quantity requirements: the following will also
demonstrate the role of the args
parameter of the Execute()
command above:
type Unmarshal struct {
Args struct {
Files []string `desc:"A list of files " required:"1-2"`
JSONConfig string `description:"a config to use for unmarshalling"`
} `positional-args:"yes"`
}
func (u *Unmarshal) Execute(args []string) error {
}
Since there no global requirements, none of the individual fields are required by default.
Thus, the JSONConfig
field is optional, while the Files
field will require at least one
argument, and at most two.
Consequently, and given the following invocation:
./program unmarshal file1.txt file2.txt config.json file3 file4
will parse file1.txt
and file2.txt
in the Files
slot, the config.json
in JSONConfig
,
and will pass the file3
and file4
words as args
to the command's Execute()
method.
The last example above is quite an unrealistic one. It's actually the moment to say that this library even supports positional argument slots' arrangements so weird that no one would possibly would use those.
See the example binary and source code for other examples of positional arguments configurations.