containous / flaeg Goto Github PK
View Code? Open in Web Editor NEWgolang CLI with magic
License: MIT License
golang CLI with magic
License: MIT License
I've stumbled unto the situation where I am using sub commands as a primary mechanism to interact with an application. This means that the rootCmd
performs no particular action at all and is left blank. As a first issue, omitting the Run
attribute yields a:
panic: runtime error: invalid memory address or nil pointer dereference
However, as primary issue, it would be beneficial to be able to simply print the help output to indicate to the user of the need to use a sub-command. Perhaps, simply, as flaeg.PrintHelp(config)
or during the omission of the Run
attribute.
In other cases, perhaps more abstractly in the concept of contextualizing arguments and their values, printing the help output would also be useful in the aim of decreasing the likelihood of program misuse.
Currently, the PrintHelp
method is exported but requires most of the backbone of the flaeg
application to perform reflection and config reading in order to work.
If only a the flag on the first level pointer field is called, the field under a pointer field must keep nil
value.
Example : In the following configuration structure :
//StructPtr : Struct with pointer
type StructPtr struct {
PtrStruct1 *Struct1 `description:"Enable Struct1"`
DurationField time.Duration `description:"Duration Field"`
}
//Struct1 : Struct with pointer
type Struct1 struct {
S1Bool bool `description:"Struct 1 Bool"`
S1PtrStruct3 *Struct3 `description:"Enable Struct3"`
}
//Struct3 : trivial Struct
type Struct3 struct {
S3Float64 float64 `description:"Struct 3 float64"`
}
S1PtrStruct3
is a pointer field under a pointer field.
If only a the flag --prtstruct1
is called, the field S1PtrStruct3
must keep nil
value.
But if the flag prtstruct1.s1ptrstruct3
is called, the field S1PtrStruct3
must get the value of the DefaultPointers
structure.
It could be nice to add the feature continue on missing parser error
./traefik -
panic: runtime error: slice bounds out of range
goroutine 1 [running]:
panic(0xdffac0, 0xc820010110)
/home/martin/.gvm/gos/go1.6/src/runtime/panic.go:464 +0x3e6
github.com/containous/traefik/vendor/github.com/containous/flaeg.argToLower(0x7fffdd35df19, 0x1, 0x0, 0x0)
/home/martin/go/src/github.com/containous/traefik/vendor/github.com/containous/flaeg/flaeg.go:765 +0x1cb
github.com/containous/traefik/vendor/github.com/containous/flaeg.argsToLower(0xc8200a0070, 0x1, 0x1, 0x0, 0x0, 0x0)
/home/martin/go/src/github.com/containous/traefik/vendor/github.com/containous/flaeg/flaeg.go:783 +0x109
github.com/containous/traefik/vendor/github.com/containous/flaeg.parseArgs(0xc8200a0070, 0x1, 0x1, 0xc82019f4f8, 0xc8202ebad0, 0x16, 0x0, 0x0)
/home/martin/go/src/github.com/containous/traefik/vendor/github.com/containous/flaeg/flaeg.go:149 +0x7d2
github.com/containous/traefik/vendor/github.com/containous/flaeg.LoadWithCommand(0xc820246c80, 0xc8200a0070, 0x1, 0x1, 0xc8202eba40, 0xc820277530, 0x2, 0x2, 0x0, 0x0)
/home/martin/go/src/github.com/containous/traefik/vendor/github.com/containous/flaeg/flaeg.go:538 +0x3c2
github.com/containous/traefik/vendor/github.com/containous/flaeg.(*Flaeg).Parse(0xc8201eacc0, 0xc820246c80, 0x1, 0x0, 0x0)
/home/martin/go/src/github.com/containous/traefik/vendor/github.com/containous/flaeg/flaeg.go:701 +0xb8
main.main()
/home/martin/go/src/github.com/containous/traefik/traefik.go:57 +0xd58
Flaeg panics if types in custom parsers don't match each other
In func (s *SliceStrings) SetValue(val interface{})
panic: interface conversion: interface is Namespaces, not []string
Example :
// Namespaces holds kubernetes namespaces
type Namespaces []string
//...
f.AddParser(reflect.TypeOf(Namespaces{}), &flaeg.SliceStrings{})
There seems to be a problem with parsing both root- and sub-commands together.
I've put together this example which illustrates the in-ability to parse sub commands and their flags when a root flag is added. In this example, a GlobalConfiguration
would be used to store the verbosity of logging under the -l
or --loglevel
flag. A subcomand, foo
, is added which has is own FooConfiguration
containing the flag --bar
to be used solely by its processor and not necessarily elsewhere:
package main
import (
"fmt"
"os"
"github.com/containous/flaeg"
)
type GlobalConfiguration struct {
LogLevel string `short:"l" long:"loglevel" description:"Output verbosity"`
}
type FooConfiguration struct {
Bar string `description:"Foo"`
}
var globalConfig = &GlobalConfiguration{
LogLevel: "info",
}
var globalPointersConfig = &GlobalConfiguration{}
var fooConfig = &FooConfiguration{
Bar: "bar",
}
var fooPointersConfig = &FooConfiguration{}
func main() {
rootCmd := &flaeg.Command{
Name: "test",
Description: `Test flaeg sub-commands`,
Config: globalConfig,
DefaultPointersConfig: globalPointersConfig,
Run: func() error {
fmt.Println("I should have access to --loglevel but not --bar!")
fmt.Println("Loglevel: ", globalConfig.LogLevel)
fmt.Println("Bar: ", fooConfig.Bar)
return nil
},
}
fooCmd := &flaeg.Command{
Name: "foo",
Description: `Foo sub-command`,
Config: fooConfig,
DefaultPointersConfig: fooPointersConfig,
Run: func() error {
fmt.Println("I should have access to --loglevel and --bar!")
fmt.Println("Loglevel: ", globalConfig.LogLevel)
fmt.Println("Bar: ", fooConfig.Bar)
return nil
},
}
// init flaeg
flaeg := flaeg.New(rootCmd, os.Args[1:])
// add sub-command foo
flaeg.AddCommand(fooCmd)
// run test
if err := flaeg.Run(); err != nil {
fmt.Println("Error %s", err.Error())
}
}
Under the normal assumption, and as per the declaration of usage, [flags]
can be put aftere the command name, in this case test
.
$ ./test -h
Test flaeg sub-commands
Usage: test [flags] <command> [<arguments>]
Use "test <command> --help" for help on any command.
Commands:
foo Foo sub-command
Flag's usage: test [--flag=flag_argument] [-f[flag_argument]] ... set flag_argument to flag(s)
or: test [--flag[=true|false| ]] [-f[true|false| ]] ... set true/false to boolean flag(s)
Flags:
-l, --loglevel Output verbosity (default "info")
-h, --help Print Help (this message) and exit
Error %s pflag: help requested
A subcommand, foo
, is available and still makes these assumptions:
$ ./test foo -h
Foo sub-command
Usage: foo [flags] <command> [<arguments>]
Use "foo <command> --help" for help on any command.
Flag's usage: foo [--flag=flag_argument] [-f[flag_argument]] ... set flag_argument to flag(s)
or: foo [--flag[=true|false| ]] [-f[true|false| ]] ... set true/false to boolean flag(s)
Flags:
--bar Foo (default "bar")
-h, --help Print Help (this message) and exit
Error %s pflag: help requested
However, when adding "global", or root, flags to a sub-command call, we are immediately prompted with the inability to recognise the subcommand foo
:
./test --loglevel=debug foo --bar=foo
Error here : unknown flag: --bar
Test flaeg sub-commands
Usage: test [flags] <command> [<arguments>]
Use "test <command> --help" for help on any command.
Commands:
foo Foo sub-command
Flag's usage: test [--flag=flag_argument] [-f[flag_argument]] ... set flag_argument to flag(s)
or: test [--flag[=true|false| ]] [-f[true|false| ]] ... set true/false to boolean flag(s)
Flags:
-l, --loglevel Output verbosity (default "info")
-h, --help Print Help (this message) and exit
Error %s unknown flag: --bar
Running the following yields the same result:
$ ./test --loglevel=debug --bar=foo foo
The doc says that you can use anonymous sub-structures, and the example implies that when the flag name is generated, the sub-structure is "squashed" (see --db.ip in the example).
I have a case that demonstrates a bug in the generation of the flag name, when an anonymous sub-structure follows a named sub-structure.
This configuration:
type EmbeddedConfig struct {
P2 int `description:"p2"`
}
type Config struct{
P1 int `description:"p1"`
EmbeddedConfig
}
generates the following flags:
Flags:
--p1 p1 (default "0")
--p1.p2 p2 (default "0")
The second flag should be --p2, not --p1.p2
If you swap the members of Config (P1 after the anonymous EmbeddedConfig), the flags are generated correctly
Attached is a source file that shows this behaviour.
To implement containous/staert#35 and reuse the parsing code, it will be good to reuse flaeg parser, as suggested for a previous tentative : https://traefik.slackarchive.io/dev/page-17/ts-1497598028679523
This requires quite a lot of renaming and prefixing in the code, so I'd like to make sure I plan this right
parse
parser.go
and parser_test.go
into a parse
folder. rename files to parse.go/parse_test.go
github.com/containous/flaeg/parse
in flaeg
parse.
Sounds good?
Looks like further development goes here: https://github.com/spf13/pflag
Flaeg doesn't manage unexported fields under a pointer on a Sub-Structure
Like this :
type ConfigPointerField struct {
PtrSubConfig *SubConfigWithUnexportedField `description:"pointer on a SubConfig with one unexported pointer field"`
}
type SubConfigWithUnexportedField struct {
Exported string `description:"Exported string field"`
ptrSubSubConfig *SubSubConfig
}
type SubSubConfig struct {
unexported string
}
It panics : panic: reflect: reflect.Value.Set using value obtained using unexported field
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.