Initial commit.

This commit is contained in:
Alec Thomas
2018-04-10 16:51:06 +10:00
commit aa1bf9dcb5
6 changed files with 237 additions and 0 deletions
+26
View File
@@ -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)
}
```
+20
View File
@@ -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)
}
+90
View File
@@ -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
}
+10
View File
@@ -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)
}
+47
View File
@@ -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)
}
+44
View File
@@ -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
}