| // 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 = template.FuncMap{ |
| "trim": strings.TrimSpace, |
| "trimRightSpace": trimRightSpace, |
| "rpad": rpad, |
| "gt": Gt, |
| "eq": Eq, |
| } |
| |
| var initializers []func() |
| |
| // automatic prefix matching can be a dangerous thing to automatically enable in CLI tools. |
| // Set this to true to enable it |
| var EnablePrefixMatching bool = false |
| |
| //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 availalble to Usage and |
| //Help template generation. |
| func AddTemplateFuncs(tmplFuncs template.FuncMap) { |
| for k, v := range tmplFuncs { |
| templateFuncs[k] = v |
| } |
| } |
| |
| //OnInitialize takes a series of func() arguments and appends them to a slice of func(). |
| func OnInitialize(y ...func()) { |
| for _, x := range y { |
| initializers = append(initializers, x) |
| } |
| } |
| |
| //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 |
| } |
| |
| //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) |
| } |
| |
| //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)] |
| } |