Initial commit.
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
# Kong is a command-line parser for Go
|
||||||
|
|
||||||
|
It parses a command-line into a struct. eg.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/alecthomas/kong"
|
||||||
|
|
||||||
|
var CLI struct {
|
||||||
|
Rm struct {
|
||||||
|
Force bool `help:"Force removal."`
|
||||||
|
Recursive bool `help:"Recursively remove files."`
|
||||||
|
|
||||||
|
Paths []string `help:"Paths to remove." type:"path"`
|
||||||
|
} `help:"Remove files."`
|
||||||
|
|
||||||
|
Ls struct {
|
||||||
|
Paths []string `help:"Paths to list." type:"path"`
|
||||||
|
} `help:"List paths."`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
kong.Parse(&CLI)
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/alecthomas/kong"
|
||||||
|
|
||||||
|
var CLI struct {
|
||||||
|
Rm struct {
|
||||||
|
Force bool `help:"Force removal."`
|
||||||
|
Recursive bool `help:"Recursively remove files."`
|
||||||
|
|
||||||
|
Paths []string `help:"Paths to remove." type:"path"`
|
||||||
|
} `help:"Remove files."`
|
||||||
|
|
||||||
|
Ls struct {
|
||||||
|
Paths []string `help:"Paths to list." type:"path"`
|
||||||
|
} `help:"List paths."`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
kong.Parse(&CLI)
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package kong
|
||||||
|
|
||||||
|
// NOTE: This code is from https://github.com/fatih/camelcase. MIT license.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Split splits the camelcase word and returns a list of words. It also
|
||||||
|
// supports digits. Both lower camel case and upper camel case are supported.
|
||||||
|
// For more info please check: http://en.wikipedia.org/wiki/CamelCase
|
||||||
|
//
|
||||||
|
// Examples
|
||||||
|
//
|
||||||
|
// "" => [""]
|
||||||
|
// "lowercase" => ["lowercase"]
|
||||||
|
// "Class" => ["Class"]
|
||||||
|
// "MyClass" => ["My", "Class"]
|
||||||
|
// "MyC" => ["My", "C"]
|
||||||
|
// "HTML" => ["HTML"]
|
||||||
|
// "PDFLoader" => ["PDF", "Loader"]
|
||||||
|
// "AString" => ["A", "String"]
|
||||||
|
// "SimpleXMLParser" => ["Simple", "XML", "Parser"]
|
||||||
|
// "vimRPCPlugin" => ["vim", "RPC", "Plugin"]
|
||||||
|
// "GL11Version" => ["GL", "11", "Version"]
|
||||||
|
// "99Bottles" => ["99", "Bottles"]
|
||||||
|
// "May5" => ["May", "5"]
|
||||||
|
// "BFG9000" => ["BFG", "9000"]
|
||||||
|
// "BöseÜberraschung" => ["Böse", "Überraschung"]
|
||||||
|
// "Two spaces" => ["Two", " ", "spaces"]
|
||||||
|
// "BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"]
|
||||||
|
//
|
||||||
|
// Splitting rules
|
||||||
|
//
|
||||||
|
// 1) If string is not valid UTF-8, return it without splitting as
|
||||||
|
// single item array.
|
||||||
|
// 2) Assign all unicode characters into one of 4 sets: lower case
|
||||||
|
// letters, upper case letters, numbers, and all other characters.
|
||||||
|
// 3) Iterate through characters of string, introducing splits
|
||||||
|
// between adjacent characters that belong to different sets.
|
||||||
|
// 4) Iterate through array of split strings, and if a given string
|
||||||
|
// is upper case:
|
||||||
|
// if subsequent string is lower case:
|
||||||
|
// move last character of upper case string to beginning of
|
||||||
|
// lower case string
|
||||||
|
func camelCase(src string) (entries []string) {
|
||||||
|
// don't split invalid utf8
|
||||||
|
if !utf8.ValidString(src) {
|
||||||
|
return []string{src}
|
||||||
|
}
|
||||||
|
entries = []string{}
|
||||||
|
var runes [][]rune
|
||||||
|
lastClass := 0
|
||||||
|
// split into fields based on class of unicode character
|
||||||
|
for _, r := range src {
|
||||||
|
var class int
|
||||||
|
switch true {
|
||||||
|
case unicode.IsLower(r):
|
||||||
|
class = 1
|
||||||
|
case unicode.IsUpper(r):
|
||||||
|
class = 2
|
||||||
|
case unicode.IsDigit(r):
|
||||||
|
class = 3
|
||||||
|
default:
|
||||||
|
class = 4
|
||||||
|
}
|
||||||
|
if class == lastClass {
|
||||||
|
runes[len(runes)-1] = append(runes[len(runes)-1], r)
|
||||||
|
} else {
|
||||||
|
runes = append(runes, []rune{r})
|
||||||
|
}
|
||||||
|
lastClass = class
|
||||||
|
}
|
||||||
|
// handle upper case -> lower case sequences, e.g.
|
||||||
|
// "PDFL", "oader" -> "PDF", "Loader"
|
||||||
|
for i := 0; i < len(runes)-1; i++ {
|
||||||
|
if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) {
|
||||||
|
runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
|
||||||
|
runes[i] = runes[i][:len(runes[i])-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// construct []string from results
|
||||||
|
for _, s := range runes {
|
||||||
|
if len(s) > 0 {
|
||||||
|
entries = append(entries, string(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package kong
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func Parse(cli interface{}) {
|
||||||
|
parser, err := New("", "", cli)
|
||||||
|
parser.FatalIfErrorf(err)
|
||||||
|
err = parser.Parse(os.Args[1:])
|
||||||
|
parser.FatalIfErrorf(err)
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package kong
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Kong struct {
|
||||||
|
Model *ApplicationModel
|
||||||
|
// Termination function (defaults to os.Exit)
|
||||||
|
Terminate func(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(name, description string, grammar interface{}) (*Kong, error) {
|
||||||
|
if name == "" {
|
||||||
|
name = filepath.Base(os.Args[0])
|
||||||
|
}
|
||||||
|
return &Kong{
|
||||||
|
Model: &ApplicationModel{
|
||||||
|
Name: name,
|
||||||
|
Description: description,
|
||||||
|
},
|
||||||
|
Terminate: os.Exit,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse arguments into target.
|
||||||
|
func (k *Kong) Parse(args []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kong) Errorf(format string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(os.Stderr, k.Model.Name+": "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kong) FatalIfErrorf(err error, args ...interface{}) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := err.Error()
|
||||||
|
if len(args) == 0 {
|
||||||
|
msg = fmt.Sprintf(args[0].(string), args...) + ": " + err.Error()
|
||||||
|
}
|
||||||
|
k.Errorf("%s", msg)
|
||||||
|
k.Terminate(1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package kong
|
||||||
|
|
||||||
|
import "flag"
|
||||||
|
|
||||||
|
type Value = flag.Getter
|
||||||
|
|
||||||
|
type ApplicationModel struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
|
||||||
|
NodeModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeModel struct {
|
||||||
|
Groups []*GroupModel
|
||||||
|
// Positional arguments.
|
||||||
|
Arguments []*ArgumentModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupModel struct {
|
||||||
|
// Flags.
|
||||||
|
Flags []*FlagModel
|
||||||
|
// Command hierarchy.
|
||||||
|
Commands []*CommandModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValueModel struct {
|
||||||
|
Name string
|
||||||
|
Help string
|
||||||
|
Value flag.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandModel struct {
|
||||||
|
NodeModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArgumentModel struct {
|
||||||
|
ValueModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlagModel struct {
|
||||||
|
ValueModel
|
||||||
|
Short rune
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user