From 9f300c423f8a765502f316be41e6928d290d53df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 17:58:47 +0200 Subject: [PATCH 01/12] + Add new gestion flag mkideal/cli --- .../github.com/Bowery/prompt/CONTRIBUTORS.md | 7 + vendor/github.com/Bowery/prompt/LICENSE | 21 + vendor/github.com/Bowery/prompt/README.md | 38 + vendor/github.com/Bowery/prompt/ansi_unix.go | 39 + .../github.com/Bowery/prompt/ansi_windows.go | 510 +++++++ vendor/github.com/Bowery/prompt/buffer.go | 152 +++ .../github.com/Bowery/prompt/buffer_unix.go | 76 ++ .../Bowery/prompt/buffer_windows.go | 150 +++ vendor/github.com/Bowery/prompt/ioctl_bsd.go | 15 + .../github.com/Bowery/prompt/ioctl_linux.go | 13 + .../github.com/Bowery/prompt/ioctl_solaris.go | 41 + vendor/github.com/Bowery/prompt/ioctl_unix.go | 62 + vendor/github.com/Bowery/prompt/keys.go | 41 + vendor/github.com/Bowery/prompt/keys_unix.go | 13 + .../github.com/Bowery/prompt/keys_windows.go | 34 + vendor/github.com/Bowery/prompt/prompt.go | 85 ++ vendor/github.com/Bowery/prompt/term.go | 471 +++++++ vendor/github.com/Bowery/prompt/term_unix.go | 96 ++ .../github.com/Bowery/prompt/term_windows.go | 116 ++ vendor/github.com/labstack/gommon/LICENSE | 22 + .../labstack/gommon/color/README.md | 86 ++ .../github.com/labstack/gommon/color/color.go | 407 ++++++ vendor/github.com/mkideal/cli/CHANGELOG.md | 22 + vendor/github.com/mkideal/cli/LICENSE | 18 + vendor/github.com/mkideal/cli/README.md | 1174 +++++++++++++++++ vendor/github.com/mkideal/cli/builtin.go | 37 + vendor/github.com/mkideal/cli/cli.go | 373 ++++++ vendor/github.com/mkideal/cli/cliutil.go | 103 ++ vendor/github.com/mkideal/cli/coder.go | 32 + vendor/github.com/mkideal/cli/command.go | 557 ++++++++ vendor/github.com/mkideal/cli/context.go | 233 ++++ vendor/github.com/mkideal/cli/editor.go | 52 + vendor/github.com/mkideal/cli/errors.go | 106 ++ vendor/github.com/mkideal/cli/flag.go | 490 +++++++ vendor/github.com/mkideal/cli/flag_set.go | 244 ++++ vendor/github.com/mkideal/cli/fuzzy.go | 71 + vendor/github.com/mkideal/cli/http.go | 156 +++ vendor/github.com/mkideal/cli/parser.go | 54 + vendor/github.com/mkideal/cli/run-examples.sh | 19 + vendor/github.com/mkideal/cli/tag.go | 149 +++ vendor/github.com/mkideal/pkg/LICENSE | 18 + vendor/github.com/mkideal/pkg/debug/debug.go | 104 ++ vendor/github.com/mkideal/pkg/debug/stack.go | 25 + vendor/github.com/mkideal/pkg/expr/README.md | 85 ++ vendor/github.com/mkideal/pkg/expr/eval.go | 133 ++ vendor/github.com/mkideal/pkg/expr/expr.go | 113 ++ .../github.com/mkideal/pkg/expr/internal.go | 115 ++ vendor/github.com/mkideal/pkg/expr/pool.go | 155 +++ vendor/github.com/mkideal/pkg/expr/value.go | 161 +++ vendor/vendor.json | 30 + 50 files changed, 7324 insertions(+) create mode 100644 vendor/github.com/Bowery/prompt/CONTRIBUTORS.md create mode 100644 vendor/github.com/Bowery/prompt/LICENSE create mode 100644 vendor/github.com/Bowery/prompt/README.md create mode 100644 vendor/github.com/Bowery/prompt/ansi_unix.go create mode 100644 vendor/github.com/Bowery/prompt/ansi_windows.go create mode 100644 vendor/github.com/Bowery/prompt/buffer.go create mode 100644 vendor/github.com/Bowery/prompt/buffer_unix.go create mode 100644 vendor/github.com/Bowery/prompt/buffer_windows.go create mode 100644 vendor/github.com/Bowery/prompt/ioctl_bsd.go create mode 100644 vendor/github.com/Bowery/prompt/ioctl_linux.go create mode 100644 vendor/github.com/Bowery/prompt/ioctl_solaris.go create mode 100644 vendor/github.com/Bowery/prompt/ioctl_unix.go create mode 100644 vendor/github.com/Bowery/prompt/keys.go create mode 100644 vendor/github.com/Bowery/prompt/keys_unix.go create mode 100644 vendor/github.com/Bowery/prompt/keys_windows.go create mode 100644 vendor/github.com/Bowery/prompt/prompt.go create mode 100644 vendor/github.com/Bowery/prompt/term.go create mode 100644 vendor/github.com/Bowery/prompt/term_unix.go create mode 100644 vendor/github.com/Bowery/prompt/term_windows.go create mode 100644 vendor/github.com/labstack/gommon/LICENSE create mode 100644 vendor/github.com/labstack/gommon/color/README.md create mode 100644 vendor/github.com/labstack/gommon/color/color.go create mode 100644 vendor/github.com/mkideal/cli/CHANGELOG.md create mode 100644 vendor/github.com/mkideal/cli/LICENSE create mode 100644 vendor/github.com/mkideal/cli/README.md create mode 100644 vendor/github.com/mkideal/cli/builtin.go create mode 100644 vendor/github.com/mkideal/cli/cli.go create mode 100644 vendor/github.com/mkideal/cli/cliutil.go create mode 100644 vendor/github.com/mkideal/cli/coder.go create mode 100644 vendor/github.com/mkideal/cli/command.go create mode 100644 vendor/github.com/mkideal/cli/context.go create mode 100644 vendor/github.com/mkideal/cli/editor.go create mode 100644 vendor/github.com/mkideal/cli/errors.go create mode 100644 vendor/github.com/mkideal/cli/flag.go create mode 100644 vendor/github.com/mkideal/cli/flag_set.go create mode 100644 vendor/github.com/mkideal/cli/fuzzy.go create mode 100644 vendor/github.com/mkideal/cli/http.go create mode 100644 vendor/github.com/mkideal/cli/parser.go create mode 100755 vendor/github.com/mkideal/cli/run-examples.sh create mode 100644 vendor/github.com/mkideal/cli/tag.go create mode 100644 vendor/github.com/mkideal/pkg/LICENSE create mode 100644 vendor/github.com/mkideal/pkg/debug/debug.go create mode 100644 vendor/github.com/mkideal/pkg/debug/stack.go create mode 100644 vendor/github.com/mkideal/pkg/expr/README.md create mode 100644 vendor/github.com/mkideal/pkg/expr/eval.go create mode 100644 vendor/github.com/mkideal/pkg/expr/expr.go create mode 100644 vendor/github.com/mkideal/pkg/expr/internal.go create mode 100644 vendor/github.com/mkideal/pkg/expr/pool.go create mode 100644 vendor/github.com/mkideal/pkg/expr/value.go diff --git a/vendor/github.com/Bowery/prompt/CONTRIBUTORS.md b/vendor/github.com/Bowery/prompt/CONTRIBUTORS.md new file mode 100644 index 0000000..7d247a3 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/CONTRIBUTORS.md @@ -0,0 +1,7 @@ +- [Larz Conwell](https://github.com/larzconwell) +- [Steve Kaliski](https://github.com/sjkaliski) +- [NHOrus](https://github.com/NHOrus) +- [Attila Fülöp](https://github.com/AttilaFueloep) +- [Gereon Frey](https://github.com/gfrey) +- [Aaron Bieber](https://github.com/qbit) +- [Ricky Medina](https://github.com/r-medina) diff --git a/vendor/github.com/Bowery/prompt/LICENSE b/vendor/github.com/Bowery/prompt/LICENSE new file mode 100644 index 0000000..0cc1fbe --- /dev/null +++ b/vendor/github.com/Bowery/prompt/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2015 Bowery, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/Bowery/prompt/README.md b/vendor/github.com/Bowery/prompt/README.md new file mode 100644 index 0000000..f9db406 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/README.md @@ -0,0 +1,38 @@ +# Prompt + +[![Circle CI](https://circleci.com/gh/Bowery/prompt/tree/master.png?style=badge)](https://circleci.com/gh/Bowery/prompt/tree/master) + +[![GoDoc](https://godoc.org/github.com/Bowery/prompt?status.png)](https://godoc.org/github.com/Bowery/prompt) + +Prompt is a cross platform line-editing prompting library. Read the GoDoc page +for more info and for API details. + +## Features +- Keyboard shortcuts in prompts +- History support +- Secure password prompt +- Custom prompt support +- Fallback prompt for unsupported terminals +- ANSI conversion for Windows + +## Todo +- Multi-line prompt as a Terminal option +- Make refresh less jittery on Windows([possible reason](https://github.com/Bowery/prompt/blob/master/output_windows.go#L108)) +- Multi-byte character support on Windows +- `AnsiWriter` should execute the equivalent ANSI escape code functionality on Windows +- Support for more ANSI escape codes on Windows. +- More keyboard shortcuts from Readlines shortcut list + +## Contributing + +Make sure Go is setup and running the latest release version, and make sure your `GOPATH` is setup properly. + +Follow the guidelines [here](https://guides.github.com/activities/contributing-to-open-source/#contributing). + +Please be sure to `gofmt` any code before doing commits. You can simply run `gofmt -w .` to format all the code in the directory. + +Lastly don't forget to add your name to [`CONTRIBUTORS.md`](https://github.com/Bowery/prompt/blob/master/CONTRIBUTORS.md) + +## License + +Prompt is MIT licensed, details can be found [here](https://raw.githubusercontent.com/Bowery/prompt/master/LICENSE). diff --git a/vendor/github.com/Bowery/prompt/ansi_unix.go b/vendor/github.com/Bowery/prompt/ansi_unix.go new file mode 100644 index 0000000..70adf68 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/ansi_unix.go @@ -0,0 +1,39 @@ +// +build linux darwin freebsd openbsd netbsd dragonfly solaris + +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "os" +) + +// AnsiReader is an io.Reader that wraps an *os.File. +type AnsiReader struct { + file *os.File +} + +// NewAnsiReader creates a AnsiReader from the given input file. +func NewAnsiReader(in *os.File) *AnsiReader { + return &AnsiReader{file: in} +} + +// Read reads data from the input file into b. +func (ar *AnsiReader) Read(b []byte) (int, error) { + return ar.file.Read(b) +} + +// AnsiWriter is an io.Writer that wraps an *os.File. +type AnsiWriter struct { + file *os.File +} + +// NewAnsiWriter creates a AnsiWriter from the given output file. +func NewAnsiWriter(out *os.File) *AnsiWriter { + return &AnsiWriter{file: out} +} + +// Write writes data from b into the input file. +func (aw *AnsiWriter) Write(b []byte) (int, error) { + return aw.file.Write(b) +} diff --git a/vendor/github.com/Bowery/prompt/ansi_windows.go b/vendor/github.com/Bowery/prompt/ansi_windows.go new file mode 100644 index 0000000..5cf18a1 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/ansi_windows.go @@ -0,0 +1,510 @@ +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "bytes" + "os" + "unicode/utf8" + "unsafe" +) + +// keyEventType is the key event type for an input record. +const keyEventType = 0x0001 + +var ( + readConsoleInput = kernel.NewProc("ReadConsoleInputW") +) + +// inputRecord describes a input event from a console. +type inputRecord struct { + eventType uint16 + // Magic to get around the union C type, cast + // event to the type using unsafe.Pointer. + _ [2]byte + event [16]byte +} + +// keyEventRecord describes a keyboard event. +type keyEventRecord struct { + keyDown int32 + repeatCount uint16 + virtualKeyCode uint16 + virtualScanCode uint16 + char uint16 + controlKeyState uint32 +} + +// AnsiReader is an io.Reader that reads from a given file and converts Windows +// key codes to their equivalent ANSI escape codes. +type AnsiReader struct { + fd uintptr + buf []rune +} + +// NewAnsiReader creates a AnsiReader from the given input file. +func NewAnsiReader(in *os.File) *AnsiReader { + return &AnsiReader{fd: in.Fd()} +} + +// Read reads data from the input converting to ANSI escape codes that can be +// read over multiple Reads. +func (ar *AnsiReader) Read(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + + if len(ar.buf) == 0 { + var runes []rune + var read uint32 + rec := new(inputRecord) + + for runes == nil { + ret, _, err := readConsoleInput.Call(ar.fd, uintptr(unsafe.Pointer(rec)), + 1, uintptr(unsafe.Pointer(&read))) + if ret == 0 { + return 0, err + } + + if rec.eventType != keyEventType { + continue + } + + ke := (*keyEventRecord)(unsafe.Pointer(&rec.event)) + if ke.keyDown == 0 { + continue + } + + shift := false + if ke.controlKeyState&shiftKey != 0 { + shift = true + } + + ctrl := false + if ke.controlKeyState&leftCtrlKey != 0 || ke.controlKeyState&rightCtrlKey != 0 { + ctrl = true + } + + alt := false + if ke.controlKeyState&leftAltKey != 0 || ke.controlKeyState&rightAltKey != 0 { + alt = true + } + + // Backspace, Return, Space. + if ke.char == ctrlH || ke.char == returnKey || ke.char == spaceKey { + code := string(returnKey) + if ke.char == ctrlH { + code = string(backKey) + } else if ke.char == spaceKey { + code = string(spaceKey) + } + + if alt { + code = string(escKey) + code + } + + runes = []rune(code) + break + } + + // Generate runes for the chars and key codes. + if ke.char > 0 { + runes = []rune{rune(ke.char)} + } else { + code := string(escKey) + + switch ke.virtualKeyCode { + case f1Key: + if ctrl { + continue + } + + code += ar.shortFunction("P", shift, ctrl, alt) + case f2Key: + code += ar.shortFunction("Q", shift, ctrl, alt) + case f3Key: + code += ar.shortFunction("R", shift, ctrl, alt) + case f4Key: + code += ar.shortFunction("S", shift, ctrl, alt) + case f5Key: + code += ar.longFunction("15", shift, ctrl, alt) + case f6Key: + code += ar.longFunction("17", shift, ctrl, alt) + case f7Key: + code += ar.longFunction("18", shift, ctrl, alt) + case f8Key: + code += ar.longFunction("19", shift, ctrl, alt) + case f9Key: + code += ar.longFunction("20", shift, ctrl, alt) + case f10Key: + code += ar.longFunction("21", shift, ctrl, alt) + case f11Key: + code += ar.longFunction("23", shift, ctrl, alt) + case f12Key: + code += ar.longFunction("24", shift, ctrl, alt) + case insertKey: + if shift || ctrl { + continue + } + + code += ar.longFunction("2", shift, ctrl, alt) + case deleteKey: + code += ar.longFunction("3", shift, ctrl, alt) + case homeKey: + code += "OH" + case endKey: + code += "OF" + case pgupKey: + if shift { + continue + } + + code += ar.longFunction("5", shift, ctrl, alt) + case pgdownKey: + if shift { + continue + } + + code += ar.longFunction("6", shift, ctrl, alt) + case upKey: + code += ar.arrow("A", shift, ctrl, alt) + case downKey: + code += ar.arrow("B", shift, ctrl, alt) + case leftKey: + code += ar.arrow("D", shift, ctrl, alt) + case rightKey: + code += ar.arrow("C", shift, ctrl, alt) + default: + continue + } + + runes = []rune(code) + } + } + + ar.buf = runes + } + + // Get items from the buffer. + var n int + for i, r := range ar.buf { + if utf8.RuneLen(r) > len(b) { + ar.buf = ar.buf[i:] + return n, nil + } + + nr := utf8.EncodeRune(b, r) + b = b[nr:] + n += nr + } + + ar.buf = nil + return n, nil +} + +// shortFunction creates a short function code. +func (ar *AnsiReader) shortFunction(ident string, shift, ctrl, alt bool) string { + code := "O" + + if shift { + code += "1;2" + } else if ctrl { + code += "1;5" + } else if alt { + code += "1;3" + } + + return code + ident +} + +// longFunction creates a long function code. +func (ar *AnsiReader) longFunction(ident string, shift, ctrl, alt bool) string { + code := "[" + code += ident + + if shift { + code += ";2" + } else if ctrl { + code += ";5" + } else if alt { + code += ";3" + } + + return code + "~" +} + +// arrow creates an arrow code. +func (ar *AnsiReader) arrow(ident string, shift, ctrl, alt bool) string { + code := "[" + + if shift { + code += "1;2" + } else if ctrl { + code += "1;5" + } else if alt { + code += "1;3" + } + + return code + ident +} + +// AnsiWriter is an io.Writer that writes to a given file and converts ANSI +// escape codes to their equivalent Windows functionality. +type AnsiWriter struct { + file *os.File + buf []byte +} + +// NewAnsiWriter creates a AnsiWriter from the given output. +func NewAnsiWriter(out *os.File) *AnsiWriter { + return &AnsiWriter{file: out} +} + +// Write writes the buffer filtering out ANSI escape codes and converting to +// the Windows functionality needed. ANSI escape codes may be found over multiple +// Writes. +func (aw *AnsiWriter) Write(b []byte) (int, error) { + needsProcessing := bytes.Contains(b, []byte(string(escKey))) + if len(aw.buf) > 0 { + needsProcessing = true + } + + if !needsProcessing { + return aw.file.Write(b) + } + var p []byte + + for _, char := range b { + // Found the beginning of an escape. + if char == escKey { + aw.buf = append(aw.buf, char) + continue + } + + // Funtion identifiers. + if len(aw.buf) == 1 && (char == '_' || char == 'P' || char == '[' || + char == ']' || char == '^' || char == ' ' || char == '#' || + char == '%' || char == '(' || char == ')' || char == '*' || + char == '+') { + aw.buf = append(aw.buf, char) + continue + } + + // Cursor functions. + if len(aw.buf) == 1 && (char == '7' || char == '8') { + // Add another char before because finish skips 2 items. + aw.buf = append(aw.buf, '_', char) + + err := aw.finish(nil) + if err != nil { + return 0, err + } + + continue + } + + // Keyboard functions. + if len(aw.buf) == 1 && (char == '=' || char == '>') { + aw.buf = append(aw.buf, char) + + err := aw.finish(nil) + if err != nil { + return 0, err + } + + continue + } + + // Bottom left function. + if len(aw.buf) == 1 && char == 'F' { + // Add extra char for finish. + aw.buf = append(aw.buf, '_', char) + + err := aw.finish(nil) + if err != nil { + return 0, err + } + + continue + } + + // Reset function. + if len(aw.buf) == 1 && char == 'c' { + // Add extra char for finish. + aw.buf = append(aw.buf, '_', char) + + err := aw.finish(nil) + if err != nil { + return 0, err + } + + continue + } + + // Space functions. + if len(aw.buf) >= 2 && aw.buf[1] == ' ' && (char == 'F' || char == 'G' || + char == 'L' || char == 'M' || char == 'N') { + aw.buf = append(aw.buf, char) + + err := aw.finish(nil) + if err != nil { + return 0, err + } + + continue + } + + // Number functions. + if len(aw.buf) >= 2 && aw.buf[1] == '#' && (char >= '3' && char <= '6') || + char == '8' { + aw.buf = append(aw.buf, char) + + err := aw.finish(nil) + if err != nil { + return 0, err + } + + continue + } + + // Percentage functions. + if len(aw.buf) >= 2 && aw.buf[1] == '%' && (char == '@' || char == 'G') { + aw.buf = append(aw.buf, char) + + err := aw.finish(nil) + if err != nil { + return 0, err + } + + continue + } + + // Character set functions. + if len(aw.buf) >= 2 && (aw.buf[1] == '(' || aw.buf[1] == ')' || + aw.buf[1] == '*' || aw.buf[1] == '+') && (char == '0' || + (char >= '4' && char <= '7') || char == '=' || (char >= 'A' && + char <= 'C') || char == 'E' || char == 'H' || char == 'K' || + char == 'Q' || char == 'R' || char == 'Y') { + aw.buf = append(aw.buf, char) + + err := aw.finish(nil) + if err != nil { + return 0, err + } + + continue + } + + // APC functions. + if len(aw.buf) >= 2 && aw.buf[1] == '_' { + aw.buf = append(aw.buf, char) + + // End of APC. + if char == '\\' && aw.buf[len(aw.buf)-1] == escKey { + err := aw.finish(nil) + if err != nil { + return 0, err + } + } + + continue + } + + // DC functions. + if len(aw.buf) >= 2 && aw.buf[1] == 'P' { + aw.buf = append(aw.buf, char) + + // End of DC. + if char == '\\' && aw.buf[len(aw.buf)-1] == escKey { + err := aw.finish(nil) + if err != nil { + return 0, err + } + } + + continue + } + + // CSI functions. + if len(aw.buf) >= 2 && aw.buf[1] == '[' { + aw.buf = append(aw.buf, char) + + // End of CSI. + if char == '@' || (char >= 'A' && char <= 'M') || char == 'P' || + char == 'S' || char == 'T' || char == 'X' || char == 'Z' || + char == '`' || (char >= 'b' && char <= 'd') || (char >= 'f' && + char <= 'i') || (char >= 'l' && char <= 'n') || (char >= 'p' && + char <= 't') || char == 'w' || char == 'x' || char == 'z' || + char == '{' || char == '|' { + err := aw.finish(nil) + if err != nil { + return 0, err + } + } + + continue + } + + // OSC functions. + if len(aw.buf) >= 2 && aw.buf[1] == ']' { + aw.buf = append(aw.buf, char) + + // Capture incomplete code. + if len(aw.buf) == 4 && aw.buf[2] == '0' && char == ';' { + err := aw.finish(nil) + if err != nil { + return 0, err + } + + continue + } + + // End of OSC. + if (char == '\\' && aw.buf[len(aw.buf)-1] == escKey) || char == ctrlG { + err := aw.finish(nil) + if err != nil { + return 0, err + } + } + + continue + } + + // PM functions. + if len(aw.buf) >= 2 && aw.buf[1] == '^' { + aw.buf = append(aw.buf, char) + + // End of PM. + if char == '\\' && aw.buf[len(aw.buf)-1] == escKey { + err := aw.finish(nil) + if err != nil { + return 0, err + } + } + + continue + } + + // Normal character, resets escape buffer. + if len(aw.buf) > 0 { + aw.buf = nil + } + p = append(p, char) + } + + _, err := aw.file.Write(p) + return len(b), err +} + +// finish finishes an ANSI escape code and calls the parsing function. Afterwards +// the escape buffer is emptied. +func (aw *AnsiWriter) finish(parse func([]byte) error) error { + var err error + + if parse != nil { + err = parse(aw.buf[2:]) + } + + aw.buf = nil + return err +} diff --git a/vendor/github.com/Bowery/prompt/buffer.go b/vendor/github.com/Bowery/prompt/buffer.go new file mode 100644 index 0000000..917da3e --- /dev/null +++ b/vendor/github.com/Bowery/prompt/buffer.go @@ -0,0 +1,152 @@ +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "os" + "unicode/utf8" +) + +// Buffer contains state for line editing and writing. +type Buffer struct { + Out *os.File + Prompt string + Echo bool + Cols int + pos int + size int + data []rune +} + +// NewBuffer creates a buffer writing to out if echo is true. +func NewBuffer(prompt string, out *os.File, echo bool) *Buffer { + return &Buffer{ + Out: out, + Prompt: prompt, + Echo: echo, + } +} + +// String returns the data as a string. +func (buf *Buffer) String() string { + return string(buf.data[:buf.size]) +} + +// Insert inserts characters at the cursors position. +func (buf *Buffer) Insert(rs ...rune) error { + rsLen := len(rs) + total := buf.size + rsLen + + if total > len(buf.data) { + buf.data = append(buf.data, make([]rune, rsLen)...) + } + + // Shift characters to make room in the correct pos. + if buf.size != buf.pos { + copy(buf.data[buf.pos+rsLen:buf.size+rsLen], buf.data[buf.pos:buf.size]) + } + + for _, r := range rs { + buf.data[buf.pos] = r + buf.pos++ + buf.size++ + } + + return buf.Refresh() +} + +// Set sets the content in the buffer. +func (buf *Buffer) Set(rs ...rune) error { + rsLen := len(rs) + buf.data = rs + buf.pos = rsLen + buf.size = rsLen + + return buf.Refresh() +} + +// Start moves the cursor to the start. +func (buf *Buffer) Start() error { + if buf.pos <= 0 { + return nil + } + + buf.pos = 0 + return buf.Refresh() +} + +// End moves the cursor to the end. +func (buf *Buffer) End() error { + if buf.pos >= buf.size { + return nil + } + + buf.pos = buf.size + return buf.Refresh() +} + +// Left moves the cursor one character left. +func (buf *Buffer) Left() error { + if buf.pos <= 0 { + return nil + } + + buf.pos-- + return buf.Refresh() +} + +// Right moves the cursor one character right. +func (buf *Buffer) Right() error { + if buf.pos >= buf.size { + return nil + } + + buf.pos++ + return buf.Refresh() +} + +// Del removes the character at the cursor position. +func (buf *Buffer) Del() error { + if buf.pos >= buf.size { + return nil + } + + // Shift characters after position back one. + copy(buf.data[buf.pos:], buf.data[buf.pos+1:buf.size]) + buf.size-- + + return buf.Refresh() +} + +// DelLeft removes the character to the left. +func (buf *Buffer) DelLeft() error { + if buf.pos <= 0 { + return nil + } + + // Shift characters from position back one. + copy(buf.data[buf.pos-1:], buf.data[buf.pos:buf.size]) + buf.pos-- + buf.size-- + + return buf.Refresh() +} + +// EndLine ends the line with CRLF. +func (buf *Buffer) EndLine() error { + _, err := buf.Out.Write(crlf) + return err +} + +// toBytes converts a slice of runes to its equivalent in bytes. +func toBytes(runes []rune) []byte { + var bytes []byte + char := make([]byte, utf8.UTFMax) + + for _, r := range runes { + n := utf8.EncodeRune(char, r) + bytes = append(bytes, char[:n]...) + } + + return bytes +} diff --git a/vendor/github.com/Bowery/prompt/buffer_unix.go b/vendor/github.com/Bowery/prompt/buffer_unix.go new file mode 100644 index 0000000..5464f25 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/buffer_unix.go @@ -0,0 +1,76 @@ +// +build linux darwin freebsd openbsd netbsd dragonfly solaris + +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "fmt" +) + +// Refresh rewrites the prompt and buffer. +func (buf *Buffer) Refresh() error { + // If we're not echoing just write prompt. + if !buf.Echo { + _, err := buf.Out.Write(mvLeftEdge) + if err != nil { + return err + } + + _, err = buf.Out.Write([]byte(buf.Prompt)) + if err != nil { + return err + } + + _, err = buf.Out.Write(delRight) + return err + } + + prLen := len(buf.Prompt) + start := 0 + size := buf.size + pos := buf.pos + + // Get slice range that should be visible. + for prLen+pos >= buf.Cols { + start++ + size-- + pos-- + } + for prLen+size > buf.Cols { + size-- + } + + _, err := buf.Out.Write(mvLeftEdge) + if err != nil { + return err + } + + _, err = buf.Out.Write([]byte(buf.Prompt)) + if err != nil { + return err + } + + _, err = buf.Out.Write(toBytes(buf.data[start : size+start])) + if err != nil { + return err + } + + _, err = buf.Out.Write(delRight) + if err != nil { + return err + } + + _, err = buf.Out.Write([]byte(fmt.Sprintf(mvToCol, pos+prLen))) + return err +} + +// ClsScreen clears the screen and refreshes. +func (buf *Buffer) ClsScreen() error { + _, err := buf.Out.Write(clsScreen) + if err != nil { + return err + } + + return buf.Refresh() +} diff --git a/vendor/github.com/Bowery/prompt/buffer_windows.go b/vendor/github.com/Bowery/prompt/buffer_windows.go new file mode 100644 index 0000000..e24c0d1 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/buffer_windows.go @@ -0,0 +1,150 @@ +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "unsafe" +) + +var ( + fillConsoleOutputCharacter = kernel.NewProc("FillConsoleOutputCharacterW") + setConsoleCursorPosition = kernel.NewProc("SetConsoleCursorPosition") +) + +// Refresh rewrites the prompt and buffer. +func (buf *Buffer) Refresh() error { + csbi := new(consoleScreenBufferInfo) + ret, _, err := getConsoleScreenBufferInfo.Call(buf.Out.Fd(), + uintptr(unsafe.Pointer(csbi))) + if ret == 0 { + return err + } + + // If we're not echoing just write prompt. + if !buf.Echo { + err = buf.delLine(csbi) + if err != nil { + return err + } + + err = buf.mvLeftEdge(csbi) + if err != nil { + return err + } + + _, err = buf.Out.Write([]byte(buf.Prompt)) + return err + } + + prLen := len(buf.Prompt) + start := 0 + size := buf.size + pos := buf.pos + + // Get slice range that should be visible. + for prLen+pos >= buf.Cols { + start++ + size-- + pos-- + } + for prLen+size > buf.Cols { + size-- + } + + err = buf.delLine(csbi) + if err != nil { + return err + } + + err = buf.mvLeftEdge(csbi) + if err != nil { + return err + } + + _, err = buf.Out.Write([]byte(buf.Prompt)) + if err != nil { + return err + } + + _, err = buf.Out.Write(toBytes(buf.data[start : size+start])) + if err != nil { + return err + } + + return buf.mvToCol(csbi, pos+prLen) +} + +// ClsScreen clears the screen and refreshes. +func (buf *Buffer) ClsScreen() error { + var written uint32 + coords := new(coord) + + csbi := new(consoleScreenBufferInfo) + ret, _, err := getConsoleScreenBufferInfo.Call(buf.Out.Fd(), + uintptr(unsafe.Pointer(csbi))) + if ret == 0 { + return err + } + + // Clear everything from 0,0. + ret, _, err = fillConsoleOutputCharacter.Call(buf.Out.Fd(), uintptr(' '), + uintptr(csbi.size.x*csbi.size.y), uintptr(*(*int32)(unsafe.Pointer(coords))), + uintptr(unsafe.Pointer(&written))) + if ret == 0 { + return err + } + + // Set cursor at 0,0. + ret, _, err = setConsoleCursorPosition.Call(buf.Out.Fd(), + uintptr(*(*int32)(unsafe.Pointer(coords)))) + if ret == 0 { + return err + } + + return buf.Refresh() +} + +// delLine deletes the line the csbi cursor is positioned on. +// TODO: Possible refresh jittering reason, instead we should copy the Unix +// code and write over contents and then remove everything to the right. +func (buf *Buffer) delLine(csbi *consoleScreenBufferInfo) error { + var written uint32 + coords := &coord{y: csbi.cursorPosition.y} + + ret, _, err := fillConsoleOutputCharacter.Call(buf.Out.Fd(), uintptr(' '), + uintptr(csbi.size.x), uintptr(*(*int32)(unsafe.Pointer(coords))), + uintptr(unsafe.Pointer(&written))) + if ret == 0 { + return err + } + + return nil +} + +// mvLeftEdge moves the cursor to the beginning of the line the csbi cursor +// is positioned on. +func (buf *Buffer) mvLeftEdge(csbi *consoleScreenBufferInfo) error { + coords := &coord{y: csbi.cursorPosition.y} + + ret, _, err := setConsoleCursorPosition.Call(buf.Out.Fd(), + uintptr(*(*int32)(unsafe.Pointer(coords)))) + if ret == 0 { + return err + } + + return nil +} + +// mvTolCol moves the cursor to the col on the line the csbi cursor is +// positioned on. +func (buf *Buffer) mvToCol(csbi *consoleScreenBufferInfo, x int) error { + coords := &coord{x: int16(x), y: csbi.cursorPosition.y} + + ret, _, err := setConsoleCursorPosition.Call(buf.Out.Fd(), + uintptr(*(*int32)(unsafe.Pointer(coords)))) + if ret == 0 { + return err + } + + return nil +} diff --git a/vendor/github.com/Bowery/prompt/ioctl_bsd.go b/vendor/github.com/Bowery/prompt/ioctl_bsd.go new file mode 100644 index 0000000..71476c8 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/ioctl_bsd.go @@ -0,0 +1,15 @@ +// +build darwin freebsd openbsd netbsd dragonfly + +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "golang.org/x/sys/unix" +) + +const ( + tcgets = unix.TIOCGETA + tcsets = unix.TIOCSETA + tcsetsf = unix.TIOCSETAF +) diff --git a/vendor/github.com/Bowery/prompt/ioctl_linux.go b/vendor/github.com/Bowery/prompt/ioctl_linux.go new file mode 100644 index 0000000..5ca9cdc --- /dev/null +++ b/vendor/github.com/Bowery/prompt/ioctl_linux.go @@ -0,0 +1,13 @@ +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "golang.org/x/sys/unix" +) + +const ( + tcgets = unix.TCGETS + tcsets = unix.TCSETS + tcsetsf = unix.TCSETSF +) diff --git a/vendor/github.com/Bowery/prompt/ioctl_solaris.go b/vendor/github.com/Bowery/prompt/ioctl_solaris.go new file mode 100644 index 0000000..4077aae --- /dev/null +++ b/vendor/github.com/Bowery/prompt/ioctl_solaris.go @@ -0,0 +1,41 @@ +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "os" + + "golang.org/x/sys/unix" +) + +const ( + tcgets = unix.TCGETS + tcsetsf = unix.TCSETSF + tcsets = unix.TCSETS +) + +// terminalSize retrieves the cols/rows for the terminal connected to out. +func terminalSize(out *os.File) (int, int, error) { + ws, err := unix.IoctlGetWinsize(int(out.Fd()), unix.TIOCGWINSZ) + if err != nil { + return 0, 0, err + } + + return int(ws.Col), int(ws.Row), nil +} + +// getTermios retrieves the termios settings for the terminal descriptor. +func getTermios(fd uintptr) (*unix.Termios, error) { + return unix.IoctlGetTermios(int(fd), tcgets) +} + +// setTermios sets the termios settings for the terminal descriptor, +// optionally flushing the buffer before setting. +func setTermios(fd uintptr, flush bool, mode *unix.Termios) error { + req := tcsets + if flush { + req = tcsetsf + } + + return unix.IoctlSetTermios(int(fd), req, mode) +} diff --git a/vendor/github.com/Bowery/prompt/ioctl_unix.go b/vendor/github.com/Bowery/prompt/ioctl_unix.go new file mode 100644 index 0000000..70dda50 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/ioctl_unix.go @@ -0,0 +1,62 @@ +// +build linux darwin freebsd openbsd netbsd dragonfly + +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +// winsize contains the size for the terminal. +type winsize struct { + rows uint16 + cols uint16 + _ uint32 +} + +// terminalSize retrieves the cols/rows for the terminal connected to out. +func terminalSize(out *os.File) (int, int, error) { + ws := new(winsize) + + _, _, err := unix.Syscall(unix.SYS_IOCTL, out.Fd(), + uintptr(unix.TIOCGWINSZ), uintptr(unsafe.Pointer(ws))) + if err != 0 { + return 0, 0, err + } + + return int(ws.cols), int(ws.rows), nil +} + +// getTermios retrieves the termios settings for the terminal descriptor. +func getTermios(fd uintptr) (*unix.Termios, error) { + termios := new(unix.Termios) + + _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, tcgets, + uintptr(unsafe.Pointer(termios))) + if err != 0 { + return nil, err + } + + return termios, nil +} + +// setTermios sets the termios settings for the terminal descriptor, +// optionally flushing the buffer before setting. +func setTermios(fd uintptr, flush bool, mode *unix.Termios) error { + req := tcsets + if flush { + req = tcsetsf + } + + _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(req), + uintptr(unsafe.Pointer(mode))) + if err != 0 { + return err + } + + return nil +} diff --git a/vendor/github.com/Bowery/prompt/keys.go b/vendor/github.com/Bowery/prompt/keys.go new file mode 100644 index 0000000..a4e2e40 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/keys.go @@ -0,0 +1,41 @@ +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +// Line ending in raw mode. +var crlf = []byte("\r\n") + +const ( + backKey = '\u007f' + escKey = '\u001B' + spaceKey = '\u0020' +) + +const ( + ctrlA = iota + 1 + ctrlB + ctrlC + ctrlD + ctrlE + ctrlF + ctrlG + ctrlH + tabKey + ctrlJ + ctrlK + ctrlL + returnKey + ctrlN + ctrlO + ctrlP + ctrlQ + ctrlR + ctrlS + ctrlT + ctrlU + ctrlV + ctrlW + ctrlX + ctrlY + ctrlZ +) diff --git a/vendor/github.com/Bowery/prompt/keys_unix.go b/vendor/github.com/Bowery/prompt/keys_unix.go new file mode 100644 index 0000000..c6dc854 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/keys_unix.go @@ -0,0 +1,13 @@ +// +build linux darwin freebsd openbsd netbsd dragonfly solaris + +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +const mvToCol = "\u001b[0G\u001b[%dC" + +var ( + mvLeftEdge = []byte("\u001b[0G") + clsScreen = []byte("\u001b[H\u001b[2J") + delRight = []byte("\u001b[0K") +) diff --git a/vendor/github.com/Bowery/prompt/keys_windows.go b/vendor/github.com/Bowery/prompt/keys_windows.go new file mode 100644 index 0000000..74db787 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/keys_windows.go @@ -0,0 +1,34 @@ +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +const ( + f1Key = 0x70 + iota + f2Key + f3Key + f4Key + f5Key + f6Key + f7Key + f8Key + f9Key + f10Key + f11Key + f12Key + + homeKey = 0x24 + endKey = 0x23 + upKey = 0x26 + downKey = 0x28 + rightKey = 0x27 + leftKey = 0x25 + insertKey = 0x2d + pgupKey = 0x21 + pgdownKey = 0x22 + deleteKey = 0x2e + leftAltKey = 0x2 + rightAltKey = 0x1 + leftCtrlKey = 0x8 + rightCtrlKey = 0x4 + shiftKey = 0x10 +) diff --git a/vendor/github.com/Bowery/prompt/prompt.go b/vendor/github.com/Bowery/prompt/prompt.go new file mode 100644 index 0000000..f603729 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/prompt.go @@ -0,0 +1,85 @@ +// Copyright 2013-2015 Bowery, Inc. + +// Package prompt implements a cross platform line-editing prompt. It also +// provides routines to use ANSI escape sequences across platforms for +// terminal connected io.Readers/io.Writers. +// +// If os.Stdin isn't connected to a terminal or (on Unix)if the terminal +// doesn't support the ANSI escape sequences needed a fallback prompt is +// provided that doesn't do line-editing. Unix terminals that are not supported +// will have the TERM environment variable set to either "dumb" or "cons25". +// +// The keyboard shortcuts are similar to those found in the Readline library: +// +// - Enter / CTRL+D +// - End the line. +// - CTRL+C +// - End the line, return error `ErrCTRLC`. +// - Backspace +// - Remove the character to the left. +// - CTRL+L +// - Clear the screen(keeping the current lines content). +// - Home / End +// - Jump to the beginning/end of the line. +// - Up arrow / Down arrow +// - Go back and forward in the history. +// - Left arrow / Right arrow +// - Move left/right one character. +// - Delete +// - Remove the character to the right. +package prompt + +// Basic is a wrapper around Terminal.Basic. +func Basic(prefix string, required bool) (string, error) { + term, err := NewTerminal() + if err != nil { + return "", err + } + defer term.Close() + + return term.Basic(prefix, required) +} + +// BasicDefault is a wrapper around Terminal.BasicDefault. +func BasicDefault(prefix, def string) (string, error) { + term, err := NewTerminal() + if err != nil { + return "", err + } + defer term.Close() + + return term.BasicDefault(prefix, def) +} + +// Ask is a wrapper around Terminal.Ask. +func Ask(question string) (bool, error) { + term, err := NewTerminal() + if err != nil { + return false, err + } + defer term.Close() + + return term.Ask(question) +} + +// Custom is a wrapper around Terminal.Custom. +func Custom(prefix string, test func(string) (string, bool)) (string, error) { + term, err := NewTerminal() + if err != nil { + return "", err + } + defer term.Close() + + return term.Custom(prefix, test) +} + +// Password is a wrapper around Terminal.Password. +func Password(prefix string) (string, error) { + term, err := NewTerminal() + if err != nil { + return "", err + } + defer term.Close() + + return term.Password(prefix) +} diff --git a/vendor/github.com/Bowery/prompt/term.go b/vendor/github.com/Bowery/prompt/term.go new file mode 100644 index 0000000..82a5bf5 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/term.go @@ -0,0 +1,471 @@ +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "bufio" + "errors" + "io" + "os" + "strings" +) + +var ( + // ErrCTRLC is returned when CTRL+C is pressed stopping the prompt. + ErrCTRLC = errors.New("Interrupted (CTRL+C)") + // ErrEOF is returned when CTRL+D is pressed stopping the prompt. + ErrEOF = errors.New("EOF (CTRL+D)") +) + +// Possible events that may occur when reading from input. +const ( + evChar = iota + evSkip + evReturn + evEOF + evCtrlC + evBack + evClear + evHome + evEnd + evUp + evDown + evRight + evLeft + evDel +) + +// IsNotTerminal checks if an error is related to the input not being a terminal. +func IsNotTerminal(err error) bool { + return isNotTerminal(err) +} + +// TerminalSize retrieves the columns/rows for the terminal connected to out. +func TerminalSize(out *os.File) (int, int, error) { + return terminalSize(out) +} + +// Terminal contains the state for raw terminal input. +type Terminal struct { + In *os.File + Out *os.File + History []string + histIdx int + simpleReader *bufio.Reader + t *terminal +} + +// NewTerminal creates a terminal and sets it to raw input mode. +func NewTerminal() (*Terminal, error) { + in := os.Stdin + + term, err := newTerminal(in) + if err != nil { + return nil, err + } + + return &Terminal{ + In: in, + Out: os.Stdout, + History: make([]string, 0, 10), + histIdx: -1, + t: term, + }, nil +} + +// Basic gets input and if required tests to ensure input was given. +func (term *Terminal) Basic(prefix string, required bool) (string, error) { + return term.Custom(prefix, func(input string) (string, bool) { + if required && input == "" { + return "", false + } + + return input, true + }) +} + +// BasicDefault gets input and if empty uses the given default. +func (term *Terminal) BasicDefault(prefix, def string) (string, error) { + return term.Custom(prefix+"(Default: "+def+")", func(input string) (string, bool) { + if input == "" { + input = def + } + + return input, true + }) +} + +// Ask gets input and checks if it's truthy or not, and returns that +// in a boolean fashion. +func (term *Terminal) Ask(question string) (bool, error) { + input, err := term.Custom(question+"?(y/n)", func(input string) (string, bool) { + if input == "" { + return "", false + } + input = strings.ToLower(input) + + if input == "y" || input == "yes" { + return "yes", true + } + + return "", true + }) + + var ok bool + if input != "" { + ok = true + } + + return ok, err +} + +// Custom gets input and calls the given test function with the input to +// check if the input is valid, a true return will return the string. +func (term *Terminal) Custom(prefix string, test func(string) (string, bool)) (string, error) { + var err error + var input string + var ok bool + + for !ok { + input, err = term.GetPrompt(prefix) + if err != nil && err != io.EOF { + return "", err + } + + input, ok = test(input) + } + + return input, nil +} + +// Password retrieves a password from stdin without echoing it. +func (term *Terminal) Password(prefix string) (string, error) { + var err error + var input string + + for input == "" { + input, err = term.GetPassword(prefix) + if err != nil && err != io.EOF { + return "", err + } + } + + return input, nil +} + +// GetPrompt gets a line with the prefix and echos input. +func (term *Terminal) GetPrompt(prefix string) (string, error) { + if !term.t.supportsEditing { + return term.simplePrompt(prefix) + } + + buf := NewBuffer(prefix, term.Out, true) + return term.prompt(buf, NewAnsiReader(term.In)) +} + +// GetPassword gets a line with the prefix and doesn't echo input. +func (term *Terminal) GetPassword(prefix string) (string, error) { + if !term.t.supportsEditing { + return term.simplePrompt(prefix) + } + + buf := NewBuffer(prefix, term.Out, false) + return term.password(buf, NewAnsiReader(term.In)) +} + +func (term *Terminal) Close() error { + return term.t.Close() +} + +// simplePrompt is a fallback prompt without line editing support. +func (term *Terminal) simplePrompt(prefix string) (string, error) { + if term.simpleReader == nil { + term.simpleReader = bufio.NewReader(term.In) + } + + _, err := term.Out.Write([]byte(prefix)) + if err != nil { + return "", err + } + + line, err := term.simpleReader.ReadString('\n') + line = strings.TrimRight(line, "\r\n ") + line = strings.TrimLeft(line, " ") + + return line, err +} + +// setup initializes a prompt. +func (term *Terminal) setup(buf *Buffer, in io.Reader) (*bufio.Reader, error) { + cols, _, err := TerminalSize(buf.Out) + if err != nil { + return nil, err + } + + buf.Cols = cols + input := bufio.NewReader(in) + + err = buf.Refresh() + if err != nil { + return nil, err + } + + return input, nil +} + +// read reads a rune and parses ANSI escape sequences found +func (term *Terminal) read(in *bufio.Reader) (int, rune, error) { + char, _, err := in.ReadRune() + if err != nil { + return 0, 0, err + } + + switch char { + default: + // Standard chars. + return evChar, char, nil + case tabKey, ctrlA, ctrlB, ctrlE, ctrlF, ctrlG, ctrlH, ctrlJ, ctrlK, ctrlN, + ctrlO, ctrlP, ctrlQ, ctrlR, ctrlS, ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, + ctrlY, ctrlZ: + // Skip. + return evSkip, char, nil + case returnKey: + // End of line. + return evReturn, char, nil + case ctrlD: + // End of file. + return evEOF, char, nil + case ctrlC: + // End of line, interrupted. + return evCtrlC, char, nil + case backKey: + // Backspace. + return evBack, char, nil + case ctrlL: + // Clear screen. + return evClear, char, nil + case escKey: + // Functions like arrows, home, etc. + esc := make([]byte, 2) + _, err = in.Read(esc) + if err != nil { + return -1, char, err + } + + // Home, end. + if esc[0] == 'O' { + switch esc[1] { + case 'H': + // Home. + return evHome, char, nil + case 'F': + // End. + return evEnd, char, nil + } + + return evSkip, char, nil + } + + // Arrows, delete, pgup, pgdown, insert. + if esc[0] == '[' { + switch esc[1] { + case 'A': + // Up. + return evUp, char, nil + case 'B': + // Down. + return evDown, char, nil + case 'C': + // Right. + return evRight, char, nil + case 'D': + // Left. + return evLeft, char, nil + } + + // Delete, pgup, pgdown, insert. + if esc[1] > '0' && esc[1] < '7' { + extEsc := make([]byte, 3) + _, err = in.Read(extEsc) + if err != nil { + return -1, char, err + } + + if extEsc[0] == '~' { + switch esc[1] { + case '2', '5', '6': + // Insert, pgup, pgdown. + return evSkip, char, err + case '3': + // Delete. + return evDel, char, err + } + } + } + } + } + + return evSkip, char, nil +} + +// prompt reads from in and parses ANSI escapes writing to buf. +func (term *Terminal) prompt(buf *Buffer, in io.Reader) (string, error) { + input, err := term.setup(buf, in) + if err != nil { + return "", err + } + term.History = append(term.History, "") + term.histIdx = len(term.History) - 1 + curHistIdx := term.histIdx + + for { + typ, char, err := term.read(input) + if err != nil { + return buf.String(), err + } + + switch typ { + case evChar: + err = buf.Insert(char) + if err != nil { + return buf.String(), err + } + + term.History[curHistIdx] = buf.String() + case evSkip: + continue + case evReturn: + err = buf.EndLine() + return buf.String(), err + case evEOF: + err = buf.EndLine() + if err == nil { + err = ErrEOF + } + + return buf.String(), err + case evCtrlC: + err = buf.EndLine() + if err == nil { + err = ErrCTRLC + } + + return buf.String(), err + case evBack: + err = buf.DelLeft() + if err != nil { + return buf.String(), err + } + + term.History[curHistIdx] = buf.String() + case evClear: + err = buf.ClsScreen() + if err != nil { + return buf.String(), err + } + case evHome: + err = buf.Start() + if err != nil { + return buf.String(), err + } + case evEnd: + err = buf.End() + if err != nil { + return buf.String(), err + } + case evUp: + idx := term.histIdx + if term.histIdx > 0 { + idx-- + } + + err = buf.Set([]rune(term.History[idx])...) + if err != nil { + return buf.String(), err + } + + term.histIdx = idx + case evDown: + idx := term.histIdx + if term.histIdx < len(term.History)-1 { + idx++ + } + + err = buf.Set([]rune(term.History[idx])...) + if err != nil { + return buf.String(), err + } + + term.histIdx = idx + case evRight: + err = buf.Right() + if err != nil { + return buf.String(), err + } + case evLeft: + err = buf.Left() + if err != nil { + return buf.String(), err + } + case evDel: + err = buf.Del() + if err != nil { + return buf.String(), err + } + + term.History[curHistIdx] = buf.String() + } + } +} + +// password reads from in and parses restricted ANSI escapes writing to buf. +func (term *Terminal) password(buf *Buffer, in io.Reader) (string, error) { + input, err := term.setup(buf, in) + if err != nil { + return "", err + } + + for { + typ, char, err := term.read(input) + if err != nil { + return buf.String(), err + } + + switch typ { + case evChar: + err = buf.Insert(char) + if err != nil { + return buf.String(), err + } + case evSkip, evHome, evEnd, evUp, evDown, evRight, evLeft, evDel: + continue + case evReturn: + err = buf.EndLine() + return buf.String(), err + case evEOF: + err = buf.EndLine() + if err == nil { + err = ErrEOF + } + + return buf.String(), err + case evCtrlC: + err = buf.EndLine() + if err == nil { + err = ErrCTRLC + } + + return buf.String(), err + case evBack: + err = buf.DelLeft() + if err != nil { + return buf.String(), err + } + case evClear: + err = buf.ClsScreen() + if err != nil { + return buf.String(), err + } + } + } +} diff --git a/vendor/github.com/Bowery/prompt/term_unix.go b/vendor/github.com/Bowery/prompt/term_unix.go new file mode 100644 index 0000000..de3265b --- /dev/null +++ b/vendor/github.com/Bowery/prompt/term_unix.go @@ -0,0 +1,96 @@ +// +build linux darwin freebsd openbsd netbsd dragonfly solaris + +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// List of unsupported $TERM values. +var unsupported = []string{"", "dumb", "cons25"} + +// supportsEditing checks if the terminal supports ansi escapes. +func supportsEditing() bool { + term := os.Getenv("TERM") + + for _, t := range unsupported { + if t == term { + return false + } + } + + return true +} + +// isNotTerminal checks if an error is related to the input not being a terminal. +func isNotTerminal(err error) bool { + return err == unix.ENOTTY +} + +// terminal contains the private fields for a Unix terminal. +type terminal struct { + supportsEditing bool + fd uintptr + origMode unix.Termios +} + +// newTerminal creates a terminal and sets it to raw input mode. +func newTerminal(in *os.File) (*terminal, error) { + term := &terminal{fd: in.Fd()} + + if !supportsEditing() { + return term, nil + } + + t, err := getTermios(term.fd) + if err != nil { + if IsNotTerminal(err) { + return term, nil + } + + return nil, err + } + term.origMode = *t + mode := term.origMode + term.supportsEditing = true + + // Set new mode flags, for reference see cfmakeraw(3). + mode.Iflag &^= (unix.BRKINT | unix.IGNBRK | unix.ICRNL | + unix.INLCR | unix.IGNCR | unix.ISTRIP | unix.IXON | + unix.PARMRK) + + mode.Oflag &^= unix.OPOST + + mode.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | + unix.ISIG | unix.IEXTEN) + + mode.Cflag &^= (unix.CSIZE | unix.PARENB) + mode.Cflag |= unix.CS8 + + // Set controls; min num of bytes, and timeouts. + mode.Cc[unix.VMIN] = 1 + mode.Cc[unix.VTIME] = 0 + + err = setTermios(term.fd, true, &mode) + if err != nil { + return nil, err + } + + return term, nil +} + +// Close disables the terminals raw input. +func (term *terminal) Close() error { + if term.supportsEditing { + err := setTermios(term.fd, false, &term.origMode) + if err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/Bowery/prompt/term_windows.go b/vendor/github.com/Bowery/prompt/term_windows.go new file mode 100644 index 0000000..0ab1352 --- /dev/null +++ b/vendor/github.com/Bowery/prompt/term_windows.go @@ -0,0 +1,116 @@ +// Copyright 2013-2015 Bowery, Inc. + +package prompt + +import ( + "os" + "syscall" + "unsafe" +) + +// Flags to control the terminals mode. +const ( + echoInputFlag = 0x0004 + insertModeFlag = 0x0020 + lineInputFlag = 0x0002 + mouseInputFlag = 0x0010 + processedInputFlag = 0x0001 + windowInputFlag = 0x0008 +) + +// Error number returned for an invalid handle. +const errnoInvalidHandle = 0x6 + +var ( + kernel = syscall.NewLazyDLL("kernel32.dll") + getConsoleScreenBufferInfo = kernel.NewProc("GetConsoleScreenBufferInfo") + setConsoleMode = kernel.NewProc("SetConsoleMode") +) + +// consoleScreenBufferInfo contains various fields for the terminal. +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes uint16 + window smallRect + maximumWindowSize coord +} + +// coord contains coords for positioning. +type coord struct { + x int16 + y int16 +} + +// smallRect contains positions for the window edges. +type smallRect struct { + left int16 + top int16 + right int16 + bottom int16 +} + +// terminalSize retrieves the cols/rows for the terminal connected to out. +func terminalSize(out *os.File) (int, int, error) { + csbi := new(consoleScreenBufferInfo) + + ret, _, err := getConsoleScreenBufferInfo.Call(out.Fd(), uintptr(unsafe.Pointer(csbi))) + if ret == 0 { + return 0, 0, err + } + + // Results are always off by one. + cols := csbi.window.right - csbi.window.left + 1 + rows := csbi.window.bottom - csbi.window.top + 1 + + return int(cols), int(rows), nil +} + +// isNotTerminal checks if an error is related to the input not being a terminal. +func isNotTerminal(err error) bool { + errno, ok := err.(syscall.Errno) + + return ok && errno == errnoInvalidHandle +} + +// terminal contains the private fields for a Windows terminal. +type terminal struct { + supportsEditing bool + fd uintptr + origMode uint32 +} + +// newTerminal creates a terminal and sets it to raw input mode. +func newTerminal(in *os.File) (*terminal, error) { + term := &terminal{fd: in.Fd()} + + err := syscall.GetConsoleMode(syscall.Handle(term.fd), &term.origMode) + if err != nil { + return term, nil + } + mode := term.origMode + term.supportsEditing = true + + // Set new mode flags. + mode &^= (echoInputFlag | insertModeFlag | lineInputFlag | mouseInputFlag | + processedInputFlag | windowInputFlag) + + ret, _, err := setConsoleMode.Call(term.fd, uintptr(mode)) + if ret == 0 { + return nil, err + } + + return term, nil +} + +// Close disables the terminals raw input. +func (term *terminal) Close() error { + if term.supportsEditing { + ret, _, err := setConsoleMode.Call(term.fd, uintptr(term.origMode)) + if ret == 0 { + return err + } + } + + return nil +} diff --git a/vendor/github.com/labstack/gommon/LICENSE b/vendor/github.com/labstack/gommon/LICENSE new file mode 100644 index 0000000..d2ae3ed --- /dev/null +++ b/vendor/github.com/labstack/gommon/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 labstack + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/labstack/gommon/color/README.md b/vendor/github.com/labstack/gommon/color/README.md new file mode 100644 index 0000000..297c351 --- /dev/null +++ b/vendor/github.com/labstack/gommon/color/README.md @@ -0,0 +1,86 @@ +# Color + +Style terminal text. + +## Installation + +```sh +go get github.com/labstack/gommon/color +``` + +## Windows? + +Try [cmder](http://bliker.github.io/cmder) or https://github.com/mattn/go-colorable + +## [Usage](https://github.com/labstack/gommon/blob/master/color/color_test.go) + +```sh +import github.com/labstack/gommon/color +``` + +### Colored text + +```go +color.Println(color.Black("black")) +color.Println(color.Red("red")) +color.Println(color.Green("green")) +color.Println(color.Yellow("yellow")) +color.Println(color.Blue("blue")) +color.Println(color.Magenta("magenta")) +color.Println(color.Cyan("cyan")) +color.Println(color.White("white")) +color.Println(color.Grey("grey")) +``` +![Colored Text](http://i.imgur.com/8RtY1QR.png) + +### Colored background + +```go +color.Println(color.BlackBg("black background", color.Wht)) +color.Println(color.RedBg("red background")) +color.Println(color.GreenBg("green background")) +color.Println(color.YellowBg("yellow background")) +color.Println(color.BlueBg("blue background")) +color.Println(color.MagentaBg("magenta background")) +color.Println(color.CyanBg("cyan background")) +color.Println(color.WhiteBg("white background")) +``` +![Colored Background](http://i.imgur.com/SrrS6lw.png) + +### Emphasis + +```go +color.Println(color.Bold("bold")) +color.Println(color.Dim("dim")) +color.Println(color.Italic("italic")) +color.Println(color.Underline("underline")) +color.Println(color.Inverse("inverse")) +color.Println(color.Hidden("hidden")) +color.Println(color.Strikeout("strikeout")) +``` +![Emphasis](http://i.imgur.com/3RSJBbc.png) + +### Mix and match + +```go +color.Println(color.Green("bold green with white background", color.B, color.WhtBg)) +color.Println(color.Red("underline red", color.U)) +color.Println(color.Yellow("dim yellow", color.D)) +color.Println(color.Cyan("inverse cyan", color.In)) +color.Println(color.Blue("bold underline dim blue", color.B, color.U, color.D)) +``` +![Mix and match](http://i.imgur.com/jWGq9Ca.png) + +### Enable/Disable the package + +```go +color.Disable() +color.Enable() +``` + +### New instance + +```go +c := New() +c.Green("green") +``` diff --git a/vendor/github.com/labstack/gommon/color/color.go b/vendor/github.com/labstack/gommon/color/color.go new file mode 100644 index 0000000..4131dcf --- /dev/null +++ b/vendor/github.com/labstack/gommon/color/color.go @@ -0,0 +1,407 @@ +package color + +import ( + "bytes" + "fmt" + "io" + "os" + + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +type ( + inner func(interface{}, []string, *Color) string +) + +// Color styles +const ( + // Blk Black text style + Blk = "30" + // Rd red text style + Rd = "31" + // Grn green text style + Grn = "32" + // Yel yellow text style + Yel = "33" + // Blu blue text style + Blu = "34" + // Mgn magenta text style + Mgn = "35" + // Cyn cyan text style + Cyn = "36" + // Wht white text style + Wht = "37" + // Gry grey text style + Gry = "90" + + // BlkBg black background style + BlkBg = "40" + // RdBg red background style + RdBg = "41" + // GrnBg green background style + GrnBg = "42" + // YelBg yellow background style + YelBg = "43" + // BluBg blue background style + BluBg = "44" + // MgnBg magenta background style + MgnBg = "45" + // CynBg cyan background style + CynBg = "46" + // WhtBg white background style + WhtBg = "47" + + // R reset emphasis style + R = "0" + // B bold emphasis style + B = "1" + // D dim emphasis style + D = "2" + // I italic emphasis style + I = "3" + // U underline emphasis style + U = "4" + // In inverse emphasis style + In = "7" + // H hidden emphasis style + H = "8" + // S strikeout emphasis style + S = "9" +) + +var ( + black = outer(Blk) + red = outer(Rd) + green = outer(Grn) + yellow = outer(Yel) + blue = outer(Blu) + magenta = outer(Mgn) + cyan = outer(Cyn) + white = outer(Wht) + grey = outer(Gry) + + blackBg = outer(BlkBg) + redBg = outer(RdBg) + greenBg = outer(GrnBg) + yellowBg = outer(YelBg) + blueBg = outer(BluBg) + magentaBg = outer(MgnBg) + cyanBg = outer(CynBg) + whiteBg = outer(WhtBg) + + reset = outer(R) + bold = outer(B) + dim = outer(D) + italic = outer(I) + underline = outer(U) + inverse = outer(In) + hidden = outer(H) + strikeout = outer(S) + + global = New() +) + +func outer(n string) inner { + return func(msg interface{}, styles []string, c *Color) string { + // TODO: Drop fmt to boost performance? + if c.disabled { + return fmt.Sprintf("%v", msg) + } + + b := new(bytes.Buffer) + b.WriteString("\x1b[") + b.WriteString(n) + for _, s := range styles { + b.WriteString(";") + b.WriteString(s) + } + b.WriteString("m") + return fmt.Sprintf("%s%v\x1b[0m", b.String(), msg) + } +} + +type ( + Color struct { + output io.Writer + disabled bool + } +) + +// New creates a Color instance. +func New() (c *Color) { + c = new(Color) + c.SetOutput(colorable.NewColorableStdout()) + return +} + +// Output returns the output. +func (c *Color) Output() io.Writer { + return c.output +} + +// SetOutput sets the output. +func (c *Color) SetOutput(w io.Writer) { + c.output = w + if w, ok := w.(*os.File); !ok || !isatty.IsTerminal(w.Fd()) { + c.disabled = true + } +} + +// Disable disables the colors and styles. +func (c *Color) Disable() { + c.disabled = true +} + +// Enable enables the colors and styles. +func (c *Color) Enable() { + c.disabled = false +} + +// Print is analogous to `fmt.Print` with termial detection. +func (c *Color) Print(args ...interface{}) { + fmt.Fprint(c.output, args...) +} + +// Println is analogous to `fmt.Println` with termial detection. +func (c *Color) Println(args ...interface{}) { + fmt.Fprintln(c.output, args...) +} + +// Printf is analogous to `fmt.Printf` with termial detection. +func (c *Color) Printf(format string, args ...interface{}) { + fmt.Fprintf(c.output, format, args...) +} + +func (c *Color) Black(msg interface{}, styles ...string) string { + return black(msg, styles, c) +} + +func (c *Color) Red(msg interface{}, styles ...string) string { + return red(msg, styles, c) +} + +func (c *Color) Green(msg interface{}, styles ...string) string { + return green(msg, styles, c) +} + +func (c *Color) Yellow(msg interface{}, styles ...string) string { + return yellow(msg, styles, c) +} + +func (c *Color) Blue(msg interface{}, styles ...string) string { + return blue(msg, styles, c) +} + +func (c *Color) Magenta(msg interface{}, styles ...string) string { + return magenta(msg, styles, c) +} + +func (c *Color) Cyan(msg interface{}, styles ...string) string { + return cyan(msg, styles, c) +} + +func (c *Color) White(msg interface{}, styles ...string) string { + return white(msg, styles, c) +} + +func (c *Color) Grey(msg interface{}, styles ...string) string { + return grey(msg, styles, c) +} + +func (c *Color) BlackBg(msg interface{}, styles ...string) string { + return blackBg(msg, styles, c) +} + +func (c *Color) RedBg(msg interface{}, styles ...string) string { + return redBg(msg, styles, c) +} + +func (c *Color) GreenBg(msg interface{}, styles ...string) string { + return greenBg(msg, styles, c) +} + +func (c *Color) YellowBg(msg interface{}, styles ...string) string { + return yellowBg(msg, styles, c) +} + +func (c *Color) BlueBg(msg interface{}, styles ...string) string { + return blueBg(msg, styles, c) +} + +func (c *Color) MagentaBg(msg interface{}, styles ...string) string { + return magentaBg(msg, styles, c) +} + +func (c *Color) CyanBg(msg interface{}, styles ...string) string { + return cyanBg(msg, styles, c) +} + +func (c *Color) WhiteBg(msg interface{}, styles ...string) string { + return whiteBg(msg, styles, c) +} + +func (c *Color) Reset(msg interface{}, styles ...string) string { + return reset(msg, styles, c) +} + +func (c *Color) Bold(msg interface{}, styles ...string) string { + return bold(msg, styles, c) +} + +func (c *Color) Dim(msg interface{}, styles ...string) string { + return dim(msg, styles, c) +} + +func (c *Color) Italic(msg interface{}, styles ...string) string { + return italic(msg, styles, c) +} + +func (c *Color) Underline(msg interface{}, styles ...string) string { + return underline(msg, styles, c) +} + +func (c *Color) Inverse(msg interface{}, styles ...string) string { + return inverse(msg, styles, c) +} + +func (c *Color) Hidden(msg interface{}, styles ...string) string { + return hidden(msg, styles, c) +} + +func (c *Color) Strikeout(msg interface{}, styles ...string) string { + return strikeout(msg, styles, c) +} + +// Output returns the output. +func Output() io.Writer { + return global.output +} + +// SetOutput sets the output. +func SetOutput(w io.Writer) { + global.SetOutput(w) +} + +func Disable() { + global.Disable() +} + +func Enable() { + global.Enable() +} + +// Print is analogous to `fmt.Print` with termial detection. +func Print(args ...interface{}) { + global.Print(args...) +} + +// Println is analogous to `fmt.Println` with termial detection. +func Println(args ...interface{}) { + global.Println(args...) +} + +// Printf is analogous to `fmt.Printf` with termial detection. +func Printf(format string, args ...interface{}) { + global.Printf(format, args...) +} + +func Black(msg interface{}, styles ...string) string { + return global.Black(msg, styles...) +} + +func Red(msg interface{}, styles ...string) string { + return global.Red(msg, styles...) +} + +func Green(msg interface{}, styles ...string) string { + return global.Green(msg, styles...) +} + +func Yellow(msg interface{}, styles ...string) string { + return global.Yellow(msg, styles...) +} + +func Blue(msg interface{}, styles ...string) string { + return global.Blue(msg, styles...) +} + +func Magenta(msg interface{}, styles ...string) string { + return global.Magenta(msg, styles...) +} + +func Cyan(msg interface{}, styles ...string) string { + return global.Cyan(msg, styles...) +} + +func White(msg interface{}, styles ...string) string { + return global.White(msg, styles...) +} + +func Grey(msg interface{}, styles ...string) string { + return global.Grey(msg, styles...) +} + +func BlackBg(msg interface{}, styles ...string) string { + return global.BlackBg(msg, styles...) +} + +func RedBg(msg interface{}, styles ...string) string { + return global.RedBg(msg, styles...) +} + +func GreenBg(msg interface{}, styles ...string) string { + return global.GreenBg(msg, styles...) +} + +func YellowBg(msg interface{}, styles ...string) string { + return global.YellowBg(msg, styles...) +} + +func BlueBg(msg interface{}, styles ...string) string { + return global.BlueBg(msg, styles...) +} + +func MagentaBg(msg interface{}, styles ...string) string { + return global.MagentaBg(msg, styles...) +} + +func CyanBg(msg interface{}, styles ...string) string { + return global.CyanBg(msg, styles...) +} + +func WhiteBg(msg interface{}, styles ...string) string { + return global.WhiteBg(msg, styles...) +} + +func Reset(msg interface{}, styles ...string) string { + return global.Reset(msg, styles...) +} + +func Bold(msg interface{}, styles ...string) string { + return global.Bold(msg, styles...) +} + +func Dim(msg interface{}, styles ...string) string { + return global.Dim(msg, styles...) +} + +func Italic(msg interface{}, styles ...string) string { + return global.Italic(msg, styles...) +} + +func Underline(msg interface{}, styles ...string) string { + return global.Underline(msg, styles...) +} + +func Inverse(msg interface{}, styles ...string) string { + return global.Inverse(msg, styles...) +} + +func Hidden(msg interface{}, styles ...string) string { + return global.Hidden(msg, styles...) +} + +func Strikeout(msg interface{}, styles ...string) string { + return global.Strikeout(msg, styles...) +} diff --git a/vendor/github.com/mkideal/cli/CHANGELOG.md b/vendor/github.com/mkideal/cli/CHANGELOG.md new file mode 100644 index 0000000..61b9554 --- /dev/null +++ b/vendor/github.com/mkideal/cli/CHANGELOG.md @@ -0,0 +1,22 @@ +CHANGELOG +========= + +# unreleased + +* Del: Remove extra package `now`. +* Add: Adds `UsageFn` for customizing usage. +* Mod: Replaces `NeedArgs` with `NumArg`. + +# v0.0.1 (2016-05-21) + +Initializes the repository, and implements many important features. + +* Add: Prases flags base on golang tag. +* Add: Supports almost all basic types, slice and map as a flag. +* Add: Fuzzy matching for suggestion. +* Add: Supports command tree. +* Add: Pretty output for usage of command. +* Add: Supports any type which implement `cli.Decoder` as a flag. +* Add: Parser for flag. +* Add: Supports editor like `git commit`. +* Add: Rich examples to help other quick start. diff --git a/vendor/github.com/mkideal/cli/LICENSE b/vendor/github.com/mkideal/cli/LICENSE new file mode 100644 index 0000000..6c2f362 --- /dev/null +++ b/vendor/github.com/mkideal/cli/LICENSE @@ -0,0 +1,18 @@ +Copyright (C) 2016 mkideal(i@mkideal.com) + +  Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to the following +conditions: +   +  The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mkideal/cli/README.md b/vendor/github.com/mkideal/cli/README.md new file mode 100644 index 0000000..7424a44 --- /dev/null +++ b/vendor/github.com/mkideal/cli/README.md @@ -0,0 +1,1174 @@ +Command line interface +====================== + +[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/mkideal/cli/master/LICENSE) [![Travis branch](https://img.shields.io/travis/mkideal/cli/master.svg)](https://travis-ci.org/mkideal/cli) [![Coverage Status](https://coveralls.io/repos/github/mkideal/cli/badge.svg?branch=master)](https://coveralls.io/github/mkideal/cli?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/mkideal/cli)](https://goreportcard.com/report/github.com/mkideal/cli) [![GoDoc](https://godoc.org/github.com/mkideal/cli?status.svg)](https://godoc.org/github.com/mkideal/cli) + +Screenshot +---------- + +![screenshot2](http://www.mkideal.com/images/screenshot2.png) + +Key features +------------ + +- Lightweight and easy to use. +- Defines flag by tag, e.g. flag name(short or/and long), description, default value, password, prompt and so on. +- Type safety. +- Output looks very nice. +- Supports custom Validator. +- Supports slice and map as a flag. +- Supports any type as a flag field which implements cli.Decoder interface. +- Supports any type as a flag field which uses FlagParser. +- Suggestions for command.(e.g. `hl` => `help`, "veron" => "version"). +- Supports default value for flag, even expression about env variable(e.g. `dft:"$HOME/dev"`). +- Supports editor like `git commit` command.(See example [21](http://www.mkideal.com/golang/cli-examples.html#example-21-editor) and [22](http://www.mkideal.com/golang/cli-examples.html#example-22-custom-editor)\) + +API documentation +----------------- + +See [**godoc**](https://godoc.org/github.com/mkideal/cli) + +Examples +-------- + +- [Example 1: Hello world](#example-1-hello) +- [Example 2: How to use **flag**](#example-2-flag) +- [Example 3: How to use **required** flag](#example-3-required-flag) +- [Example 4: How to use **default** flag](#example-4-default-flag) +- [Example 5: How to use **slice**](#example-5-slice) +- [Example 6: How to use **map**](#example-6-map) +- [Example 7: Usage of **force** flag](#example-7-force-flag) +- [Example 8: Usage of **child command**](#example-8-child-command) +- [Example 9: **Auto help**](#example-9-auto-help) +- [Example 10: Usage of **Validator**](#example-10-usage-of-validator) +- [Example 11: **Prompt** and **Password**](#example-11-prompt-and-password) +- [Example 12: How to use **Decoder**](#example-12-decoder) +- [Example 13: Builtin Decoder: **PidFile**](#example-13-pid-file) +- [Example 14: Builtin Decoder: **Time** and **Duration**](#example-14-time-and-duration) +- [Example 15: Builtin Decoder: **File**](#example-15-file) +- [Example 16: **Parser**](#example-16-parser) +- [Example 17: Builtin Parser: **JSONFileParser**](#example-17-json-file) +- [Example 18: How to use **custom parser**](#example-18-custom-parser) +- [Example 19: How to use **Hooks**](#example-19-hooks) +- [Example 20: How to use **Daemon**](#example-20-daemon) +- [Example 21: How to use **Editor**](#example-21-editor) +- [Example 22: Custom **Editor**](#example-22-custom-editor) + +### Example 1: Hello + +[back to **examples**](#examples) + +```go +// main.go +// This is a HelloWorld-like example + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + Name string `cli:"name" usage:"tell me your name"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String("Hello, %s!\n", argv.Name) + return nil + }) +} +``` + +```sh +$ go build -o hello +$ ./hello --name Clipher +Hello, Clipher! +``` + +### Example 2: Flag + +[back to **examples**](#examples) + +```go +// main.go +// This example show basic usage of flag + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + cli.Helper + Port int `cli:"p,port" usage:"short and long format flags both are supported"` + X bool `cli:"x" usage:"boolean type"` + Y bool `cli:"y" usage:"boolean type, too"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String("port=%d, x=%v, y=%v\n", argv.Port, argv.X, argv.Y) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app -h +Options: + + -h, --help display help information + -p, --port short and long format flags both are supported + -x boolean type + -y boolean type, too +$ ./app -p=8080 -x +port=8080, x=true, y=false +$ ./app -p 8080 -x=true +port=8080, x=true, y=false +$ ./app -p8080 -y true +port=8080, x=false, y=true +$ ./app --port=8080 -xy +port=8080, x=true, y=true +$ ./app --port 8080 -yx +port=8080, x=true, y=true +``` + +### Example 3: Required flag + +[back to **examples**](#examples) + +```go +// main.go +// This example show how to use required flag + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + cli.Helper + Id uint8 `cli:"*id" usage:"this is a required flag, note the *"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String("%d\n", argv.Id) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app +ERR! required argument --id missing +$ ./app --id=2 +2 +``` + +### Example 4: Default flag + +[back to **examples**](#examples) + +```go +// main.go +// This example show how to use default flag + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + cli.Helper + Basic int `cli:"basic" usage:"basic usage of default" dft:"2"` + Env string `cli:"env" usage:"env variable as default" dft:"$HOME"` + Expr int `cli:"expr" usage:"expression as default" dft:"$BASE_PORT+1000"` + DevDir string `cli:"devdir" usage:"directory of developer" dft:"$HOME/dev"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String("%d, %s, %d, %s\n", argv.Basic, argv.Env, argv.Expr, argv.DevDir) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app -h +Options: + + -h, --help display help information + --basic[=2] basic usage of default + --env[=$HOME] env variable as default + --expr[=$BASE_PORT+1000] expression as default + --devdir[=$HOME/dev] directory of developer +$ ./app +2, /Users/wang, 1000, /Users/wang/dev +$ BASE_PORT=8000 ./app --basic=3 +3, /Users/wang, 9000, /Users/wang/dev +``` + +### Example 5: Slice + +[back to **examples**](#examples) + +```go +// main.go +// This example show how to use slice as a flag + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + // []bool, []int, []float32, ... supported too. + Friends []string `cli:"F" usage:"my friends"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + ctx.JSONln(ctx.Argv()) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app +{"Friends":null} +$ ./app -FAlice -FBob -F Charlie +{"Friends":["Alice","Bob","Charlie"]} +``` + +### Example 6: Map + +[back to **examples**](#examples) + +```go +// main.go +// This example show how to use map as a flag + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + Macros map[string]int `cli:"D" usage:"define macros"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + ctx.JSONln(ctx.Argv()) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app +{"Macros":null} +$ ./app -Dx=not-a-number +ERR! `not-a-number` couldn't converted to an int value +$ ./app -Dx=1 -D y=2 +{"Macros":{"x":1,"y":2}} +``` + +### Example 7: Force flag + +[back to **examples**](#examples) + +```go +// main.go +// This example show usage of force flag +// Force flag has prefix !, and must be a boolean. +// Will prevent validating flags if some force flag assigned true + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + Version bool `cli:"!v" usage:"force flag, note the !"` + Required int `cli:"*r" usage:"required flag"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + if argv.Version { + ctx.String("v0.0.1\n") + } + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app +ERR! required argument -r missing + +# -v is a force flag, and assigned true, so `ERR` disappear. +$ ./app -v +v0.0.1 +``` + +### Example 8: Child command + +[back to **examples**](#examples) + +```go +// main.go +// This example demonstrates usage of child command + +package main + +import ( + "fmt" + "os" + + "github.com/mkideal/cli" +) + +func main() { + if err := cli.Root(root, + cli.Tree(help), + cli.Tree(child), + ).Run(os.Args[1:]); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +var help = cli.HelpCommand("display help information") + +// root command +type rootT struct { + cli.Helper + Name string `cli:"name" usage:"your name"` +} + +var root = &cli.Command{ + Desc: "this is root command", + // Argv is a factory function of argument object + // ctx.Argv() is if Command.Argv == nil or Command.Argv() is nil + Argv: func() interface{} { return new(rootT) }, + Fn: func(ctx *cli.Context) error { + argv := ctx.Argv().(*rootT) + ctx.String("Hello, root command, I am %s\n", argv.Name) + return nil + }, +} + +// child command +type childT struct { + cli.Helper + Name string `cli:"name" usage:"your name"` +} + +var child = &cli.Command{ + Name: "child", + Desc: "this is a child command", + Argv: func() interface{} { return new(childT) }, + Fn: func(ctx *cli.Context) error { + argv := ctx.Argv().(*childT) + ctx.String("Hello, child command, I am %s\n", argv.Name) + return nil + }, +} +``` + +```sh +$ go build -o app + +# help for root +# equivalent to "./app -h" +$ ./app help +this is root command + +Options: + + -h, --help display help information + --name your name + +Commands: + help display help information + child this is a child command + +# help for specific command +# equivalent to "./app child -h" +$ ./app help child +this is a child command + +Options: + + -h, --help display help information + --name your name + +# execute root command +$ ./app --name 123 +Hello, root command, I am 123 + +# execute child command +$ ./app child --name=123 +Hello, child command, I am 123 + +# something wrong, but got a suggestion. +$ ./app chd +ERR! command chd not found +Did you mean child? +``` + +### Example 9: Auto help + +[back to **examples**](#examples) + +```go +// main.go +// This example demonstrates cli.AutoHelper + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + Help bool `cli:"h,help" usage:"show help"` +} + +// AutoHelp implements cli.AutoHelper interface +// NOTE: cli.Helper is a predefined type which implements cli.AutoHelper +func (argv *argT) AutoHelp() bool { + return argv.Help +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app -h +Options: + + -h, --help show help +``` + +Try comment `AutoHelp` method and rerun it. + +### Example 10: Usage of Validator + +[back to **examples**](#examples) + +```go +// main.go +// This example demonstrates how to utilize Validator + +package main + +import ( + "fmt" + + "github.com/mkideal/cli" +) + +type argT struct { + cli.Helper + Age int `cli:"age" usage:"your age"` + Gender string `cli:"g,gender" usage:"your gender" dft:"male"` +} + +// Validate implements cli.Validator interface +func (argv *argT) Validate(ctx *cli.Context) error { + if argv.Age < 0 || argv.Age > 300 { + return fmt.Errorf("age %d out of range", argv.Age) + } + if argv.Gender != "male" && argv.Gender != "female" { + return fmt.Errorf("invalid gender %s", ctx.Color().Yellow(argv.Gender)) + } + return nil +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + ctx.JSONln(ctx.Argv()) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app --age=-1 +ERR! age -1 out of range +$ ./app --age=1000 +ERR! age 1000 out of range +$ ./app -g balabala +ERR! invalid gender balabala +$ ./app --age 88 --gender female +{"Help":false,"Age":88,"Gender":"female"} +``` + +### Example 11: Prompt and Password + +[back to **examples**](#examples) + +```go +// main.go +// This example introduce prompt and pw tag + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + cli.Helper + Username string `cli:"u,username" usage:"github account" prompt:"type github account"` + Password string `pw:"p,password" usage:"password of github account" prompt:"type the password"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String("username=%s, password=%s\n", argv.Username, argv.Password) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app +type github account: hahaha # visible +type the password: # invisible because of `pw` tag +username=hahaha, password=123456 +``` + +### Example 12: Decoder + +[back to **examples**](#examples) + +```go +// main.go +// This example show how to use decoder + +package main + +import ( + "strings" + + "github.com/mkideal/cli" +) + +type exampleDecoder struct { + list []string +} + +// Decode implements cli.Decoder interface +func (d *exampleDecoder) Decode(s string) error { + d.list = strings.Split(s, ",") + return nil +} + +type argT struct { + Example exampleDecoder `cli:"d" usage:"example decoder"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.JSONln(argv.Example.list) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app -d a,b,c +["a","b","c"] +``` + +### Example 13: Pid file + +[back to **examples**](#examples) + +```go +// main.go +// This example show how to use builtin Decoder: PidFile + +package main + +import ( + "github.com/mkideal/cli" + clix "github.com/mkideal/cli/ext" +) + +type argT struct { + cli.Helper + PidFile clix.PidFile `cli:"pid" usage:"pid file" dft:"013-pidfile.pid"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + + if err := argv.PidFile.New(); err != nil { + return err + } + defer argv.PidFile.Remove() + + return nil + }) +} +``` + +### Example 14: Time and Duration + +[back to **examples**](#examples) + +```go +// main.go +// This example show how to use builtin Decoder: Time and Duration + +package main + +import ( + "github.com/mkideal/cli" + clix "github.com/mkideal/cli/ext" +) + +type argT struct { + Time clix.Time `cli:"t" usage:"time"` + Duration clix.Duration `cli:"d" usage:"duration"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String("time=%v, duration=%v\n", argv.Time, argv.Duration) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app -t '2016-1-2 3:5' -d=10ms +time=2016-01-02 03:05:00 +0800 CST, duration=10ms +``` + +### Example 15: File + +[back to **examples**](#examples) + +```go +// main.go +// This example show how to use builtin Decoder: File + +package main + +import ( + "github.com/mkideal/cli" + clix "github.com/mkideal/cli/ext" +) + +type argT struct { + Content clix.File `cli:"f,file" usage:"read content from file or stdin"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String(argv.Content.String()) + return nil + }) +} +``` + +```sh +$ go build -o app +# read from stdin +$ echo hello | ./app -f +hello +# read from file +$ echo hello > test.txt && ./app -f test.txt +hello +$ rm test.txt +``` + +### Example 16: Parser + +[back to **examples**](#examples) + +```go +// main.go +// This example introduce Parser +// `Parser` is another way to use custom type of data. +// Unlike `Decoder`, `Parser` used to parse string according to specific rule, +// like json,yaml and so on. +// +// Builtin parsers: +// * json +// * jsonfile + +package main + +import ( + "github.com/mkideal/cli" +) + +type config struct { + A string + B int + C bool +} + +type argT struct { + JSON config `cli:"c,config" usage:"parse json string" parser:"json"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.JSONIndentln(argv.JSON, "", " ") + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app +{ + "A": "", + "B": 0, + "C": false +} +$ ./app -c '{"A": "hello", "b": 22, "C": true}' +{ + "A": "hello", + "B": 22, + "C": true +} +``` + +### Example 17: JSON file + +[back to **examples**](#examples) + +```go +// main.go +// This example show how to use builtin parser: jsonfile +// It's similar to json, but read string from file. + +package main + +import ( + "github.com/mkideal/cli" +) + +type config struct { + A string + B int + C bool +} + +type argT struct { + JSON config `cli:"c,config" usage:"parse json from file" parser:"jsonfile"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.JSONIndentln(argv.JSON, "", " ") + return nil + }) +} +``` + +```sh +$ go build -o app +$ echo '{"A": "hello", "b": 22, "C": true}' > test.json +$ ./app -c test.json +{ + "A": "hello", + "B": 22, + "C": true +} +$ rm test.json +``` + +### Example 18: Custom parser + +[back to **examples**](#examples) + +```go +// main.go +// This example demonstrates how to use custom parser + +package main + +import ( + "reflect" + + "github.com/mkideal/cli" +) + +type myParser struct { + ptr interface{} +} + +func newMyParser(ptr interface{}) cli.FlagParser { + return &myParser{ptr} +} + +// Parse implements FlagParser.Parse interface +func (parser *myParser) Parse(s string) error { + typ := reflect.TypeOf(parser.ptr) + val := reflect.ValueOf(parser.ptr) + if typ.Kind() == reflect.Ptr { + kind := reflect.Indirect(val).Type().Kind() + if kind == reflect.Struct { + typElem, valElem := typ.Elem(), val.Elem() + numField := valElem.NumField() + for i := 0; i < numField; i++ { + _, valField := typElem.Field(i), valElem.Field(i) + if valField.Kind() == reflect.Int && + valField.CanSet() { + valField.SetInt(2) + } + if valField.Kind() == reflect.String && + valField.CanSet() { + valField.SetString("B") + } + } + } + } + return nil +} + +type config struct { + A int + B string +} + +type argT struct { + Cfg config `cli:"cfg" parser:"myparser"` +} + +func main() { + // register parser factory function + cli.RegisterFlagParser("myparser", newMyParser) + + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String("%v\n", argv.Cfg) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app +{0 } +$ ./app --cfg xxx +{2 B} +``` + +### Example 19: Hooks + +[back to **examples**](#examples) + +```go +// main.go +// This example demonstrates how to use hooks + +package main + +import ( + "fmt" + "os" + + "github.com/mkideal/cli" +) + +func main() { + if err := cli.Root(root, + cli.Tree(child1), + cli.Tree(child2), + ).Run(os.Args[1:]); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +type argT struct { + Error bool `cli:"e" usage:"return error"` +} + +var root = &cli.Command{ + Name: "app", + Argv: func() interface{} { return new(argT) }, + OnRootBefore: func(ctx *cli.Context) error { + ctx.String("OnRootBefore invoked\n") + return nil + }, + OnRootAfter: func(ctx *cli.Context) error { + ctx.String("OnRootAfter invoked\n") + return nil + }, + Fn: func(ctx *cli.Context) error { + ctx.String("exec root command\n") + argv := ctx.Argv().(*argT) + if argv.Error { + return fmt.Errorf("root command returns error") + } + return nil + }, +} + +var child1 = &cli.Command{ + Name: "child1", + Argv: func() interface{} { return new(argT) }, + OnBefore: func(ctx *cli.Context) error { + ctx.String("child1's OnBefore invoked\n") + return nil + }, + OnAfter: func(ctx *cli.Context) error { + ctx.String("child1's OnAfter invoked\n") + return nil + }, + Fn: func(ctx *cli.Context) error { + ctx.String("exec child1 command\n") + argv := ctx.Argv().(*argT) + if argv.Error { + return fmt.Errorf("child1 command returns error") + } + return nil + }, +} + +var child2 = &cli.Command{ + Name: "child2", + NoHook: true, + Fn: func(ctx *cli.Context) error { + ctx.String("exec child2 command\n") + return nil + }, +} +``` + +```sh +$ go build -o app +# OnRootBefore => Fn => OnRootAfter +$ ./app +OnRootBefore invoked +exec root command +OnRootAfter invoked +# OnBefore => OnRootBefore => Fn => OnRootAfter => OnAfter +$ ./app child1 +child1 OnBefore invoked +OnRootBefore invoked +exec child1 command +OnRootAfter invoked +child1 OnAfter invoked +# No hooks +$ ./app child2 +exec child2 command +# OnRootBefore => Fn --> Error +$ ./app -e +OnRootBefore invoked +exec root command +root command returns error +# OnBefore => OnRootBefore => Fn --> Error +$ ./app child1 -e +child1 OnBefore invoked +OnRootBefore invoked +exec child1 command +child1 command returns error +``` + +### Example 20: Daemon + +[back to **examples**](#examples) + +```go +// main.go +// This example demonstrates how to use `Daemon` + +package main + +import ( + "fmt" + "os" + "time" + + "github.com/mkideal/cli" +) + +type argT struct { + cli.Helper + Wait uint `cli:"wait" usage:"seconds for waiting" dft:"10"` + Error bool `cli:"e" usage:"create an error"` +} + +const successResponsePrefix = "start ok" + +func main() { + if err := cli.Root(root, + cli.Tree(daemon), + ).Run(os.Args[1:]); err != nil { + fmt.Fprintln(os.Stderr, err) + } +} + +var root = &cli.Command{ + Argv: func() interface{} { return new(argT) }, + Fn: func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + if argv.Error { + err := fmt.Errorf("occurs error") + cli.DaemonResponse(err.Error()) + return err + } + cli.DaemonResponse(successResponsePrefix) + <-time.After(time.Duration(argv.Wait) * time.Second) + return nil + }, +} + +var daemon = &cli.Command{ + Name: "daemon", + Argv: func() interface{} { return new(argT) }, + Fn: func(ctx *cli.Context) error { + return cli.Daemon(ctx, successResponsePrefix) + }, +} +``` + +```sh +$ go build -o daemon-app +$ ./daemone-app daemon +start ok +# Within 10 seconds, you will see process "./daemon-app" +$ ps | grep daemon-app +11913 ttys002 0:00.01 ./daemon-app +11915 ttys002 0:00.00 grep daemon-app +# After 10 seconds +$ ps | grep daemon-app +11936 ttys002 0:00.00 grep daemon-app +# try again with an error +$ ./daemon-app daemon -e +occurs error +$ ps | grep daemon-app +11936 ttys002 0:00.00 grep daemon-app +``` + +### Example 21: Editor + +[back to **examples**](#examples) + +```go +// main.go +// This example demonstrates how to use `editor`. This similar to git commit + +package main + +import ( + "github.com/mkideal/cli" +) + +type argT struct { + cli.Helper + Msg string `edit:"m" usage:"message"` +} + +func main() { + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String("msg: %s", argv.Msg) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app -m "hello, editor" +msg: hello, editor +$ ./app # Then, launch a editor(default is vim) and type `hello, editor`, quit the editor +msg: hello, editor +``` + +### Example 22: Custom Editor + +[back to **examples**](#examples) + +```go +// main.go +// This example demonstrates specific editor. + +package main + +import ( + "os" + + "github.com/mkideal/cli" +) + +type argT struct { + cli.Helper + Msg string `edit:"m" usage:"message"` +} + +func main() { + cli.GetEditor = func() (string, error) { + if editor := os.Getenv("EDITOR"); editor != "" { + return editor, nil + } + return cli.DefaultEditor, nil + } + cli.Run(new(argT), func(ctx *cli.Context) error { + argv := ctx.Argv().(*argT) + ctx.String("msg: %s", argv.Msg) + return nil + }) +} +``` + +```sh +$ go build -o app +$ ./app -m "hello, editor" +msg: hello, editor +$ EDITOR=nano ./app # Then, launch nano and type `hello, editor`, quit the editor +msg: hello, editor +``` diff --git a/vendor/github.com/mkideal/cli/builtin.go b/vendor/github.com/mkideal/cli/builtin.go new file mode 100644 index 0000000..68d0fa5 --- /dev/null +++ b/vendor/github.com/mkideal/cli/builtin.go @@ -0,0 +1,37 @@ +package cli + +import ( + "fmt" +) + +// Helper is builtin Help flag +type Helper struct { + Help bool `cli:"!h,help" usage:"display help information" json:"-"` +} + +// AutoHelp implements AutoHelper interface +func (h Helper) AutoHelp() bool { + return h.Help +} + +// Deprecated: Addr is builtin host,port flag +type Addr struct { + Host string `cli:"host" usage:"specify host" dft:"0.0.0.0"` + Port uint16 `cli:"port" usage:"specify port" dft:"8080"` +} + +// Deprecated: AddrWithShort is builtin host,port flag contains short flag +type AddrWithShort struct { + Host string `cli:"H,host" usage:"specify host" dft:"0.0.0.0"` + Port uint16 `cli:"p,port" usage:"specify port" dft:"8080"` +} + +// Deprecated: ToString ... +func (addr Addr) ToString() string { + return fmt.Sprintf("%s:%d", addr.Host, addr.Port) +} + +// Deprecated: ToString ... +func (addr AddrWithShort) ToString() string { + return fmt.Sprintf("%s:%d", addr.Host, addr.Port) +} diff --git a/vendor/github.com/mkideal/cli/cli.go b/vendor/github.com/mkideal/cli/cli.go new file mode 100644 index 0000000..41ce39e --- /dev/null +++ b/vendor/github.com/mkideal/cli/cli.go @@ -0,0 +1,373 @@ +package cli + +import ( + "bytes" + "fmt" + "os" + "reflect" + "strings" + + "github.com/labstack/gommon/color" +) + +// Run runs a single command app +func Run(argv interface{}, fn CommandFunc, descs ...string) { + RunWithArgs(argv, os.Args, fn, descs...) +} + +// RunWithArgs is similar to Run, but with args instead of os.Args +func RunWithArgs(argv interface{}, args []string, fn CommandFunc, descs ...string) { + desc := "" + if len(descs) > 0 { + desc = strings.Join(descs, "\n") + } + err := (&Command{ + Name: args[0], + Desc: desc, + Argv: func() interface{} { return argv }, + CanSubRoute: true, + Fn: fn, + }).Run(args[1:]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } +} + +// Root registers forest for root and returns root +func Root(root *Command, forest ...*CommandTree) *Command { + root.RegisterTree(forest...) + return root +} + +// Tree creates a CommandTree +func Tree(cmd *Command, forest ...*CommandTree) *CommandTree { + return &CommandTree{ + command: cmd, + forest: forest, + } +} + +// Parse parses args to object argv +func Parse(args []string, argv interface{}) error { + clr := color.Color{} + fset := parseArgv(args, argv, clr) + return fset.err +} + +func parseArgv(args []string, argv interface{}, clr color.Color) *flagSet { + return parseArgvList(args, []interface{}{argv}, clr) +} + +func parseArgvList(args []string, argvList []interface{}, clr color.Color) *flagSet { + flagSet := newFlagSet() + for _, argv := range argvList { + if argv == nil { + continue + } + var ( + typ = reflect.TypeOf(argv) + val = reflect.ValueOf(argv) + ) + switch typ.Kind() { + case reflect.Ptr: + if reflect.Indirect(val).Type().Kind() != reflect.Struct { + flagSet.err = errNotAPointerToStruct + return flagSet + } + initFlagSet(typ, val, flagSet, clr, false) + if flagSet.err != nil { + return flagSet + } + default: + flagSet.err = errNotAPointer + return flagSet + } + } + parseArgsToFlagSet(args, flagSet, clr) + return flagSet +} + +func usage(argvList []interface{}, clr color.Color, style UsageStyle) string { + flagSet := newFlagSet() + buf := bytes.NewBufferString("") + for i := len(argvList) - 1; i >= 0; i-- { + v := argvList[i] + if v == nil { + continue + } + var ( + typ = reflect.TypeOf(v) + val = reflect.ValueOf(v) + ) + if typ.Kind() == reflect.Ptr && + reflect.Indirect(val).Type().Kind() == reflect.Struct { + // initialize flagSet + initFlagSet(typ, val, flagSet, clr, true) + if flagSet.err != nil { + return "" + } + } + } + buf.WriteString(flagSlice(flagSet.flagSlice).StringWithStyle(clr, style)) + return buf.String() +} + +func initFlagSet(typ reflect.Type, val reflect.Value, flagSet *flagSet, clr color.Color, dontSetValue bool) { + var ( + typElem = typ.Elem() + valElem = val.Elem() + numField = valElem.NumField() + ) + for i := 0; i < numField; i++ { + var ( + typField = typElem.Field(i) + valField = valElem.Field(i) + tag, isEmpty, err = parseTag(typField.Name, typField.Tag) + ) + if err != nil { + flagSet.err = err + return + } + if tag == nil { + continue + } + + // if `cli` tag is empty and the field is a struct + if isEmpty && valField.Kind() == reflect.Struct { + var ( + subObj = valField.Addr().Interface() + subType = reflect.TypeOf(subObj) + subValue = reflect.ValueOf(subObj) + ) + initFlagSet(subType, subValue, flagSet, clr, dontSetValue) + if flagSet.err != nil { + return + } + continue + } + fl, err := newFlag(typField, valField, tag, clr, dontSetValue) + if flagSet.err = err; err != nil { + return + } + // ignored flag + if fl == nil { + continue + } + flagSet.flagSlice = append(flagSet.flagSlice, fl) + + // encode flag value + value := "" + if fl.isAssigned { + if !valField.CanInterface() { + flagSet.err = fmt.Errorf("field %s cannot interface", typField.Name) + return + } + intf := valField.Interface() + if encoder, ok := intf.(Encoder); ok { + value = encoder.Encode() + } else { + value = fmt.Sprintf("%v", intf) + } + } + + names := append(fl.tag.shortNames, fl.tag.longNames...) + for i, name := range names { + if _, ok := flagSet.flagMap[name]; ok { + flagSet.err = fmt.Errorf("option %s repeated", clr.Bold(name)) + return + } + flagSet.flagMap[name] = fl + if dontSetValue { + continue + } + if fl.isAssigned && i == 0 { + flagSet.values[name] = []string{value} + } + } + } +} + +func parseArgsToFlagSet(args []string, flagSet *flagSet, clr color.Color) { + size := len(args) + for i := 0; i < size; i++ { + arg := args[i] + if !strings.HasPrefix(arg, dashOne) { + // append a free argument + flagSet.args = append(flagSet.args, arg) + continue + } + + var ( + next = "" + offset = 0 + ) + if i+1 < size { + if !strings.HasPrefix(args[i+1], dashOne) { + next = args[i+1] + offset = 1 + } + } + + if arg == dashOne { + flagSet.err = fmt.Errorf("unexpected single dash") + return + } + + // terminate the flag parse while occur `--` + if arg == dashTwo { + flagSet.args = append(flagSet.args, args[i+1:]...) + break + } + + // split arg by "="(key=value) + strs := []string{arg} + index := strings.Index(arg, "=") + if index >= 0 { + strs = []string{arg[:index], arg[index+1:]} + } + + arg = strs[0] + fl, ok := flagSet.flagMap[arg] + + // found in flagMap + if ok { + retOffset := parseToFoundFlag(flagSet, fl, strs, arg, next, offset, clr) + if flagSet.err != nil { + return + } + i += retOffset + continue + } + + // not found in flagMap + // it's an invalid flag if arg has prefix `--` + if strings.HasPrefix(arg, dashTwo) { + flagSet.err = fmt.Errorf("undefined option %s", clr.Bold(arg)) + return + } + + // try parse `-F` + if _, ok := parseSiameseFlag(flagSet, arg[0:2], args[i][2:], clr); ok { + continue + } else if flagSet.err != nil { + return + } + + // other cases, find flag char by char + arg = strings.TrimPrefix(arg, dashOne) + parseFlagCharByChar(flagSet, arg, clr) + if flagSet.err != nil { + return + } + continue + } + + // read delay flags + for _, fl := range flagSet.flagSlice { + if fl.isNeedDelaySet && fl.isAssigned { + err := setWithProperType(fl, fl.field.Type, fl.value, fl.lastValue, clr, false) + if flagSet.err == nil && err != nil { + flagSet.err = err + } + } + if fl.tag.isForce && fl.getBool() { + flagSet.hasForce = true + } + } + + // read prompt flags + if !flagSet.hasForce { + if flagSet.err != nil { + return + } + flagSet.readPrompt(os.Stdout, clr) + if flagSet.err != nil { + return + } + flagSet.readEditor(clr) + if flagSet.err != nil { + return + } + } else { + flagSet.err = nil + } + + buff := bytes.NewBufferString("") + for _, fl := range flagSet.flagSlice { + if !fl.isAssigned && fl.tag.isRequired { + if buff.Len() > 0 { + buff.WriteByte('\n') + } + fmt.Fprintf(buff, "required parameter %s missing", clr.Bold(fl.name())) + } + } + if buff.Len() > 0 && !flagSet.hasForce { + flagSet.err = fmt.Errorf(buff.String()) + } +} + +func parseToFoundFlag(flagSet *flagSet, fl *flag, strs []string, arg, next string, offset int, clr color.Color) int { + retOffset := 0 + l := len(strs) + if l == 1 { + if fl.isBoolean() { + flagSet.err = fl.set(arg, "true", clr) + } else if fl.isCounter() { + fl.counterIncr("", clr) + } else if offset > 0 { + flagSet.err = fl.set(arg, next, clr) + retOffset = offset + } else { + //flagSet.err = fmt.Errorf("missing argument") + flagSet.err = fl.set(arg, "", clr) + } + } else if l == 2 { + flagSet.err = fl.set(arg, strs[1], clr) + } else { + flagSet.err = fmt.Errorf("too many(%d) arguments", l) + } + if flagSet.err != nil { + flagSet.err = fmt.Errorf("parameter %s invalid: %v", clr.Bold(arg), flagSet.err) + return retOffset + } + flagSet.values[arg] = []string{fmt.Sprintf("%v", fl.value.Interface())} + return retOffset +} + +func parseFlagCharByChar(flagSet *flagSet, arg string, clr color.Color) { + // NOTE: each fold flag should be boolean + chars := []byte(arg) + for _, c := range chars { + tmp := dashOne + string([]byte{c}) + fl, ok := flagSet.flagMap[tmp] + if !ok { + flagSet.err = fmt.Errorf("undefined option %s", clr.Bold(tmp)) + return + } + + if fl.isBoolean() { + fl.set(tmp, "true", clr) + flagSet.values[tmp] = []string{"true"} + } else if fl.isCounter() { + fl.counterIncr("", clr) + } else { + flagSet.err = fmt.Errorf("each fold option should be boolean, but %s not", clr.Bold(tmp)) + return + } + } +} + +func parseSiameseFlag(flagSet *flagSet, firstHalf, latterHalf string, clr color.Color) (*flag, bool) { + // NOTE: fl must be not a boolean + key, val := firstHalf, latterHalf + if fl, ok := flagSet.flagMap[key]; ok && !fl.isBoolean() { + if fl.isCounter() { + return nil, false + } + if flagSet.err = fl.set(key, val, clr); flagSet.err != nil { + return fl, false + } + return fl, true + } + return nil, false +} diff --git a/vendor/github.com/mkideal/cli/cliutil.go b/vendor/github.com/mkideal/cli/cliutil.go new file mode 100644 index 0000000..cdfb622 --- /dev/null +++ b/vendor/github.com/mkideal/cli/cliutil.go @@ -0,0 +1,103 @@ +package cli + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "os" + "os/exec" + "strings" + + "github.com/labstack/gommon/color" + "github.com/mattn/go-isatty" +) + +func colorSwitch(clr *color.Color, w io.Writer, fds ...uintptr) { + clr.Disable() + if len(fds) > 0 { + if isatty.IsTerminal(fds[0]) { + clr.Enable() + } + } else if w, ok := w.(*os.File); ok && isatty.IsTerminal(w.Fd()) { + clr.Enable() + } +} + +// HelpCommandFn implements buildin help command function +func HelpCommandFn(ctx *Context) error { + var ( + args = ctx.NativeArgs() + parent = ctx.Command().Parent() + ) + if len(args) == 0 { + ctx.String(parent.Usage(ctx)) + return nil + } + var ( + child = parent.Route(args) + clr = ctx.Color() + ) + if child == nil { + return fmt.Errorf("command %s not found", clr.Yellow(strings.Join(args, " "))) + } + ctx.String(child.Usage(ctx)) + return nil +} + +// HelpCommand returns a buildin help command +func HelpCommand(desc string) *Command { + return &Command{ + Name: "help", + Desc: desc, + CanSubRoute: true, + NoHook: true, + Fn: HelpCommandFn, + } +} + +// Daemon startup app as a daemon process, success if result from stderr has prefix successPrefix +func Daemon(ctx *Context, successPrefix string) error { + cmd := exec.Command(os.Args[0], ctx.NativeArgs()...) + serr, err := cmd.StderrPipe() + if err != nil { + return err + } + if err := cmd.Start(); err != nil { + return err + } + reader := bufio.NewReader(serr) + line, err := reader.ReadString('\n') + if err != nil { + return err + } + + if strings.HasPrefix(line, successPrefix) { + ctx.String(line) + cmd.Process.Release() + } else { + cmd.Process.Kill() + line = strings.TrimSuffix(line, "\n") + return fmt.Errorf(line) + } + return nil +} + +func DaemonResponse(resp string) { + fmt.Fprintln(os.Stderr, resp) +} + +// ReadJSON reads data as a json structure into argv +func ReadJSON(r io.Reader, argv interface{}) error { + return json.NewDecoder(r).Decode(argv) +} + +// ReadJSONFromFile is similar to ReadJSONFromFile, but read from file +func ReadJSONFromFile(filename string, argv interface{}) error { + file, err := os.Open(filename) + if err == nil { + defer file.Close() + err = ReadJSON(file, argv) + } + return err +} diff --git a/vendor/github.com/mkideal/cli/coder.go b/vendor/github.com/mkideal/cli/coder.go new file mode 100644 index 0000000..d74fada --- /dev/null +++ b/vendor/github.com/mkideal/cli/coder.go @@ -0,0 +1,32 @@ +package cli + +type Decoder interface { + Decode(s string) error +} + +type SliceDecoder interface { + Decoder + DecodeSlice() +} + +type Encoder interface { + Encode() string +} + +type CounterDecoder interface { + Decoder + IsCounter() +} + +type Counter struct { + value int +} + +func (c Counter) Value() int { return c.value } + +func (c *Counter) Decode(s string) error { + c.value++ + return nil +} + +func (c Counter) IsCounter() {} diff --git a/vendor/github.com/mkideal/cli/command.go b/vendor/github.com/mkideal/cli/command.go new file mode 100644 index 0000000..ee6c2af --- /dev/null +++ b/vendor/github.com/mkideal/cli/command.go @@ -0,0 +1,557 @@ +package cli + +import ( + "bytes" + "fmt" + "io" + "net/http" + "os" + "regexp" + "sort" + "strings" + "sync" + + "github.com/labstack/gommon/color" + "github.com/mattn/go-colorable" + "github.com/mkideal/pkg/debug" +) + +var commandNameRegexp = regexp.MustCompile("^[a-zA-Z_0-9][a-zA-Z_\\-0-9]*$") + +// IsValidCommandName validates name of command +func IsValidCommandName(commandName string) bool { + return commandNameRegexp.MatchString(commandName) +} + +type ( + // CommandFunc ... + CommandFunc func(*Context) error + + // ArgvFunc ... + ArgvFunc func() interface{} + + // NumCheckFunc represents function type which used to check num of args + NumCheckFunc func(n int) bool + + // UsageFunc represents custom function of usage + UsageFunc func() string +) + +func ExactN(num int) NumCheckFunc { return func(n int) bool { return n == num } } +func AtLeast(num int) NumCheckFunc { return func(n int) bool { return n >= num } } +func AtMost(num int) NumCheckFunc { return func(n int) bool { return n <= num } } + +type ( + // Command is the top-level instance in command-line app + Command struct { + Name string // Command name + Aliases []string // Command aliases name + Desc string // Command abstract + Text string // Command detail description + + CanSubRoute bool + NoHook bool + NoHTTP bool + Global bool + + // functions + Fn CommandFunc // Command handler + UsageFn UsageFunc // Custom usage function + Argv ArgvFunc // Command argument factory function + NumArg NumCheckFunc + NumOption NumCheckFunc + + HTTPRouters []string + HTTPMethods []string + + // hooks for current command + OnBefore func(*Context) error + OnAfter func(*Context) error + + // hooks for all commands if current command is root command + OnRootPrepareError func(error) error + OnRootBefore func(*Context) error + OnRootAfter func(*Context) error + + routersMap map[string]string + + parent *Command + children []*Command + + isServer bool + + locker sync.Mutex // protect following data + usage string + usageStyle UsageStyle + } + + // CommandTree represents a tree of commands + CommandTree struct { + command *Command + forest []*CommandTree + } +) + +// Register registers a child command +func (cmd *Command) Register(child *Command) *Command { + if child == nil { + debug.Panicf("command `%s` try register a nil command", cmd.Name) + } + if !IsValidCommandName(child.Name) { + debug.Panicf("illegal command name `%s`", cmd.Name) + } + if cmd.children == nil { + cmd.children = []*Command{} + } + if child.parent != nil { + debug.Panicf("command `%s` has been child of `%s`", child.Name, child.parent.Name) + } + if cmd.findChild(child.Name) != nil { + debug.Panicf("repeat register child `%s` for command `%s`", child.Name, cmd.Name) + } + if child.Aliases != nil { + for _, alias := range child.Aliases { + if cmd.findChild(alias) != nil { + debug.Panicf("repeat register child `%s` for command `%s`", alias, cmd.Name) + } + } + } + cmd.children = append(cmd.children, child) + child.parent = cmd + + return child +} + +// RegisterFunc registers handler as child command +func (cmd *Command) RegisterFunc(name string, fn CommandFunc, argvFn ArgvFunc) *Command { + return cmd.Register(&Command{Name: name, Fn: fn, Argv: argvFn}) +} + +// RegisterTree registers a command tree +func (cmd *Command) RegisterTree(forest ...*CommandTree) { + for _, tree := range forest { + cmd.Register(tree.command) + if tree.forest != nil && len(tree.forest) > 0 { + tree.command.RegisterTree(tree.forest...) + } + } +} + +// Parent returns command's parent +func (cmd *Command) Parent() *Command { + return cmd.parent +} + +// IsServer returns command whether if run as server +func (cmd *Command) IsServer() bool { + return cmd.isServer +} + +// IsClient returns command whether if run as client +func (cmd *Command) IsClient() bool { + return !cmd.IsServer() +} + +// SetIsServer sets command running mode(server or not) +func (cmd *Command) SetIsServer(yes bool) { + cmd.Root().isServer = yes +} + +// Run runs the command with args +func (cmd *Command) Run(args []string) error { + return cmd.RunWith(args, nil, nil) +} + +// RunWith runs the command with args and writer,httpMethods +func (cmd *Command) RunWith(args []string, writer io.Writer, resp http.ResponseWriter, httpMethods ...string) error { + fds := []uintptr{} + if writer == nil { + writer = colorable.NewColorableStdout() + fds = append(fds, os.Stdout.Fd()) + } + clr := color.Color{} + colorSwitch(&clr, writer, fds...) + + var ctx *Context + var suggestion string + ctx, suggestion, err := cmd.prepare(clr, args, writer, resp, httpMethods...) + if err == ExitError { + return nil + } + + if err != nil { + if cmd.OnRootPrepareError != nil { + err = cmd.OnRootPrepareError(err) + } + if err != nil { + return wrapErr(err, suggestion, clr) + } + return nil + } + + if ctx.command.NoHook { + return ctx.command.Fn(ctx) + } + + funcs := []func(*Context) error{ + ctx.command.OnBefore, + cmd.OnRootBefore, + ctx.command.Fn, + cmd.OnRootAfter, + ctx.command.OnAfter, + } + for _, f := range funcs { + if f != nil { + if err := f(ctx); err != nil { + if err == ExitError { + return nil + } + return err + } + } + } + return nil +} + +func isEmptyArgvList(argvList []interface{}) bool { + if argvList == nil { + return true + } + for _, argv := range argvList { + if argv != nil { + return false + } + } + return true +} + +func (cmd *Command) argvList() []interface{} { + argvList := make([]interface{}, 0, 1) + if cmd.Argv != nil { + argvList = append(argvList, cmd.Argv()) + } else { + argvList = append(argvList, nil) + } + next := cmd.parent + for next != nil { + if next.Argv != nil && next.Global { + argvList = append(argvList, next.Argv()) + } else { + argvList = append(argvList, nil) + } + next = next.parent + } + return argvList +} + +func (cmd *Command) prepare(clr color.Color, args []string, writer io.Writer, resp http.ResponseWriter, httpMethods ...string) (ctx *Context, suggestion string, err error) { + // split args + router := []string{} + for _, arg := range args { + if strings.HasPrefix(arg, dashOne) { + break + } + router = append(router, arg) + } + path := strings.Join(router, " ") + child, end := cmd.SubRoute(router) + + // if route fail + if !child.CanSubRoute && end != len(router) { + suggestions := cmd.Suggestions(path) + buff := bytes.NewBufferString("") + if suggestions != nil && len(suggestions) > 0 { + if len(suggestions) == 1 { + fmt.Fprintf(buff, "\nDid you mean %s?", clr.Bold(suggestions[0])) + } else { + fmt.Fprintf(buff, "\n\nDid you mean one of these?\n") + for _, sug := range suggestions { + fmt.Fprintf(buff, " %s\n", sug) + } + } + } + suggestion = buff.String() + err = throwCommandNotFound(clr.Yellow(path)) + return + } + + methodAllowed := false + if len(httpMethods) == 0 || + child.HTTPMethods == nil || + len(child.HTTPMethods) == 0 { + methodAllowed = true + } else { + method := httpMethods[0] + for _, m := range child.HTTPMethods { + if method == m { + methodAllowed = true + break + } + } + } + if !methodAllowed { + err = throwMethodNotAllowed(clr.Yellow(httpMethods[0])) + return + } + + // create argvList + argvList := child.argvList() + + // create Context + path = child.Path() + ctx, err = newContext(path, router[:end], args[end:], argvList, clr) + ctx.command = child + ctx.writer = writer + if !ctx.flagSet.hasForce { + if !child.checkNumOption(ctx.NOpt()) || !ctx.command.checkNumArg(ctx.NArg()) { + ctx.WriteUsage() + err = ExitError + return + } + } + if err != nil { + return + } + ctx.HTTPResponse = resp + + // auto help + for _, argv := range argvList { + if argv != nil { + if helper, ok := argv.(AutoHelper); ok && helper.AutoHelp() { + ctx.WriteUsage() + err = ExitError + return + } + } + } + + if len(router) == 0 && cmd.Fn == nil { + err = throwCommandNotFound(clr.Yellow(cmd.Name)) + return + } + + if !ctx.flagSet.hasForce { + for _, argv := range argvList { + // validate argv if argv implements interface Validator + if argv != nil { + if validator, ok := argv.(Validator); ok { + err = validator.Validate(ctx) + if err != nil { + return + } + } + } + } + } + + return +} + +func (cmd *Command) checkNumArg(num int) bool { + return cmd.NumArg == nil || cmd.NumArg(num) +} + +func (cmd *Command) checkNumOption(num int) bool { + return cmd.NumOption == nil || cmd.NumOption(num) +} + +// Usage returns the usage string of command +func (cmd *Command) Usage(ctx *Context) string { + if cmd.UsageFn != nil { + return cmd.UsageFn() + } + return cmd.defaultUsageFn(ctx) +} + +func (cmd *Command) defaultUsageFn(ctx *Context) string { + var ( + style = GetUsageStyle() + clr = *(ctx.Color()) + ) + + // get usage form cache + cmd.locker.Lock() + tmpUsage := cmd.usage + usageStyle := cmd.usageStyle + cmd.locker.Unlock() + if tmpUsage != "" && usageStyle == style { + debug.Debugf("get usage of command %s from cache", clr.Bold(cmd.Name)) + return tmpUsage + } + + buff := bytes.NewBufferString("") + if cmd.Desc != "" { + fmt.Fprintf(buff, "%s\n\n", cmd.Desc) + } + if cmd.Text != "" { + fmt.Fprintf(buff, "%s\n\n", cmd.Text) + } + argvList := cmd.argvList() + isEmpty := isEmptyArgvList(argvList) + if !isEmpty { + fmt.Fprintf(buff, "%s:\n\n%s", clr.Bold("Options"), usage(argvList, clr, style)) + } + if cmd.children != nil && len(cmd.children) > 0 { + if !isEmpty { + buff.WriteByte('\n') + } + fmt.Fprintf(buff, "%s:\n\n%v", clr.Bold("Commands"), cmd.ChildrenDescriptions(" ", " ")) + } + tmpUsage = buff.String() + cmd.locker.Lock() + cmd.usage = tmpUsage + cmd.usageStyle = style + cmd.locker.Unlock() + return tmpUsage +} + +// Path returns space-separated command full name +func (cmd *Command) Path() string { + return cmd.pathWithSep(" ") +} + +func (cmd *Command) pathWithSep(sep string) string { + var ( + path = "" + cur = cmd + ) + for cur.parent != nil { + if cur.Name != "" { + if path == "" { + path = cur.Name + } else { + path = cur.Name + sep + path + } + } + cur = cur.parent + } + return path +} + +// Root returns command's ancestor +func (cmd *Command) Root() *Command { + ancestor := cmd + for ancestor.parent != nil { + ancestor = ancestor.parent + } + return ancestor +} + +// Route finds command full matching router +func (cmd *Command) Route(router []string) *Command { + child, end := cmd.SubRoute(router) + if end != len(router) { + return nil + } + return child +} + +// SubRoute finds command partial matching router +func (cmd *Command) SubRoute(router []string) (*Command, int) { + cur := cmd + for i, name := range router { + child := cur.findChild(name) + if child == nil { + return cur, i + } + cur = child + } + return cur, len(router) +} + +// findChild finds child command by name +func (cmd *Command) findChild(name string) *Command { + if cmd.nochild() { + return nil + } + for _, child := range cmd.children { + if child.Name == name { + return child + } + if child.Aliases != nil { + for _, alias := range child.Aliases { + if alias == name { + return child + } + } + } + } + return nil +} + +// ListChildren returns all names of command children +func (cmd *Command) ListChildren() []string { + if cmd.nochild() { + return []string{} + } + + ret := make([]string, 0, len(cmd.children)) + for _, child := range cmd.children { + ret = append(ret, child.Name) + } + return ret +} + +// ChildrenDescriptions returns all children's brief infos by one string +func (cmd *Command) ChildrenDescriptions(prefix, indent string) string { + if cmd.nochild() { + return "" + } + buff := bytes.NewBufferString("") + length := 0 + for _, child := range cmd.children { + if len(child.Name) > length { + length = len(child.Name) + } + } + format := fmt.Sprintf("%s%%-%ds%s%%s%%s\n", prefix, length, indent) + for _, child := range cmd.children { + aliases := "" + if child.Aliases != nil && len(child.Aliases) > 0 { + aliasesBuff := bytes.NewBufferString("(aliases ") + aliasesBuff.WriteString(strings.Join(child.Aliases, ",")) + aliasesBuff.WriteString(")") + aliases = aliasesBuff.String() + } + fmt.Fprintf(buff, format, child.Name, child.Desc, aliases) + } + return buff.String() +} + +func (cmd *Command) nochild() bool { + return cmd.children == nil || len(cmd.children) == 0 +} + +// Suggestions returns all similar commands +func (cmd *Command) Suggestions(path string) []string { + if cmd.parent != nil { + return cmd.Root().Suggestions(path) + } + + var ( + cmds = []*Command{cmd} + targets = []string{} + ) + for len(cmds) > 0 { + if cmds[0].nochild() { + cmds = cmds[1:] + } else { + for _, child := range cmds[0].children { + targets = append(targets, child.Path()) + } + cmds = append(cmds[0].children, cmds[1:]...) + } + } + + dists := []editDistanceRank{} + for i, size := 0, len(targets); i < size; i++ { + if d, ok := match(path, targets[i]); ok { + dists = append(dists, editDistanceRank{s: targets[i], d: d}) + } + } + sort.Sort(editDistanceRankSlice(dists)) + for i := 0; i < len(dists); i++ { + targets[i] = dists[i].s + } + return targets[:len(dists)] +} diff --git a/vendor/github.com/mkideal/cli/context.go b/vendor/github.com/mkideal/cli/context.go new file mode 100644 index 0000000..0958c2c --- /dev/null +++ b/vendor/github.com/mkideal/cli/context.go @@ -0,0 +1,233 @@ +package cli + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + + "github.com/labstack/gommon/color" + "github.com/mattn/go-colorable" + "github.com/mkideal/pkg/debug" +) + +type ( + // Context provides running context + Context struct { + router []string + path string + argvList []interface{} + nativeArgs []string + flagSet *flagSet + command *Command + writer io.Writer + color color.Color + + HTTPRequest *http.Request + HTTPResponse http.ResponseWriter + } + + // Validator validates flag before running command + Validator interface { + Validate(*Context) error + } + + // AutoHelper represents interface for showing help information automatically + AutoHelper interface { + AutoHelp() bool + } +) + +func newContext(path string, router, args []string, argvList []interface{}, clr color.Color) (*Context, error) { + ctx := &Context{ + path: path, + router: router, + argvList: argvList, + nativeArgs: args, + color: clr, + flagSet: newFlagSet(), + } + if !isEmptyArgvList(argvList) { + ctx.flagSet = parseArgvList(args, argvList, ctx.color) + if ctx.flagSet.err != nil { + return ctx, ctx.flagSet.err + } + } + return ctx, nil +} + +// Path returns full command name +// `./app hello world -a --xyz=1` will returns "hello world" +func (ctx *Context) Path() string { + return ctx.path +} + +// Router returns full command name with string array +// `./app hello world -a --xyz=1` will returns ["hello" "world"] +func (ctx *Context) Router() []string { + return ctx.router +} + +// NativeArgs returns native args +// `./app hello world -a --xyz=1` will return ["-a" "--xyz=1"] +func (ctx *Context) NativeArgs() []string { + return ctx.nativeArgs +} + +// Args returns free args +// `./app hello world -a=1 abc xyz` will return ["abc" "xyz"] +func (ctx *Context) Args() []string { + return ctx.flagSet.args +} + +// NArg returns length of Args +func (ctx *Context) NArg() int { + return len(ctx.flagSet.args) +} + +// NOpt returns num of options +func (ctx *Context) NOpt() int { + if ctx.flagSet == nil || ctx.flagSet.flagSlice == nil { + return 0 + } + n := 0 + for _, fl := range ctx.flagSet.flagSlice { + if fl.isSet { + n++ + } + } + return n +} + +// Argv returns parsed args object +func (ctx *Context) Argv() interface{} { + if ctx.argvList == nil || len(ctx.argvList) == 0 { + return nil + } + return ctx.argvList[0] +} + +func (ctx *Context) RootArgv() interface{} { + if isEmptyArgvList(ctx.argvList) { + return nil + } + index := len(ctx.argvList) - 1 + return ctx.argvList[index] +} + +func (ctx *Context) GetArgvList(curr interface{}, parents ...interface{}) error { + if isEmptyArgvList(ctx.argvList) { + return argvError{isEmpty: true} + } + for i, argv := range append([]interface{}{curr}, parents...) { + if argv == nil { + continue + } + if i >= len(ctx.argvList) { + return argvError{isOutOfRange: true} + } + if ctx.argvList[i] == nil { + return argvError{ith: i, msg: "source is nil"} + } + + buf := bytes.NewBufferString("") + if err := json.NewEncoder(buf).Encode(ctx.argvList[i]); err != nil { + return err + } + if err := json.NewDecoder(buf).Decode(argv); err != nil { + return err + } + } + return nil +} + +// IsSet determins whether `flag` is set +func (ctx *Context) IsSet(flag string, aliasFlags ...string) bool { + fl, ok := ctx.flagSet.flagMap[flag] + if ok { + return fl.isSet + } + for _, alias := range aliasFlags { + if fl, ok := ctx.flagSet.flagMap[alias]; ok { + return fl.isSet + } + } + return false +} + +// FormValues returns parsed args as url.Values +func (ctx *Context) FormValues() url.Values { + if ctx.flagSet == nil { + debug.Panicf("ctx.flagSet == nil") + } + return ctx.flagSet.values +} + +// Command returns current command instance +func (ctx *Context) Command() *Command { + return ctx.command +} + +// Usage returns current command's usage with current context +func (ctx *Context) Usage() string { + return ctx.command.Usage(ctx) +} + +// WriteUsage writes usage to writer +func (ctx *Context) WriteUsage() { + ctx.String(ctx.Usage()) +} + +// Writer returns writer +func (ctx *Context) Writer() io.Writer { + if ctx.writer == nil { + ctx.writer = colorable.NewColorableStdout() + } + return ctx.writer +} + +// Write implements io.Writer +func (ctx *Context) Write(data []byte) (n int, err error) { + return ctx.Writer().Write(data) +} + +// Color returns color instance +func (ctx *Context) Color() *color.Color { + return &ctx.color +} + +// String writes formatted string to writer +func (ctx *Context) String(format string, args ...interface{}) *Context { + fmt.Fprintf(ctx.Writer(), format, args...) + return ctx +} + +// JSON writes json string of obj to writer +func (ctx *Context) JSON(obj interface{}) *Context { + data, err := json.Marshal(obj) + if err == nil { + fmt.Fprint(ctx.Writer(), string(data)) + } + return ctx +} + +// JSONln writes json string of obj end with "\n" to writer +func (ctx *Context) JSONln(obj interface{}) *Context { + return ctx.JSON(obj).String("\n") +} + +// JSONIndent writes pretty json string of obj to writer +func (ctx *Context) JSONIndent(obj interface{}, prefix, indent string) *Context { + data, err := json.MarshalIndent(obj, prefix, indent) + if err == nil { + fmt.Fprint(ctx.Writer(), string(data)) + } + return ctx +} + +// JSONIndentln writes pretty json string of obj end with "\n" to writer +func (ctx *Context) JSONIndentln(obj interface{}, prefix, indent string) *Context { + return ctx.JSONIndent(obj, prefix, indent).String("\n") +} diff --git a/vendor/github.com/mkideal/cli/editor.go b/vendor/github.com/mkideal/cli/editor.go new file mode 100644 index 0000000..088bbc1 --- /dev/null +++ b/vendor/github.com/mkideal/cli/editor.go @@ -0,0 +1,52 @@ +package cli + +import ( + "crypto/rand" + "fmt" + "io/ioutil" + "os" + "os/exec" +) + +const DefaultEditor = "vim" + +// GetEditor sets callback to get editor program +var GetEditor func() (string, error) + +func getEditor() (string, error) { + if GetEditor != nil { + return GetEditor() + } + return exec.LookPath(DefaultEditor) +} + +func randomFilename() string { + buf := make([]byte, 16) + if _, err := rand.Read(buf); err != nil { + return "CLI_EDIT_FILE" + } + return fmt.Sprintf(".%x", buf) +} + +func LaunchEditor(editor string) (content []byte, err error) { + return launchEditorWithFilename(editor, randomFilename()) +} + +func launchEditorWithFilename(editor, filename string) (content []byte, err error) { + cmd := exec.Command(editor, filename) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + defer os.Remove(filename) + err = cmd.Run() + if err != nil { + if _, isExitError := err.(*exec.ExitError); !isExitError { + return + } + } + content, err = ioutil.ReadFile(filename) + if err != nil { + return []byte{}, nil + } + return +} diff --git a/vendor/github.com/mkideal/cli/errors.go b/vendor/github.com/mkideal/cli/errors.go new file mode 100644 index 0000000..f0158ef --- /dev/null +++ b/vendor/github.com/mkideal/cli/errors.go @@ -0,0 +1,106 @@ +package cli + +import ( + "bytes" + "errors" + "fmt" + "strings" + + "github.com/labstack/gommon/color" +) + +var ( + errNotAPointerToStruct = errors.New("not a pointer to struct") + errNotAPointer = errors.New("argv is not a pointer") + errCliTagTooMany = errors.New("cli tag too many") +) + +type ( + exitError struct{} + + commandNotFoundError struct { + command string + } + + methodNotAllowedError struct { + method string + } + + routerRepeatError struct { + router string + } + + wrapError struct { + err error + msg string + } + + argvError struct { + isEmpty bool + isOutOfRange bool + + ith int + msg string + } +) + +func (e exitError) Error() string { return "exit" } + +// ExitError is a special error, should be ignored but return +var ExitError = exitError{} + +func throwCommandNotFound(command string) commandNotFoundError { + return commandNotFoundError{command: command} +} + +func throwMethodNotAllowed(method string) methodNotAllowedError { + return methodNotAllowedError{method: method} +} + +func throwRouterRepeat(router string) routerRepeatError { + return routerRepeatError{router: router} +} + +func (e commandNotFoundError) Error() string { + return fmt.Sprintf("command %s not found", e.command) +} + +func (e methodNotAllowedError) Error() string { + return fmt.Sprintf("method %s not allowed", e.method) +} + +func (e routerRepeatError) Error() string { + return fmt.Sprintf("router %s repeat", e.router) +} + +func (e wrapError) Error() string { + return e.msg +} + +func wrapErr(err error, appendString string, clr color.Color) error { + if err == nil { + return err + } + errs := strings.Split(err.Error(), "\n") + buff := bytes.NewBufferString("") + errPrefix := clr.Red("ERR!") + " " + for i, e := range errs { + if i != 0 { + buff.WriteByte('\n') + } + buff.WriteString(errPrefix) + buff.WriteString(e) + } + buff.WriteString(appendString) + return wrapError{err: err, msg: buff.String()} +} + +func (e argvError) Error() string { + if e.isEmpty { + return "argv list is empty" + } + if e.isOutOfRange { + return "argv list out of range" + } + return fmt.Sprintf("%dth argv: %s", e.ith, e.msg) +} diff --git a/vendor/github.com/mkideal/cli/flag.go b/vendor/github.com/mkideal/cli/flag.go new file mode 100644 index 0000000..b2ed689 --- /dev/null +++ b/vendor/github.com/mkideal/cli/flag.go @@ -0,0 +1,490 @@ +package cli + +import ( + "bytes" + "errors" + "fmt" + "math" + "os" + "reflect" + "strconv" + "strings" + + "github.com/labstack/gommon/color" + "github.com/mkideal/pkg/expr" +) + +type flag struct { + field reflect.StructField + value reflect.Value + + // isAssigned indicates whether the flag is set(contains default value) + isAssigned bool + + // isSet indicates whether the flag is set + isSet bool + + // tag properties + tag tagProperty + + // actual flag name + actualFlagName string + + isNeedDelaySet bool + + // last value for need delay set + // flag maybe assigned too many times, like: + // -f xx -f yy -f zz + // `zz` is the last value + lastValue string +} + +func newFlag(field reflect.StructField, value reflect.Value, tag *tagProperty, clr color.Color, dontSetValue bool) (fl *flag, err error) { + fl = &flag{field: field, value: value} + if !fl.value.CanSet() { + return nil, fmt.Errorf("field %s can not set", clr.Bold(fl.field.Name)) + } + fl.tag = *tag + if fl.isPtr() && fl.value.IsNil() { + fl.value.Set(reflect.New(fl.field.Type.Elem())) + } + isSliceDecoder := fl.value.Type().Implements(reflect.TypeOf((*SliceDecoder)(nil)).Elem()) + if !isSliceDecoder && fl.value.CanAddr() { + isSliceDecoder = fl.value.Addr().Type().Implements(reflect.TypeOf((*SliceDecoder)(nil)).Elem()) + } + fl.isNeedDelaySet = fl.tag.parserCreator != nil || + (fl.field.Type.Kind() != reflect.Slice && fl.field.Type.Kind() != reflect.Map && !isSliceDecoder) + err = fl.init(clr, dontSetValue) + return +} + +func (fl *flag) init(clr color.Color, dontSetValue bool) error { + var ( + isNumber = fl.isInteger() || fl.isFloat() + isDecoder = fl.value.Type().Implements(reflect.TypeOf((*Decoder)(nil)).Elem()) + dft string + err error + ) + if !isDecoder && fl.value.CanAddr() { + isDecoder = fl.value.Addr().Type().Implements(reflect.TypeOf((*Decoder)(nil)).Elem()) + } + dft, err = parseExpression(fl.tag.dft, isNumber) + if err != nil { + return err + } + if isNumber && !isDecoder { + v, err := expr.Eval(dft, nil, nil) + if err == nil { + if fl.isInteger() { + dft = fmt.Sprintf("%d", v.Int()) + } else if fl.isFloat() { + dft = fmt.Sprintf("%f", v.Float()) + } + } + } + if !dontSetValue && fl.tag.dft != "" && dft != "" { + if fl.isPtr() || isDecoder || isEmpty(fl.value) { + return fl.setDefault(dft, clr) + } + } + return nil +} + +func isEmpty(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} + +func isWordByte(b byte) bool { + return (b >= 'a' && b <= 'z') || + (b >= 'A' && b <= 'Z') || + (b >= '0' && b <= '9') || + b == '_' +} + +func parseExpression(s string, isNumber bool) (string, error) { + const escapeByte = '$' + var ( + src = []byte(s) + escaping = false + exprBuf bytes.Buffer + envvarBuf bytes.Buffer + ) + writeEnv := func(envName string) error { + if envName == "" { + return fmt.Errorf("unexpected end after %v", escapeByte) + } + env := os.Getenv(envName) + if env == "" && isNumber { + env = "0" + } + exprBuf.WriteString(env) + return nil + } + for i, b := range src { + if b == escapeByte { + if escaping && envvarBuf.Len() == 0 { + exprBuf.WriteByte(b) + escaping = false + } else { + escaping = true + if i+1 == len(src) { + return "", fmt.Errorf("unexpected end after %v", escapeByte) + } + envvarBuf.Reset() + } + continue + } + if escaping { + if isWordByte(b) { + envvarBuf.WriteByte(b) + if i+1 == len(src) { + if err := writeEnv(envvarBuf.String()); err != nil { + return "", err + } + } + } else { + if err := writeEnv(envvarBuf.String()); err != nil { + return "", err + } + exprBuf.WriteByte(b) + envvarBuf.Reset() + escaping = false + } + } else { + exprBuf.WriteByte(b) + } + } + return exprBuf.String(), nil +} + +func (fl *flag) name() string { + if fl.actualFlagName != "" { + return fl.actualFlagName + } + if len(fl.tag.longNames) > 0 { + return fl.tag.longNames[0] + } + if len(fl.tag.shortNames) > 0 { + return fl.tag.shortNames[0] + } + return "" +} + +func (fl *flag) isBoolean() bool { + return fl.field.Type.Kind() == reflect.Bool +} + +func (fl *flag) isInteger() bool { + switch fl.field.Type.Kind() { + case reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64, + reflect.Uint, + reflect.Uint8, + reflect.Uint16, + reflect.Uint32, + reflect.Uint64: + return true + } + return false +} + +func (fl *flag) isSlice() bool { + return fl.field.Type.Kind() == reflect.Slice +} + +func (fl *flag) isMap() bool { + return fl.field.Type.Kind() == reflect.Map +} + +func (fl *flag) isFloat() bool { + kind := fl.field.Type.Kind() + return kind == reflect.Float32 || kind == reflect.Float64 +} + +func (fl *flag) isString() bool { + return fl.field.Type.Kind() == reflect.String +} + +func (fl *flag) isPtr() bool { + return fl.field.Type.Kind() == reflect.Ptr +} + +func (fl *flag) getBool() bool { + if !fl.isBoolean() { + return false + } + return fl.value.Bool() +} + +func (fl *flag) setDefault(s string, clr color.Color) error { + fl.isAssigned = true + if fl.isNeedDelaySet { + fl.lastValue = s + return nil + } + return setWithProperType(fl, fl.field.Type, fl.value, s, clr, false) +} + +func (fl *flag) set(actualFlagName, s string, clr color.Color) error { + fl.isSet = true + fl.isAssigned = true + fl.actualFlagName = actualFlagName + if fl.isNeedDelaySet { + fl.lastValue = s + return nil + } + return setWithProperType(fl, fl.field.Type, fl.value, s, clr, false) +} + +func (fl *flag) counterIncr(s string, clr color.Color) error { + return setWithProperType(fl, fl.field.Type, fl.value, s, clr, false) +} + +func (fl *flag) isCounter() bool { + if decoder := tryGetDecoder(fl.value.Type().Kind(), fl.value); decoder != nil { + if _, ok := decoder.(CounterDecoder); ok { + return true + } + } + return false +} + +func (fl *flag) setWithNoDelay(actualFlagName, s string, clr color.Color) error { + fl.isSet = true + fl.isAssigned = true + fl.actualFlagName = actualFlagName + return setWithProperType(fl, fl.field.Type, fl.value, s, clr, false) +} + +func tryGetDecoder(kind reflect.Kind, val reflect.Value) Decoder { + if val.CanInterface() { + var addrVal = val + if kind != reflect.Ptr && val.CanAddr() { + addrVal = val.Addr() + } + // try Decoder + if addrVal.CanInterface() { + if i := addrVal.Interface(); i != nil { + if decoder, ok := i.(Decoder); ok { + return decoder + } + } + } + } + return nil +} + +func setWithProperType(fl *flag, typ reflect.Type, val reflect.Value, s string, clr color.Color, isSubField bool) error { + kind := typ.Kind() + + // try parser first of all + if fl.tag.parserCreator != nil && val.CanInterface() { + if kind != reflect.Ptr && val.CanAddr() { + val = val.Addr() + } + return fl.tag.parserCreator(val.Interface()).Parse(s) + } + + if decoder := tryGetDecoder(kind, val); decoder != nil { + return decoder.Decode(s) + } + + switch kind { + case reflect.Bool: + if v, err := getBool(s, clr); err == nil { + val.SetBool(v) + } else { + return err + } + + case reflect.String: + val.SetString(s) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if v, err := getInt(s, clr); err == nil { + if minmaxIntCheck(kind, v) { + val.SetInt(v) + } else { + return errors.New(clr.Red("value overflow")) + } + } else { + return err + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if v, err := getUint(s, clr); err == nil { + if minmaxUintCheck(kind, v) { + val.SetUint(uint64(v)) + } else { + return errors.New(clr.Red("value overflow")) + } + } else { + return err + } + + case reflect.Float32, reflect.Float64: + if v, err := getFloat(s, clr); err == nil { + if minmaxFloatCheck(kind, v) { + val.SetFloat(float64(v)) + } else { + return errors.New(clr.Red("value overflow")) + } + } else { + return err + } + + case reflect.Slice: + if isSubField { + return fmt.Errorf("unsupported type %s as a sub field", kind.String()) + } + sliceOf := typ.Elem() + if val.IsNil() { + slice := reflect.MakeSlice(typ, 0, 4) + val.Set(slice) + } + index := val.Len() + sliceCap := val.Cap() + if index+1 <= sliceCap { + val.SetLen(index + 1) + } else { + slice := reflect.MakeSlice(typ, index+1, index+sliceCap/2+1) + for k := 0; k < index; k++ { + slice.Index(k).Set(val.Index(k)) + } + val.Set(slice) + } + return setWithProperType(fl, sliceOf, val.Index(index), s, clr, true) + + case reflect.Map: + if isSubField { + return fmt.Errorf("unsupported type %s as a sub field", kind.String()) + } + keyString, valString, err := splitKeyVal(s, fl.tag.sep) + if err != nil { + return err + } + keyType := typ.Key() + valType := typ.Elem() + if val.IsNil() { + val.Set(reflect.MakeMap(typ)) + } + k, v := reflect.New(keyType), reflect.New(valType) + if err := setWithProperType(fl, keyType, k.Elem(), keyString, clr, true); err != nil { + return err + } + if err := setWithProperType(fl, valType, v.Elem(), valString, clr, true); err != nil { + return err + } + val.SetMapIndex(k.Elem(), v.Elem()) + + default: + return fmt.Errorf("unsupported type: %s", kind.String()) + } + return nil +} + +func splitKeyVal(s, sep string) (key, val string, err error) { + if s == "" { + err = fmt.Errorf("empty key,val pair") + return + } + index := strings.Index(s, sep) + if index == -1 { + return s, "", nil + } + return s[:index], s[index+1:], nil +} + +func minmaxIntCheck(kind reflect.Kind, v int64) bool { + switch kind { + case reflect.Int, reflect.Int64: + return v >= int64(math.MinInt64) && v <= int64(math.MaxInt64) + case reflect.Int8: + return v >= int64(math.MinInt8) && v <= int64(math.MaxInt8) + case reflect.Int16: + return v >= int64(math.MinInt16) && v <= int64(math.MaxInt16) + case reflect.Int32: + return v >= int64(math.MinInt32) && v <= int64(math.MaxInt32) + } + return true +} + +func minmaxUintCheck(kind reflect.Kind, v uint64) bool { + switch kind { + case reflect.Uint, reflect.Uint64: + return v <= math.MaxUint64 + case reflect.Uint8: + return v <= math.MaxUint8 + case reflect.Uint16: + return v <= math.MaxUint16 + case reflect.Uint32: + return v <= math.MaxUint32 + } + return true +} + +func minmaxFloatCheck(kind reflect.Kind, v float64) bool { + switch kind { + case reflect.Float32: + return v >= -float64(math.MaxFloat32) && v <= float64(math.MaxFloat32) + case reflect.Float64: + return v >= -float64(math.MaxFloat64) && v <= float64(math.MaxFloat64) + } + return true +} + +func getBool(s string, clr color.Color) (bool, error) { + s = strings.ToLower(s) + if s == "true" || s == "yes" || s == "y" || s == "" { + return true, nil + } + if s == "false" || s == "none" || s == "no" || s == "not" || s == "n" { + return false, nil + } + i, err := strconv.Atoi(s) + if err != nil { + return false, fmt.Errorf("`%s' couldn't converted to a %s", s, clr.Bold("bool")) + } + return i != 0, nil +} + +func getInt(s string, clr color.Color) (int64, error) { + i, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return 0, fmt.Errorf("`%s' couldn't converted to an %s", s, clr.Bold("int")) + } + return i, nil +} + +func getUint(s string, clr color.Color) (uint64, error) { + i, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return 0, fmt.Errorf("`%s' couldn't converted to an %s", s, clr.Bold("uint")) + } + return i, nil +} + +func getFloat(s string, clr color.Color) (float64, error) { + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0, fmt.Errorf("`%s' couldn't converted to a %s", s, clr.Bold("float")) + } + return f, nil +} diff --git a/vendor/github.com/mkideal/cli/flag_set.go b/vendor/github.com/mkideal/cli/flag_set.go new file mode 100644 index 0000000..070e9da --- /dev/null +++ b/vendor/github.com/mkideal/cli/flag_set.go @@ -0,0 +1,244 @@ +package cli + +import ( + "bytes" + "fmt" + "io" + "net/url" + "strings" + + "github.com/Bowery/prompt" + "github.com/labstack/gommon/color" +) + +type flagSet struct { + err error + values url.Values + args []string + + flagMap map[string]*flag + flagSlice []*flag + + hasForce bool +} + +func newFlagSet() *flagSet { + return &flagSet{ + flagMap: make(map[string]*flag), + flagSlice: []*flag{}, + values: url.Values(make(map[string][]string)), + args: make([]string, 0), + } +} + +func (fs *flagSet) readPrompt(w io.Writer, clr color.Color) { + for _, fl := range fs.flagSlice { + if fl.isAssigned || fl.tag.prompt == "" { + continue + } + // read ... + prefix := fl.tag.prompt + ": " + var ( + data string + yes bool + ) + if fl.tag.isPassword { + data, fs.err = prompt.Password(prefix) + if fs.err == nil && data != "" { + fl.setWithNoDelay("", data, clr) + } + } else if fl.isBoolean() { + yes, fs.err = prompt.Ask(prefix) + if fs.err == nil { + fl.setWithNoDelay("", fmt.Sprintf("%v", yes), clr) + } + } else if fl.tag.dft != "" { + data, fs.err = prompt.BasicDefault(prefix, fl.tag.dft) + if fs.err == nil { + fl.setWithNoDelay("", data, clr) + } + } else { + data, fs.err = prompt.Basic(prefix, fl.tag.isRequired) + if fs.err == nil { + fl.setWithNoDelay("", data, clr) + } + } + if fs.err != nil { + return + } + } +} + +func (fs *flagSet) readEditor(clr color.Color) { + editor, editorErr := getEditor() + for _, fl := range fs.flagSlice { + if fl.isAssigned || !fl.tag.isEdit { + continue + } + if editorErr != nil { + fs.err = editorErr + return + } + filename := fl.tag.editFile + if filename == "" { + filename = randomFilename() + } + data, err := launchEditorWithFilename(editor, filename) + if fs.err = err; err != nil { + return + } + if fs.err = fl.setWithNoDelay("", string(data), clr); fs.err != nil { + return + } + } +} + +// UsageStyle is style of usage +type UsageStyle int32 + +const ( + // NormalStyle : left-right + NormalStyle UsageStyle = iota + // ManualStyle : up-down + ManualStyle + // DenseManualStyle : up-down, too + DenseManualStyle +) + +var defaultStyle = NormalStyle + +// GetUsageStyle gets default style +func GetUsageStyle() UsageStyle { + return defaultStyle +} + +// SetUsageStyle sets default style +func SetUsageStyle(style UsageStyle) { + defaultStyle = style +} + +type flagSlice []*flag + +func (fs flagSlice) String(clr color.Color) string { + var ( + lenShort = 0 + lenLong = 0 + lenNameAndDefaultAndLong = 0 + lenSep = len(sepName) + sepSpaces = strings.Repeat(" ", lenSep) + ) + for _, fl := range fs { + tag := fl.tag + l := 0 + for _, shortName := range tag.shortNames { + l += len(shortName) + lenSep + } + if l > lenShort { + lenShort = l + } + l = 0 + for _, longName := range tag.longNames { + l += len(longName) + lenSep + } + if l > lenLong { + lenLong = l + } + lenDft := 0 + if tag.dft != "" { + lenDft = len(tag.dft) + 3 // 3=len("[=]") + } + l += lenDft + if tag.name != "" { + l += len(tag.name) + 1 // 1=len("=") + } + if l > lenNameAndDefaultAndLong { + lenNameAndDefaultAndLong = l + } + } + + buff := bytes.NewBufferString("") + for _, fl := range fs { + var ( + tag = fl.tag + shortStr = strings.Join(tag.shortNames, sepName) + longStr = strings.Join(tag.longNames, sepName) + format = "" + defaultStr = "" + nameStr = "" + usagePrefix = " " + ) + if tag.dft != "" { + defaultStr = fmt.Sprintf("[=%s]", tag.dft) + } + if tag.name != "" { + nameStr = "=" + tag.name + } + if tag.isRequired { + usagePrefix = clr.Red("*") + } + usage := usagePrefix + tag.usage + + spaceSize := lenNameAndDefaultAndLong + spaceSize -= len(nameStr) + len(defaultStr) + len(longStr) + + if defaultStr != "" { + defaultStr = clr.Grey(defaultStr) + } + if nameStr != "" { + nameStr = "=" + clr.Bold(tag.name) + } + + if longStr == "" { + format = fmt.Sprintf("%%%ds%%s%s%%s", lenShort, sepSpaces) + fillStr := fillSpaces(nameStr+defaultStr, spaceSize) + fmt.Fprintf(buff, format+"\n", shortStr, fillStr, usage) + } else { + if shortStr == "" { + format = fmt.Sprintf("%%%ds%%s%%s", lenShort+lenSep) + } else { + format = fmt.Sprintf("%%%ds%s%%s%%s", lenShort, sepName) + } + fillStr := fillSpaces(longStr+nameStr+defaultStr, spaceSize) + fmt.Fprintf(buff, format+"\n", shortStr, fillStr, usage) + } + } + return buff.String() +} + +func fillSpaces(s string, spaceSize int) string { + return s + strings.Repeat(" ", spaceSize) +} + +func (fs flagSlice) StringWithStyle(clr color.Color, style UsageStyle) string { + if style != ManualStyle && style != DenseManualStyle { + return fs.String(clr) + } + + buf := bytes.NewBufferString("") + linePrefix := " " + for i, fl := range fs { + if i != 0 { + buf.WriteString("\n") + } + names := strings.Join(append(fl.tag.shortNames, fl.tag.longNames...), sepName) + buf.WriteString(linePrefix) + buf.WriteString(clr.Bold(names)) + if fl.tag.name != "" { + buf.WriteString("=" + clr.Bold(fl.tag.name)) + } + if fl.tag.dft != "" { + buf.WriteString(clr.Grey(fmt.Sprintf("[=%s]", fl.tag.dft))) + } + buf.WriteString("\n") + buf.WriteString(linePrefix) + buf.WriteString(" ") + if fl.tag.isRequired { + buf.WriteString(clr.Red("*")) + } + buf.WriteString(fl.tag.usage) + if style != DenseManualStyle { + buf.WriteString("\n") + } + } + return buf.String() +} diff --git a/vendor/github.com/mkideal/cli/fuzzy.go b/vendor/github.com/mkideal/cli/fuzzy.go new file mode 100644 index 0000000..1800348 --- /dev/null +++ b/vendor/github.com/mkideal/cli/fuzzy.go @@ -0,0 +1,71 @@ +package cli + +func match(s, t string) (float32, bool) { + return matchWithMinDifferRate(s, t, 0.3) +} + +func matchWithMinDifferRate(s, t string, minDifferRate float32) (float32, bool) { + dist := editDistance([]byte(s), []byte(t)) + differRate := float32(dist) / float32(max(len(s), len(t))+4) + return differRate, differRate <= minDifferRate +} + +func editDistance(s, t []byte) float32 { + var ( + m = len(s) + n = len(t) + d = make([][]float32, m+1) + ) + for i := 0; i < m+1; i++ { + d[i] = make([]float32, n+1) + d[i][0] = float32(i) + } + for j := 0; j < n+1; j++ { + d[0][j] = float32(j) + } + + for j := 1; j < n+1; j++ { + for i := 1; i < m+1; i++ { + if s[i-1] == t[j-1] { + d[i][j] = d[i-1][j-1] + } else { + d[i][j] = min(d[i-1][j]+1, min(d[i][j-1]+1, d[i-1][j-1]+1)) + } + } + } + + return d[m][n] +} + +func min(x, y float32) float32 { + if x < y { + return x + } + return y +} + +func max(x, y int) int { + if x > y { + return x + } + return y +} + +type editDistanceRank struct { + s string + d float32 +} + +type editDistanceRankSlice []editDistanceRank + +func (dists editDistanceRankSlice) Len() int { + return len(dists) +} + +func (dists editDistanceRankSlice) Less(i, j int) bool { + return dists[i].d < dists[j].d +} + +func (dists editDistanceRankSlice) Swap(i, j int) { + dists[i], dists[j] = dists[j], dists[i] +} diff --git a/vendor/github.com/mkideal/cli/http.go b/vendor/github.com/mkideal/cli/http.go new file mode 100644 index 0000000..5653281 --- /dev/null +++ b/vendor/github.com/mkideal/cli/http.go @@ -0,0 +1,156 @@ +package cli + +import ( + "bytes" + "io" + "net" + "net/http" + "strings" + "sync" + + "github.com/labstack/gommon/color" + "github.com/mkideal/pkg/debug" +) + +// RegisterHTTP init HTTPRouters for command +func (cmd *Command) RegisterHTTP(ctxs ...*Context) error { + clr := color.Color{} + clr.Disable() + if len(ctxs) > 0 { + clr = ctxs[0].color + } + if cmd.routersMap == nil { + cmd.routersMap = make(map[string]string) + } + commands := []*Command{cmd} + for len(commands) > 0 { + c := commands[0] + commands = commands[1:] + if c.HTTPRouters != nil { + for _, r := range c.HTTPRouters { + if _, exists := c.routersMap[r]; exists { + return throwRouterRepeat(clr.Yellow(r)) + } + cmd.routersMap[r] = c.Path() + } + } + if c.nochild() { + continue + } + commands = append(commands, c.children...) + } + return nil +} + +// ServeHTTP implements HTTP handler +func (cmd *Command) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + return + } + + var ( + path = r.URL.Path + found = false + ) + if cmd.routersMap != nil { + path, found = cmd.routersMap[path] + } + if !found { + path = strings.TrimPrefix(r.URL.Path, "/") + path = strings.TrimSuffix(path, "/") + } + + router := strings.Split(path, "/") + args := make([]string, 0, len(r.Form)*2+len(router)) + for _, r := range router { + args = append(args, r) + } + for key, values := range r.Form { + if len(key) == 0 || len(values) == 0 { + continue + } + if !strings.HasPrefix(key, dashOne) { + if len(key) == 1 { + key = dashOne + key + } else { + key = dashTwo + key + } + } + args = append(args, key, values[len(values)-1]) + } + debug.Debugf("agent: %s", r.UserAgent()) + debug.Debugf("path: %s", path) + debug.Debugf("args: %q", args) + + buf := new(bytes.Buffer) + statusCode := http.StatusOK + if err := cmd.RunWith(args, buf, w, r.Method); err != nil { + buf.Write([]byte(err.Error())) + nativeError := err + if werr, ok := err.(wrapError); ok { + nativeError = werr.err + } + switch nativeError.(type) { + case commandNotFoundError: + statusCode = http.StatusNotFound + + case methodNotAllowedError: + statusCode = http.StatusMethodNotAllowed + + default: + statusCode = http.StatusInternalServerError + } + } + debug.Debugf("resp: %s", buf.String()) + w.WriteHeader(statusCode) + w.Write(buf.Bytes()) +} + +// ListenAndServeHTTP set IsServer flag with true and startup http service +func (cmd *Command) ListenAndServeHTTP(addr string) error { + cmd.SetIsServer(true) + return http.ListenAndServe(addr, cmd) +} + +// Serve set IsServer with true and serve http with listeners +func (cmd *Command) Serve(listeners ...net.Listener) (err error) { + cmd.SetIsServer(true) + var g sync.WaitGroup + for _, ln := range listeners { + g.Add(1) + go func(ln net.Listener) { + if e := http.Serve(ln, cmd); e != nil { + panic(e.Error()) + } + g.Done() + }(ln) + } + g.Wait() + return +} + +// RPC runs the command from remote +func (cmd *Command) RPC(httpc *http.Client, ctx *Context) error { + addr := "http://rpc/" + ctx.Command().pathWithSep("/") + method := "POST" + if cmd.HTTPMethods != nil && len(cmd.HTTPMethods) > 0 { + method = cmd.HTTPMethods[0] + } + var body io.Reader + if values := ctx.FormValues(); values != nil { + body = strings.NewReader(values.Encode()) + } + req, err := http.NewRequest(method, addr, body) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("User-Agent", "cli-RPC") + resp, err := httpc.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = io.Copy(ctx, resp.Body) + return err +} diff --git a/vendor/github.com/mkideal/cli/parser.go b/vendor/github.com/mkideal/cli/parser.go new file mode 100644 index 0000000..0c62643 --- /dev/null +++ b/vendor/github.com/mkideal/cli/parser.go @@ -0,0 +1,54 @@ +package cli + +import ( + "encoding/json" +) + +// FlagParser represents a parser for parsing flag +type FlagParser interface { + Parse(s string) error +} + +// FlagParserCreator represents factory function of FlagParser +type FlagParserCreator func(ptr interface{}) FlagParser + +var parserCreators = map[string]FlagParserCreator{} + +// RegisterFlagParser registers FlagParserCreator by name +func RegisterFlagParser(name string, creator FlagParserCreator) { + if _, ok := parserCreators[name]; ok { + panic("RegisterFlagParser has registered: " + name) + } + parserCreators[name] = creator +} + +func init() { + RegisterFlagParser("json", newJSONParser) + RegisterFlagParser("jsonfile", newJSONFileParser) +} + +// JSON parser +type JSONParser struct { + ptr interface{} +} + +func newJSONParser(ptr interface{}) FlagParser { + return &JSONParser{ptr} +} + +func (p JSONParser) Parse(s string) error { + return json.Unmarshal([]byte(s), p.ptr) +} + +// JSON file parser +type JSONFileParser struct { + ptr interface{} +} + +func newJSONFileParser(ptr interface{}) FlagParser { + return &JSONFileParser{ptr} +} + +func (p JSONFileParser) Parse(s string) error { + return ReadJSONFromFile(s, p.ptr) +} diff --git a/vendor/github.com/mkideal/cli/run-examples.sh b/vendor/github.com/mkideal/cli/run-examples.sh new file mode 100755 index 0000000..bdc854c --- /dev/null +++ b/vendor/github.com/mkideal/cli/run-examples.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +CWD=`pwd` +EXAMPLE_DIR="./_examples" +EXAMPLES=`ls $EXAMPLE_DIR` + +for APP in $EXAMPLES +do + cd $EXAMPLE_DIR/$APP + SCRIPT="./run.sh" + if [ -f "$SCRIPT" ]; then + echo ">>>> exmaple: $APP" + chmod +x $SCRIPT + $SCRIPT + fi + cd $CWD +done diff --git a/vendor/github.com/mkideal/cli/tag.go b/vendor/github.com/mkideal/cli/tag.go new file mode 100644 index 0000000..cf1c3e4 --- /dev/null +++ b/vendor/github.com/mkideal/cli/tag.go @@ -0,0 +1,149 @@ +package cli + +import ( + "reflect" + "strings" +) + +const ( + tagCli = "cli" + tagPw = "pw" // password + tagEdit = "edit" + + tagUsage = "usage" + tagDefaut = "dft" + tagName = "name" + tagPrompt = "prompt" + tagParser = "parser" + tagSep = "sep" // used to seperate key/value pair of map, default is `=` + + dashOne = "-" + dashTwo = "--" + + sepName = ", " + + defaultSepForKeyValueOfMap = "=" +) + +type tagProperty struct { + // is a required flag? + isRequired bool `cli:"*x" pw:"*y" edit:"*z"` + + // is a force flag? + isForce bool `cli:"!x" pw:"!y" edit:"!z"` + + // is a password flag? + isPassword bool `pw:"xxx"` + + // is a edit flag? + isEdit bool `edit:"xxx"` + editFile string `edit:"FILE:xxx"` + + usage string `usage:"usage string"` + dft string `dft:"default value or expression"` + name string `name:"tag reference name"` + prompt string `prompt:"prompt string"` + sep string `sep:"string for seperate kay/value pair of map"` + parserCreator FlagParserCreator `parser:"parser for flag"` + + // flag names + shortNames []string + longNames []string +} + +func parseTag(fieldName string, tag reflect.StructTag) (p *tagProperty, isEmpty bool, err error) { + p = &tagProperty{ + shortNames: []string{}, + longNames: []string{}, + } + cliLikeTagCount := 0 + + // `cli` TAG + cli := tag.Get(tagCli) + if cli != "" { + cliLikeTagCount++ + } + + // `pw` TAG + if pw := tag.Get(tagPw); pw != "" { + p.isPassword = true + cli = pw + cliLikeTagCount++ + } + + // `edit` TAG + if edit := tag.Get(tagEdit); edit != "" { + // specific filename for editor + sepIndex := strings.Index(edit, ":") + if sepIndex > 0 { + p.editFile = edit[:sepIndex] + edit = edit[sepIndex+1:] + } + p.isEdit = true + cli = edit + cliLikeTagCount++ + } + + if cliLikeTagCount > 1 { + err = errCliTagTooMany + return + } + + // `usage` TAG + p.usage = tag.Get(tagUsage) + + // `dft` TAG + p.dft = tag.Get(tagDefaut) + + // `name` TAG + p.name = tag.Get(tagName) + + // `prompt` TAG + p.prompt = tag.Get(tagPrompt) + + // `parser` TAG + if parserName := tag.Get(tagParser); parserName != "" { + if parserCreator, ok := parserCreators[parserName]; ok { + p.parserCreator = parserCreator + } + } + + // `sep` TAG + p.sep = defaultSepForKeyValueOfMap + if sep := tag.Get(tagSep); sep != "" { + p.sep = sep + } + + cli = strings.TrimSpace(cli) + for { + if strings.HasPrefix(cli, "*") { + p.isRequired = true + cli = strings.TrimSpace(strings.TrimPrefix(cli, "*")) + } else if strings.HasPrefix(cli, "!") { + p.isForce = true + cli = strings.TrimSpace(strings.TrimPrefix(cli, "!")) + } else { + break + } + } + + names := strings.Split(cli, ",") + isEmpty = true + for _, name := range names { + if name = strings.TrimSpace(name); name == dashOne { + return nil, false, nil + } + if len(name) == 0 { + continue + } else if len(name) == 1 { + p.shortNames = append(p.shortNames, dashOne+name) + } else { + p.longNames = append(p.longNames, dashTwo+name) + } + isEmpty = false + } + if isEmpty { + p.longNames = append(p.longNames, dashTwo+fieldName) + } + return +} diff --git a/vendor/github.com/mkideal/pkg/LICENSE b/vendor/github.com/mkideal/pkg/LICENSE new file mode 100644 index 0000000..6c2f362 --- /dev/null +++ b/vendor/github.com/mkideal/pkg/LICENSE @@ -0,0 +1,18 @@ +Copyright (C) 2016 mkideal(i@mkideal.com) + +  Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to the following +conditions: +   +  The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mkideal/pkg/debug/debug.go b/vendor/github.com/mkideal/pkg/debug/debug.go new file mode 100644 index 0000000..e345034 --- /dev/null +++ b/vendor/github.com/mkideal/pkg/debug/debug.go @@ -0,0 +1,104 @@ +package debug + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "runtime" + "strings" + + "github.com/labstack/gommon/color" + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +var enableDebug = false + +func Switch(on bool) { + enableDebug = on +} + +var gopaths = func() []string { + paths := strings.Split(os.Getenv("GOPATH"), ";") + for i, path := range paths { + paths[i] = filepath.Join(path, "src") + "/" + } + if goroot := runtime.GOROOT(); goroot != "" { + paths = append(paths, filepath.Join(goroot, "src")+"/") + } + return paths +}() + +var debugOut, debugColor = func() (io.Writer, color.Color) { + clr := color.Color{} + out := colorable.NewColorableStdout() + ColorSwitch(&clr, out, os.Stdout.Fd()) + return out, clr +}() + +func Debugf(format string, args ...interface{}) { + if !enableDebug { + return + } + _, file, line, _ := runtime.Caller(1) + fileline := makeFileLine(file, line) + fmt.Fprintf(debugOut, "[DEBUG]"+debugColor.Bold(fileline)+" "+format+"\n", args...) +} + +func Panicf(format string, args ...interface{}) { + buff := bytes.NewBufferString("") + buff.WriteString(fmt.Sprintf(format, args...)) + buff.WriteString("\n\n[stack]\n") + skip := 1 + for { + _, file, line, ok := runtime.Caller(skip) + if !ok { + break + } + skip++ + buff.WriteString(makeFileLine(file, line)) + buff.WriteString("\n") + } + panic(buff.String()) +} + +func TypeName(i interface{}) string { + t := reflect.TypeOf(i) + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t.Name() +} + +func JSON(v interface{}) string { + data, err := json.MarshalIndent(v, "", "\t") + if err != nil { + return "" + } + return string(data) +} + +func makeFileLine(file string, line int) string { + for _, path := range gopaths { + if strings.HasPrefix(file, path) { + file = strings.TrimPrefix(strings.TrimPrefix(file, path), "/") + break + } + } + return fmt.Sprintf("%s:%d", file, line) +} + +func ColorSwitch(clr *color.Color, w io.Writer, fds ...uintptr) { + clr.Disable() + if len(fds) > 0 { + if isatty.IsTerminal(fds[0]) { + clr.Enable() + } + } else if w, ok := w.(*os.File); ok && isatty.IsTerminal(w.Fd()) { + clr.Enable() + } +} diff --git a/vendor/github.com/mkideal/pkg/debug/stack.go b/vendor/github.com/mkideal/pkg/debug/stack.go new file mode 100644 index 0000000..d70c4d3 --- /dev/null +++ b/vendor/github.com/mkideal/pkg/debug/stack.go @@ -0,0 +1,25 @@ +package debug + +import ( + "runtime" +) + +// Stack gets the call stack +func Stack(calldepth int) []byte { + var ( + e = make([]byte, 1<<16) // 64k + nbytes = runtime.Stack(e, false) + ignorelinenum = 2*calldepth + 1 + count = 0 + startIndex = 0 + ) + for i := range e { + if e[i] == '\n' { + count++ + if count == ignorelinenum { + startIndex = i + 1 + } + } + } + return e[startIndex:nbytes] +} diff --git a/vendor/github.com/mkideal/pkg/expr/README.md b/vendor/github.com/mkideal/pkg/expr/README.md new file mode 100644 index 0000000..aa970d7 --- /dev/null +++ b/vendor/github.com/mkideal/pkg/expr/README.md @@ -0,0 +1,85 @@ +expr [![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/mkideal/pkg/master/LICENSE) +=================================================================================================================================================== + +License +------- + +[The MIT License (MIT)](https://raw.githubusercontent.com/mkideal/pkg/master/LICENSE) + +Install +------- + +```shell +go get github.com/mkideal/pkg/expr +``` + +Expr +---- + +`Expr` is top-level object of `expr` package. + +`New` function new an Expr from string `s` and `pool`(using default if nil) or get from pool. + +```go +func New(s string, pool *Pool) (*Expr, error) +```` + +`Eval` method evaluate expression by VarGetter + +```go +func (e *Expr) Eval(getter VarGetter) (float64, error) +``` + +example: + +```go +e, _ := expr.New("x+1", nil) +getter := expr.VarGetter(map[string]float64{"x": 1}) +result, _ := e.Eval(getter) // result: 2 +``` + +VarGetter +--------- + +`VarGetter` define an interface for getting variable by name. + +`Getter` implements VarGetter using `map[string]float64`. + +```go +type VarGetter interface { + GetVar(string) (float64, error) +} + +// default VarGetter implement +type Getter map[string]float64 +``` + +Func +---- + +`Func` define function type used in expression. + +```go +type Func func(...float64) (float64, error) +``` + +builtin functions: + +- min(arg1[, arg2, ...]) +- max(arg1[, arg2, ...]) +- rand([arg1[, arg2]]) +- iff(ok, x, y) <==> ok ? x : y + +Pool +---- + +`Pool` used for cache expression objects. + +```go +func NewPool(factories ...map[string]Func) (*Pool, error) +``` + +External +-------- + +- [exp](https://github.com/mkideal/tools/tree/master/exp) - `a command line app for evaluate expression` diff --git a/vendor/github.com/mkideal/pkg/expr/eval.go b/vendor/github.com/mkideal/pkg/expr/eval.go new file mode 100644 index 0000000..bf0c3ca --- /dev/null +++ b/vendor/github.com/mkideal/pkg/expr/eval.go @@ -0,0 +1,133 @@ +package expr + +import ( + "fmt" + "go/ast" + "go/token" + "strconv" +) + +// eval the expression +func eval(e *Expr, getter VarGetter, node ast.Expr) (Value, error) { + switch n := node.(type) { + case *ast.Ident: + if getter == nil { + return e.pool.onVarMissing(n.Name) + } + val, ok := getter.GetVar(n.Name) + if !ok { + return e.pool.onVarMissing(n.Name) + } + return val, nil + + case *ast.BasicLit: + switch n.Kind { + case token.INT: + i, err := strconv.ParseInt(n.Value, 10, 64) + if err != nil { + return Zero(), err + } + return Int(i), nil + case token.FLOAT: + f, err := strconv.ParseFloat(n.Value, 64) + if err != nil { + return Zero(), err + } + return Float(f), nil + case token.CHAR, token.STRING: + s, err := strconv.Unquote(n.Value) + if err != nil { + return Zero(), err + } + return String(s), nil + default: + return Zero(), fmt.Errorf("unsupported token: %s(%v)", n.Value, n.Kind) + } + + case *ast.ParenExpr: + return eval(e, getter, n.X) + + case *ast.CallExpr: + args := make([]Value, 0, len(n.Args)) + for _, arg := range n.Args { + if val, err := eval(e, getter, arg); err != nil { + return Zero(), err + } else { + args = append(args, val) + } + } + if fnIdent, ok := n.Fun.(*ast.Ident); ok { + if fn, ok := e.pool.fn(fnIdent.Name); !ok { + return Zero(), fmt.Errorf("undefined function `%v`", fnIdent.Name) + } else { + return fn(args...) + } + } + return Zero(), fmt.Errorf("unexpected func type: %T", n.Fun) + + case *ast.UnaryExpr: + switch n.Op { + case token.ADD: + return eval(e, getter, n.X) + case token.SUB: + x, err := eval(e, getter, n.X) + if err == nil { + x, err = Zero().Sub(x) + } + return x, err + case token.NOT: + x, err := eval(e, getter, n.X) + if err == nil { + x = x.Not() + } + return x, err + default: + return Zero(), fmt.Errorf("unsupported unary op: %v", n.Op) + } + + case *ast.BinaryExpr: + x, err := eval(e, getter, n.X) + if err != nil { + return Zero(), err + } + y, err := eval(e, getter, n.Y) + if err != nil { + return Zero(), err + } + switch n.Op { + case token.ADD: + return x.Add(y) + case token.SUB: + return x.Sub(y) + case token.MUL: + return x.Mul(y) + case token.QUO: + return x.Quo(y) + case token.REM: + return x.Rem(y) + case token.XOR: + return x.Pow(y) + case token.LAND: + return x.And(y), nil + case token.LOR: + return x.Or(y), nil + case token.EQL: + return x.Eq(y) + case token.NEQ: + return x.Ne(y) + case token.GTR: + return x.Gt(y) + case token.GEQ: + return x.Ge(y) + case token.LSS: + return x.Lt(y) + case token.LEQ: + return x.Le(y) + default: + return Zero(), fmt.Errorf("unexpected binary operator: %v", n.Op) + } + + default: + return Zero(), fmt.Errorf("unexpected node type %T", n) + } +} diff --git a/vendor/github.com/mkideal/pkg/expr/expr.go b/vendor/github.com/mkideal/pkg/expr/expr.go new file mode 100644 index 0000000..ca1dc12 --- /dev/null +++ b/vendor/github.com/mkideal/pkg/expr/expr.go @@ -0,0 +1,113 @@ +package expr + +import ( + "fmt" + "go/ast" + "go/parser" + "strings" +) + +type ( + Func func(...Value) (Value, error) + + // VarGetter defines interface for getting value of variable + VarGetter interface { + GetVar(string) (Value, bool) + } + + // Expr is top-level object of expr package + Expr struct { + root ast.Expr + pool *Pool + } +) + +// default VarGetter implementation +type Getter map[string]Value + +// GetVar gets the value of variable +func (getter Getter) GetVar(name string) (Value, bool) { + if getter == nil { + return nilValue, false + } + v, ok := getter[name] + return v, ok +} + +// New creates an Expr and parses string s, pool can be nil +func New(s string, pool *Pool) (*Expr, error) { + s = strings.TrimSpace(s) + if pool == nil { + pool = defaultPool + } + if e, ok := pool.get(s); ok { + return e, nil + } + e := new(Expr) + e.pool = pool + if err := e.parse(s); err != nil { + return nil, err + } + pool.set(s, e) + return e, nil +} + +// parse parses string s +func (e *Expr) parse(s string) error { + if s == "" { + return nil + } + node, err := parser.ParseExpr(s) + if err != nil { + return err + } + e.root = node + + v := &visitor{pool: e.pool} + ast.Walk(v, e.root) + return v.err +} + +type visitor struct { + pool *Pool + err error +} + +// Visit implements ast.Visitor Visit method +func (v *visitor) Visit(node ast.Node) ast.Visitor { + if n, ok := node.(*ast.CallExpr); ok { + if fnIdent, ok := n.Fun.(*ast.Ident); ok { + if _, ok := v.pool.fn(fnIdent.Name); ok { + return v + } else { + v.err = fmt.Errorf("undefined function `%v`", fnIdent.Name) + } + } else { + v.err = fmt.Errorf("unsupported call expr") + } + return nil + } + return v +} + +// Eval calculate the expression +// getter maybe nil +func (e *Expr) Eval(getter VarGetter) (Value, error) { + if e.root == nil { + return Zero(), nil + } + v, err := eval(e, getter, e.root) + if err != nil { + return Zero(), err + } + return v, nil +} + +// Eval calculate expression with pool(maybe nil) +func Eval(s string, getter map[string]Value, pool *Pool) (Value, error) { + e, err := New(s, pool) + if err != nil { + return Zero(), err + } + return e.Eval(Getter(getter)) +} diff --git a/vendor/github.com/mkideal/pkg/expr/internal.go b/vendor/github.com/mkideal/pkg/expr/internal.go new file mode 100644 index 0000000..f8ab64c --- /dev/null +++ b/vendor/github.com/mkideal/pkg/expr/internal.go @@ -0,0 +1,115 @@ +package expr + +import ( + "fmt" + "math" + "strconv" +) + +func intRawString(i int64) string { return strconv.FormatInt(i, 10) } +func floatRawString(f float64) string { return fmt.Sprintf("%f", f) } + +func stringAdd(s1, s2 Value) Value { + return Value{ + kind: KindString, + rawValue: s1.rawValue + s2.rawValue, + } +} + +func intAdd(v1, v2 Value) (Value, error) { return Int(v1.intValue + v2.intValue), nil } +func intSub(v1, v2 Value) (Value, error) { return Int(v1.intValue - v2.intValue), nil } +func intMul(v1, v2 Value) (Value, error) { return Int(v1.intValue * v2.intValue), nil } +func intQuo(v1, v2 Value) (Value, error) { + if v2.intValue == 0 { + return Zero(), ErrDivideZero + } + return Int(v1.intValue / v2.intValue), nil +} +func intRem(v1, v2 Value) (Value, error) { + if v2.intValue == 0 { + return Zero(), ErrDivideZero + } + return Int(v1.intValue % v2.intValue), nil +} +func intPow(v1, v2 Value) (Value, error) { + if v1.intValue == 0 { + return Zero(), ErrPowOfZero + } + return Int(int64(math.Pow(float64(v1.intValue), float64(v2.intValue)))), nil +} + +func floatAdd(v1, v2 Value) (Value, error) { return Float(v1.floatValue + v2.floatValue), nil } +func floatSub(v1, v2 Value) (Value, error) { return Float(v1.floatValue - v2.floatValue), nil } +func floatMul(v1, v2 Value) (Value, error) { return Float(v1.floatValue * v2.floatValue), nil } +func floatQuo(v1, v2 Value) (Value, error) { return Float(v1.floatValue / v2.floatValue), nil } +func floatRem(v1, v2 Value) (Value, error) { + return Float(math.Remainder(v1.floatValue, v2.floatValue)), nil +} +func floatPow(v1, v2 Value) (Value, error) { + if v1.floatValue == 0 { + return Zero(), ErrPowOfZero + } + return Float(math.Pow(v1.floatValue, v2.floatValue)), nil +} + +type binaryOpFunc func(Value, Value) (Value, error) + +func binaryOp(v1, v2 Value, iop, fop binaryOpFunc) (Value, error) { + if v1.kind == KindString || v2.kind == KindString { + return Zero(), ErrTypeMismatchForOp + } + if v1.kind == KindInvalid || v2.kind == KindInvalid { + return Zero(), ErrUnsupportedType + } + if v1.kind == KindFloat { + if v2.kind == KindInt { + return fop(v1, Float(float64(v2.intValue))) + } + return fop(v1, v2) + } else { + if v2.kind == KindInt { + return iop(v1, v2) + } + return fop(Float(float64(v1.intValue)), v2) + } +} + +type compareFunc func(Value, Value) Value + +func stringEq(v1, v2 Value) Value { return Bool(v1.rawValue == v2.rawValue) } +func stringGt(v1, v2 Value) Value { return Bool(v1.rawValue > v2.rawValue) } +func stringGe(v1, v2 Value) Value { return Bool(v1.rawValue >= v2.rawValue) } + +func intEq(v1, v2 Value) Value { return Bool(v1.intValue == v2.intValue) } +func intGt(v1, v2 Value) Value { return Bool(v1.intValue > v2.intValue) } +func intGe(v1, v2 Value) Value { return Bool(v1.intValue >= v2.intValue) } + +func floatEq(v1, v2 Value) Value { return Bool(v1.floatValue == v2.floatValue) } +func floatGt(v1, v2 Value) Value { return Bool(v1.floatValue > v2.floatValue) } +func floatGe(v1, v2 Value) Value { return Bool(v1.floatValue >= v2.floatValue) } + +func compare(v1, v2 Value, scmp, icmp, fcmp compareFunc) (Value, error) { + switch v1.kind { + case KindString: + if v2.kind == KindString { + return scmp(v1, v2), nil + } + return False(), ErrComparedTypesMismatch + case KindInt: + if v2.kind == KindInt { + return icmp(v1, v2), nil + } else if v2.kind == KindFloat { + return fcmp(Float(float64(v1.intValue)), v2), nil + } + return False(), ErrComparedTypesMismatch + case KindFloat: + if v2.kind == KindInt { + return fcmp(v1, Float(float64(v2.intValue))), nil + } else if v2.kind == KindFloat { + return fcmp(v1, v2), nil + } + return False(), ErrComparedTypesMismatch + default: + return False(), ErrUnsupportedType + } +} diff --git a/vendor/github.com/mkideal/pkg/expr/pool.go b/vendor/github.com/mkideal/pkg/expr/pool.go new file mode 100644 index 0000000..65df341 --- /dev/null +++ b/vendor/github.com/mkideal/pkg/expr/pool.go @@ -0,0 +1,155 @@ +package expr + +import ( + "fmt" + "math/rand" + "regexp" + "sync" +) + +type VarMissingFunc func(string) (Value, error) + +func DefaultOnVarMissing(varName string) (Value, error) { + return Zero(), fmt.Errorf("var `%s' missing", varName) +} + +type Pool struct { + locker sync.RWMutex + pool map[string]*Expr + + factory map[string]Func + onVarMissing VarMissingFunc +} + +func MustNewPool(factories ...map[string]Func) *Pool { + pool, err := NewPool(factories...) + if err != nil { + panic(err) + } + return pool +} + +func NewPool(factories ...map[string]Func) (*Pool, error) { + p := &Pool{ + pool: make(map[string]*Expr), + factory: newDefaultFactory(), + onVarMissing: DefaultOnVarMissing, + } + for _, factory := range factories { + if factory == nil { + continue + } + for name, fn := range factory { + if !validateFuncName(name) { + return nil, fmt.Errorf("illegal function name `%s`", name) + } + p.factory[name] = fn + } + } + return p, nil +} + +func (p *Pool) SetOnVarMissing(fn VarMissingFunc) { + p.onVarMissing = fn +} + +func (p *Pool) get(s string) (*Expr, bool) { + p.locker.RLock() + defer p.locker.RUnlock() + e, ok := p.pool[s] + return e, ok && e != nil +} + +func (p *Pool) set(s string, e *Expr) { + p.locker.Lock() + defer p.locker.Unlock() + p.pool[s] = e +} + +func (p *Pool) fn(name string) (Func, bool) { + fn, ok := p.factory[name] + return fn, ok +} + +// validate function name +var funcNameRegexp = regexp.MustCompile("[a-zA-Z_][a-z-A-Z_0-9]{0,254}") + +func validateFuncName(name string) bool { + return funcNameRegexp.MatchString(name) +} + +// default Pool +var defaultPool = func() *Pool { + p, err := NewPool() + if err != nil { + panic(err) + } + return p +}() + +// default factory +var newDefaultFactory = func() map[string]Func { + return map[string]Func{ + "min": builtin_min, + "max": builtin_max, + "rand": builtin_rand, + } +} + +//------------------ +// builtin function +//------------------ + +func builtin_min(args ...Value) (Value, error) { + if len(args) == 0 { + return nilValue, fmt.Errorf("missing arguments for function `min`") + } + x := args[0] + for i, size := 1, len(args); i < size; i++ { + lt, err := args[i].Lt(x) + if err != nil { + return Zero(), err + } + if lt.Bool() { + x = args[i] + } + } + return x, nil +} + +func builtin_max(args ...Value) (Value, error) { + if len(args) == 0 { + return nilValue, fmt.Errorf("missing arguments for function `max`") + } + x := args[0] + for i, size := 1, len(args); i < size; i++ { + gt, err := args[i].Gt(x) + if err != nil { + return Zero(), err + } + if gt.Bool() { + x = args[i] + } + } + return x, nil +} + +func builtin_rand(args ...Value) (Value, error) { + if len(args) == 0 { + return Int(int64(rand.Intn(10000))), nil + } + if len(args) == 1 { + x := args[0].Int() + if x <= 0 { + return Zero(), fmt.Errorf("bad argument for function `rand`: argument %v <= 0", x) + } + } + if len(args) == 2 { + x, y := int(args[0].Int()), int(args[1].Int()) + if x > y { + return Zero(), fmt.Errorf("bad arguments for function `rand`: first > second") + } + return Int(int64(rand.Intn(y-x+1) + x)), nil + } + return Zero(), fmt.Errorf("too many arguments for function `rand`: arguments size=%d", len(args)) +} diff --git a/vendor/github.com/mkideal/pkg/expr/value.go b/vendor/github.com/mkideal/pkg/expr/value.go new file mode 100644 index 0000000..b868c9a --- /dev/null +++ b/vendor/github.com/mkideal/pkg/expr/value.go @@ -0,0 +1,161 @@ +package expr + +import ( + "errors" + "strconv" + "strings" +) + +var ( + ErrFailedToParseInteger = errors.New("failed to parse integer") + ErrFailedToParseFloat = errors.New("failed to parse float") + ErrNotAnInteger = errors.New("not an integer") + ErrNotAFloat = errors.New("not a float") + ErrUnsupportedType = errors.New("unsupported type") + ErrTypeMismatchForOp = errors.New("type mismatch for operater") + ErrDivideZero = errors.New("divide zero") + ErrPowOfZero = errors.New("power of zero") + ErrComparedTypesMismatch = errors.New("compared types mismatch") + ErrBadArgumentsSize = errors.New("bad arguments size") +) + +type Kind int + +const ( + KindInvalid Kind = iota + KindInt + KindFloat + KindString +) + +var ( + nilValue = Value{kind: KindInvalid} + + varZero = Value{kind: KindInt, rawValue: "0"} + varTrue = Value{kind: KindInt, intValue: 1, rawValue: "true"} + varFalse = Value{kind: KindInt, intValue: 0, rawValue: "false"} +) + +func Nil() Value { return nilValue } +func Zero() Value { return varZero } +func True() Value { return varTrue } +func False() Value { return varFalse } +func Equal(v1, v2 Value) bool { + eq, err := v1.Eq(v2) + return err == nil && eq.Bool() +} + +func Bool(ok bool) Value { + if ok { + return True() + } + return False() +} + +func Int(i int64) Value { return Value{kind: KindInt, intValue: i, rawValue: intRawString(i)} } +func Float(f float64) Value { return Value{kind: KindFloat, floatValue: f, rawValue: floatRawString(f)} } +func String(s string) Value { return Value{kind: KindString, rawValue: s} } + +type Value struct { + kind Kind + rawValue string + intValue int64 + floatValue float64 +} + +func NewValue(kind Kind) Value { + return Value{kind: kind} +} + +func (v *Value) Set(s string) error { + switch v.kind { + case KindString: + // donothing + case KindInt: + if i, err := strconv.ParseInt(s, 0, 64); err == nil { + v.intValue = i + } else { + return ErrFailedToParseInteger + } + case KindFloat: + if f, err := strconv.ParseFloat(s, 64); err == nil { + v.floatValue = f + } else { + return ErrFailedToParseFloat + } + default: + return ErrUnsupportedType + } + v.rawValue = s + return nil +} + +func (v Value) Kind() Kind { return v.kind } +func (v Value) String() string { return v.rawValue } +func (v Value) Int() int64 { + if v.kind == KindFloat { + return int64(v.floatValue) + } + return v.intValue +} +func (v Value) Float() float64 { + if v.kind == KindInt { + return float64(v.intValue) + } + return v.floatValue +} +func (v Value) Bool() bool { + switch v.kind { + case KindString: + return v.rawValue != "" + case KindInt: + return v.intValue != 0 + case KindFloat: + return v.floatValue != 0 + } + return false +} + +func (v Value) Add(v2 Value) (Value, error) { + if v.kind == KindString && v2.kind == KindString { + return stringAdd(v, v2), nil + } + return binaryOp(v, v2, intAdd, floatAdd) +} + +func (v Value) Sub(v2 Value) (Value, error) { return binaryOp(v, v2, intSub, floatSub) } +func (v Value) Mul(v2 Value) (Value, error) { return binaryOp(v, v2, intMul, floatMul) } +func (v Value) Quo(v2 Value) (Value, error) { return binaryOp(v, v2, intQuo, floatQuo) } +func (v Value) Rem(v2 Value) (Value, error) { return binaryOp(v, v2, intRem, floatRem) } +func (v Value) Pow(v2 Value) (Value, error) { return binaryOp(v, v2, intPow, floatPow) } +func (v Value) And(v2 Value) Value { return Bool(v.Bool() && v2.Bool()) } +func (v Value) Or(v2 Value) Value { return Bool(v.Bool() || v2.Bool()) } +func (v Value) Not() Value { return Bool(!v.Bool()) } +func (v Value) Eq(v2 Value) (Value, error) { return compare(v, v2, stringEq, intEq, floatEq) } + +func (v Value) Ne(v2 Value) (Value, error) { + result, err := v.Eq(v2) + if err == nil { + result = result.Not() + } + return result, err +} + +func (v Value) Gt(v2 Value) (Value, error) { return compare(v, v2, stringGt, intGt, floatGt) } +func (v Value) Ge(v2 Value) (Value, error) { return compare(v, v2, stringGe, intGe, floatGe) } +func (v Value) Lt(v2 Value) (Value, error) { return v2.Gt(v) } +func (v Value) Le(v2 Value) (Value, error) { return v2.Ge(v) } + +func (v Value) Contains(v2 Value) Value { + if v.kind == KindString && v2.kind == KindString { + return Bool(strings.Contains(v.rawValue, v2.rawValue)) + } + return False() +} + +func ExpectNArg(got, want int) error { + if got != want { + return ErrBadArgumentsSize + } + return nil +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 0d8cd17..1fd9658 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -2,6 +2,12 @@ "comment": "", "ignore": "test", "package": [ + { + "checksumSHA1": "4Tc07iR3HloUYC4HNT4xc0875WY=", + "path": "github.com/Bowery/prompt", + "revision": "0f1139e9a1c74b57ccce6bdb3cd2f7cd04dd3449", + "revisionTime": "2017-02-19T07:16:37Z" + }, { "checksumSHA1": "ER6Xu2LxkhGM6d58xJPR8Y5YTe4=", "path": "github.com/aws/aws-sdk-go/aws", @@ -176,6 +182,12 @@ "revision": "bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d", "revisionTime": "2016-08-03T19:07:31Z" }, + { + "checksumSHA1": "R6DzcBLEP0BONPpsyr+11N7xh5w=", + "path": "github.com/labstack/gommon/color", + "revision": "1121fd3e243c202482226a7afe4dcd07ffc4139a", + "revisionTime": "2017-05-06T16:25:42Z" + }, { "checksumSHA1": "uAVg4Tj5Fxi62z7/ScDEkHr6Ue8=", "path": "github.com/mattn/go-colorable", @@ -188,6 +200,24 @@ "revision": "fc9e8d8ef48496124e79ae0df75490096eccf6fe", "revisionTime": "2017-03-22T23:44:13Z" }, + { + "checksumSHA1": "Wt3B+VyuxldXaHqtSy50Gwy27g8=", + "path": "github.com/mkideal/cli", + "revision": "a9c1104566927924fdb041d198f05617492913f9", + "revisionTime": "2017-03-24T15:56:50Z" + }, + { + "checksumSHA1": "/dGz97uMuuCCAWFgNqI1Wf/pU8g=", + "path": "github.com/mkideal/pkg/debug", + "revision": "3e188c9e7ecc83d0fe7040a9161ce3c67885470d", + "revisionTime": "2017-05-03T15:41:53Z" + }, + { + "checksumSHA1": "Fg4yMJny/QorwTrQWkwJXHvW3Pc=", + "path": "github.com/mkideal/pkg/expr", + "revision": "3e188c9e7ecc83d0fe7040a9161ce3c67885470d", + "revisionTime": "2017-05-03T15:41:53Z" + }, { "checksumSHA1": "ZOhewV1DsQjTYlx8a+ifrZki2Vg=", "path": "github.com/ryanuber/columnize", From 9a3c117c5005b3c40577eeb38133e34e9401f1f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 17:59:48 +0200 Subject: [PATCH 02/12] - Delete old func Cloud --- cloud/aws/session.go | 51 -------------------------------------------- cloud/cloud.go | 44 -------------------------------------- help.go | 47 ---------------------------------------- 3 files changed, 142 deletions(-) delete mode 100644 cloud/aws/session.go delete mode 100644 cloud/cloud.go delete mode 100644 help.go diff --git a/cloud/aws/session.go b/cloud/aws/session.go deleted file mode 100644 index 409270d..0000000 --- a/cloud/aws/session.go +++ /dev/null @@ -1,51 +0,0 @@ -package aws - -import ( - "fmt" - - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/perriea/tfversion/error" -) - -var ( - sess *session.Session - ec2svc *ec2.EC2 - err error -) - -func TestConnect() { - - // The SDK has support for the shared configuration file (~/.aws/config) - // Create a session to share configuration, and load external configuration. - sess = session.Must(session.NewSessionWithOptions(session.Options{ - SharedConfigState: session.SharedConfigEnable, - })) - - params := &ec2.DescribeInstancesInput{} - - // Create the service's client with the session. - ec2svc = ec2.New(sess) - _, err := ec2svc.DescribeInstances(params) - - if err != nil { - tferror.Run(2, "[WARN] Your AWS access is not correct") - if awsErr, ok := err.(awserr.Error); ok { - - if reqErr, ok := err.(awserr.RequestFailure); ok { - // A service error occurred - fmt.Printf("%s : %s (%s)", awsErr.Code(), awsErr.Message(), reqErr.RequestID()) - } else { - // Generic AWS Error with Code, Message, and original error (if any) - fmt.Printf("%s : %s\n%s", awsErr.Code(), awsErr.Message(), awsErr.OrigErr()) - } - - } else { - tferror.Panic(err) - } - - } else { - tferror.Run(1, "Your AWS access is correct") - } -} diff --git a/cloud/cloud.go b/cloud/cloud.go deleted file mode 100644 index e36fc0c..0000000 --- a/cloud/cloud.go +++ /dev/null @@ -1,44 +0,0 @@ -package tfcloud - -import ( - "flag" - "fmt" - - "github.com/perriea/tfversion/cloud/aws" -) - -var ( - testaws bool - testgcp bool - testazure bool - err error -) - -func Run(params []string) error { - - cloud := flag.NewFlagSet("cloud", flag.ExitOnError) - cloud.BoolVar(&testaws, "aws", false, "Test connection on AWS)") - cloud.BoolVar(&testgcp, "gcp", false, "Test connection on GCP)") - cloud.BoolVar(&testazure, "azure", false, "Test connection on Azure)") - cloud.Parse(params) - - if testaws && testgcp && testazure { - return fmt.Errorf("--aws, --gcp and --azure are mutually exclusive") - } - - if len(params) != 1 { - return fmt.Errorf("Too many arguments ...") - } - - if testaws { - aws.TestConnect() - } else if testgcp { - return fmt.Errorf("GCP test is not actived") - } else if testazure { - return fmt.Errorf("Azure test is not actived") - } else { - return fmt.Errorf("This provider doesn't exist !") - } - - return nil -} diff --git a/help.go b/help.go deleted file mode 100644 index dfd7080..0000000 --- a/help.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/perriea/tfversion/error" - "github.com/perriea/tfversion/github" -) - -// DoHelp : Lauch Helper Command -func DoHelp() error { - - var version string - - keys := make([]string, 0, len(commands)) - for k := range commands { - keys = append(keys, k) - } - - fmt.Printf("tfversion v%s\n\n", version) - fmt.Printf("Usage: tfversion [args]\n\n") - fmt.Printf("Common commands:\n") - - for _, k := range keys { - fmt.Printf("%10s: %s\n", k, commands[k].desc) - fmt.Printf("\tUsage: %s %s\n", k, commands[k].usage) - } - - fmt.Printf(" help: Show this help message\n\n") - - // Show if the last version - lastrelease, release := tfgithub.Lastversion(version) - if !lastrelease && release != nil { - tferror.Run(2, fmt.Sprintf("Your version is out of date !\nThe latest version is %s (%s)", *release.TagName, *release.HTMLURL)) - } - - return nil -} - -// CmdUnknown : Unknown commands -func CmdUnknown() { - fmt.Fprintf(os.Stderr, "Unknown command '%s'\n", os.Args[1]) - fmt.Fprintf(os.Stderr, "Usage: %s command command_arguments\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "\tUse help command to list available commands\n") - fmt.Fprintf(os.Stderr, "\tUse command help to get commands accepting options\n") -} From 5101bc7793595bca77c3db3bb2e841b5e7ecee0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 18:00:37 +0200 Subject: [PATCH 03/12] + Add new gestion Cloud --- terraform/cloud/aws/session.go | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 terraform/cloud/aws/session.go diff --git a/terraform/cloud/aws/session.go b/terraform/cloud/aws/session.go new file mode 100644 index 0000000..ca5d6bf --- /dev/null +++ b/terraform/cloud/aws/session.go @@ -0,0 +1,51 @@ +package tfaws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/perriea/tfversion/error" +) + +var ( + sess *session.Session + ec2svc *ec2.EC2 + err error +) + +func TestConnect() { + + // The SDK has support for the shared configuration file (~/.aws/config) + // Create a session to share configuration, and load external configuration. + sess = session.Must(session.NewSessionWithOptions(session.Options{ + SharedConfigState: session.SharedConfigEnable, + })) + + params := &ec2.DescribeInstancesInput{} + + // Create the service's client with the session. + ec2svc = ec2.New(sess) + _, err := ec2svc.DescribeInstances(params) + + if err != nil { + tferror.Run(2, "[WARN] Your AWS access is not correct") + if awsErr, ok := err.(awserr.Error); ok { + + if reqErr, ok := err.(awserr.RequestFailure); ok { + // A service error occurred + fmt.Printf("%s : %s (%s)", awsErr.Code(), awsErr.Message(), reqErr.RequestID()) + } else { + // Generic AWS Error with Code, Message, and original error (if any) + fmt.Printf("%s : %s\n%s", awsErr.Code(), awsErr.Message(), awsErr.OrigErr()) + } + + } else { + tferror.Panic(err) + } + + } else { + tferror.Run(1, "Your AWS access is correct") + } +} From 7a048d1da4bb23234ffb25720fd124bee91876aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 18:01:40 +0200 Subject: [PATCH 04/12] + Add new gestion commands --- command.go | 131 +++++++++++++++++++++++++++++++++++ main.go | 53 +++++--------- terraform/install/install.go | 23 ++---- terraform/list/list.go | 26 ------- 4 files changed, 152 insertions(+), 81 deletions(-) create mode 100644 command.go diff --git a/command.go b/command.go new file mode 100644 index 0000000..238947a --- /dev/null +++ b/command.go @@ -0,0 +1,131 @@ +package main + +import ( + "fmt" + "os" + + "github.com/mkideal/cli" + "github.com/perriea/tfversion/github" + "github.com/perriea/tfversion/terraform/cloud/aws" + "github.com/perriea/tfversion/terraform/init" + "github.com/perriea/tfversion/terraform/install" + "github.com/perriea/tfversion/terraform/list" +) + +var help = cli.HelpCommand("Display help informations") +var tfversion = "0.1.2" + +// root command +type rootT struct { + Version bool `cli:"v,version" usage:"Show version and check update"` +} + +var root = &cli.Command{ + Desc: fmt.Sprintf("tfversion v%s \n\n\033[1mUsage:\033[0m\n\n tfversion [option]", tfversion), + Argv: func() interface{} { return new(rootT) }, + OnRootBefore: func(ctx *cli.Context) error { + tfinit.CreateTree() + return nil + }, + Fn: func(ctx *cli.Context) error { + argv := ctx.Argv().(*rootT) + if argv.Version { + fmt.Fprintf(os.Stderr, "tfversion v%s\n\n", tfversion) + + // Show if the last version + lastrelease, release := tfgithub.Lastversion(tfversion) + if !lastrelease && release != nil { + fmt.Printf("Your version is out of date ! The latest version is %s. You can update by downloading from Github (%s).", *release.TagName, *release.HTMLURL) + } + } + return nil + }, +} + +// child command +type installT struct { + cli.Helper + Version string `cli:"*version" usage:"Install or switch version"` +} + +var install = &cli.Command{ + Name: "install", + Desc: "Install new versions or switch.", + Argv: func() interface{} { return new(installT) }, + OnBefore: func(ctx *cli.Context) error { + tfinit.CreateTree() + return nil + }, + Fn: func(ctx *cli.Context) error { + argv := ctx.Argv().(*installT) + tfinstall.Run(argv.Version) + return nil + }, +} + +// child command +type uninstallT struct { + cli.Helper + Version string `cli:"*v,version" usage:"uninstall version"` +} + +var uninstall = &cli.Command{ + Name: "uninstall", + Desc: "Uninstall local version of Terraform", + Argv: func() interface{} { return new(uninstallT) }, + OnBefore: func(ctx *cli.Context) error { + tfinit.CreateTree() + return nil + }, + Fn: func(ctx *cli.Context) error { + argv := ctx.Argv().(*uninstallT) + ctx.String("Hello, child command, I am %s\n", argv.Version) + return nil + }, +} + +// child command +type listT struct { + cli.Helper + On bool `cli:"!on,online" usage:"list online version"` + Off bool `cli:"off,offline" usage:"list offline version"` +} + +var list = &cli.Command{ + Name: "list", + Desc: "List online or offline version of terraform", + Argv: func() interface{} { return new(listT) }, + OnBefore: func(ctx *cli.Context) error { + tfinit.CreateTree() + return nil + }, + Fn: func(ctx *cli.Context) error { + argv := ctx.Argv().(*listT) + if argv.On { + tflist.ListOn() + } else { + tflist.ListOff() + } + return nil + }, +} + +// child command +type testT struct { + cli.Helper + Aws bool `cli:"*aws,amazon" usage:"test connection to AWS"` + //Gcp bool `cli:"*gcp,google" usage:"test connection to GCP"` +} + +var test = &cli.Command{ + Name: "test", + Desc: "Test provider cloud (AWS)", + Argv: func() interface{} { return new(testT) }, + Fn: func(ctx *cli.Context) error { + argv := ctx.Argv().(*testT) + if argv.Aws { + tfaws.TestConnect() + } + return nil + }, +} diff --git a/main.go b/main.go index ea65e28..2a85c25 100644 --- a/main.go +++ b/main.go @@ -4,52 +4,31 @@ import ( "fmt" "os" - "github.com/perriea/tfversion/cloud" - "github.com/perriea/tfversion/error" - "github.com/perriea/tfversion/terraform/install" - "github.com/perriea/tfversion/terraform/list" - "github.com/perriea/tfversion/terraform/uninstall" + "github.com/fatih/color" + "github.com/mkideal/cli" ) -type command struct { - desc string - usage string - Func cmdHandler -} - var ( - // Errors - commands map[string]command - version string - err error + bold *color.Color + err error ) -type cmdHandler func([]string) error - func init() { - // list commands - commands = map[string]command{ - "install": command{"Install new versions or switch.", "[0.8.8 version of terraform]", tfinstall.Run}, - "uninstall": command{"Clean cache (tmp files).", "[-a all], [-v version specific]", tfuninstall.Run}, - "list": command{"List online or offline version of terraform.", "[-on list online], [-off list local]", tflist.Run}, - "cloud": command{"Action cloud (Beta)", "[--aws test AWS]", tfcloud.Run}, - } + bold = color.New(color.FgWhite, color.Bold) } func main() { - // Check if the command is helper - if len(os.Args) < 2 || os.Args[1] == "help" { - err = DoHelp() - tferror.Panic(err) - return - } - // Look command - if cmd, ok := commands[os.Args[1]]; ok { - if err = cmd.Func(os.Args[2:]); err != nil { - fmt.Fprintln(os.Stderr, err) - } - } else { - CmdUnknown() + err = cli.Root(root, + cli.Tree(help), + cli.Tree(install), + cli.Tree(uninstall), + cli.Tree(list), + cli.Tree(test), + ).Run(os.Args[1:]) + + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) } } diff --git a/terraform/install/install.go b/terraform/install/install.go index 74d2d2c..318a838 100644 --- a/terraform/install/install.go +++ b/terraform/install/install.go @@ -1,7 +1,6 @@ package tfinstall import ( - "flag" "fmt" "os/user" "path/filepath" @@ -9,7 +8,6 @@ import ( "github.com/perriea/tfversion/error" "github.com/perriea/tfversion/system/files" "github.com/perriea/tfversion/terraform/download" - "github.com/perriea/tfversion/terraform/init" ) var ( @@ -31,32 +29,21 @@ func init() { pathZip = pathTmp + "/terraform-%s.zip" } -func Run(params []string) error { - - var dl *flag.FlagSet - - dl = flag.NewFlagSet("install", flag.ExitOnError) - dl.Parse(params) - params = dl.Args() - - if len(params) != 1 { - return fmt.Errorf("One parameter is accepted ...") - } +func Run(version string) error { // Lauch Terraform download - tfinit.CreateTree() - check = tfdownload.Run(params[0]) + check = tfdownload.Run(version) // Check if download is done and install if check { // Unzip zip archive fmt.Println("Unzip file ...") - tffiles.UnZip(fmt.Sprintf(pathZip, params[0]), pathBin) + tffiles.UnZip(fmt.Sprintf(pathZip, version), pathBin) fmt.Println("Install the binary file ...") - tffiles.CreateText(params[0]) + tffiles.CreateText(version) - tferror.Run(1, fmt.Sprintf("Installed %s, Thanks ! ♥", params[0])) + tferror.Run(1, fmt.Sprintf("Installed %s, Thanks ! ♥", version)) } return err diff --git a/terraform/list/list.go b/terraform/list/list.go index dec3f0c..45b10f8 100644 --- a/terraform/list/list.go +++ b/terraform/list/list.go @@ -1,7 +1,6 @@ package tflist import ( - "flag" "fmt" "github.com/fatih/color" @@ -11,16 +10,12 @@ import ( var ( online bool offline bool - clist *flag.FlagSet good *color.Color err error ) func init() { good = color.New(color.FgGreen, color.Bold) - clist = flag.NewFlagSet("list", flag.ExitOnError) - clist.BoolVar(&online, "on", false, "View all versions available.") - clist.BoolVar(&offline, "off", false, "View all version already downloaded.") } func showList(list []string, tfversion string) { @@ -58,24 +53,3 @@ func showList(list []string, tfversion string) { result := columnize.SimpleFormat(reslist) fmt.Println(result) } - -func Run(params []string) error { - - clist.Parse(params) - - if online && offline { - return fmt.Errorf("-on and -off are mutually exclusive") - } - - if len(params) != 1 { - return fmt.Errorf("One parameter is accepted ...") - } - - if online { - ListOn() - } else { - ListOff() - } - - return nil -} From 3b5f732f3243d31faf52b2e19d4a3adb443af12e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 18:47:47 +0200 Subject: [PATCH 05/12] - Delete library fatih/color --- vendor/github.com/fatih/color/LICENSE.md | 20 - vendor/github.com/fatih/color/README.md | 177 -------- vendor/github.com/fatih/color/color.go | 526 ----------------------- vendor/github.com/fatih/color/doc.go | 128 ------ vendor/vendor.json | 6 - 5 files changed, 857 deletions(-) delete mode 100644 vendor/github.com/fatih/color/LICENSE.md delete mode 100644 vendor/github.com/fatih/color/README.md delete mode 100644 vendor/github.com/fatih/color/color.go delete mode 100644 vendor/github.com/fatih/color/doc.go diff --git a/vendor/github.com/fatih/color/LICENSE.md b/vendor/github.com/fatih/color/LICENSE.md deleted file mode 100644 index 25fdaf6..0000000 --- a/vendor/github.com/fatih/color/LICENSE.md +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Fatih Arslan - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/fatih/color/README.md b/vendor/github.com/fatih/color/README.md deleted file mode 100644 index 623baf3..0000000 --- a/vendor/github.com/fatih/color/README.md +++ /dev/null @@ -1,177 +0,0 @@ -# Color [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/color) [![Build Status](http://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color) - - - -Color lets you use colorized outputs in terms of [ANSI Escape -Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It -has support for Windows too! The API can be used in several ways, pick one that -suits you. - - -![Color](http://i.imgur.com/c1JI0lA.png) - - -## Install - -```bash -go get github.com/fatih/color -``` - -Note that the `vendor` folder is here for stability. Remove the folder if you -already have the dependencies in your GOPATH. - -## Examples - -### Standard colors - -```go -// Print with default helper functions -color.Cyan("Prints text in cyan.") - -// A newline will be appended automatically -color.Blue("Prints %s in blue.", "text") - -// These are using the default foreground colors -color.Red("We have red") -color.Magenta("And many others ..") - -``` - -### Mix and reuse colors - -```go -// Create a new color object -c := color.New(color.FgCyan).Add(color.Underline) -c.Println("Prints cyan text with an underline.") - -// Or just add them to New() -d := color.New(color.FgCyan, color.Bold) -d.Printf("This prints bold cyan %s\n", "too!.") - -// Mix up foreground and background colors, create new mixes! -red := color.New(color.FgRed) - -boldRed := red.Add(color.Bold) -boldRed.Println("This will print text in bold red.") - -whiteBackground := red.Add(color.BgWhite) -whiteBackground.Println("Red text with white background.") -``` - -### Use your own output (io.Writer) - -```go -// Use your own io.Writer output -color.New(color.FgBlue).Fprintln(myWriter, "blue color!") - -blue := color.New(color.FgBlue) -blue.Fprint(writer, "This will print text in blue.") -``` - -### Custom print functions (PrintFunc) - -```go -// Create a custom print function for convenience -red := color.New(color.FgRed).PrintfFunc() -red("Warning") -red("Error: %s", err) - -// Mix up multiple attributes -notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() -notice("Don't forget this...") -``` - -### Custom fprint functions (FprintFunc) - -```go -blue := color.New(FgBlue).FprintfFunc() -blue(myWriter, "important notice: %s", stars) - -// Mix up with multiple attributes -success := color.New(color.Bold, color.FgGreen).FprintlnFunc() -success(myWriter, "Don't forget this...") -``` - -### Insert into noncolor strings (SprintFunc) - -```go -// Create SprintXxx functions to mix strings with other non-colorized strings: -yellow := color.New(color.FgYellow).SprintFunc() -red := color.New(color.FgRed).SprintFunc() -fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) - -info := color.New(color.FgWhite, color.BgGreen).SprintFunc() -fmt.Printf("This %s rocks!\n", info("package")) - -// Use helper functions -fmt.Println("This", color.RedString("warning"), "should be not neglected.") -fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") - -// Windows supported too! Just don't forget to change the output to color.Output -fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) -``` - -### Plug into existing code - -```go -// Use handy standard colors -color.Set(color.FgYellow) - -fmt.Println("Existing text will now be in yellow") -fmt.Printf("This one %s\n", "too") - -color.Unset() // Don't forget to unset - -// You can mix up parameters -color.Set(color.FgMagenta, color.Bold) -defer color.Unset() // Use it in your function - -fmt.Println("All text will now be bold magenta.") -``` - -### Disable color - -There might be a case where you want to disable color output (for example to -pipe the standard output of your app to somewhere else). `Color` has support to -disable colors both globally and for single color definition. For example -suppose you have a CLI app and a `--no-color` bool flag. You can easily disable -the color output with: - -```go - -var flagNoColor = flag.Bool("no-color", false, "Disable color output") - -if *flagNoColor { - color.NoColor = true // disables colorized output -} -``` - -It also has support for single color definitions (local). You can -disable/enable color output on the fly: - -```go -c := color.New(color.FgCyan) -c.Println("Prints cyan text") - -c.DisableColor() -c.Println("This is printed without any color") - -c.EnableColor() -c.Println("This prints again cyan...") -``` - -## Todo - -* Save/Return previous values -* Evaluate fmt.Formatter interface - - -## Credits - - * [Fatih Arslan](https://github.com/fatih) - * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) - -## License - -The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details - diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go deleted file mode 100644 index f76c73a..0000000 --- a/vendor/github.com/fatih/color/color.go +++ /dev/null @@ -1,526 +0,0 @@ -package color - -import ( - "fmt" - "io" - "os" - "strconv" - "strings" - "sync" - - "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" -) - -var ( - // NoColor defines if the output is colorized or not. It's dynamically set to - // false or true based on the stdout's file descriptor referring to a terminal - // or not. This is a global option and affects all colors. For more control - // over each color block use the methods DisableColor() individually. - NoColor = os.Getenv("TERM") == "dumb" || - (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) - - // Output defines the standard output of the print functions. By default - // os.Stdout is used. - Output = colorable.NewColorableStdout() - - // colorsCache is used to reduce the count of created Color objects and - // allows to reuse already created objects with required Attribute. - colorsCache = make(map[Attribute]*Color) - colorsCacheMu sync.Mutex // protects colorsCache -) - -// Color defines a custom color object which is defined by SGR parameters. -type Color struct { - params []Attribute - noColor *bool -} - -// Attribute defines a single SGR Code -type Attribute int - -const escape = "\x1b" - -// Base attributes -const ( - Reset Attribute = iota - Bold - Faint - Italic - Underline - BlinkSlow - BlinkRapid - ReverseVideo - Concealed - CrossedOut -) - -// Foreground text colors -const ( - FgBlack Attribute = iota + 30 - FgRed - FgGreen - FgYellow - FgBlue - FgMagenta - FgCyan - FgWhite -) - -// Foreground Hi-Intensity text colors -const ( - FgHiBlack Attribute = iota + 90 - FgHiRed - FgHiGreen - FgHiYellow - FgHiBlue - FgHiMagenta - FgHiCyan - FgHiWhite -) - -// Background text colors -const ( - BgBlack Attribute = iota + 40 - BgRed - BgGreen - BgYellow - BgBlue - BgMagenta - BgCyan - BgWhite -) - -// Background Hi-Intensity text colors -const ( - BgHiBlack Attribute = iota + 100 - BgHiRed - BgHiGreen - BgHiYellow - BgHiBlue - BgHiMagenta - BgHiCyan - BgHiWhite -) - -// New returns a newly created color object. -func New(value ...Attribute) *Color { - c := &Color{params: make([]Attribute, 0)} - c.Add(value...) - return c -} - -// Set sets the given parameters immediately. It will change the color of -// output with the given SGR parameters until color.Unset() is called. -func Set(p ...Attribute) *Color { - c := New(p...) - c.Set() - return c -} - -// Unset resets all escape attributes and clears the output. Usually should -// be called after Set(). -func Unset() { - if NoColor { - return - } - - fmt.Fprintf(Output, "%s[%dm", escape, Reset) -} - -// Set sets the SGR sequence. -func (c *Color) Set() *Color { - if c.isNoColorSet() { - return c - } - - fmt.Fprintf(Output, c.format()) - return c -} - -func (c *Color) unset() { - if c.isNoColorSet() { - return - } - - Unset() -} - -func (c *Color) setWriter(w io.Writer) *Color { - if c.isNoColorSet() { - return c - } - - fmt.Fprintf(w, c.format()) - return c -} - -func (c *Color) unsetWriter(w io.Writer) { - if c.isNoColorSet() { - return - } - - if NoColor { - return - } - - fmt.Fprintf(w, "%s[%dm", escape, Reset) -} - -// Add is used to chain SGR parameters. Use as many as parameters to combine -// and create custom color objects. Example: Add(color.FgRed, color.Underline). -func (c *Color) Add(value ...Attribute) *Color { - c.params = append(c.params, value...) - return c -} - -func (c *Color) prepend(value Attribute) { - c.params = append(c.params, 0) - copy(c.params[1:], c.params[0:]) - c.params[0] = value -} - -// Fprint formats using the default formats for its operands and writes to w. -// Spaces are added between operands when neither is a string. -// It returns the number of bytes written and any write error encountered. -// On Windows, users should wrap w with colorable.NewColorable() if w is of -// type *os.File. -func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { - c.setWriter(w) - defer c.unsetWriter(w) - - return fmt.Fprint(w, a...) -} - -// Print formats using the default formats for its operands and writes to -// standard output. Spaces are added between operands when neither is a -// string. It returns the number of bytes written and any write error -// encountered. This is the standard fmt.Print() method wrapped with the given -// color. -func (c *Color) Print(a ...interface{}) (n int, err error) { - c.Set() - defer c.unset() - - return fmt.Fprint(Output, a...) -} - -// Fprintf formats according to a format specifier and writes to w. -// It returns the number of bytes written and any write error encountered. -// On Windows, users should wrap w with colorable.NewColorable() if w is of -// type *os.File. -func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { - c.setWriter(w) - defer c.unsetWriter(w) - - return fmt.Fprintf(w, format, a...) -} - -// Printf formats according to a format specifier and writes to standard output. -// It returns the number of bytes written and any write error encountered. -// This is the standard fmt.Printf() method wrapped with the given color. -func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { - c.Set() - defer c.unset() - - return fmt.Fprintf(Output, format, a...) -} - -// Fprintln formats using the default formats for its operands and writes to w. -// Spaces are always added between operands and a newline is appended. -// On Windows, users should wrap w with colorable.NewColorable() if w is of -// type *os.File. -func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { - c.setWriter(w) - defer c.unsetWriter(w) - - return fmt.Fprintln(w, a...) -} - -// Println formats using the default formats for its operands and writes to -// standard output. Spaces are always added between operands and a newline is -// appended. It returns the number of bytes written and any write error -// encountered. This is the standard fmt.Print() method wrapped with the given -// color. -func (c *Color) Println(a ...interface{}) (n int, err error) { - c.Set() - defer c.unset() - - return fmt.Fprintln(Output, a...) -} - -// Sprint is just like Print, but returns a string instead of printing it. -func (c *Color) Sprint(a ...interface{}) string { - return c.wrap(fmt.Sprint(a...)) -} - -// Sprintln is just like Println, but returns a string instead of printing it. -func (c *Color) Sprintln(a ...interface{}) string { - return c.wrap(fmt.Sprintln(a...)) -} - -// Sprintf is just like Printf, but returns a string instead of printing it. -func (c *Color) Sprintf(format string, a ...interface{}) string { - return c.wrap(fmt.Sprintf(format, a...)) -} - -// FprintFunc returns a new function that prints the passed arguments as -// colorized with color.Fprint(). -func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { - return func(w io.Writer, a ...interface{}) { - c.Fprint(w, a...) - } -} - -// PrintFunc returns a new function that prints the passed arguments as -// colorized with color.Print(). -func (c *Color) PrintFunc() func(a ...interface{}) { - return func(a ...interface{}) { - c.Print(a...) - } -} - -// FprintfFunc returns a new function that prints the passed arguments as -// colorized with color.Fprintf(). -func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { - return func(w io.Writer, format string, a ...interface{}) { - c.Fprintf(w, format, a...) - } -} - -// PrintfFunc returns a new function that prints the passed arguments as -// colorized with color.Printf(). -func (c *Color) PrintfFunc() func(format string, a ...interface{}) { - return func(format string, a ...interface{}) { - c.Printf(format, a...) - } -} - -// FprintlnFunc returns a new function that prints the passed arguments as -// colorized with color.Fprintln(). -func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { - return func(w io.Writer, a ...interface{}) { - c.Fprintln(w, a...) - } -} - -// PrintlnFunc returns a new function that prints the passed arguments as -// colorized with color.Println(). -func (c *Color) PrintlnFunc() func(a ...interface{}) { - return func(a ...interface{}) { - c.Println(a...) - } -} - -// SprintFunc returns a new function that returns colorized strings for the -// given arguments with fmt.Sprint(). Useful to put into or mix into other -// string. Windows users should use this in conjunction with color.Output, example: -// -// put := New(FgYellow).SprintFunc() -// fmt.Fprintf(color.Output, "This is a %s", put("warning")) -func (c *Color) SprintFunc() func(a ...interface{}) string { - return func(a ...interface{}) string { - return c.wrap(fmt.Sprint(a...)) - } -} - -// SprintfFunc returns a new function that returns colorized strings for the -// given arguments with fmt.Sprintf(). Useful to put into or mix into other -// string. Windows users should use this in conjunction with color.Output. -func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { - return func(format string, a ...interface{}) string { - return c.wrap(fmt.Sprintf(format, a...)) - } -} - -// SprintlnFunc returns a new function that returns colorized strings for the -// given arguments with fmt.Sprintln(). Useful to put into or mix into other -// string. Windows users should use this in conjunction with color.Output. -func (c *Color) SprintlnFunc() func(a ...interface{}) string { - return func(a ...interface{}) string { - return c.wrap(fmt.Sprintln(a...)) - } -} - -// sequence returns a formated SGR sequence to be plugged into a "\x1b[...m" -// an example output might be: "1;36" -> bold cyan -func (c *Color) sequence() string { - format := make([]string, len(c.params)) - for i, v := range c.params { - format[i] = strconv.Itoa(int(v)) - } - - return strings.Join(format, ";") -} - -// wrap wraps the s string with the colors attributes. The string is ready to -// be printed. -func (c *Color) wrap(s string) string { - if c.isNoColorSet() { - return s - } - - return c.format() + s + c.unformat() -} - -func (c *Color) format() string { - return fmt.Sprintf("%s[%sm", escape, c.sequence()) -} - -func (c *Color) unformat() string { - return fmt.Sprintf("%s[%dm", escape, Reset) -} - -// DisableColor disables the color output. Useful to not change any existing -// code and still being able to output. Can be used for flags like -// "--no-color". To enable back use EnableColor() method. -func (c *Color) DisableColor() { - c.noColor = boolPtr(true) -} - -// EnableColor enables the color output. Use it in conjunction with -// DisableColor(). Otherwise this method has no side effects. -func (c *Color) EnableColor() { - c.noColor = boolPtr(false) -} - -func (c *Color) isNoColorSet() bool { - // check first if we have user setted action - if c.noColor != nil { - return *c.noColor - } - - // if not return the global option, which is disabled by default - return NoColor -} - -// Equals returns a boolean value indicating whether two colors are equal. -func (c *Color) Equals(c2 *Color) bool { - if len(c.params) != len(c2.params) { - return false - } - - for _, attr := range c.params { - if !c2.attrExists(attr) { - return false - } - } - - return true -} - -func (c *Color) attrExists(a Attribute) bool { - for _, attr := range c.params { - if attr == a { - return true - } - } - - return false -} - -func boolPtr(v bool) *bool { - return &v -} - -func getCachedColor(p Attribute) *Color { - colorsCacheMu.Lock() - defer colorsCacheMu.Unlock() - - c, ok := colorsCache[p] - if !ok { - c = New(p) - colorsCache[p] = c - } - - return c -} - -func colorPrint(format string, p Attribute, a ...interface{}) { - c := getCachedColor(p) - - if !strings.HasSuffix(format, "\n") { - format += "\n" - } - - if len(a) == 0 { - c.Print(format) - } else { - c.Printf(format, a...) - } -} - -func colorString(format string, p Attribute, a ...interface{}) string { - c := getCachedColor(p) - - if len(a) == 0 { - return c.SprintFunc()(format) - } - - return c.SprintfFunc()(format, a...) -} - -// Black is an convenient helper function to print with black foreground. A -// newline is appended to format by default. -func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } - -// Red is an convenient helper function to print with red foreground. A -// newline is appended to format by default. -func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } - -// Green is an convenient helper function to print with green foreground. A -// newline is appended to format by default. -func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } - -// Yellow is an convenient helper function to print with yellow foreground. -// A newline is appended to format by default. -func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } - -// Blue is an convenient helper function to print with blue foreground. A -// newline is appended to format by default. -func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } - -// Magenta is an convenient helper function to print with magenta foreground. -// A newline is appended to format by default. -func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } - -// Cyan is an convenient helper function to print with cyan foreground. A -// newline is appended to format by default. -func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } - -// White is an convenient helper function to print with white foreground. A -// newline is appended to format by default. -func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } - -// BlackString is an convenient helper function to return a string with black -// foreground. -func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } - -// RedString is an convenient helper function to return a string with red -// foreground. -func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } - -// GreenString is an convenient helper function to return a string with green -// foreground. -func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } - -// YellowString is an convenient helper function to return a string with yellow -// foreground. -func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } - -// BlueString is an convenient helper function to return a string with blue -// foreground. -func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } - -// MagentaString is an convenient helper function to return a string with magenta -// foreground. -func MagentaString(format string, a ...interface{}) string { - return colorString(format, FgMagenta, a...) -} - -// CyanString is an convenient helper function to return a string with cyan -// foreground. -func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } - -// WhiteString is an convenient helper function to return a string with white -// foreground. -func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } diff --git a/vendor/github.com/fatih/color/doc.go b/vendor/github.com/fatih/color/doc.go deleted file mode 100644 index 1e57812..0000000 --- a/vendor/github.com/fatih/color/doc.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Package color is an ANSI color package to output colorized or SGR defined -output to the standard output. The API can be used in several way, pick one -that suits you. - -Use simple and default helper functions with predefined foreground colors: - - color.Cyan("Prints text in cyan.") - - // a newline will be appended automatically - color.Blue("Prints %s in blue.", "text") - - // More default foreground colors.. - color.Red("We have red") - color.Yellow("Yellow color too!") - color.Magenta("And many others ..") - -However there are times where custom color mixes are required. Below are some -examples to create custom color objects and use the print functions of each -separate color object. - - // Create a new color object - c := color.New(color.FgCyan).Add(color.Underline) - c.Println("Prints cyan text with an underline.") - - // Or just add them to New() - d := color.New(color.FgCyan, color.Bold) - d.Printf("This prints bold cyan %s\n", "too!.") - - - // Mix up foreground and background colors, create new mixes! - red := color.New(color.FgRed) - - boldRed := red.Add(color.Bold) - boldRed.Println("This will print text in bold red.") - - whiteBackground := red.Add(color.BgWhite) - whiteBackground.Println("Red text with White background.") - - // Use your own io.Writer output - color.New(color.FgBlue).Fprintln(myWriter, "blue color!") - - blue := color.New(color.FgBlue) - blue.Fprint(myWriter, "This will print text in blue.") - -You can create PrintXxx functions to simplify even more: - - // Create a custom print function for convenient - red := color.New(color.FgRed).PrintfFunc() - red("warning") - red("error: %s", err) - - // Mix up multiple attributes - notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() - notice("don't forget this...") - -You can also FprintXxx functions to pass your own io.Writer: - - blue := color.New(FgBlue).FprintfFunc() - blue(myWriter, "important notice: %s", stars) - - // Mix up with multiple attributes - success := color.New(color.Bold, color.FgGreen).FprintlnFunc() - success(myWriter, don't forget this...") - - -Or create SprintXxx functions to mix strings with other non-colorized strings: - - yellow := New(FgYellow).SprintFunc() - red := New(FgRed).SprintFunc() - - fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) - - info := New(FgWhite, BgGreen).SprintFunc() - fmt.Printf("this %s rocks!\n", info("package")) - -Windows support is enabled by default. All Print functions works as intended. -However only for color.SprintXXX functions, user should use fmt.FprintXXX and -set the output to color.Output: - - fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) - - info := New(FgWhite, BgGreen).SprintFunc() - fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) - -Using with existing code is possible. Just use the Set() method to set the -standard output to the given parameters. That way a rewrite of an existing -code is not required. - - // Use handy standard colors. - color.Set(color.FgYellow) - - fmt.Println("Existing text will be now in Yellow") - fmt.Printf("This one %s\n", "too") - - color.Unset() // don't forget to unset - - // You can mix up parameters - color.Set(color.FgMagenta, color.Bold) - defer color.Unset() // use it in your function - - fmt.Println("All text will be now bold magenta.") - -There might be a case where you want to disable color output (for example to -pipe the standard output of your app to somewhere else). `Color` has support to -disable colors both globally and for single color definition. For example -suppose you have a CLI app and a `--no-color` bool flag. You can easily disable -the color output with: - - var flagNoColor = flag.Bool("no-color", false, "Disable color output") - - if *flagNoColor { - color.NoColor = true // disables colorized output - } - -It also has support for single color definitions (local). You can -disable/enable color output on the fly: - - c := color.New(color.FgCyan) - c.Println("Prints cyan text") - - c.DisableColor() - c.Println("This is printed without any color") - - c.EnableColor() - c.Println("This prints again cyan...") -*/ -package color diff --git a/vendor/vendor.json b/vendor/vendor.json index 1fd9658..16e8b96 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -152,12 +152,6 @@ "revision": "a5b389b7b05c59adf7ac44f50d0d8be6e0c85710", "revisionTime": "2017-05-04T23:50:54Z" }, - { - "checksumSHA1": "16fWSep+3jUx8wADdbiTxSHjW14=", - "path": "github.com/fatih/color", - "revision": "9131ab34cf20d2f6d83fdc67168a5430d1c7dc23", - "revisionTime": "2017-03-07T19:28:53Z" - }, { "checksumSHA1": "+IH9gXMht4fL/fxKRZ4sqGBps1g=", "path": "github.com/go-ini/ini", From 4e3797255084d1b44917443056ba0ca63714596a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 18:49:00 +0200 Subject: [PATCH 06/12] - Delete error colors fmt --- error/error.go | 49 ++++---------------------------- main.go | 8 +----- terraform/cloud/aws/session.go | 4 +-- terraform/download/download.go | 10 +++---- terraform/install/install.go | 6 ++-- terraform/list/list.go | 10 ++----- terraform/list/list_offline.go | 11 +++---- terraform/list/list_online.go | 5 ++-- terraform/uninstall/uninstall.go | 4 +-- 9 files changed, 30 insertions(+), 77 deletions(-) diff --git a/error/error.go b/error/error.go index d30c5b7..933b90e 100644 --- a/error/error.go +++ b/error/error.go @@ -1,50 +1,13 @@ package tferror -import ( - "errors" - "fmt" - - "github.com/fatih/color" -) - var ( - // Errors - info *color.Color - good *color.Color - warn *color.Color - fatal *color.Color - err error + // Errors + err error ) -func init() { - info = color.New(color.FgBlue, color.Bold) - good = color.New(color.FgGreen, color.Bold) - warn = color.New(color.FgYellow, color.Bold) - fatal = color.New(color.FgRed, color.Bold) -} - -func Run(level int, message string) { - - err = errors.New(message) - if err != nil { - switch level { - case 0: - info.Println(err) - case 1: - good.Println(err) - case 2: - warn.Println(err) - case 3: - fatal.Println(err) - default: - fmt.Println(err) - } - } -} - -func Panic(err error) { +func Panic(err error) { - if err != nil { - panic(err) - } + if err != nil { + panic(err) + } } diff --git a/main.go b/main.go index 2a85c25..bfd773d 100644 --- a/main.go +++ b/main.go @@ -4,19 +4,13 @@ import ( "fmt" "os" - "github.com/fatih/color" "github.com/mkideal/cli" ) var ( - bold *color.Color - err error + err error ) -func init() { - bold = color.New(color.FgWhite, color.Bold) -} - func main() { err = cli.Root(root, diff --git a/terraform/cloud/aws/session.go b/terraform/cloud/aws/session.go index ca5d6bf..a6b6668 100644 --- a/terraform/cloud/aws/session.go +++ b/terraform/cloud/aws/session.go @@ -30,7 +30,7 @@ func TestConnect() { _, err := ec2svc.DescribeInstances(params) if err != nil { - tferror.Run(2, "[WARN] Your AWS access is not correct") + fmt.Println("\033[1;33m[WARN] Your AWS access is not correct") if awsErr, ok := err.(awserr.Error); ok { if reqErr, ok := err.(awserr.RequestFailure); ok { @@ -46,6 +46,6 @@ func TestConnect() { } } else { - tferror.Run(1, "Your AWS access is correct") + fmt.Println("\033[1;32mYour AWS access is correct") } } diff --git a/terraform/download/download.go b/terraform/download/download.go index ba2bb2f..bbf9a56 100644 --- a/terraform/download/download.go +++ b/terraform/download/download.go @@ -67,7 +67,7 @@ func Run(version string) bool { // Verify code equal 200 if (err == nil) && (resp.StatusCode == 200) { - tferror.Run(1, "Start download ...") + fmt.Printf("\033[1;32mStart download ...\n") path_tf = fmt.Sprintf(do_path_tf, version) file_unzip, err = os.Create(path_tf) tferror.Panic(err) @@ -80,21 +80,21 @@ func Run(version string) bool { return true } else { - tferror.Run(3, "[ERROR] Download impossible, this version doesn't exist !") + fmt.Printf("\033[1;31m[ERROR] Download impossible, this version doesn't exist !\n") return false } } else { - tferror.Run(3, "[ERROR] No internet connection ...") + fmt.Printf("\033[1;31m[ERROR] No internet connection ...\n") return false } } else { - tferror.Run(3, "[ERROR] The version format is not correct ...") + fmt.Printf("\033[1;31m[ERROR] The version format is not correct ...\n") return false } } else { - tferror.Run(0, "Already in cache ...") + fmt.Printf("\033[1;34mAlready in cache ...\n") return true } } diff --git a/terraform/install/install.go b/terraform/install/install.go index 318a838..e89797e 100644 --- a/terraform/install/install.go +++ b/terraform/install/install.go @@ -37,13 +37,13 @@ func Run(version string) error { // Check if download is done and install if check { // Unzip zip archive - fmt.Println("Unzip file ...") + fmt.Printf("\033[0;37mUnzip file ...\n") tffiles.UnZip(fmt.Sprintf(pathZip, version), pathBin) - fmt.Println("Install the binary file ...") + fmt.Println("\033[0;37mInstall the binary file ...") tffiles.CreateText(version) - tferror.Run(1, fmt.Sprintf("Installed %s, Thanks ! ♥", version)) + fmt.Printf("\033[1;32mInstalled %s, Thanks ! ♥\n", version) } return err diff --git a/terraform/list/list.go b/terraform/list/list.go index 45b10f8..185aafd 100644 --- a/terraform/list/list.go +++ b/terraform/list/list.go @@ -3,21 +3,15 @@ package tflist import ( "fmt" - "github.com/fatih/color" "github.com/ryanuber/columnize" ) var ( online bool offline bool - good *color.Color err error ) -func init() { - good = color.New(color.FgGreen, color.Bold) -} - func showList(list []string, tfversion string) { var ( @@ -35,9 +29,9 @@ func showList(list []string, tfversion string) { for k <= max { if (k != max) && (len(list)-i) > 0 { if list[i] == tfversion { - newlist = newlist + good.Sprintf(list[i]) + " | " + newlist = "\033[0;37m" + newlist + fmt.Sprintf("\033[1;32m"+list[i]+"\033[0;37m") + " | " } else { - newlist = newlist + list[i] + " | " + newlist = "\033[0;37m" + newlist + list[i] + " | " } } else { if (len(list) - i) >= 0 { diff --git a/terraform/list/list_offline.go b/terraform/list/list_offline.go index 3eea06c..929193f 100644 --- a/terraform/list/list_offline.go +++ b/terraform/list/list_offline.go @@ -1,6 +1,7 @@ package tflist import ( + "fmt" "io/ioutil" "os" "os/user" @@ -49,13 +50,13 @@ func ListOff() { } if count == 0 { - tferror.Run(0, "[INFO] No local versions !") + fmt.Printf("\033[1;34m[INFO] No local versions !\n") } else { - tferror.Run(0, "[INFO] All local version:") + fmt.Printf("\033[1;34m[INFO] All local version:\n") showList(dirs, string(tversion)) } } else { - tferror.Run(2, "[WARN] No installed version yet") + fmt.Printf("\033[1;33m[WARN] No installed version yet\n") } } @@ -70,8 +71,8 @@ func Cleanup() { } if count == 0 { - tferror.Run(0, "[INFO] Nothing deleted !") + fmt.Printf("\033[1;34m[INFO] Nothing deleted !\n") } else { - tferror.Run(1, "All files are deleted !") + fmt.Printf("\033[1;32mAll files are deleted !\n") } } diff --git a/terraform/list/list_online.go b/terraform/list/list_online.go index b90dae9..4a0a8a5 100644 --- a/terraform/list/list_online.go +++ b/terraform/list/list_online.go @@ -3,6 +3,7 @@ package tflist import ( "bytes" "crypto/tls" + "fmt" "net/http" "regexp" @@ -59,7 +60,7 @@ func ListOn() { buf.ReadFrom(resp.Body) newStr := buf.String() - tferror.Run(0, "[INFO] Versions availables of terraform :") + fmt.Printf("\033[1;34m[INFO] Versions availables of terraform :\n") tfversions = r.FindAllString(newStr, -1) // Clean doublon @@ -73,6 +74,6 @@ func ListOn() { showList(cleaned, "0") } } else { - tferror.Run(3, "[ERROR] No internet connection ...") + fmt.Printf("\033[1;31m[ERROR] No internet connection ...\n") } } diff --git a/terraform/uninstall/uninstall.go b/terraform/uninstall/uninstall.go index fe88705..832ad76 100644 --- a/terraform/uninstall/uninstall.go +++ b/terraform/uninstall/uninstall.go @@ -47,9 +47,9 @@ func Run(params []string) error { } if count == 0 { - tferror.Run(0, "[INFO] Nothing deleted !") + fmt.Printf("\033[1;34m[INFO] Nothing deleted !\n") } else { - tferror.Run(1, "All files are deleted !") + fmt.Printf("\033[1;32mAll files are deleted !\n") } return nil From 0eb6465b986652d01c844fc8746b074ca586bec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 19:49:56 +0200 Subject: [PATCH 07/12] + Add command uninstall in the new CLI --- command.go | 13 +++++++++++-- terraform/uninstall/uninstall.go | 30 ++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/command.go b/command.go index 238947a..7bdad52 100644 --- a/command.go +++ b/command.go @@ -10,6 +10,7 @@ import ( "github.com/perriea/tfversion/terraform/init" "github.com/perriea/tfversion/terraform/install" "github.com/perriea/tfversion/terraform/list" + "github.com/perriea/tfversion/terraform/uninstall" ) var help = cli.HelpCommand("Display help informations") @@ -66,7 +67,8 @@ var install = &cli.Command{ // child command type uninstallT struct { cli.Helper - Version string `cli:"*v,version" usage:"uninstall version"` + All bool `cli:"a,all" usage:"Delete all version (tmp)"` + Version string `cli:"!v,version" usage:"Delete version (tmp)"` } var uninstall = &cli.Command{ @@ -79,7 +81,14 @@ var uninstall = &cli.Command{ }, Fn: func(ctx *cli.Context) error { argv := ctx.Argv().(*uninstallT) - ctx.String("Hello, child command, I am %s\n", argv.Version) + if argv.All { + tfuninstall.All() + } + + if argv.Version != "" { + tfuninstall.Uniq(argv.Version) + } + return nil }, } diff --git a/terraform/uninstall/uninstall.go b/terraform/uninstall/uninstall.go index 832ad76..924e083 100644 --- a/terraform/uninstall/uninstall.go +++ b/terraform/uninstall/uninstall.go @@ -1,7 +1,6 @@ package tfuninstall import ( - "flag" "fmt" "io/ioutil" "os" @@ -16,27 +15,38 @@ var ( all bool version string usr *user.User - clean *flag.FlagSet err error ) func init() { usr, err = user.Current() tferror.Panic(err) +} + +func Uniq(version string) error { count = 0 + files, err := ioutil.ReadDir(filepath.Join(usr.HomeDir, "/terraform/tmp/")) - clean = flag.NewFlagSet("uninstall", flag.ExitOnError) - clean.BoolVar(&all, "all", false, "Delete all versions locale.") -} + tferror.Panic(err) + for _, f := range files { + if f.Name() == fmt.Sprintf("terraform-%s.zip", version) { + err = os.Remove(filepath.Join(usr.HomeDir, "/terraform/tmp/", f.Name())) + tferror.Panic(err) + count++ + } + } -func Run(params []string) error { + if count == 0 { + fmt.Printf("\033[1;34m[INFO] Nothing deleted !\n") + } else { + fmt.Printf("\033[1;32mVersion is deleted !\n") + } - clean.Parse(params) + return nil +} - if len(params) != 1 { - return fmt.Errorf("Only one argument is accepted.") - } +func All() error { files, err := ioutil.ReadDir(filepath.Join(usr.HomeDir, "/terraform/tmp/")) tferror.Panic(err) From 4d9165b7a5882d012bff5e5d6d67968b5e016386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 20:02:12 +0200 Subject: [PATCH 08/12] + Update Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index cf6a17c..237ae57 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.8.1-alpine +FROM golang:1.8.3-alpine ENV tfversion_path /go/src/github.com/perriea/tfversion/ ENV terraform_path /root/terraform/bin @@ -23,4 +23,4 @@ VOLUME ['/root/.aws', '/root/.ssh'] WORKDIR /root/repo -RUN tfversion install 0.9.4 +RUN tfversion install --version 0.9.6 From 9ac0a9743e5f23bd01bb5388d0fbd244bdf999bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 20:22:17 +0200 Subject: [PATCH 09/12] ~ Modify gitignore and change readme --- .gitignore | 1 + README.md | 33 ++++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 666d898..8460d84 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ _testmain.go *.test *.prof +.vscode/settings.json tfversion* diff --git a/README.md b/README.md index dd79f8e..17d0906 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,32 @@ Functional for all versions. ## Build Project - [Install Golang](https://golang.org/doc/install) (add var ENV), -- [Install Govendor](https://github.com/kardianos/govendor), -- Build `go build .`, +- Build `go build` or `go get github.com/perriea/tfversion`, - Add in your `.bashrc` (Linux) or `.bash_profile` : `export PATH=$PATH:~/terraform/bin` +## Commands + +``` shell +➜ ~ tfversion help +tfversion v0.1.2 + +Usage: + + tfversion [option] + +Options: + + -v, --version Show version and check update + +Commands: + + help Display help informations + install Install new versions or switch. + uninstall Uninstall local version of Terraform + list List online or offline version of terraform + test Test provider cloud (AWS) +``` + ## Docker ### Require @@ -23,16 +45,9 @@ Functional for all versions. Pull image `docker pull perriea/tfversion`. Execute command in the terminal : `docker run -it -v ~/.aws:/root/.aws -v ~/.ssh:/root/.ssh perriea/tfversion sh`. -## Roadmap - -- Install script. - -And other things ... - ## Dependancies - [kardianos/govendor](https://github.com/kardianos/govendor), -- [fatih/color](https://github.com/fatih/color), - [google/go-github](https://github.com/google/go-github), - [aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) (modules: Session, EC2, AWSErr). From 1fc8fc774d2ce14bc4b3c485f28e8630af396737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 23:40:16 +0200 Subject: [PATCH 10/12] + Add in new flags autohelper --- command.go | 39 ++++++++++++++++++++++++++------------- main.go | 7 ++----- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/command.go b/command.go index 7bdad52..d55b648 100644 --- a/command.go +++ b/command.go @@ -13,12 +13,13 @@ import ( "github.com/perriea/tfversion/terraform/uninstall" ) -var help = cli.HelpCommand("Display help informations") +var help = cli.HelpCommand("display help informations") var tfversion = "0.1.2" // root command type rootT struct { - Version bool `cli:"v,version" usage:"Show version and check update"` + cli.Helper + Version bool `cli:"v,version" usage:"show version and check update"` } var root = &cli.Command{ @@ -30,28 +31,36 @@ var root = &cli.Command{ }, Fn: func(ctx *cli.Context) error { argv := ctx.Argv().(*rootT) + + // AutoHelper + if argv.Help || len(os.Args) == 1 { + ctx.WriteUsage() + return nil + } + if argv.Version { - fmt.Fprintf(os.Stderr, "tfversion v%s\n\n", tfversion) + fmt.Printf("tfversion v%s\n\n", tfversion) // Show if the last version lastrelease, release := tfgithub.Lastversion(tfversion) if !lastrelease && release != nil { fmt.Printf("Your version is out of date ! The latest version is %s. You can update by downloading from Github (%s).", *release.TagName, *release.HTMLURL) } + return nil } return nil }, } -// child command +// install command type installT struct { cli.Helper - Version string `cli:"*version" usage:"Install or switch version"` + Version string `cli:"*version" usage:"install or switch version"` } var install = &cli.Command{ Name: "install", - Desc: "Install new versions or switch.", + Desc: "install new versions or switch.", Argv: func() interface{} { return new(installT) }, OnBefore: func(ctx *cli.Context) error { tfinit.CreateTree() @@ -59,21 +68,22 @@ var install = &cli.Command{ }, Fn: func(ctx *cli.Context) error { argv := ctx.Argv().(*installT) + tfinstall.Run(argv.Version) return nil }, } -// child command +// uninstall command type uninstallT struct { cli.Helper - All bool `cli:"a,all" usage:"Delete all version (tmp)"` + All bool `cli:"a,all" usage:"delete all version (tmp)"` Version string `cli:"!v,version" usage:"Delete version (tmp)"` } var uninstall = &cli.Command{ Name: "uninstall", - Desc: "Uninstall local version of Terraform", + Desc: "uninstall local version of Terraform", Argv: func() interface{} { return new(uninstallT) }, OnBefore: func(ctx *cli.Context) error { tfinit.CreateTree() @@ -81,6 +91,7 @@ var uninstall = &cli.Command{ }, Fn: func(ctx *cli.Context) error { argv := ctx.Argv().(*uninstallT) + if argv.All { tfuninstall.All() } @@ -93,7 +104,7 @@ var uninstall = &cli.Command{ }, } -// child command +// list command type listT struct { cli.Helper On bool `cli:"!on,online" usage:"list online version"` @@ -102,7 +113,7 @@ type listT struct { var list = &cli.Command{ Name: "list", - Desc: "List online or offline version of terraform", + Desc: "list online or offline version of terraform", Argv: func() interface{} { return new(listT) }, OnBefore: func(ctx *cli.Context) error { tfinit.CreateTree() @@ -110,6 +121,7 @@ var list = &cli.Command{ }, Fn: func(ctx *cli.Context) error { argv := ctx.Argv().(*listT) + if argv.On { tflist.ListOn() } else { @@ -119,7 +131,7 @@ var list = &cli.Command{ }, } -// child command +// test command type testT struct { cli.Helper Aws bool `cli:"*aws,amazon" usage:"test connection to AWS"` @@ -128,10 +140,11 @@ type testT struct { var test = &cli.Command{ Name: "test", - Desc: "Test provider cloud (AWS)", + Desc: "test provider cloud (AWS)", Argv: func() interface{} { return new(testT) }, Fn: func(ctx *cli.Context) error { argv := ctx.Argv().(*testT) + if argv.Aws { tfaws.TestConnect() } diff --git a/main.go b/main.go index bfd773d..97efb1a 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,10 @@ package main import ( - "fmt" "os" "github.com/mkideal/cli" + "github.com/perriea/tfversion/error" ) var ( @@ -21,8 +21,5 @@ func main() { cli.Tree(test), ).Run(os.Args[1:]) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } + tferror.Panic(err) } From a632fbebe17b5ece8ff170d1e7d7bfbe7a864cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sat, 10 Jun 2017 23:58:58 +0200 Subject: [PATCH 11/12] + Add in command install option -v --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index d55b648..4e1bb8c 100644 --- a/command.go +++ b/command.go @@ -55,7 +55,7 @@ var root = &cli.Command{ // install command type installT struct { cli.Helper - Version string `cli:"*version" usage:"install or switch version"` + Version string `cli:"*v,version" usage:"install or switch version"` } var install = &cli.Command{ From 9e5e2cb78b03727e4e360f13d411adc631590699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Perrier?= Date: Sun, 11 Jun 2017 00:01:18 +0200 Subject: [PATCH 12/12] + Add commentaries and error gestion --- error/error.go | 1 + main.go | 7 +++++-- terraform/init/init.go | 1 + terraform/uninstall/uninstall.go | 5 ++++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/error/error.go b/error/error.go index 933b90e..aac368c 100644 --- a/error/error.go +++ b/error/error.go @@ -5,6 +5,7 @@ var ( err error ) +// Panic : Show fatal errors func Panic(err error) { if err != nil { diff --git a/main.go b/main.go index 97efb1a..bfd773d 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,10 @@ package main import ( + "fmt" "os" "github.com/mkideal/cli" - "github.com/perriea/tfversion/error" ) var ( @@ -21,5 +21,8 @@ func main() { cli.Tree(test), ).Run(os.Args[1:]) - tferror.Panic(err) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } } diff --git a/terraform/init/init.go b/terraform/init/init.go index c97459e..6634706 100644 --- a/terraform/init/init.go +++ b/terraform/init/init.go @@ -18,6 +18,7 @@ func init() { tferror.Panic(err) } +// CreateTree : Create folders (init) func CreateTree() { tffolder.CreateFolder(filepath.Join(usr.HomeDir, "/terraform"), 0755) tffolder.CreateFolder(filepath.Join(usr.HomeDir, "/terraform/tmp"), 0755) diff --git a/terraform/uninstall/uninstall.go b/terraform/uninstall/uninstall.go index 924e083..d897a49 100644 --- a/terraform/uninstall/uninstall.go +++ b/terraform/uninstall/uninstall.go @@ -23,6 +23,7 @@ func init() { tferror.Panic(err) } +// Uniq : Delete one version func Uniq(version string) error { count = 0 @@ -40,16 +41,18 @@ func Uniq(version string) error { if count == 0 { fmt.Printf("\033[1;34m[INFO] Nothing deleted !\n") } else { - fmt.Printf("\033[1;32mVersion is deleted !\n") + fmt.Printf("\033[1;32mVersion %s is deleted !\n", version) } return nil } +// All : Delete all cache func All() error { files, err := ioutil.ReadDir(filepath.Join(usr.HomeDir, "/terraform/tmp/")) tferror.Panic(err) + for _, f := range files { err = os.Remove(filepath.Join(usr.HomeDir, "/terraform/tmp/", f.Name())) tferror.Panic(err)