2023-03-06 02:28:31 +00:00
|
|
|
// Copyright 2013-2023 The Cobra Authors
|
2013-09-03 18:54:51 -04:00
|
|
|
//
|
|
|
|
// 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
|
2022-09-16 13:55:56 +02:00
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2013-09-03 18:54:51 -04:00
|
|
|
//
|
|
|
|
// 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 (
|
2014-01-17 13:36:57 +01:00
|
|
|
"fmt"
|
2013-09-11 09:51:24 -04:00
|
|
|
"io"
|
2021-02-08 00:08:50 +00:00
|
|
|
"os"
|
2017-05-09 15:07:05 -04:00
|
|
|
"reflect"
|
|
|
|
"strconv"
|
2013-09-03 18:54:51 -04:00
|
|
|
"strings"
|
2013-09-12 10:32:51 -04:00
|
|
|
"text/template"
|
2019-03-21 01:05:52 +01:00
|
|
|
"time"
|
2015-09-11 18:44:03 -04:00
|
|
|
"unicode"
|
2013-09-03 18:54:51 -04:00
|
|
|
)
|
|
|
|
|
2016-03-31 09:53:34 -04:00
|
|
|
var templateFuncs = template.FuncMap{
|
2017-05-09 15:07:05 -04:00
|
|
|
"trim": strings.TrimSpace,
|
|
|
|
"trimRightSpace": trimRightSpace,
|
|
|
|
"trimTrailingWhitespaces": trimRightSpace,
|
|
|
|
"appendIfNotPresent": appendIfNotPresent,
|
|
|
|
"rpad": rpad,
|
|
|
|
"gt": Gt,
|
|
|
|
"eq": Eq,
|
2015-08-31 22:36:55 -05:00
|
|
|
}
|
|
|
|
|
2014-07-11 10:57:53 -04:00
|
|
|
var initializers []func()
|
2022-10-03 16:52:50 +02:00
|
|
|
var finalizers []func()
|
2014-06-27 12:27:46 -04:00
|
|
|
|
2022-09-11 15:25:22 +03:00
|
|
|
const (
|
2023-10-22 03:36:12 +03:00
|
|
|
defaultPrefixMatching = false
|
|
|
|
defaultCommandSorting = true
|
|
|
|
defaultCaseInsensitive = false
|
|
|
|
defaultTraverseRunHooks = false
|
2022-09-11 15:25:22 +03:00
|
|
|
)
|
|
|
|
|
2023-06-13 18:12:49 +03:00
|
|
|
// EnablePrefixMatching allows setting automatic prefix matching. Automatic prefix matching can be a dangerous thing
|
2017-01-24 11:30:45 -05:00
|
|
|
// to automatically enable in CLI tools.
|
2016-08-30 22:14:27 +05:00
|
|
|
// Set this to true to enable it.
|
2022-09-11 15:25:22 +03:00
|
|
|
var EnablePrefixMatching = defaultPrefixMatching
|
2014-10-07 16:15:19 -04:00
|
|
|
|
2016-08-30 22:14:27 +05:00
|
|
|
// EnableCommandSorting controls sorting of the slice of commands, which is turned on by default.
|
|
|
|
// To disable sorting, set it to false.
|
2022-09-11 15:25:22 +03:00
|
|
|
var EnableCommandSorting = defaultCommandSorting
|
|
|
|
|
|
|
|
// EnableCaseInsensitive allows case-insensitive commands names. (case sensitive by default)
|
|
|
|
var EnableCaseInsensitive = defaultCaseInsensitive
|
2016-06-14 17:04:53 +03:00
|
|
|
|
2023-10-22 03:36:12 +03:00
|
|
|
// EnableTraverseRunHooks executes persistent pre-run and post-run hooks from all parents.
|
|
|
|
// By default this is disabled, which means only the first run hook to be found is executed.
|
|
|
|
var EnableTraverseRunHooks = defaultTraverseRunHooks
|
|
|
|
|
2017-07-10 21:27:14 +02:00
|
|
|
// 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.
|
2019-07-30 15:26:11 -07:00
|
|
|
var MousetrapHelpText = `This is a command line tool.
|
2017-07-10 21:27:14 +02:00
|
|
|
|
|
|
|
You need to open cmd.exe and run it from there.
|
|
|
|
`
|
|
|
|
|
2019-03-21 01:05:52 +01:00
|
|
|
// MousetrapDisplayDuration controls how long the MousetrapHelpText message is displayed on Windows
|
|
|
|
// if the CLI is started from explorer.exe. Set to 0 to wait for the return key to be pressed.
|
|
|
|
// To disable the mousetrap, just set MousetrapHelpText to blank string ("").
|
|
|
|
// Works only on Microsoft Windows.
|
2019-07-30 15:26:11 -07:00
|
|
|
var MousetrapDisplayDuration = 5 * time.Second
|
2019-03-21 01:05:52 +01:00
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// AddTemplateFunc registers a new template function with the given name, making it available for use in the Usage and Help templates.
|
2015-08-31 22:36:55 -05:00
|
|
|
func AddTemplateFunc(name string, tmplFunc interface{}) {
|
|
|
|
templateFuncs[name] = tmplFunc
|
|
|
|
}
|
|
|
|
|
2017-04-17 10:03:07 -07:00
|
|
|
// AddTemplateFuncs adds multiple template functions that are available to Usage and
|
2025-04-26 20:14:27 +00:00
|
|
|
// Help template generation. It takes a map of template function names to their implementations
|
|
|
|
// and merges them into the global template function map, allowing these functions to be used
|
|
|
|
// in usage and help text templates.
|
2015-08-31 22:36:55 -05:00
|
|
|
func AddTemplateFuncs(tmplFuncs template.FuncMap) {
|
|
|
|
for k, v := range tmplFuncs {
|
|
|
|
templateFuncs[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// OnInitialize registers functions that will be executed whenever a command's
|
|
|
|
// Execute method is invoked. These functions are typically used for setup or initialization tasks.
|
2014-07-11 10:57:53 -04:00
|
|
|
func OnInitialize(y ...func()) {
|
2016-08-30 21:58:52 +05:00
|
|
|
initializers = append(initializers, y...)
|
2014-06-27 12:27:46 -04:00
|
|
|
}
|
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// OnFinalize sets the provided functions to be executed when each command's
|
|
|
|
// Execute method is completed. The functions are called in the order they are provided.
|
2022-10-03 16:52:50 +02:00
|
|
|
func OnFinalize(y ...func()) {
|
|
|
|
finalizers = append(finalizers, y...)
|
|
|
|
}
|
|
|
|
|
2017-05-09 15:07:05 -04:00
|
|
|
// FIXME Gt is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra.
|
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// Gt compares two types and returns true if the first type is greater than the second. For arrays, channels,
|
|
|
|
// maps, and slices, it compares their lengths. Ints are compared directly, while strings are first converted to ints
|
|
|
|
// before comparison. If either conversion fails (e.g., string is not a valid integer), Gt will return false.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// a - the first value to compare.
|
|
|
|
// b - the second value to compare.
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// true if a is greater than b, false otherwise.
|
|
|
|
//
|
|
|
|
// Errors:
|
|
|
|
// None. The function handles invalid string conversions internally and returns false in such cases.
|
2017-05-09 15:07:05 -04:00
|
|
|
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.
|
2025-04-26 20:14:27 +00:00
|
|
|
// Parameters:
|
|
|
|
// a - the first value to compare
|
|
|
|
// b - the second value to compare
|
|
|
|
// Returns:
|
|
|
|
// true if a and b are equal, false otherwise
|
|
|
|
// Panics:
|
|
|
|
// if a or b is of an unsupported type (array, chan, map, slice)
|
2017-05-09 15:07:05 -04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// TrimRightSpace returns a copy of the input string with trailing white space removed.
|
2017-05-09 15:07:05 -04:00
|
|
|
func trimRightSpace(s string) string {
|
2015-09-11 18:44:03 -04:00
|
|
|
return strings.TrimRightFunc(s, unicode.IsSpace)
|
|
|
|
}
|
|
|
|
|
2017-05-09 15:07:05 -04:00
|
|
|
// FIXME appendIfNotPresent is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra.
|
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// AppendIfNotPresent appends a string to the end of the slice if it's not already present.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// - s: The slice to append to.
|
|
|
|
// - stringToAppend: The string to be appended.
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// The updated slice with the string appended, or the original slice unchanged if the string was already present.
|
2017-05-09 15:07:05 -04:00
|
|
|
func appendIfNotPresent(s, stringToAppend string) string {
|
|
|
|
if strings.Contains(s, stringToAppend) {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
return s + " " + stringToAppend
|
|
|
|
}
|
|
|
|
|
2016-08-30 22:14:27 +05:00
|
|
|
// rpad adds padding to the right of a string.
|
2025-04-26 20:14:27 +00:00
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// - s: The input string to pad.
|
|
|
|
// - padding: The number of spaces to add as padding on the right.
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// - The formatted string with right-padding applied.
|
2014-01-17 13:36:57 +01:00
|
|
|
func rpad(s string, padding int) string {
|
2022-12-15 16:09:50 +05:30
|
|
|
formattedString := fmt.Sprintf("%%-%ds", padding)
|
|
|
|
return fmt.Sprintf(formattedString, s)
|
2014-01-17 13:36:57 +01:00
|
|
|
}
|
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// tmpl returns a new tmplFunc instance with the provided text.
|
Allow linker to perform deadcode elimination for program using Cobra (#1956)
* Restructure code to let linker perform deadcode elimination step
Cobra, in its default configuration, will execute a template to generate
help, usage and version outputs. Text/template execution calls MethodByName
and MethodByName disables dead code elimination in the Go linker, therefore
all programs that make use of cobra will be linked with dead code
elimination disabled, even if they end up replacing the default usage, help
and version formatters with a custom function and no actual text/template
evaluations are ever made at runtime.
Dead code elimination in the linker helps reduce disk space and memory
utilization of programs. For example, for the simple example program used by
TestDeadcodeElimination 40% of the final executable size is dead code. For a
more realistic example, 12% of the size of Delve's executable is deadcode.
This PR changes Cobra so that, in its default configuration, it does not
automatically inhibit deadcode elimination by:
1. changing Cobra's default behavior to emit output for usage and help using
simple Go functions instead of template execution
2. quarantining all calls to template execution into SetUsageTemplate,
SetHelpTemplate and SetVersionTemplate so that the linker can statically
determine if they are reachable
Co-authored-by: Marc Khouzam <marc.khouzam@gmail.com>
2025-01-27 15:43:43 +01:00
|
|
|
func tmpl(text string) *tmplFunc {
|
|
|
|
return &tmplFunc{
|
|
|
|
tmpl: text,
|
|
|
|
fn: func(w io.Writer, data interface{}) error {
|
|
|
|
t := template.New("top")
|
|
|
|
t.Funcs(templateFuncs)
|
|
|
|
template.Must(t.Parse(text))
|
|
|
|
return t.Execute(w, data)
|
|
|
|
},
|
|
|
|
}
|
2013-09-12 10:32:51 -04:00
|
|
|
}
|
2015-09-11 17:04:58 -03:00
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// ld calculates the Levenshtein distance between two strings, optionally ignoring case.
|
|
|
|
// It returns the minimum number of single-character edits required to change one word into the other.
|
|
|
|
// Parameters:
|
|
|
|
// s - the first string
|
|
|
|
// t - the second string
|
|
|
|
// ignoreCase - if true, the comparison is case-insensitive
|
|
|
|
// Returns:
|
|
|
|
// The Levenshtein distance between the two strings.
|
2015-09-11 17:04:58 -03:00
|
|
|
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)
|
|
|
|
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)]
|
|
|
|
}
|
2017-10-31 19:58:37 +01:00
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// stringInSlice checks if a string is present in the given slice of strings.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// - a: The string to search for.
|
|
|
|
// - list: The slice of strings to search within.
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// - true if the string is found in the slice, false otherwise.
|
2017-10-31 19:58:37 +01:00
|
|
|
func stringInSlice(a string, list []string) bool {
|
|
|
|
for _, b := range list {
|
|
|
|
if b == a {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2021-02-08 00:08:50 +00:00
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// CheckErr prints the provided message with the prefix 'Error:' and exits with an error code of 1. If the message is `nil`, it does nothing.
|
2021-02-08 00:08:50 +00:00
|
|
|
func CheckErr(msg interface{}) {
|
|
|
|
if msg != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, "Error:", msg)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-26 20:14:27 +00:00
|
|
|
// WriteStringAndCheck writes a string into a buffer and checks if the error is not nil.
|
|
|
|
// It takes an io.StringWriter `b` and a string `s`, writes the string to the writer,
|
|
|
|
// and calls CheckErr with the resulting error, handling any errors that occur during the write operation.
|
2021-02-08 00:08:50 +00:00
|
|
|
func WriteStringAndCheck(b io.StringWriter, s string) {
|
|
|
|
_, err := b.WriteString(s)
|
|
|
|
CheckErr(err)
|
|
|
|
}
|