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 }