feat: implemented non-breaking way to define environment variable to flag values.
This commit is contained in:
parent
5d79542278
commit
cf9db63da6
4 changed files with 345 additions and 222 deletions
|
@ -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 ; i++ {
|
||||
fmt.Println(args)
|
||||
}
|
||||
}).Desc(
|
||||
"print string array to standard output",
|
||||
).Usage(
|
||||
|
|
10
mtool/handler.go
Normal file
10
mtool/handler.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package mtool
|
||||
|
||||
type Handler interface {
|
||||
Handle(*Flags)
|
||||
}
|
||||
|
||||
type HandlerFunc func(*Flags)
|
||||
func (fn HandlerFunc) Handle(flags *Flags) {
|
||||
fn(flags)
|
||||
}
|
322
mtool/main.go
322
mtool/main.go
|
@ -4,13 +4,9 @@ package mtool
|
|||
// to make multitool CLI applications.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
//path "path/filepath"
|
||||
"flag"
|
||||
"sort"
|
||||
"text/tabwriter"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
|
@ -19,225 +15,113 @@ type Flags struct {
|
|||
tool *Tool
|
||||
args []string
|
||||
parsedArgs []string
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
Handle(*Flags)
|
||||
}
|
||||
|
||||
type HandlerFunc func(*Flags)
|
||||
func (fn HandlerFunc) Handle(flags *Flags) {
|
||||
fn(flags)
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
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 <command>\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
|
||||
}
|
||||
|
|
215
mtool/tool.go
Normal file
215
mtool/tool.go
Normal file
|
@ -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 <command>\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)
|
||||
}
|
Loading…
Reference in a new issue