diff --git a/cmd/test/main.go b/cmd/test/main.go index c176f30..577e510 100644 --- a/cmd/test/main.go +++ b/cmd/test/main.go @@ -5,15 +5,29 @@ import ( "strconv" "fmt" "os" + "strings" ) var ( root = mtool.T("test").Subs( mtool.T("echo").Func(func(flags *mtool.Flags) { - var b bool - flags.BoolVar(&b, "b", false, "the check flag") + var ( + b bool + n int + ) + flags.BoolVar(&b, "u", false, "convert to uppercase", "UPPERCASE") + flags.IntVar(&n, "n", 1, "amount of times to print", "NUMPRINT", "NUMPRINT1") args := flags.Parse() - fmt.Println(args) + + if b { + for i, arg := range args { + args[i] = strings.ToUpper(arg) + } + } + + for i:=0 ; i\n", t.FullName()) - - if t.ldesc != "" { - fmt.Fprintf(out, "\nDescription:\n %s\n", t.ldesc) - } - - if len(t.subs) > 0 { - fmt.Fprint(out, "\nCommands:\n") - t.PrintSubs(out) - } - - os.Exit(1) - } - toolName := args[0] - args = args[1:] - - if _, ok := t.subs[toolName] ; !ok { - fmt.Printf("%s: No such util %q'\n", t.ProgName(), toolName) - os.Exit(1) - } - - sub := t.subs[toolName] - usageTool = sub - sub.Run(args) + envMap map[string]string } type ToolMap map[string] *Tool +func (flags *Flags) wasPassed(name string) bool { + found := false + flags.Visit(func(f *flag.Flag){ + if f.Name == name { + found = true + } + }) + return found +} + +func (flags *Flags) setEnv( + name string, + env []string, +) bool { + for _, k := range env { + value, has := os.LookupEnv(k) + if !has { + continue + } + + flags.envMap[name] = value + return true + } + + return false +} + +func (flags *Flags) StringVar( + p *string, + name string, + value string, + usage string, + env ...string, +) { + flags.FlagSet.StringVar(p, name, value, usage) + flags.setEnv(name, env) +} + +func (flags *Flags) IntVar( + p *int, + name string, + value int, + usage string, + env ...string, +) { + flags.FlagSet.IntVar(p, name, value, usage) + flags.setEnv(name, env) +} + +func (flags *Flags) Int64Var( + p *int64, + name string, + value int64, + usage string, + env ...string, +) { + flags.FlagSet.Int64Var(p, name, value, usage) + flags.setEnv(name, env) +} + +func (flags *Flags) BoolVar( + p *bool, + name string, + value bool, + usage string, + env ...string, +) { + flags.FlagSet.BoolVar(p, name, value, usage) + flags.setEnv(name, env) +} + +func (flags *Flags) Float64Var( + p *float64, + name string, + value float64, + usage string, + env ...string, +) { + flags.FlagSet.Float64Var(p, name, value, usage) + flags.setEnv(name, env) +} + +func (flags *Flags) DurationVar( + p *time.Duration, + name string, + value time.Duration, + usage string, + env ...string, +) { + flags.FlagSet.DurationVar(p, name, value, usage) + flags.setEnv(name, env) +} + func (flags *Flags) Parse() []string { + flags.FlagSet.Parse(flags.args) + for name, v := range flags.envMap { + if !flags.wasPassed(name) { + flags.FlagSet.Set(name, v) + } + } + flags.parsedArgs = flags.FlagSet.Args() return flags.parsedArgs } diff --git a/mtool/tool.go b/mtool/tool.go new file mode 100644 index 0000000..930d79d --- /dev/null +++ b/mtool/tool.go @@ -0,0 +1,215 @@ +package mtool + +import ( + "text/tabwriter" + "flag" + "sort" + "fmt" + "io" + "os" +) + +type Tool struct { + name string + handler Handler + desc, ldesc, usage string + subs ToolMap + parent *Tool +} + +// Returns new empty tool with specified name. +func T(name string) *Tool { + ret := &Tool{} + ret.name = name + return ret +} + +func (t *Tool) Handler(h Handler) *Tool { + t.handler = h + return t +} + +func (t *Tool) Func(fn HandlerFunc) *Tool { + t.handler = fn + return t +} + +func (t *Tool) Desc(d string) *Tool { + t.desc = d + return t +} + +func (t *Tool) Ldesc(d string) *Tool { + t.ldesc = d + return t +} + +func (t *Tool) Usage(u string) *Tool { + t.usage = u + return t +} + +func (t *Tool) Subs(tools ...*Tool) *Tool { + if t.subs == nil { + t.subs = ToolMap{} + } + for _, tool := range tools { + tool.parent = t + t.subs[tool.name] = tool + } + return t +} + +func (t *Tool) Name() string { + return t.name +} + +func (t *Tool) FullName() string { + ret := "" + for t != nil { + ret = t.name + func() string { + if ret != "" { + return " " + } + return "" + }() + ret + t = t.parent + } + return ret +} + +func (t *Tool) IsRoot() bool { + return t.parent == nil +} + +func (t *Tool) ProgName() string { + for t.parent != nil { + t = t.parent + } + return t.name +} + +func (t *Tool) PrintSubs(out io.Writer) { + w := new(tabwriter.Writer) + w.Init(out, 0, 0, 1, ' ', 0) + defer w.Flush() + keys := make([]string, len(t.subs)) + i := 0 + for k, _ := range t.subs { + keys[i] = k + i++ + } + sort.Strings(keys) + for _, k := range keys { + tool := t.subs[k] + fmt.Fprintf(w, " %s\t%s\n", k, tool.desc) + } +} + +func (t *Tool) Run(args []string) { + var( + usageTool *Tool + ) + // Should implement the shit later + //binBase := path.Base(arg0) ; + //binBase = binBase[:len(binBase)-len(path.Ext(binBase))] + + flagSet := flag.NewFlagSet(t.FullName(), flag.ExitOnError) + flags := &Flags{ + FlagSet : flagSet, + envMap: make(map[string]string), + } + out := flags.Output() + flags.Usage = func() { + n := 0 + flags.VisitAll(func(f *flag.Flag){ + n++ + }) + hasOptions := n != 0 + + // Name + if usageTool.desc != "" { + fmt.Fprintf( + out, "Name:\n %s - %s\n\n", + usageTool.FullName(), t.desc, + ) + } + + + // Usage + fmt.Fprintf( + out, "Usage:\n %s", + usageTool.FullName(), + ) + if hasOptions { + fmt.Fprintf(out, " [options]") + } + + if usageTool.usage != "" { + fmt.Fprintf( + out, + " %s", + usageTool.usage, + ) + } + + fmt.Fprintln(out, "") + + if usageTool.ldesc != "" { + fmt.Fprintf(out, "Description:\n %s", usageTool.ldesc) + } + + // Options + if hasOptions { + fmt.Fprintln(out, "\nOptions:") + flags.PrintDefaults() + } + + } + + flags.args = args + + // If the tool has its own handler run it. + if t.handler != nil { + usageTool = t + t.handler.Handle(flags) + return + } + + // Print available sub commands if + // got no arguments. + if len(args) == 0 { + + if t.desc != "" { + fmt.Fprintf( + out, "Name:\n %s - %s\n\n", + t.FullName(), t.desc, + ) + } + + fmt.Fprintf(out, "Usage:\n"+ + " %s \n", t.FullName()) + + if t.ldesc != "" { + fmt.Fprintf(out, "\nDescription:\n %s\n", t.ldesc) + } + + if len(t.subs) > 0 { + fmt.Fprint(out, "\nCommands:\n") + t.PrintSubs(out) + } + + os.Exit(1) + } + toolName := args[0] + args = args[1:] + + if _, ok := t.subs[toolName] ; !ok { + fmt.Printf("%s: No such util %q'\n", t.ProgName(), toolName) + os.Exit(1) + } + + sub := t.subs[toolName] + usageTool = sub + sub.Run(args) +}