github.com/jfrog/jfrog-cli-core/v2@v2.52.0/utils/coreutils/cmdutils.go (about)

     1  package coreutils
     2  
     3  import (
     4  	"github.com/forPelevin/gomoji"
     5  	"github.com/jfrog/jfrog-client-go/utils/log"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/gookit/color"
    10  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    11  )
    12  
    13  // Removes the provided flag and value from the command arguments
    14  func RemoveFlagFromCommand(args *[]string, flagIndex, flagValueIndex int) {
    15  	argsCopy := *args
    16  	// Remove flag from Command if required.
    17  	if flagIndex != -1 {
    18  		argsCopy = append(argsCopy[:flagIndex], argsCopy[flagValueIndex+1:]...)
    19  	}
    20  	*args = argsCopy
    21  }
    22  
    23  // Find value of required CLI flag in Command.
    24  // If flag does not exist, the returned index is -1 and nil is returned as the error.
    25  // Return values:
    26  // err - error if flag exists but failed to extract its value.
    27  // flagIndex - index of flagName in Command.
    28  // flagValueIndex - index in Command in which the value of the flag exists.
    29  // flagValue - value of flagName.
    30  func FindFlag(flagName string, args []string) (flagIndex, flagValueIndex int, flagValue string, err error) {
    31  	flagIndex = -1
    32  	flagValueIndex = -1
    33  	for index, arg := range args {
    34  		// Check current argument.
    35  		if !strings.HasPrefix(arg, flagName) {
    36  			continue
    37  		}
    38  
    39  		// Get flag value.
    40  		flagValue, flagValueIndex, err = getFlagValueAndValueIndex(flagName, args, index)
    41  		if err != nil {
    42  			return
    43  		}
    44  
    45  		// If was not the correct flag, continue looking.
    46  		if flagValueIndex == -1 {
    47  			continue
    48  		}
    49  
    50  		// Return value.
    51  		flagIndex = index
    52  		return
    53  	}
    54  
    55  	// Flag not found.
    56  	return
    57  }
    58  
    59  // Get the provided flag's value, and the index of the value.
    60  // Value-index can either be same as flag's index, or the next one.
    61  // Return error if flag is found, but couldn't extract value.
    62  // If the provided index doesn't contain the searched flag, return flagIndex = -1.
    63  func getFlagValueAndValueIndex(flagName string, args []string, flagIndex int) (flagValue string, flagValueIndex int, err error) {
    64  	indexValue := args[flagIndex]
    65  
    66  	// Check if flag is in form '--key=value'
    67  	indexValue = strings.TrimPrefix(indexValue, flagName)
    68  	if strings.HasPrefix(indexValue, "=") {
    69  		if len(indexValue) > 1 {
    70  			return indexValue[1:], flagIndex, nil
    71  		}
    72  		return "", -1, errorutils.CheckErrorf("Flag %s is provided with empty value.", flagName)
    73  	}
    74  
    75  	// Check if it is a different flag with same prefix, e.g --server-id-another
    76  	if len(indexValue) > 0 {
    77  		return "", -1, nil
    78  	}
    79  
    80  	// If reached here, expect the flag value in next argument.
    81  	if len(args) < flagIndex+2 {
    82  		// Flag value does not exist.
    83  		return "", -1, errorutils.CheckErrorf("Failed extracting value of provided flag: %s.", flagName)
    84  	}
    85  
    86  	nextIndexValue := args[flagIndex+1]
    87  	// Don't allow next value to be a flag.
    88  	if strings.HasPrefix(nextIndexValue, "-") {
    89  		// Flag value does not exist.
    90  		return "", -1, errorutils.CheckErrorf("Failed extracting value of provided flag: %s.", flagName)
    91  	}
    92  
    93  	return nextIndexValue, flagIndex + 1, nil
    94  }
    95  
    96  // Boolean flag can be provided in one of the following forms:
    97  // 1. --flag=value, where value can be true/false
    98  // 2. --flag, here the value is true
    99  // Return values:
   100  // flagIndex - index of flagName in args.
   101  // flagValue - value of flagName.
   102  // err - error if flag exists, but we failed to extract its value.
   103  // If flag does not exist flagIndex = -1 with false value and nil error.
   104  func FindBooleanFlag(flagName string, args []string) (flagIndex int, flagValue bool, err error) {
   105  	var arg string
   106  	for flagIndex, arg = range args {
   107  		if strings.HasPrefix(arg, flagName) {
   108  			value := strings.TrimPrefix(arg, flagName)
   109  			switch {
   110  			case len(value) == 0:
   111  				flagValue = true
   112  			case strings.HasPrefix(value, "="):
   113  				flagValue, err = strconv.ParseBool(value[1:])
   114  			default:
   115  				continue
   116  			}
   117  			return
   118  		}
   119  	}
   120  	return -1, false, nil
   121  }
   122  
   123  // Find the first match of the provided flags in args.
   124  // Return same values as FindFlag.
   125  func FindFlagFirstMatch(flags, args []string) (flagIndex, flagValueIndex int, flagValue string, err error) {
   126  	// Look for provided flags.
   127  	for _, flag := range flags {
   128  		flagIndex, flagValueIndex, flagValue, err = FindFlag(flag, args)
   129  		if err != nil {
   130  			return
   131  		}
   132  		if flagIndex != -1 {
   133  			// Found value for flag.
   134  			return
   135  		}
   136  	}
   137  	return
   138  }
   139  
   140  func ExtractServerIdFromCommand(args []string) (cleanArgs []string, serverId string, err error) {
   141  	return extractStringOptionFromArgs(args, "server-id")
   142  }
   143  
   144  func ExtractThreadsFromArgs(args []string, defaultValue int) (cleanArgs []string, threads int, err error) {
   145  	cleanArgs = append([]string(nil), args...)
   146  	threads = defaultValue
   147  	// Extract threads information from the args.
   148  	flagIndex, valueIndex, numOfThreads, err := FindFlag("--threads", cleanArgs)
   149  	if err != nil {
   150  		return
   151  	}
   152  
   153  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valueIndex)
   154  	if numOfThreads != "" {
   155  		threads, err = strconv.Atoi(numOfThreads)
   156  		if err != nil {
   157  			err = errorutils.CheckError(err)
   158  		}
   159  	}
   160  	return
   161  }
   162  
   163  func ExtractInsecureTlsFromArgs(args []string) (cleanArgs []string, insecureTls bool, err error) {
   164  	return extractBoolOptionFromArgs(args, "insecure-tls")
   165  }
   166  
   167  // Used by docker
   168  func ExtractSkipLoginFromArgs(args []string) (cleanArgs []string, skipLogin bool, err error) {
   169  	return extractBoolOptionFromArgs(args, "skip-login")
   170  }
   171  
   172  // Used by docker
   173  func ExtractFailFromArgs(args []string) (cleanArgs []string, fail bool, err error) {
   174  	return extractBoolOptionFromArgs(args, "fail")
   175  }
   176  
   177  // Used by docker  scan (Xray)
   178  func ExtractLicensesFromArgs(args []string) (cleanArgs []string, licenses bool, err error) {
   179  	return extractBoolOptionFromArgs(args, "licenses")
   180  }
   181  
   182  // Used by docker scan (Xray)
   183  func ExtractRepoPathFromArgs(args []string) (cleanArgs []string, repoPath string, err error) {
   184  	return extractStringOptionFromArgs(args, "repo-path")
   185  }
   186  
   187  // Used by docker scan (Xray)
   188  func ExtractWatchesFromArgs(args []string) (cleanArgs []string, watches string, err error) {
   189  	return extractStringOptionFromArgs(args, "watches")
   190  }
   191  
   192  func ExtractDetailedSummaryFromArgs(args []string) (cleanArgs []string, detailedSummary bool, err error) {
   193  	return extractBoolOptionFromArgs(args, "detailed-summary")
   194  }
   195  
   196  func ExtractXrayScanFromArgs(args []string) (cleanArgs []string, xrayScan bool, err error) {
   197  	return extractBoolOptionFromArgs(args, "scan")
   198  }
   199  
   200  func ExtractXrayOutputFormatFromArgs(args []string) (cleanArgs []string, format string, err error) {
   201  	return extractStringOptionFromArgs(args, "format")
   202  }
   203  
   204  func ExtractTagFromArgs(args []string) (cleanArgs []string, tag string, err error) {
   205  	return extractStringOptionFromArgs(args, "tag")
   206  }
   207  
   208  func extractStringOptionFromArgs(args []string, optionName string) (cleanArgs []string, value string, err error) {
   209  	cleanArgs = append([]string(nil), args...)
   210  
   211  	flagIndex, valIndex, value, err := FindFlag("--"+optionName, cleanArgs)
   212  	if err != nil {
   213  		return
   214  	}
   215  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valIndex)
   216  	return
   217  }
   218  
   219  func extractBoolOptionFromArgs(args []string, optionName string) (cleanArgs []string, value bool, err error) {
   220  	cleanArgs = append([]string(nil), args...)
   221  
   222  	flagIndex, value, err := FindBooleanFlag("--"+optionName, cleanArgs)
   223  	if err != nil {
   224  		return
   225  	}
   226  	// Since boolean flag might appear as --flag or --flag=value, the value index is the same as the flag index.
   227  	RemoveFlagFromCommand(&cleanArgs, flagIndex, flagIndex)
   228  	return
   229  }
   230  
   231  // Add green color style to the string if possible.
   232  func PrintTitle(str string) string {
   233  	return colorStr(str, color.Green)
   234  }
   235  
   236  // Add cyan color style to the string if possible.
   237  func PrintLink(str string) string {
   238  	return colorStr(str, color.Cyan)
   239  }
   240  
   241  // Add bold style to the string if possible.
   242  func PrintBold(str string) string {
   243  	return colorStr(str, color.Bold)
   244  }
   245  
   246  // Add bold and green style to the string if possible.
   247  func PrintBoldTitle(str string) string {
   248  	return PrintBold(PrintTitle(str))
   249  }
   250  
   251  // Add gray color style to the string if possible.
   252  func PrintComment(str string) string {
   253  	return colorStr(str, color.Gray)
   254  }
   255  
   256  // Add yellow color style to the string if possible.
   257  func PrintYellow(str string) string {
   258  	return colorStr(str, color.Yellow)
   259  }
   260  
   261  // Add the requested style to the string if possible.
   262  func colorStr(str string, c color.Color) string {
   263  	// Add styles only on supported terminals
   264  	if log.IsStdOutTerminal() && log.IsColorsSupported() {
   265  		return c.Render(str)
   266  	}
   267  	// Remove emojis from non-supported terminals
   268  	if gomoji.ContainsEmoji(str) {
   269  		str = gomoji.RemoveEmojis(str)
   270  	}
   271  	return str
   272  }
   273  
   274  // Remove emojis from non-supported terminals
   275  func RemoveEmojisIfNonSupportedTerminal(msg string) string {
   276  	if !(log.IsStdOutTerminal() && log.IsColorsSupported()) {
   277  		if gomoji.ContainsEmoji(msg) {
   278  			msg = gomoji.RemoveEmojis(msg)
   279  		}
   280  	}
   281  	return msg
   282  }