github.com/jfrog/jfrog-cli-core/v2@v2.51.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  	cleanArgs = append([]string(nil), args...)
   142  
   143  	// Get --server-id flag value from the command, and remove it.
   144  	flagIndex, valueIndex, serverId, err := FindFlag("--server-id", cleanArgs)
   145  	if err != nil {
   146  		return nil, "", err
   147  	}
   148  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valueIndex)
   149  	return
   150  }
   151  
   152  func ExtractThreadsFromArgs(args []string, defaultValue int) (cleanArgs []string, threads int, err error) {
   153  	cleanArgs = append([]string(nil), args...)
   154  	threads = defaultValue
   155  	// Extract threads information from the args.
   156  	flagIndex, valueIndex, numOfThreads, err := FindFlag("--threads", cleanArgs)
   157  	if err != nil {
   158  		return
   159  	}
   160  
   161  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valueIndex)
   162  	if numOfThreads != "" {
   163  		threads, err = strconv.Atoi(numOfThreads)
   164  		if err != nil {
   165  			err = errorutils.CheckError(err)
   166  		}
   167  	}
   168  	return
   169  }
   170  
   171  func ExtractInsecureTlsFromArgs(args []string) (cleanArgs []string, insecureTls bool, err error) {
   172  	cleanArgs = append([]string(nil), args...)
   173  
   174  	flagIndex, insecureTls, err := FindBooleanFlag("--insecure-tls", args)
   175  	if err != nil {
   176  		return
   177  	}
   178  	RemoveFlagFromCommand(&cleanArgs, flagIndex, flagIndex)
   179  	return
   180  }
   181  
   182  // Used by docker
   183  func ExtractSkipLoginFromArgs(args []string) (cleanArgs []string, skipLogin bool, err error) {
   184  	cleanArgs = append([]string(nil), args...)
   185  
   186  	flagIndex, skipLogin, err := FindBooleanFlag("--skip-login", cleanArgs)
   187  	if err != nil {
   188  		return
   189  	}
   190  	// Since boolean flag might appear as --flag or --flag=value, the value index is the same as the flag index.
   191  	RemoveFlagFromCommand(&cleanArgs, flagIndex, flagIndex)
   192  	return
   193  }
   194  
   195  // Used by docker
   196  func ExtractFailFromArgs(args []string) (cleanArgs []string, fail bool, err error) {
   197  	cleanArgs = append([]string(nil), args...)
   198  
   199  	flagIndex, fail, err := FindBooleanFlag("--fail", cleanArgs)
   200  	if err != nil {
   201  		return
   202  	}
   203  	// Since boolean flag might appear as --flag or --flag=value, the value index is the same as the flag index.
   204  	RemoveFlagFromCommand(&cleanArgs, flagIndex, flagIndex)
   205  	return
   206  }
   207  
   208  // Used by docker  scan (Xray)
   209  func ExtractLicensesFromArgs(args []string) (cleanArgs []string, licenses bool, err error) {
   210  	cleanArgs = append([]string(nil), args...)
   211  
   212  	flagIndex, licenses, err := FindBooleanFlag("--licenses", cleanArgs)
   213  	if err != nil {
   214  		return
   215  	}
   216  	// Since boolean flag might appear as --flag or --flag=value, the value index is the same as the flag index.
   217  	RemoveFlagFromCommand(&cleanArgs, flagIndex, flagIndex)
   218  	return
   219  }
   220  
   221  // Used by docker scan (Xray)
   222  func ExtractRepoPathFromArgs(args []string) (cleanArgs []string, repoPath string, err error) {
   223  	cleanArgs = append([]string(nil), args...)
   224  
   225  	flagIndex, valIndex, repoPath, err := FindFlag("--repo-path", cleanArgs)
   226  	if err != nil {
   227  		return
   228  	}
   229  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valIndex)
   230  	return
   231  }
   232  
   233  // Used by docker scan (Xray)
   234  func ExtractWatchesFromArgs(args []string) (cleanArgs []string, watches string, err error) {
   235  	cleanArgs = append([]string(nil), args...)
   236  
   237  	flagIndex, valIndex, watches, err := FindFlag("--watches", cleanArgs)
   238  	if err != nil {
   239  		return
   240  	}
   241  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valIndex)
   242  	return
   243  }
   244  
   245  func ExtractDetailedSummaryFromArgs(args []string) (cleanArgs []string, detailedSummary bool, err error) {
   246  	cleanArgs = append([]string(nil), args...)
   247  
   248  	flagIndex, detailedSummary, err := FindBooleanFlag("--detailed-summary", cleanArgs)
   249  	if err != nil {
   250  		return
   251  	}
   252  	// Since boolean flag might appear as --flag or --flag=value, the value index is the same as the flag index.
   253  	RemoveFlagFromCommand(&cleanArgs, flagIndex, flagIndex)
   254  	return
   255  }
   256  
   257  func ExtractXrayScanFromArgs(args []string) (cleanArgs []string, xrayScan bool, err error) {
   258  	cleanArgs = append([]string(nil), args...)
   259  
   260  	flagIndex, xrayScan, err := FindBooleanFlag("--scan", cleanArgs)
   261  	if err != nil {
   262  		return
   263  	}
   264  	// Since boolean flag might appear as --flag or --flag=value, the value index is the same as the flag index.
   265  	RemoveFlagFromCommand(&cleanArgs, flagIndex, flagIndex)
   266  	return
   267  }
   268  
   269  func ExtractXrayOutputFormatFromArgs(args []string) (cleanArgs []string, format string, err error) {
   270  	cleanArgs = append([]string(nil), args...)
   271  
   272  	flagIndex, valIndex, format, err := FindFlag("--format", cleanArgs)
   273  	if err != nil {
   274  		return
   275  	}
   276  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valIndex)
   277  	return
   278  }
   279  
   280  // Add green color style to the string if possible.
   281  func PrintTitle(str string) string {
   282  	return colorStr(str, color.Green)
   283  }
   284  
   285  // Add cyan color style to the string if possible.
   286  func PrintLink(str string) string {
   287  	return colorStr(str, color.Cyan)
   288  }
   289  
   290  // Add bold style to the string if possible.
   291  func PrintBold(str string) string {
   292  	return colorStr(str, color.Bold)
   293  }
   294  
   295  // Add bold and green style to the string if possible.
   296  func PrintBoldTitle(str string) string {
   297  	return PrintBold(PrintTitle(str))
   298  }
   299  
   300  // Add gray color style to the string if possible.
   301  func PrintComment(str string) string {
   302  	return colorStr(str, color.Gray)
   303  }
   304  
   305  // Add yellow color style to the string if possible.
   306  func PrintYellow(str string) string {
   307  	return colorStr(str, color.Yellow)
   308  }
   309  
   310  // Add the requested style to the string if possible.
   311  func colorStr(str string, c color.Color) string {
   312  	// Add styles only on supported terminals
   313  	if log.IsStdOutTerminal() && log.IsColorsSupported() {
   314  		return c.Render(str)
   315  	}
   316  	// Remove emojis from non-supported terminals
   317  	if gomoji.ContainsEmoji(str) {
   318  		str = gomoji.RemoveEmojis(str)
   319  	}
   320  	return str
   321  }
   322  
   323  // Remove emojis from non-supported terminals
   324  func RemoveEmojisIfNonSupportedTerminal(msg string) string {
   325  	if !(log.IsStdOutTerminal() && log.IsColorsSupported()) {
   326  		if gomoji.ContainsEmoji(msg) {
   327  			msg = gomoji.RemoveEmojis(msg)
   328  		}
   329  	}
   330  	return msg
   331  }