Skip to content

Commit

Permalink
Merge pull request #43 from WillAbides/complete
Browse files Browse the repository at this point in the history
Shell completion
  • Loading branch information
WillAbides authored Feb 18, 2020
2 parents 76780bd + 9375b84 commit fe3c44b
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 47 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,8 @@ Commands:
config validate <bin>
validate that downloads work

config install-completions
install shell completions

Run "bindown <command> --help" for more information on a command.
```
110 changes: 110 additions & 0 deletions cmd/bindown/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package main

import (
"os"
"path/filepath"
"strings"

"github.com/killa-beez/gopkgs/sets/builtins"
"github.com/posener/complete"
"github.com/willabides/bindown/v2"
)

func findConfigFileForPredictor(args []string) string {
for i, arg := range args {
if len(args) == i+1 {
continue
}
if arg != "--configfile" {
continue
}
return multifileFindExisting(args[i+1])
}
cf, ok := os.LookupEnv("BINDOWN_CONFIG_FILE")
if ok {
return multifileFindExisting(cf)
}
return multifileFindExisting(kongVars["configfile_default"])
}

func predictorConfig(args []string) *bindown.ConfigFile {
path := findConfigFileForPredictor(args)
if path == "" {
return nil
}
configFile, err := bindown.LoadConfigFile(path)
if err != nil {
return nil
}
return configFile
}

func allBins(cfg *bindown.ConfigFile) []string {
if cfg == nil {
return []string{}
}
bins := builtins.NewStringSet(len(cfg.Downloaders) * 10)
for dlName, downloaders := range cfg.Downloaders {
for _, dl := range downloaders {
if dl.BinName == "" {
bins.Add(dlName)
continue
}
bins.Add(dl.BinName)
}
}
return bins.Values()
}

var binPredictor = complete.PredictFunc(func(a complete.Args) []string {
cfg := predictorConfig(a.Completed)
return complete.PredictSet(allBins(cfg)...).Predict(a)
})

var binPathPredictor = complete.PredictFunc(func(a complete.Args) []string {
cfg := predictorConfig(a.Completed)
bins := allBins(cfg)
dir, _ := filepath.Split(a.Last)
for i, bin := range bins {
bins[i] = filepath.Join(dir, bin)
}
return complete.PredictOr(
complete.PredictDirs("*"),
complete.PredictSet(bins...),
).Predict(a)
})

var osPredictor = complete.PredictSet(strings.Split(goosVals, "\n")...)

//from `go tool dist list | cut -f 1 -d '/' | sort | uniq`
const goosVals = `aix
android
darwin
dragonfly
freebsd
illumos
js
linux
nacl
netbsd
openbsd
plan9
solaris
windows`

var archPredictor = complete.PredictSet(strings.Split(goarchVals, "\n")...)

//from `go tool dist list | cut -f 2 -d '/' | sort | uniq`
const goarchVals = `386
amd64
amd64p32
arm
arm64
mips
mips64
mips64le
mipsle
ppc64
ppc64le
s390x
wasm`
23 changes: 13 additions & 10 deletions cmd/bindown/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,23 @@ import (

"github.com/alecthomas/kong"
"github.com/willabides/bindown/v2"
"github.com/willabides/kongplete"
)

var configKongVars = kong.Vars{
"config_format_help": `formats the config file`,
"config_checksums_help": `update checksums in the config file`,
"config_checksums_bin_help": `name of the binary to update`,
"config_validate_bin_help": `name of the binary to validate`,
"config_validate_help": `validate that downloads work`,
"config_format_help": `formats the config file`,
"config_checksums_help": `update checksums in the config file`,
"config_checksums_bin_help": `name of the binary to update`,
"config_validate_bin_help": `name of the binary to validate`,
"config_validate_help": `validate that downloads work`,
"config_install_completions_help": `install shell completions`,
}

type configCmd struct {
Format configFmtCmd `kong:"cmd,help=${config_format_help}"`
UpdateChecksums configUpdateChecksumsCmd `kong:"cmd,help=${config_checksums_bin_help}"`
Validate configValidateCmd `kong:"cmd,help=${config_validate_help}"`
Format configFmtCmd `kong:"cmd,help=${config_format_help}"`
UpdateChecksums configUpdateChecksumsCmd `kong:"cmd,help=${config_checksums_bin_help}"`
Validate configValidateCmd `kong:"cmd,help=${config_validate_help}"`
InstallCompletions kongplete.InstallCompletions `kong:"cmd,help=${config_install_completions_help}"`
}

type configFmtCmd struct{}
Expand All @@ -37,7 +40,7 @@ func (c configFmtCmd) Run() error {
}

type configUpdateChecksumsCmd struct {
TargetFile string `kong:"required=true,arg,help=${config_checksums_bin_help}"`
TargetFile string `kong:"required=true,arg,help=${config_checksums_bin_help},predictor=bin"`
}

func (d *configUpdateChecksumsCmd) Run(kctx *kong.Context) error {
Expand Down Expand Up @@ -84,7 +87,7 @@ func (d *configUpdateChecksumsCmd) Run(kctx *kong.Context) error {
}

type configValidateCmd struct {
Bin string `kong:"required=true,arg,help=${config_validate_bin_help}"`
Bin string `kong:"required=true,arg,help=${config_validate_bin_help},predictor=bin"`
}

func (d configValidateCmd) Run(kctx *kong.Context) error {
Expand Down
6 changes: 3 additions & 3 deletions cmd/bindown/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ var downloadKongVars = kong.Vars{
}

type downloadCmd struct {
Arch string `kong:"help=${download_arch_help},default=${download_arch_default}"`
OS string `kong:"help=${download_os_help},default=${download_os_default}"`
Arch string `kong:"help=${download_arch_help},default=${download_arch_default},predictor=arch"`
OS string `kong:"help=${download_os_help},default=${download_os_default},predictor=os"`
Force bool `kong:"help=${download_force_help}"`
TargetFile string `kong:"required=true,arg,help=${download_target_file_help}"`
TargetFile string `kong:"required=true,arg,help=${download_target_file_help},predictor=binpath"`
}

func (d *downloadCmd) Run(*kong.Context) error {
Expand Down
45 changes: 15 additions & 30 deletions cmd/bindown/main.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package main

import (
"fmt"
"os"
"reflect"
"strings"

"github.com/alecthomas/kong"
"github.com/posener/complete"
"github.com/willabides/kongplete"
)

var kongVars = kong.Vars{
Expand All @@ -20,33 +19,8 @@ var cli struct {
Version versionCmd `kong:"cmd"`
Download downloadCmd `kong:"cmd,help=${download_help}"`
Config configCmd `kong:"cmd"`
Configfile string `kong:"type=multipath,help=${configfile_help},default=${configfile_default},env='BINDOWN_CONFIG_FILE'"`
CellarDir string `kong:"type=path,help=${cellar_dir_help},env='BINDOWN_CELLAR'"`
}

func multipathMapper(ctx *kong.DecodeContext, target reflect.Value) error {
if target.Kind() != reflect.String {
return fmt.Errorf("\"multipath\" type must be applied to a string not %s", target.Type())
}
var path string
err := ctx.Scan.PopValueInto("file", &path)
if err != nil {
return err
}

for _, configFile := range strings.Split(path, "|") {
configFile = kong.ExpandPath(configFile)
stat, err := os.Stat(configFile)
if err != nil {
continue
}
if stat.IsDir() {
continue
}
target.SetString(configFile)
return nil
}
return fmt.Errorf("not found")
Configfile string `kong:"type=multipath,help=${configfile_help},default=${configfile_default},env='BINDOWN_CONFIG_FILE',predictor=file"`
CellarDir string `kong:"type=path,help=${cellar_dir_help},env='BINDOWN_CELLAR',predictor=dir"`
}

func main() {
Expand All @@ -59,6 +33,17 @@ func main() {
kong.NamedMapper("multipath", kong.MapperFunc(multipathMapper)),
)

kongplete.Complete(parser,
kongplete.WithPredictors(map[string]complete.Predictor{
"file": complete.PredictFiles("*"),
"dir": complete.PredictDirs("*"),
"binpath": binPathPredictor,
"arch": archPredictor,
"os": osPredictor,
"bin": binPredictor,
}),
)

kongCtx, err := parser.Parse(os.Args[1:])
parser.FatalIfErrorf(err)
kongCtx.FatalIfErrorf(kongCtx.Run(kongCtx))
Expand Down
43 changes: 43 additions & 0 deletions cmd/bindown/multipath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"fmt"
"os"
"reflect"
"strings"

"github.com/alecthomas/kong"
)

func multipathMapper(ctx *kong.DecodeContext, target reflect.Value) error {
if target.Kind() != reflect.String {
return fmt.Errorf("\"multipath\" type must be applied to a string not %s", target.Type())
}
var path string
err := ctx.Scan.PopValueInto("file", &path)
if err != nil {
return err
}

existing := multifileFindExisting(path)
if existing == "" {
return fmt.Errorf("not found")
}
target.SetString(existing)
return nil
}

func multifileFindExisting(multiFile string) string {
for _, configFile := range strings.Split(multiFile, "|") {
configFile = kong.ExpandPath(configFile)
stat, err := os.Stat(configFile)
if err != nil {
continue
}
if stat.IsDir() {
continue
}
return configFile
}
return ""
}
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ module github.com/willabides/bindown/v2
go 1.12

require (
github.com/alecthomas/kong v0.2.1
github.com/alecthomas/kong v0.2.2
github.com/andybalholm/brotli v1.0.0 // indirect
github.com/frankban/quicktest v1.4.2 // indirect
github.com/killa-beez/gopkgs/sets/builtins v0.0.0-20191206232703-3018f97f77a9
github.com/mholt/archiver/v3 v3.3.0
github.com/pierrec/lz4 v2.3.0+incompatible // indirect
github.com/posener/complete v1.2.3
github.com/stretchr/testify v1.4.0
github.com/udhos/equalfile v0.3.0
github.com/willabides/kongplete v0.0.0-20200218163413-c50671632cd8
gopkg.in/yaml.v2 v2.2.2
)
15 changes: 12 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/alecthomas/kong v0.2.1 h1:V1tLBhyQBC4rsbXbcOvm3GBaytJSwRNX69fp1WJxbqQ=
github.com/alecthomas/kong v0.2.1/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/kong v0.2.2 h1:sk9ucwuUP/T4+byYEdNU13ZNYzoQRML4IsrMbbUUKLk=
github.com/alecthomas/kong v0.2.2/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
Expand All @@ -16,6 +16,12 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/killa-beez/gopkgs/sets/builtins v0.0.0-20191206232703-3018f97f77a9 h1:BStNA6fVmKPuDXvzJnZQQq++4FSoUYAxlJPH2J5UjNE=
github.com/killa-beez/gopkgs/sets/builtins v0.0.0-20191206232703-3018f97f77a9/go.mod h1:ZP4kZV3WGKCESlO7hZkyyKM1Zprcf2FQ5rDq44f799M=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY=
github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
Expand All @@ -29,7 +35,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mholt/archiver/v3 v3.3.0 h1:vWjhY8SQp5yzM9P6OJ/eZEkmi3UAbRrxCq48MxjAzig=
github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
Expand All @@ -39,6 +44,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
Expand All @@ -47,6 +54,8 @@ github.com/udhos/equalfile v0.3.0 h1:KhG4xhhkittrgIV/ekHtpEPh7MLxtbjm6kLEwp5Dlbg
github.com/udhos/equalfile v0.3.0/go.mod h1:1LOX9HjdFMke7ryP3IPby09FkswyY5KzhhsT37wLz/Y=
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/willabides/kongplete v0.0.0-20200218163413-c50671632cd8 h1:fozkUJwUVv1Wt2/+wOqoVjnttFMu5pH1mArJ7Ueqssc=
github.com/willabides/kongplete v0.0.0-20200218163413-c50671632cd8/go.mod h1:kFVw+PkQsqkV7O4tfIBo6iJ9qY94PJC8sPfMgFG5AdM=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down

0 comments on commit fe3c44b

Please sign in to comment.