github.com/tiagovtristao/plz@v13.4.0+incompatible/src/cli/flags.go (about)

     1  // Package cli contains helper functions related to flag parsing and logging.
     2  package cli
     3  
     4  import (
     5  	"fmt"
     6  	"net/url"
     7  	"os"
     8  	"path/filepath"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/coreos/go-semver/semver"
    14  	"github.com/dustin/go-humanize"
    15  	"github.com/jessevdk/go-flags"
    16  	"github.com/peterebden/go-cli-init"
    17  )
    18  
    19  // GiByte is a re-export for convenience of other things using it.
    20  const GiByte = humanize.GiByte
    21  
    22  // MinVerbosity is the minimum verbosity we support.
    23  const MinVerbosity = cli.MinVerbosity
    24  
    25  // MaxVerbosity is the maximum verbosity we support.
    26  const MaxVerbosity = cli.MaxVerbosity
    27  
    28  // ParseFlagsOrDie parses the app's flags and dies if unsuccessful.
    29  // Also dies if any unexpected arguments are passed.
    30  // It returns the active command if there is one.
    31  func ParseFlagsOrDie(appname string, data interface{}) string {
    32  	return cli.ParseFlagsOrDie(appname, data)
    33  }
    34  
    35  // ParseFlagsFromArgsOrDie is similar to ParseFlagsOrDie but allows control over the
    36  // flags passed.
    37  // It returns the active command if there is one.
    38  func ParseFlagsFromArgsOrDie(appname string, data interface{}, args []string) string {
    39  	return cli.ParseFlagsFromArgsOrDie(appname, data, args)
    40  }
    41  
    42  // ParseFlags parses the app's flags and returns the parser, any extra arguments, and any error encountered.
    43  // It may exit if certain options are encountered (eg. --help).
    44  func ParseFlags(appname string, data interface{}, args []string, opts flags.Options, completionHandler cli.CompletionHandler) (*flags.Parser, []string, error) {
    45  	return cli.ParseFlags(appname, data, args, opts, completionHandler)
    46  }
    47  
    48  // PrintCompletions prints a set of completions to stdout.
    49  func PrintCompletions(items []flags.Completion) {
    50  	for _, item := range items {
    51  		fmt.Println(item.Item)
    52  	}
    53  }
    54  
    55  // ActiveCommand returns the name of the currently active command.
    56  func ActiveCommand(command *flags.Command) string {
    57  	return cli.ActiveCommand(command)
    58  }
    59  
    60  // A ByteSize is used for flags that represent some quantity of bytes that can be
    61  // passed as human-readable quantities (eg. "10G").
    62  type ByteSize uint64
    63  
    64  // UnmarshalFlag implements the flags.Unmarshaler interface.
    65  func (b *ByteSize) UnmarshalFlag(in string) error {
    66  	b2, err := humanize.ParseBytes(in)
    67  	*b = ByteSize(b2)
    68  	return flagsError(err)
    69  }
    70  
    71  // UnmarshalText implements the encoding.TextUnmarshaler interface
    72  func (b *ByteSize) UnmarshalText(text []byte) error {
    73  	return b.UnmarshalFlag(string(text))
    74  }
    75  
    76  // A Duration is used for flags that represent a time duration; it's just a wrapper
    77  // around time.Duration that implements the flags.Unmarshaler and
    78  // encoding.TextUnmarshaler interfaces.
    79  type Duration time.Duration
    80  
    81  // UnmarshalFlag implements the flags.Unmarshaler interface.
    82  func (d *Duration) UnmarshalFlag(in string) error {
    83  	d2, err := time.ParseDuration(in)
    84  	// For backwards compatibility, treat missing units as seconds.
    85  	if err != nil {
    86  		if d3, err := strconv.Atoi(in); err == nil {
    87  			*d = Duration(time.Duration(d3) * time.Second)
    88  			return nil
    89  		}
    90  	}
    91  	*d = Duration(d2)
    92  	return flagsError(err)
    93  }
    94  
    95  // UnmarshalText implements the encoding.TextUnmarshaler interface
    96  func (d *Duration) UnmarshalText(text []byte) error {
    97  	return d.UnmarshalFlag(string(text))
    98  }
    99  
   100  // A URL is used for flags or config fields that represent a URL.
   101  // It's just a string because it's more convenient that way; we haven't needed them as a net.URL so far.
   102  type URL string
   103  
   104  // UnmarshalFlag implements the flags.Unmarshaler interface.
   105  func (u *URL) UnmarshalFlag(in string) error {
   106  	if _, err := url.Parse(in); err != nil {
   107  		return flagsError(err)
   108  	}
   109  	*u = URL(in)
   110  	return nil
   111  }
   112  
   113  // UnmarshalText implements the encoding.TextUnmarshaler interface
   114  func (u *URL) UnmarshalText(text []byte) error {
   115  	return u.UnmarshalFlag(string(text))
   116  }
   117  
   118  // String implements the fmt.Stringer interface
   119  func (u *URL) String() string {
   120  	return string(*u)
   121  }
   122  
   123  // A Version is an extension to semver.Version extending it with the ability to
   124  // recognise >= prefixes.
   125  type Version struct {
   126  	semver.Version
   127  	IsGTE bool
   128  	IsSet bool
   129  }
   130  
   131  // NewVersion creates a new version from the given string.
   132  func NewVersion(in string) (*Version, error) {
   133  	v := &Version{}
   134  	return v, v.UnmarshalFlag(in)
   135  }
   136  
   137  // MustNewVersion creates a new version and dies if it is not parseable.
   138  func MustNewVersion(in string) *Version {
   139  	v, err := NewVersion(in)
   140  	if err != nil {
   141  		log.Fatalf("Failed to parse version: %s", in)
   142  	}
   143  	return v
   144  }
   145  
   146  // UnmarshalText implements the encoding.TextUnmarshaler interface
   147  func (v *Version) UnmarshalText(text []byte) error {
   148  	return v.UnmarshalFlag(string(text))
   149  }
   150  
   151  // UnmarshalFlag implements the flags.Unmarshaler interface.
   152  func (v *Version) UnmarshalFlag(in string) error {
   153  	if strings.HasPrefix(in, ">=") {
   154  		v.IsGTE = true
   155  		in = strings.TrimSpace(strings.TrimPrefix(in, ">="))
   156  	}
   157  	v.IsSet = true
   158  	return v.Set(in)
   159  }
   160  
   161  // String implements the fmt.Stringer interface
   162  func (v Version) String() string {
   163  	if v.IsGTE {
   164  		return ">=" + v.Version.String()
   165  	}
   166  	return v.Version.String()
   167  }
   168  
   169  // VersionString returns just the version, without any preceding >=.
   170  func (v *Version) VersionString() string {
   171  	return v.Version.String()
   172  }
   173  
   174  // Semver converts a Version to a semver.Version
   175  func (v *Version) Semver() semver.Version {
   176  	return v.Version
   177  }
   178  
   179  // Unset resets this version to the default.
   180  func (v *Version) Unset() {
   181  	*v = Version{}
   182  }
   183  
   184  // flagsError converts an error to a flags.Error, which is required for flag parsing.
   185  func flagsError(err error) error {
   186  	if err == nil {
   187  		return err
   188  	}
   189  	return &flags.Error{Type: flags.ErrMarshal, Message: err.Error()}
   190  }
   191  
   192  // A Filepath implements completion for file paths.
   193  // This is distinct from upstream's in that it knows about completing into directories.
   194  type Filepath string
   195  
   196  // Complete implements the flags.Completer interface.
   197  func (f *Filepath) Complete(match string) []flags.Completion {
   198  	matches, _ := filepath.Glob(match + "*")
   199  	// If there's exactly one match and it's a directory, take its contents instead.
   200  	if len(matches) == 1 {
   201  		if info, err := os.Stat(matches[0]); err == nil && info.IsDir() {
   202  			matches, _ = filepath.Glob(matches[0] + "/*")
   203  		}
   204  	}
   205  	ret := make([]flags.Completion, len(matches))
   206  	for i, match := range matches {
   207  		ret[i].Item = match
   208  	}
   209  	return ret
   210  }
   211  
   212  // Arch represents a combined Go-style operating system and architecture pair, as in "linux_amd64".
   213  type Arch struct {
   214  	OS, Arch string
   215  }
   216  
   217  // NewArch constructs a new Arch instance.
   218  func NewArch(os, arch string) Arch {
   219  	return Arch{OS: os, Arch: arch}
   220  }
   221  
   222  // String prints this Arch to its string representation.
   223  func (arch *Arch) String() string {
   224  	return arch.OS + "_" + arch.Arch
   225  }
   226  
   227  // UnmarshalText implements the encoding.TextUnmarshaler interface
   228  func (arch *Arch) UnmarshalText(text []byte) error {
   229  	return arch.UnmarshalFlag(string(text))
   230  }
   231  
   232  // UnmarshalFlag implements the flags.Unmarshaler interface.
   233  func (arch *Arch) UnmarshalFlag(in string) error {
   234  	if parts := strings.Split(in, "_"); len(parts) == 2 && !strings.ContainsRune(in, '/') {
   235  		arch.OS = parts[0]
   236  		arch.Arch = parts[1]
   237  		return nil
   238  	}
   239  	return fmt.Errorf("Can't parse architecture %s (should be a Go-style arch pair, like 'linux_amd64' etc)", in)
   240  }
   241  
   242  // XOS returns the "alternative" OS spelling which some things prefer.
   243  // The difference here is that "darwin" is instead returned as "osx".
   244  func (arch *Arch) XOS() string {
   245  	if arch.OS == "darwin" {
   246  		return "osx"
   247  	}
   248  	return arch.OS
   249  }
   250  
   251  // XArch returns the "alternative" architecture spelling which some things prefer.
   252  // In this case amd64 is instead returned as x86_64 and x86 as x86_32.
   253  func (arch *Arch) XArch() string {
   254  	if arch.Arch == "amd64" {
   255  		return "x86_64"
   256  	} else if arch.Arch == "x86" {
   257  		return "x86_32"
   258  	}
   259  	return arch.Arch
   260  }
   261  
   262  // GoArch returns the architecture as Go would name it.
   263  func (arch *Arch) GoArch() string {
   264  	if arch.Arch == "x86" {
   265  		return "386"
   266  	}
   267  	return arch.Arch
   268  }