Implemented more unified way for usage, description and options.
This commit is contained in:
parent
7a1e2d6a35
commit
6f7bea24d0
4 changed files with 258 additions and 156 deletions
|
@ -1,40 +1,54 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/omnipunk/cli"
|
"github.com/omnipunk/cli/mtool"
|
||||||
"strconv"
|
"strconv"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tools = mtool.Tools{
|
root = mtool.T("test").Subs(
|
||||||
"echo": mtool.Tool{
|
mtool.T("echo").Func(func(flags *mtool.Flags) {
|
||||||
func(flags *mtool.Flags) {
|
|
||||||
var b bool
|
var b bool
|
||||||
flags.BoolVar(&b, "b", false, "the check flag")
|
flags.BoolVar(&b, "b", false, "the check flag")
|
||||||
flags.Parse()
|
args := flags.Parse()
|
||||||
|
|
||||||
args := flags.Args()
|
|
||||||
|
|
||||||
fmt.Println(args)
|
fmt.Println(args)
|
||||||
},
|
}).Desc(
|
||||||
"print string to standard output string",
|
"print string to standard output string",
|
||||||
|
).Usage(
|
||||||
"[str1 str2 ... strN]",
|
"[str1 str2 ... strN]",
|
||||||
},
|
),
|
||||||
"sum": mtool.Tool{
|
mtool.T("sum").Func(func(flags *mtool.Flags) {
|
||||||
func(flags *mtool.Flags) {
|
|
||||||
flags.Parse()
|
flags.Parse()
|
||||||
args := flags.Args()
|
args := flags.Args()
|
||||||
one, _ := strconv.Atoi(args[1])
|
one, _ := strconv.Atoi(args[1])
|
||||||
two, _ := strconv.Atoi(args[2])
|
two, _ := strconv.Atoi(args[2])
|
||||||
fmt.Println(one + two)
|
fmt.Println(one + two)
|
||||||
},
|
}).Desc(
|
||||||
"add one value to another",
|
"add one value to another",
|
||||||
|
).Usage(
|
||||||
"<int1> <int2>",
|
"<int1> <int2>",
|
||||||
},
|
),
|
||||||
}
|
mtool.T("sub").Subs(
|
||||||
|
mtool.T("first").Func(func(flags *mtool.Flags) {
|
||||||
|
fmt.Println("called the first", flags.Parse())
|
||||||
|
}).Desc(
|
||||||
|
"description",
|
||||||
|
).Usage(
|
||||||
|
"[nothing here]",
|
||||||
|
),
|
||||||
|
mtool.T("second").Func(func(flags *mtool.Flags){
|
||||||
|
fmt.Println("called the second", flags.Parse())
|
||||||
|
}).Desc(
|
||||||
|
"description",
|
||||||
|
).Usage(
|
||||||
|
"[nothing here]",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
mtool.Main("test", tools)
|
root.Run(os.Args[1:])
|
||||||
}
|
}
|
||||||
|
|
136
main.go
136
main.go
|
@ -1,136 +0,0 @@
|
||||||
package mtool
|
|
||||||
|
|
||||||
import(
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
path "path/filepath"
|
|
||||||
"flag"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Flags struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
progName string
|
|
||||||
utilName string
|
|
||||||
args []string
|
|
||||||
parsedArgs []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Handler func(*Flags)
|
|
||||||
|
|
||||||
type Tool struct {
|
|
||||||
Handler Handler
|
|
||||||
Desc, Usage string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tools map[string] Tool
|
|
||||||
|
|
||||||
func (flags *Flags) Parse() []string {
|
|
||||||
flags.FlagSet.Parse(flags.args)
|
|
||||||
flags.parsedArgs = flags.FlagSet.Args()
|
|
||||||
return flags.parsedArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flags *Flags) AllArgs() []string {
|
|
||||||
return flags.args
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flags *Flags) Args() []string {
|
|
||||||
return flags.parsedArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flags *Flags) ProgName() string {
|
|
||||||
return flags.progName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (flags *Flags) UtilName() string {
|
|
||||||
return flags.utilName
|
|
||||||
}
|
|
||||||
|
|
||||||
func Main(name string, m Tools) {
|
|
||||||
var(
|
|
||||||
utilName string
|
|
||||||
args []string
|
|
||||||
)
|
|
||||||
|
|
||||||
arg0 := os.Args[0]
|
|
||||||
binBase := path.Base(arg0) ;
|
|
||||||
binBase = binBase[:len(binBase)-len(path.Ext(binBase))]
|
|
||||||
if binBase != name {
|
|
||||||
utilName = binBase
|
|
||||||
args = os.Args
|
|
||||||
} else {
|
|
||||||
if len(os.Args)<2 {
|
|
||||||
keys := make([]string, len(m))
|
|
||||||
i := 0
|
|
||||||
for k, _ := range m {
|
|
||||||
keys[i] = k
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
|
|
||||||
for _, k := range keys {
|
|
||||||
tool := m[k]
|
|
||||||
fmt.Printf("%s: %s\n", k, tool.Desc)
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
utilName = os.Args[1]
|
|
||||||
args = os.Args[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := m[utilName] ; !ok {
|
|
||||||
fmt.Printf("%s: No such uitl as '%s'.\n", arg0, utilName )
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
util := m[utilName]
|
|
||||||
|
|
||||||
flagSet := flag.NewFlagSet(utilName, flag.ExitOnError)
|
|
||||||
flags := &Flags{
|
|
||||||
FlagSet : flagSet,
|
|
||||||
}
|
|
||||||
flags.Usage = func() {
|
|
||||||
out := flags.Output()
|
|
||||||
n := 0
|
|
||||||
flags.VisitAll(func(f *flag.Flag){
|
|
||||||
n++
|
|
||||||
})
|
|
||||||
|
|
||||||
hasOptions := n != 0
|
|
||||||
|
|
||||||
fmt.Fprintf(
|
|
||||||
out,
|
|
||||||
"Usage of %s:\n\t%s",
|
|
||||||
utilName, utilName,
|
|
||||||
)
|
|
||||||
if hasOptions {
|
|
||||||
fmt.Fprintf(out, " [options]")
|
|
||||||
}
|
|
||||||
|
|
||||||
if util.Usage != "" {
|
|
||||||
fmt.Fprintf(
|
|
||||||
out,
|
|
||||||
" %s",
|
|
||||||
util.Usage,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(out, "")
|
|
||||||
|
|
||||||
if hasOptions {
|
|
||||||
fmt.Fprintln(out, "Options:")
|
|
||||||
flags.PrintDefaults()
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
flags.progName = name
|
|
||||||
flags.utilName = utilName
|
|
||||||
flags.args = args[1:]
|
|
||||||
|
|
||||||
util.Handler(flags)
|
|
||||||
}
|
|
||||||
|
|
217
mtool/main.go
Normal file
217
mtool/main.go
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
package mtool
|
||||||
|
|
||||||
|
// The package implements fast way
|
||||||
|
// to make multitool CLI applications.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
//path "path/filepath"
|
||||||
|
"flag"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type Flags struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
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, 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) 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) ProgName() string {
|
||||||
|
for t.parent != nil {
|
||||||
|
t = t.parent
|
||||||
|
}
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// We can visit the flags since the
|
||||||
|
// function will be called after
|
||||||
|
// parsing.
|
||||||
|
flags.VisitAll(func(f *flag.Flag){
|
||||||
|
n++
|
||||||
|
})
|
||||||
|
|
||||||
|
hasOptions := n != 0
|
||||||
|
|
||||||
|
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 hasOptions {
|
||||||
|
fmt.Fprintln(out, "\nOptions:")
|
||||||
|
flags.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
keys := make([]string, len(t.subs))
|
||||||
|
i := 0
|
||||||
|
for k, _ := range t.subs {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
fmt.Fprintf(out, "Usage:\n"+
|
||||||
|
" %s <command> [options] [arguments]\n\n" +
|
||||||
|
"Commands:\n", t.FullName())
|
||||||
|
for i, k := range keys {
|
||||||
|
tool := t.subs[k]
|
||||||
|
fmt.Fprintf(out, " %s\t%s\n", k, tool.desc)
|
||||||
|
if i != len(keys) - 1 {
|
||||||
|
fmt.Fprintln(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToolMap map[string] *Tool
|
||||||
|
|
||||||
|
func (flags *Flags) Parse() []string {
|
||||||
|
flags.FlagSet.Parse(flags.args)
|
||||||
|
flags.parsedArgs = flags.FlagSet.Args()
|
||||||
|
return flags.parsedArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flags *Flags) AllArgs() []string {
|
||||||
|
return flags.args
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flags *Flags) Args() []string {
|
||||||
|
return flags.parsedArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flags *Flags) Tool() *Tool {
|
||||||
|
return flags.tool
|
||||||
|
}
|
||||||
|
|
7
taskfile.yml
Normal file
7
taskfile.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
btest:
|
||||||
|
cmds:
|
||||||
|
- go build ./cmd/test
|
||||||
|
|
Loading…
Reference in a new issue