// see http://godoc.org/github.com/arnehormann/goof/envflag package main import ( "fmt" "io" "os" "sort" "strings" "text/tabwriter" "github.com/arnehormann/goof/envflag" ) // example for a custom envflag.Enumerator type mode string func Mode(m string) *mode { // this is very, very ugly and just for demonstration purposes... defaultMode := "prod" result := (*mode)(&defaultMode) result.Set(m) // ignore errors, keep default return result } func (m *mode) Values() []string { return []string{"prod", "dev", "test"} } func (m *mode) Set(v string) error { switch v { case "prod", "dev", "test": default: return fmt.Errorf("unknown mode %q", v) } *m = mode(v) return nil } func (m *mode) String() string { return *(*string)(m) } func (m *mode) Describe(v string) string { switch v { case "prod": return "production environment" case "dev": return "development environment" case "test": return "test environment" } return "" } func main() { var err error // this is the whole configuration. If no optional struct tags exist, // everything is inferred from the field name, type and initial value. config := struct { Host string `desc:"target smtp server name or ip"` Port uint `desc:"smtp port (usually 25 or 587)"` User string `desc:"user name"` Password string `key:"pass" desc:"login password"` SortArgs bool `arg:"sorthelp" tag:"help"` FullHelp bool `args:"fh,v" desc:"print full help text" tag:"help"` Mode *mode `desc:"program mode"` }{ Host: "127.0.0.1", Port: 587, SortArgs: true, Mode: Mode("test"), } // create a set of parameters (environment variables prefixed with MYAPP_) env := envflag.Environment("myapp/") params := env.WithParameters("") // api messup, I'll get rid of this // register the struct params.Register(&config) // seed configuration parameters from environment variables // could also load from a file instead of os.Getenv err = params.SetValues(os.Getenv) if err != nil { panic(err) } // parse command line arguments, overwrite existing values with ones given there err = params.Parse(os.Args[1:]) if err != nil { panic(err) } // fetch available parameters and sort them by command line argument name p := parameters(params.Explore()) if config.SortArgs { sort.Sort(p) } w := tabwriter.NewWriter(os.Stdout, 4, 0, 2, ' ', 0) if config.FullHelp { printHelp(w, p) } else { printArgTable(w, p) } w.Flush() } type parameters []envflag.Parameter func (ps parameters) Len() int { return len(ps) } func (ps parameters) Swap(i, j int) { ps[i], ps[j] = ps[j], ps[i] } func (ps parameters) Less(i, j int) bool { return ps[i].ArgKey < ps[j].ArgKey } func HasTag(tag, key string) bool { parts := strings.Split(tag, " ") for _, p := range parts { if p == key { return true } } return false } func printArgTable(w io.Writer, params parameters) { fmt.Fprintf(w, "Key\tArg\tEnv\tValue\tDefault\tDescription\n") for i := range params { p := ¶ms[i] // show usage of tag if HasTag(p.Tag, "help") { fmt.Fprintf(w, "%s\t-%s\t%s\t%q\t%q\t[?] %s\n", p.Key, p.ArgKey, p.EnvKey, p.Value, p.DefaultValue, p.Description, ) } else { fmt.Fprintf(w, "%s\t-%s\t%s\t%q\t%q\t%s\n", p.Key, p.ArgKey, p.EnvKey, p.Value, p.DefaultValue, p.Description, ) } } } func printHelp(w io.Writer, params parameters) { fmt.Fprintf(w, "List of available configuration options:\n\n") for _, p := range params { var defaultVal string if p.Value != p.DefaultValue { defaultVal = fmt.Sprintf(" (default %q)", p.DefaultValue) } desc := p.Description if desc == "" { desc = "" } fmt.Fprintf( w, "ENV: $%s\tType: %s\n"+ "ARG: -%s\n"+ "\t%s\n"+ "\tvalue: %q%s\n", p.EnvKey, p.Type, strings.Join(append([]string{p.ArgKey}, p.ArgAliases...), ", -"), desc, p.Value, defaultVal, ) // show usage of options for _, o := range p.Options { fmt.Fprintf(w, "\t[%s]\t%s\n", o.Value, o.Description) } fmt.Fprintln(w) } }