| // Copyright © 2013 Steve Francia <spf@spf13.com>. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Commands similar to git, go tools and other modern CLI tools |
| // inspired by go, go-Commander, gh and subcommand |
| |
| package cobra |
| |
| import ( |
| "fmt" |
| "io" |
| "reflect" |
| "strconv" |
| "strings" |
| "text/template" |
| "unicode" |
| ) |
| |
| var templateFuncs = template.FuncMap{ |
| "trim": strings.TrimSpace, |
| "trimRightSpace": trimRightSpace, |
| "trimTrailingWhitespaces": trimRightSpace, |
| "appendIfNotPresent": appendIfNotPresent, |
| "rpad": rpad, |
| "gt": Gt, |
| "eq": Eq, |
| } |
| |
| var initializers []func() |
| |
| // EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing |
| // to automatically enable in CLI tools. |
| // Set this to true to enable it. |
| var EnablePrefixMatching = false |
| |
| // EnableCommandSorting controls sorting of the slice of commands, which is turned on by default. |
| // To disable sorting, set it to false. |
| var EnableCommandSorting = true |
| |
| // MousetrapHelpText enables an information splash screen on Windows |
| // if the CLI is started from explorer.exe. |
| // To disable the mousetrap, just set this variable to blank string (""). |
| // Works only on Microsoft Windows. |
| var MousetrapHelpText string = `This is a command line tool. |
| |
| You need to open cmd.exe and run it from there. |
| ` |
| |
| // AddTemplateFunc adds a template function that's available to Usage and Help |
| // template generation. |
| func AddTemplateFunc(name string, tmplFunc interface{}) { |
| templateFuncs[name] = tmplFunc |
| } |
| |
| // AddTemplateFuncs adds multiple template functions that are available to Usage and |
| // Help template generation. |
| func AddTemplateFuncs(tmplFuncs template.FuncMap) { |
| for k, v := range tmplFuncs { |
| templateFuncs[k] = v |
| } |
| } |
| |
| // OnInitialize sets the passed functions to be run when each command's |
| // Execute method is called. |
| func OnInitialize(y ...func()) { |
| initializers = append(initializers, y...) |
| } |
| |
| // FIXME Gt is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra. |
| |
| // Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans, |
| // Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as |
| // ints and then compared. |
| func Gt(a interface{}, b interface{}) bool { |
| var left, right int64 |
| av := reflect.ValueOf(a) |
| |
| switch av.Kind() { |
| case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: |
| left = int64(av.Len()) |
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| left = av.Int() |
| case reflect.String: |
| left, _ = strconv.ParseInt(av.String(), 10, 64) |
| } |
| |
| bv := reflect.ValueOf(b) |
| |
| switch bv.Kind() { |
| case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: |
| right = int64(bv.Len()) |
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| right = bv.Int() |
| case reflect.String: |
| right, _ = strconv.ParseInt(bv.String(), 10, 64) |
| } |
| |
| return left > right |
| } |
| |
| // FIXME Eq is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra. |
| |
| // Eq takes two types and checks whether they are equal. Supported types are int and string. Unsupported types will panic. |
| func Eq(a interface{}, b interface{}) bool { |
| av := reflect.ValueOf(a) |
| bv := reflect.ValueOf(b) |
| |
| switch av.Kind() { |
| case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: |
| panic("Eq called on unsupported type") |
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| return av.Int() == bv.Int() |
| case reflect.String: |
| return av.String() == bv.String() |
| } |
| return false |
| } |
| |
| func trimRightSpace(s string) string { |
| return strings.TrimRightFunc(s, unicode.IsSpace) |
| } |
| |
| // FIXME appendIfNotPresent is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra. |
| |
| // appendIfNotPresent will append stringToAppend to the end of s, but only if it's not yet present in s. |
| func appendIfNotPresent(s, stringToAppend string) string { |
| if strings.Contains(s, stringToAppend) { |
| return s |
| } |
| return s + " " + stringToAppend |
| } |
| |
| // rpad adds padding to the right of a string. |
| func rpad(s string, padding int) string { |
| template := fmt.Sprintf("%%-%ds", padding) |
| return fmt.Sprintf(template, s) |
| } |
| |
| // tmpl executes the given template text on data, writing the result to w. |
| func tmpl(w io.Writer, text string, data interface{}) error { |
| t := template.New("top") |
| t.Funcs(templateFuncs) |
| template.Must(t.Parse(text)) |
| return t.Execute(w, data) |
| } |
| |
| // ld compares two strings and returns the levenshtein distance between them. |
| func ld(s, t string, ignoreCase bool) int { |
| if ignoreCase { |
| s = strings.ToLower(s) |
| t = strings.ToLower(t) |
| } |
| d := make([][]int, len(s)+1) |
| for i := range d { |
| d[i] = make([]int, len(t)+1) |
| } |
| for i := range d { |
| d[i][0] = i |
| } |
| for j := range d[0] { |
| d[0][j] = j |
| } |
| for j := 1; j <= len(t); j++ { |
| for i := 1; i <= len(s); i++ { |
| if s[i-1] == t[j-1] { |
| d[i][j] = d[i-1][j-1] |
| } else { |
| min := d[i-1][j] |
| if d[i][j-1] < min { |
| min = d[i][j-1] |
| } |
| if d[i-1][j-1] < min { |
| min = d[i-1][j-1] |
| } |
| d[i][j] = min + 1 |
| } |
| } |
| |
| } |
| return d[len(s)][len(t)] |
| } |
| |
| func stringInSlice(a string, list []string) bool { |
| for _, b := range list { |
| if b == a { |
| return true |
| } |
| } |
| return false |
| } |