From 890d49ca7a15a3fb7f44d33e5cdb499f0e5c2331 Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Sat, 7 Sep 2019 20:41:44 -0700 Subject: [PATCH 01/35] Release 1.22.0 - adds the changelog for 1.22.0 - updates the changelog for 1.21.0. some PRs were missed, as was mentioned here https://github.com/urfave/cli/pull/829#issuecomment-517968795 - closes https://github.com/urfave/cli/issues/867 --- CHANGELOG.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e49d54..ae95be2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,28 @@ ## [Unreleased] +## [1.22.0] - 2019-09-07 + +### Fixed + +* Fix Subcommands not falling back to `app.ExitEventHandler` in [urfave/cli/pull/856](https://github.com/urfave/cli/pull/856) via [@FaranIdo](https://github.com/FaranIdo) + +### Changed + +* Clarify that altsrc supports both TOML and JSON in [urfave/cli/pull/774](https://github.com/urfave/cli/pull/774) via [@whereswaldon](https://github.com/whereswaldon) +* Made the exit code example more clear in [urfave/cli/pull/823](https://github.com/urfave/cli/pull/823) via [@xordspar0](https://github.com/xordspar0) +* Removed the use of python for internal flag generation in [urfave/cli/pull/836](https://github.com/urfave/cli/pull/836) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) +* Changed the supported go versions to `1.10`, `1.11`, `1.12` in [https://github.com/urfave/cli/pull/843](urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks) +* Changed the v1 releases section in the readme in [urfave/cli/pull/862](https://github.com/urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) +* Cleaned up go modules in [urfave/cli/pull/874](https://github.com/urfave/cli/pull/874) via [@saschagrunert](https://github.com/saschagrunert) + +### Added + +* Added `UseShortOptionHandling` for combining short flags in [urfave/cli/pull/735](https://github.com/urfave/cli/pull/735) via [@rliebz](https://github.com/rliebz) +* Added support for flags bash completion in [urfave/cli/pull/808](https://github.com/urfave/cli/pull/808) via [@yogeshlonkar](https://github.com/yogeshlonkar) +* Added the `TakesFile` indicator to flag in [urfave/cli/pull/851](https://github.com/urfave/cli/pull/851) via [@saschagrunert](https://github.com/saschagrunert) +* Added fish shell completion support in [urfave/cli/pull/848](https://github.com/urfave/cli/pull/848) via [@saschagrunert](https://github.com/saschagrunert) + ## [1.21.0] - 2019-08-02 ### Fixed @@ -13,17 +35,24 @@ * Fix handling `ShortOptions` and `SkipArgReorder` in [urfave/cli/pull/686](https://github.com/urfave/cli/pull/686) via [@baude](https://github.com/baude) * Fix args reordering when bool flags are present in [urfave/cli/pull/712](https://github.com/urfave/cli/pull/712) via [@windler](https://github.com/windler) * Fix parsing of short options in [urfave/cli/pull/758](https://github.com/urfave/cli/pull/758) via [@vrothberg](https://github.com/vrothberg) +* Fix unaligned indents for the command help messages in [urfave/cli/pull/806](https://github.com/urfave/cli/pull/806) via [@mingrammer](https://github.com/mingrammer) -### Added / Changed +### Changed -* Added _"required flags"_ support in [urfave/cli/pull/819](https://github.com/urfave/cli/pull/819) via [@lynncyrin](https://github.com/lynncyrin/) * Cleaned up help output in [urfave/cli/pull/664](https://github.com/urfave/cli/pull/664) via [@maguro](https://github.com/maguro) +* Remove redundant nil checks in [urfave/cli/pull/773](https://github.com/urfave/cli/pull/773) via [@teresy](https://github.com/teresy) * Case is now considered when sorting strings in [urfave/cli/pull/676](https://github.com/urfave/cli/pull/676) via [@rliebz](https://github.com/rliebz) + +### Added + +* Added _"required flags"_ support in [urfave/cli/pull/819](https://github.com/urfave/cli/pull/819) via [@lynncyrin](https://github.com/lynncyrin/) * Backport JSON `InputSource` to v1 in [urfave/cli/pull/598](https://github.com/urfave/cli/pull/598) via [@jszwedko](https://github.com/jszwedko) * Allow more customization of flag help strings in [urfave/cli/pull/661](https://github.com/urfave/cli/pull/661) via [@rliebz](https://github.com/rliebz) * Allow custom `ExitError` handler function in [urfave/cli/pull/628](https://github.com/urfave/cli/pull/628) via [@phinnaeus](https://github.com/phinnaeus) * Allow loading a variable from a file in [urfave/cli/pull/675](https://github.com/urfave/cli/pull/675) via [@jmccann](https://github.com/jmccann) * Allow combining short bool names in [urfave/cli/pull/684](https://github.com/urfave/cli/pull/684) via [@baude](https://github.com/baude) +* Added test coverage to context in [urfave/cli/pull/788](https://github.com/urfave/cli/pull/788) via [@benzvan](https://github.com/benzvan) +* Added go module support in [urfave/cli/pull/831](https://github.com/urfave/cli/pull/831) via [@saschagrunert](https://github.com/saschagrunert) ## [1.20.0] - 2017-08-10 @@ -428,7 +457,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. ### Added - Initial implementation. -[Unreleased]: https://github.com/urfave/cli/compare/v1.21.0...HEAD +[Unreleased]: https://github.com/urfave/cli/compare/v1.22.0...HEAD +[1.22.0]: https://github.com/urfave/cli/compare/v1.21.0...v1.22.0 [1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0 [1.20.0]: https://github.com/urfave/cli/compare/v1.19.1...v1.20.0 [1.19.1]: https://github.com/urfave/cli/compare/v1.19.0...v1.19.1 From 980742b7cc56e8f7b14b4668fb8ddcf89025c9ea Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Sat, 7 Sep 2019 20:43:56 -0700 Subject: [PATCH 02/35] typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae95be2..b118580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ * Made the exit code example more clear in [urfave/cli/pull/823](https://github.com/urfave/cli/pull/823) via [@xordspar0](https://github.com/xordspar0) * Removed the use of python for internal flag generation in [urfave/cli/pull/836](https://github.com/urfave/cli/pull/836) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) * Changed the supported go versions to `1.10`, `1.11`, `1.12` in [https://github.com/urfave/cli/pull/843](urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks) -* Changed the v1 releases section in the readme in [urfave/cli/pull/862](https://github.com/urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) +* Changed the v1 releases section in the readme in [https://github.com/urfave/cli/pull/862](urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) * Cleaned up go modules in [urfave/cli/pull/874](https://github.com/urfave/cli/pull/874) via [@saschagrunert](https://github.com/saschagrunert) ### Added From 238c80f9b5e54a834f25423fd0887f68b9737cbb Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Sat, 7 Sep 2019 20:44:48 -0700 Subject: [PATCH 03/35] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b118580..2a30fcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,8 @@ * Clarify that altsrc supports both TOML and JSON in [urfave/cli/pull/774](https://github.com/urfave/cli/pull/774) via [@whereswaldon](https://github.com/whereswaldon) * Made the exit code example more clear in [urfave/cli/pull/823](https://github.com/urfave/cli/pull/823) via [@xordspar0](https://github.com/xordspar0) * Removed the use of python for internal flag generation in [urfave/cli/pull/836](https://github.com/urfave/cli/pull/836) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) -* Changed the supported go versions to `1.10`, `1.11`, `1.12` in [https://github.com/urfave/cli/pull/843](urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks) -* Changed the v1 releases section in the readme in [https://github.com/urfave/cli/pull/862](urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) +* Changed the supported go versions to `1.10`, `1.11`, `1.12` in [urfave/cli/pull/843](https://github.com/urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks) +* Changed the v1 releases section in the readme in [urfave/cli/pull/862](https://github.com/urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) * Cleaned up go modules in [urfave/cli/pull/874](https://github.com/urfave/cli/pull/874) via [@saschagrunert](https://github.com/saschagrunert) ### Added From b6c5d17a835d17e8dd8f2e34c02e96af7f43c9e4 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 07:44:07 +0530 Subject: [PATCH 04/35] Remove Flag Generation Remove Legacy Python Scripts --- build.go | 23 - flag-gen/.gitignore | 1 - flag-gen/assets_generate.go | 55 - flag-gen/assets_vfsdata.go | 214 ---- flag-gen/go.mod | 9 - flag-gen/go.sum | 9 - flag-gen/main.go | 115 --- flag-gen/source/flag-types.json | 139 --- .../templates/altsrc_flags_generated.gotpl | 35 - flag-gen/templates/cli_flags_generated.gotpl | 89 -- flag.go | 464 --------- flag_bool.go | 109 ++ flag_bool_t.go | 110 ++ flag_duration.go | 106 ++ flag_float64.go | 106 ++ flag_generated.go | 943 ------------------ flag_generic.go | 110 ++ flag_int.go | 105 ++ flag_int64.go | 106 ++ flag_int64_slice.go | 141 +++ flag_int_slice.go | 142 +++ flag_string.go | 98 ++ flag_string_slice.go | 138 +++ flag_uint.go | 106 ++ flag_uint64.go | 106 ++ runtests | 109 -- 26 files changed, 1483 insertions(+), 2205 deletions(-) delete mode 100644 flag-gen/.gitignore delete mode 100644 flag-gen/assets_generate.go delete mode 100644 flag-gen/assets_vfsdata.go delete mode 100644 flag-gen/go.mod delete mode 100644 flag-gen/go.sum delete mode 100644 flag-gen/main.go delete mode 100644 flag-gen/source/flag-types.json delete mode 100644 flag-gen/templates/altsrc_flags_generated.gotpl delete mode 100644 flag-gen/templates/cli_flags_generated.gotpl create mode 100644 flag_bool.go create mode 100644 flag_bool_t.go create mode 100644 flag_duration.go create mode 100644 flag_float64.go delete mode 100644 flag_generated.go create mode 100644 flag_generic.go create mode 100644 flag_int.go create mode 100644 flag_int64.go create mode 100644 flag_int64_slice.go create mode 100644 flag_int_slice.go create mode 100644 flag_string.go create mode 100644 flag_string_slice.go create mode 100644 flag_uint.go create mode 100644 flag_uint64.go delete mode 100755 runtests diff --git a/build.go b/build.go index bd1b3cf..a828ae4 100644 --- a/build.go +++ b/build.go @@ -40,10 +40,6 @@ func main() { Name: "toc", Action: TocActionFunc, }, - cli.Command{ - Name: "generate", - Action: GenActionFunc, - }, } err := app.Run(os.Args) @@ -166,22 +162,3 @@ func TocActionFunc(_ *cli.Context) error { return nil } - -func GenActionFunc(_ *cli.Context) error { - err := runCmd("go", "generate", "flag-gen/main.go") - if err != nil { - return err - } - - err = runCmd("go", "generate", "cli.go") - if err != nil { - return err - } - - err = runCmd("git", "diff", "--exit-code") - if err != nil { - return err - } - - return nil -} diff --git a/flag-gen/.gitignore b/flag-gen/.gitignore deleted file mode 100644 index 5657f6e..0000000 --- a/flag-gen/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vendor \ No newline at end of file diff --git a/flag-gen/assets_generate.go b/flag-gen/assets_generate.go deleted file mode 100644 index 021da71..0000000 --- a/flag-gen/assets_generate.go +++ /dev/null @@ -1,55 +0,0 @@ -// +build ignore - -package main - -import ( - "github.com/shurcooL/httpfs/union" - "github.com/shurcooL/vfsgen" - "log" - "net/http" - "os" - "time" -) - -// zeroModTimeFileSystem is an http.FileSystem wrapper. -// It exposes a filesystem exactly like Source, except -// all file modification times are changed to zero. -// See https://github.com/shurcooL/vfsgen/pull/40#issuecomment-355416103 -type zeroModTimeFileSystem struct { - Source http.FileSystem -} - -func (fs zeroModTimeFileSystem) Open(name string) (http.File, error) { - f, err := fs.Source.Open(name) - return file{f}, err -} - -type file struct { - http.File -} - -func (f file) Stat() (os.FileInfo, error) { - fi, err := f.File.Stat() - return fileInfo{fi}, err -} - -type fileInfo struct { - os.FileInfo -} - -func (fi fileInfo) ModTime() time.Time { return time.Time{} } - -func main() { - fs := zeroModTimeFileSystem{ - Source: union.New(map[string]http.FileSystem{ - "/templates": http.Dir("templates"), - "/source": http.Dir("source"), - }), - } - - err := vfsgen.Generate(fs, vfsgen.Options{}) - - if err != nil { - log.Fatal(err) - } -} diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go deleted file mode 100644 index ab600d9..0000000 --- a/flag-gen/assets_vfsdata.go +++ /dev/null @@ -1,214 +0,0 @@ -// Code generated by vfsgen; DO NOT EDIT. - -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - pathpkg "path" - "time" -) - -// assets statically implements the virtual filesystem provided to vfsgen. -var assets = func() http.FileSystem { - fs := vfsgen۰FS{ - "/": &vfsgen۰DirInfo{ - name: "/", - modTime: time.Time{}, - }, - "/source": &vfsgen۰DirInfo{ - name: "source", - modTime: time.Time{}, - }, - "/source/flag-types.json": &vfsgen۰CompressedFileInfo{ - name: "flag-types.json", - modTime: time.Time{}, - uncompressedSize: 3686, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x95\x41\x6f\x9b\x40\x10\x85\xef\xfc\x8a\xe9\x56\x95\xa0\xb2\x20\x07\xcb\x07\x4b\xbd\x54\x55\x2b\xdf\x2a\xb9\xe9\x25\x44\xd6\x06\x16\xb2\xca\x7a\x17\x2d\x43\xd4\xc8\xf2\x7f\xaf\x16\x8c\x0d\x6b\xb0\x4d\x23\x23\xf7\x92\xc3\x9b\x2c\x33\xef\xcb\x9b\xcc\x83\x03\xb0\x71\x00\x00\x88\xa4\x6b\x46\xe6\x40\xbe\x2a\x25\xc8\xa4\xd2\xf0\x2d\x2b\xb5\xa7\x86\xf6\x4a\x45\x61\xc4\x84\x8a\x9c\xed\xb4\x98\xe5\x48\xe6\x80\xba\xa8\x95\x48\x49\x64\x7f\x70\x15\xb3\x84\x16\xc2\x14\x49\xf9\xa0\xfe\x4a\x46\x75\xce\xb4\x91\x73\xd4\x91\x92\xaf\xfe\x4f\xa3\x98\xe6\x6e\xe2\xff\x36\x3d\xfc\x25\x6a\x2e\x53\xd7\xf3\x5a\xad\x2b\xd5\xbc\xd4\x0c\x0b\x2d\x21\x24\x21\xd9\x0f\x4c\x5f\x58\xbe\x4a\xb8\xd8\x4f\xe8\x00\x6c\x27\xdd\x2e\x7f\xbd\xdf\x66\xac\x22\xa4\x5c\x98\xc7\x80\xcf\x14\x81\xe7\x65\x19\x9e\xde\xa0\xb6\xfe\x9f\x10\xf9\x56\x68\x8a\x5c\x49\x1b\x0a\xf2\x35\xf3\xed\x62\x4d\xa7\x89\xe2\x24\x1c\x37\x67\x0c\x9e\x11\xb3\x7c\x1e\x04\xa9\x12\x54\xa6\xbe\xd2\x69\x90\xbd\xa4\x81\xe9\x10\x7c\x2c\xdd\xd6\x7d\xbc\x13\xd4\xee\x8e\x89\x95\x33\xb6\x3e\x30\x14\x99\xfd\xeb\x83\xf1\x7d\x17\x8a\xe2\x6c\x6a\xd3\x4b\xda\xf2\x45\xdc\x2e\xf3\xdc\x4a\x49\xd9\xfd\xc8\xf3\x04\x66\xd3\x73\xbe\xd7\xe8\x2f\x33\xcd\x25\x26\x6e\x48\x3e\x25\x21\x99\xd4\x2c\x86\x33\xf8\xc1\x24\xd3\x3c\xb2\x19\x58\x72\x3f\x83\xe6\xae\x75\x40\x90\x5c\xd8\xb1\xa8\x5b\x70\x89\x4c\x27\x34\x62\x9b\x6d\x8f\x5f\x9e\xd4\xbe\xe0\xc3\x17\x90\x5c\xc0\x26\x94\x21\x86\xd8\x13\x00\x53\xdc\x9a\x1f\xe7\x76\xca\xd8\xe8\xe1\xb1\x90\x1d\x89\xe0\x72\x84\x3c\x2c\x64\x57\x1a\xee\x86\x07\x22\x7e\x57\x20\x16\x12\x3b\xec\xdf\x82\xf9\xea\xe9\x2a\xa2\x65\x3f\x33\x95\x5b\x4a\xf1\xc8\x78\x96\x82\x47\xcc\x66\xf4\xd9\x2e\x5c\x63\x65\x1e\x1e\x1b\x7f\x8a\x03\xc9\x3d\x39\x77\x3f\x85\xe7\x55\x92\xa1\xc8\xb4\x56\xda\x95\x5c\xf4\x71\xba\xda\x9e\x9d\x01\x39\x9b\xf6\xa2\xb4\x4a\x57\x83\x79\x58\xeb\x3e\x9c\xbb\x49\x6e\x1f\xe8\xae\xbd\x05\x33\x6f\xa9\xff\xba\xbe\xcd\x81\x0e\xa0\x8e\x17\xf6\x2c\x99\x36\x80\xa1\xff\x9c\xab\xef\x74\x87\xa6\xa3\x76\x9d\xd4\xb4\x89\x76\xc6\xa6\x31\xcb\x6d\xe4\xe6\x04\xd3\x7b\xde\x75\xf1\x8a\x51\x4e\x9e\xe9\x7d\x0b\x37\xef\x9e\x1f\x1f\xbd\x62\x84\xab\x77\x91\x7f\xeb\xec\x15\x23\xdc\x3d\xe7\xd1\xf9\x1b\x00\x00\xff\xff\xc7\xe4\x19\x16\x66\x0e\x00\x00"), - }, - "/templates": &vfsgen۰DirInfo{ - name: "templates", - modTime: time.Time{}, - }, - "/templates/altsrc_flags_generated.gotpl": &vfsgen۰CompressedFileInfo{ - name: "altsrc_flags_generated.gotpl", - modTime: time.Time{}, - uncompressedSize: 1044, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x4f\x8b\xdb\x30\x10\xc5\xcf\xd6\xa7\x78\x84\xa5\xc4\x4b\xb0\xef\x29\x7b\x28\xdd\x2d\xf4\x92\x2d\xec\x42\xcf\x8a\x3c\xb2\x45\x15\xdb\x48\xe3\x84\x20\xfc\xdd\xcb\x38\x61\xfb\x4f\x29\xbd\xf4\x66\x69\xf4\xde\x9b\xdf\x8c\xeb\x1a\x1f\x87\x86\xd0\x52\x4f\x41\x33\x35\xd8\x9f\x61\xdb\xf7\x78\x7c\xc6\xee\xf9\x15\x4f\x8f\x9f\x5f\x2b\xa5\x46\x6d\xbe\xe9\x96\x90\x12\xaa\x2f\x97\xef\x9d\x3e\x10\xe6\x59\x29\x77\x18\x87\xc0\x58\xab\x62\x65\xbd\x6e\x57\xaa\x58\xb5\x8e\xbb\x69\x5f\x99\xe1\x50\x4f\xc1\xea\x23\xd5\xc6\xbb\x95\x2a\x55\x4a\x08\xba\x6f\x09\x77\x6e\x83\x3b\x79\x8e\xed\x03\xaa\x4f\x5e\xb7\x51\xcc\xea\x5a\x22\x96\x42\x75\x0d\x90\x1a\x5c\x04\x77\x84\x45\xc0\xe7\x91\xc0\x9d\x66\x9c\x82\x1e\x23\x8c\x77\x55\x56\xc4\x03\xb4\xf7\xc3\x49\x5c\xed\x10\x30\x70\x47\x01\x47\xed\x27\x8a\x52\xdc\x13\xe2\x48\xc6\x59\x47\x8d\x5a\x5c\xb3\x36\x91\xc3\x64\x18\x49\x15\xb7\x92\x54\x11\x89\x71\xbf\x5c\xcb\xf9\x85\x58\xcd\x4a\x62\x77\x74\xca\x7a\x9a\x40\x9a\x29\x42\xa3\xa7\x53\x36\x56\xd9\xa9\x37\xb7\xf4\x6b\xeb\x6f\x62\x97\xb8\xcf\x46\x26\x55\x04\xe2\x29\xf4\x78\x97\xab\xa7\x6c\x17\x5b\x58\xbf\x41\x24\xde\xa2\x77\x1e\xf3\x15\xeb\xc3\x38\xfa\x33\xa2\x3e\xd2\x8f\xbd\xbc\x10\x2f\x53\xf6\x9a\x29\x60\x8a\xf2\xbb\x18\xed\x7d\xdc\xc8\x93\xfe\xf2\x2d\x62\x11\xc8\xe6\x46\x6a\xb2\x99\xd5\xe2\x7e\xe1\x5f\xdb\x3c\x4d\x79\x69\x61\xfd\xc7\xdc\x4b\xe1\xb4\x95\xdc\x3f\x48\xdf\x72\xb8\x1d\x22\xfa\xf2\x67\xa6\xaf\x8e\xbb\xa7\x10\x86\xf0\x7f\xe1\xde\x62\xfe\x89\xf2\xed\x75\x0e\x97\x96\x6e\x7f\x87\xbe\x6e\xfa\x6f\xec\xbf\xb8\xca\x10\x52\x02\xf5\x0d\xe6\xf9\x7b\x00\x00\x00\xff\xff\x6d\x3d\x9e\xe0\x14\x04\x00\x00"), - }, - "/templates/cli_flags_generated.gotpl": &vfsgen۰CompressedFileInfo{ - name: "cli_flags_generated.gotpl", - modTime: time.Time{}, - uncompressedSize: 2638, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x4d\x6f\xe3\x36\x10\x3d\x4b\xbf\x62\x6a\xec\x41\x0a\x5c\xfb\xde\xc5\x9e\xe2\xcd\x76\x81\x62\x37\xe8\xa6\xb9\xd3\xd2\x50\x26\x42\x93\x0a\x39\x4a\x1a\x18\xfe\xef\xc5\x90\xd4\x87\x3f\x10\xfb\x92\x9e\x2c\x72\x66\xde\x7b\x7c\x33\xa4\x97\x4b\xb8\xb5\x35\x42\x83\x06\x9d\x20\xac\x61\xfd\x06\xb2\xf9\x0c\xab\x9f\xf0\xe3\xe7\x03\x7c\x5d\x7d\x7f\x58\xe4\x79\x2b\xaa\x27\xd1\x20\xec\x76\xb0\xb8\x8f\xdf\x3f\xc4\x16\x61\xbf\xcf\x73\xb5\x6d\xad\x23\x28\xf2\x6c\x26\xb5\x68\x66\xfc\xbb\x25\xfe\xf1\xe4\x2a\x6b\x5e\xf8\x93\xd4\x16\x67\x79\x99\xef\x76\xe0\x84\x69\x10\x3e\xa9\x39\x7c\xe2\x7c\xf8\xe3\x0b\x2c\xee\xb4\x68\x3c\xa3\x2d\x97\xcc\x11\x02\x8b\xc4\xc0\x31\x50\x1e\x04\x84\xf4\x57\x45\x1b\xa0\xb7\x16\xc7\xc4\x07\x5e\xed\xf7\xc3\x7a\x65\x2b\x12\x4a\x33\xde\x61\xe2\x14\xd1\x93\xeb\x2a\x82\x5d\x9e\xf1\x6e\x96\x79\x72\xca\x34\x79\xf6\x8f\x17\xcd\x64\xf9\xd5\xbc\x3c\x0a\x37\xae\xef\x94\xc6\x7b\x41\x9b\x61\xe3\x6f\x7c\xee\x94\xc3\x3a\x5b\x5b\xab\xf3\xec\x4f\x55\xd7\x68\xb2\xb4\xda\xed\x7e\x07\x25\x01\x9f\x7b\xa5\xe2\x09\x3d\x43\x00\xb9\x2e\xd8\x97\x0d\x5b\x93\x12\x34\x75\x88\x1d\x97\x3f\x0a\xdd\x4d\x4a\xc3\x32\x3b\xb6\xe1\x7d\x88\x15\x7a\x52\x46\x90\xb2\x66\x04\x9a\x6c\x66\x37\xef\xe2\xed\x73\x6e\xd1\xaf\x70\x76\x70\x48\x9d\x33\xdc\x19\x87\xa2\x16\x6b\x8d\xe0\xb0\x75\xe8\xd1\x50\x64\xb0\x12\x68\xa3\x3c\xbc\xb0\x52\xae\x2c\xa4\x75\xd0\xb1\xc5\x50\xa3\x14\x9d\x26\x5f\xe6\xb2\x33\x15\x14\xf2\x6c\x9f\xca\x44\x56\x94\x10\x1d\xe7\x8e\x45\x62\xe0\x78\x8c\xa2\x2b\x64\x99\xc4\x7d\x43\x0a\xf5\xbd\x3a\xda\x20\x18\xde\x08\x62\x30\x4c\xd1\x05\xca\x04\x71\x8e\x53\x86\xcc\x44\xf5\xdd\xf7\xdd\x1f\xd8\x5e\x37\x48\x1b\x74\x60\x1d\x18\x4b\x03\x21\x0f\xb0\x4b\xb9\x17\xc8\x47\xd0\xa2\x04\x9e\x89\x03\xf6\x3e\x96\x14\x84\xe9\x89\x63\x31\x9c\x97\xbb\x3a\x39\x2b\x10\xe7\x80\x88\x4d\x98\x83\x65\x7d\xaf\xca\x23\x48\xa1\x3d\x5e\x50\x33\x12\x9c\xaa\x19\x2a\xa2\x80\x61\x3c\xbe\x21\x85\x5b\x74\xd0\x82\xd8\xf4\xe4\x27\x4f\xc1\xf5\xbd\x08\x60\xe7\x9b\x11\x42\x23\xed\x91\x13\x89\x21\xcd\x1f\x08\xdf\x43\x1c\x8d\xa9\x30\x35\x08\x03\xb8\x6d\xe9\x8d\x91\x52\x96\x3a\x31\xd1\xd8\x1e\x8a\x40\x68\xbd\xb8\xac\xbd\xb7\x6e\xd4\x7e\xe8\x5a\xba\x4a\x83\x77\xc7\x40\xa0\xad\x7d\xf2\xd0\xb5\x41\x4a\x24\xb7\x12\x04\x68\x5b\x09\x7d\x96\x77\xde\x1b\x70\x80\x77\x6b\x0d\xe1\xbf\xb4\x8a\xb7\x8e\x91\x95\x0c\x23\x2a\x6d\x67\xfa\x99\xac\xe0\x26\xe5\x95\x27\xd0\x45\xb8\x44\xf1\x1c\x21\xca\xf5\x08\x3d\x70\x78\x2d\x66\xb3\xe9\x3b\x3c\x8d\x84\x6d\xd4\xfe\xe0\xa1\x1e\x22\xfd\xfb\x32\xe9\x2d\x9f\xbb\x6b\xcf\x8a\x98\x43\xb5\xe0\xcd\x5f\x48\xc3\xa5\xd7\x76\x2d\xf4\xd5\xe6\x35\x21\xfd\x23\xdd\x3b\x2f\xe8\xff\xf2\x50\x49\x90\x9e\xff\x59\xa3\x8b\x51\xcc\x5d\xb4\xac\xb7\xb0\xfc\xcc\x39\xbf\x7d\x01\xa3\xc2\xa5\xbe\xca\x78\xe9\xcb\x3c\xdb\x9f\xbe\x00\x27\xfe\x70\x5f\x82\x2b\xef\xc0\x25\x23\xe6\xe0\x91\xe0\x26\x84\x93\xc6\x8f\xf4\x46\xb2\x2f\x1e\x69\xf1\x57\x50\x16\x94\x94\xd1\xb2\xa9\x1b\xa3\x80\x7b\xe1\x3c\xba\xc8\xdd\xf2\x77\x3d\x07\x74\x8e\x61\x06\xa6\x94\x33\x55\x71\x94\x2a\xe3\x7d\x0f\x6b\xeb\x0a\xa3\x74\xc9\xb9\xe9\x6f\x9a\xe9\x39\x71\x22\xe0\x0a\x8f\x33\x6e\xc5\x89\xd2\x5b\xe1\x29\xaa\x3d\x46\x98\xc4\xa7\x4a\x53\x5a\x14\x3c\x11\x75\x65\x9f\xc7\x8a\xff\x02\x00\x00\xff\xff\x73\xc9\x52\x5f\x4e\x0a\x00\x00"), - }, - } - fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/source"].(os.FileInfo), - fs["/templates"].(os.FileInfo), - } - fs["/source"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/source/flag-types.json"].(os.FileInfo), - } - fs["/templates"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/templates/altsrc_flags_generated.gotpl"].(os.FileInfo), - fs["/templates/cli_flags_generated.gotpl"].(os.FileInfo), - } - - return fs -}() - -type vfsgen۰FS map[string]interface{} - -func (fs vfsgen۰FS) Open(path string) (http.File, error) { - path = pathpkg.Clean("/" + path) - f, ok := fs[path] - if !ok { - return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist} - } - - switch f := f.(type) { - case *vfsgen۰CompressedFileInfo: - gr, err := gzip.NewReader(bytes.NewReader(f.compressedContent)) - if err != nil { - // This should never happen because we generate the gzip bytes such that they are always valid. - panic("unexpected error reading own gzip compressed bytes: " + err.Error()) - } - return &vfsgen۰CompressedFile{ - vfsgen۰CompressedFileInfo: f, - gr: gr, - }, nil - case *vfsgen۰DirInfo: - return &vfsgen۰Dir{ - vfsgen۰DirInfo: f, - }, nil - default: - // This should never happen because we generate only the above types. - panic(fmt.Sprintf("unexpected type %T", f)) - } -} - -// vfsgen۰CompressedFileInfo is a static definition of a gzip compressed file. -type vfsgen۰CompressedFileInfo struct { - name string - modTime time.Time - compressedContent []byte - uncompressedSize int64 -} - -func (f *vfsgen۰CompressedFileInfo) Readdir(count int) ([]os.FileInfo, error) { - return nil, fmt.Errorf("cannot Readdir from file %s", f.name) -} -func (f *vfsgen۰CompressedFileInfo) Stat() (os.FileInfo, error) { return f, nil } - -func (f *vfsgen۰CompressedFileInfo) GzipBytes() []byte { - return f.compressedContent -} - -func (f *vfsgen۰CompressedFileInfo) Name() string { return f.name } -func (f *vfsgen۰CompressedFileInfo) Size() int64 { return f.uncompressedSize } -func (f *vfsgen۰CompressedFileInfo) Mode() os.FileMode { return 0444 } -func (f *vfsgen۰CompressedFileInfo) ModTime() time.Time { return f.modTime } -func (f *vfsgen۰CompressedFileInfo) IsDir() bool { return false } -func (f *vfsgen۰CompressedFileInfo) Sys() interface{} { return nil } - -// vfsgen۰CompressedFile is an opened compressedFile instance. -type vfsgen۰CompressedFile struct { - *vfsgen۰CompressedFileInfo - gr *gzip.Reader - grPos int64 // Actual gr uncompressed position. - seekPos int64 // Seek uncompressed position. -} - -func (f *vfsgen۰CompressedFile) Read(p []byte) (n int, err error) { - if f.grPos > f.seekPos { - // Rewind to beginning. - err = f.gr.Reset(bytes.NewReader(f.compressedContent)) - if err != nil { - return 0, err - } - f.grPos = 0 - } - if f.grPos < f.seekPos { - // Fast-forward. - _, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos) - if err != nil { - return 0, err - } - f.grPos = f.seekPos - } - n, err = f.gr.Read(p) - f.grPos += int64(n) - f.seekPos = f.grPos - return n, err -} -func (f *vfsgen۰CompressedFile) Seek(offset int64, whence int) (int64, error) { - switch whence { - case io.SeekStart: - f.seekPos = 0 + offset - case io.SeekCurrent: - f.seekPos += offset - case io.SeekEnd: - f.seekPos = f.uncompressedSize + offset - default: - panic(fmt.Errorf("invalid whence value: %v", whence)) - } - return f.seekPos, nil -} -func (f *vfsgen۰CompressedFile) Close() error { - return f.gr.Close() -} - -// vfsgen۰DirInfo is a static definition of a directory. -type vfsgen۰DirInfo struct { - name string - modTime time.Time - entries []os.FileInfo -} - -func (d *vfsgen۰DirInfo) Read([]byte) (int, error) { - return 0, fmt.Errorf("cannot Read from directory %s", d.name) -} -func (d *vfsgen۰DirInfo) Close() error { return nil } -func (d *vfsgen۰DirInfo) Stat() (os.FileInfo, error) { return d, nil } - -func (d *vfsgen۰DirInfo) Name() string { return d.name } -func (d *vfsgen۰DirInfo) Size() int64 { return 0 } -func (d *vfsgen۰DirInfo) Mode() os.FileMode { return 0755 | os.ModeDir } -func (d *vfsgen۰DirInfo) ModTime() time.Time { return d.modTime } -func (d *vfsgen۰DirInfo) IsDir() bool { return true } -func (d *vfsgen۰DirInfo) Sys() interface{} { return nil } - -// vfsgen۰Dir is an opened dir instance. -type vfsgen۰Dir struct { - *vfsgen۰DirInfo - pos int // Position within entries for Seek and Readdir. -} - -func (d *vfsgen۰Dir) Seek(offset int64, whence int) (int64, error) { - if offset == 0 && whence == io.SeekStart { - d.pos = 0 - return 0, nil - } - return 0, fmt.Errorf("unsupported Seek in directory %s", d.name) -} - -func (d *vfsgen۰Dir) Readdir(count int) ([]os.FileInfo, error) { - if d.pos >= len(d.entries) && count > 0 { - return nil, io.EOF - } - if count <= 0 || count > len(d.entries)-d.pos { - count = len(d.entries) - d.pos - } - e := d.entries[d.pos : d.pos+count] - d.pos += count - return e, nil -} diff --git a/flag-gen/go.mod b/flag-gen/go.mod deleted file mode 100644 index c93d180..0000000 --- a/flag-gen/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/urfave/cli/flag-generator - -go 1.12 - -require ( - github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 - github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd - github.com/urfave/cli v1.21.0 -) diff --git a/flag-gen/go.sum b/flag-gen/go.sum deleted file mode 100644 index e68bf88..0000000 --- a/flag-gen/go.sum +++ /dev/null @@ -1,9 +0,0 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE= -github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/flag-gen/main.go b/flag-gen/main.go deleted file mode 100644 index 52cc3b1..0000000 --- a/flag-gen/main.go +++ /dev/null @@ -1,115 +0,0 @@ -package main - -//go:generate go run assets_generate.go - -import ( - "encoding/json" - "fmt" - "github.com/urfave/cli" - "io/ioutil" - "log" - "os" - "text/template" -) - -type CliFlagInfo struct { - PackageName string - Flags []FlagType -} - -type FlagType struct { - Name string `json:"name"` - Type string `json:"type"` - Value bool `json:"value"` - Destination bool `json:"dest"` - Doctail string `json:"doctail"` - ContextDefault string `json:"context_default"` - ContextType string `json:"context_type"` - Parser string `json:"parser"` - ParserCast string `json:"parser_cast"` - ValueString string `json:"valueString"` - TakesFile bool `json:"takes_file"` -} - -func main() { - app := cli.NewApp() - - app.Name = "flag-generator" - app.Usage = "Generate flag type code!" - - app.Action = ActionFunc - - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} - -func ActionFunc(_ *cli.Context) error { - var info CliFlagInfo - var tpl *template.Template - - inFile, err := assets.Open("/source/flag-types.json") - if err != nil { - log.Fatal(err) - } - - decoder := json.NewDecoder(inFile) - err = decoder.Decode(&info.Flags) - if err != nil { - log.Fatal(err) - } - - err = inFile.Close() - if err != nil { - log.Fatal(err) - } - - for _, packageName := range []string{"cli", "altsrc"} { - info.PackageName = packageName - - bytes, err := ReadTemplate(packageName) - if err != nil { - log.Fatal(err) - } - - tpl = template.Must(template.New("").Parse(string(bytes))) - - var outFile *os.File - - if packageName == "cli" { - outFile, err = os.Create("flag_generated.go") - } else { - outFile, err = os.Create("altsrc/flag_generated.go") - } - if err != nil { - log.Fatal(err) - } - - err = tpl.Execute(outFile, info) - if err != nil { - log.Fatal(err) - } - - err = outFile.Close() - if err != nil { - log.Fatal(err) - } - } - - return nil -} - -func ReadTemplate(packageName string) ([]byte, error) { - templateFile, err := assets.Open(fmt.Sprintf("/templates/%s_flags_generated.gotpl", packageName)) - if err != nil { - return nil, err - } - - templateFileBytes, err := ioutil.ReadAll(templateFile) - if err != nil { - return nil, err - } - - return templateFileBytes, nil -} diff --git a/flag-gen/source/flag-types.json b/flag-gen/source/flag-types.json deleted file mode 100644 index 472e572..0000000 --- a/flag-gen/source/flag-types.json +++ /dev/null @@ -1,139 +0,0 @@ -[ - { - "name": "Bool", - "type": "bool", - "value": false, - "dest": true, - "context_default": "false", - "parser": "strconv.ParseBool(f.Value.String())", - "valueString": "return \"\"", - "takes_file": false - }, - { - "name": "BoolT", - "type": "bool", - "value": false, - "dest": true, - "doctail": " that is true by default", - "context_default": "false", - "parser": "strconv.ParseBool(f.Value.String())", - "valueString": "return \"\"", - "takes_file": false - }, - { - "name": "Duration", - "type": "time.Duration", - "value": true, - "dest": true, - "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", - "context_default": "0", - "parser": "time.ParseDuration(f.Value.String())", - "valueString": "return f.Value.String()", - "takes_file": false - }, - { - "name": "Float64", - "type": "float64", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseFloat(f.Value.String(), 64)", - "valueString": "return fmt.Sprintf(\"%f\", f.Value)", - "takes_file": false - }, - { - "name": "Generic", - "type": "Generic", - "value": true, - "dest": false, - "context_default": "nil", - "context_type": "interface{}", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", - "takes_file": true - }, - { - "name": "Int64", - "type": "int64", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)", - "takes_file": false - }, - { - "name": "Int", - "type": "int", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", - "parser_cast": "int(parsed)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)", - "takes_file": false - }, - { - "name": "IntSlice", - "type": "*IntSlice", - "value": true, - "dest": false, - "context_default": "nil", - "context_type": "[]int", - "parser": "(f.Value.(*IntSlice)).Value(), error(nil)", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", - "takes_file": false - }, - { - "name": "Int64Slice", - "type": "*Int64Slice", - "value": true, - "dest": false, - "context_default": "nil", - "context_type": "[]int64", - "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", - "takes_file": false - }, - { - "name": "String", - "type": "string", - "value": true, - "dest": true, - "context_default": "\"\"", - "parser": "f.Value.String(), error(nil)", - "valueString": "return f.Value", - "takes_file": true - }, - { - "name": "StringSlice", - "type": "*StringSlice", - "value": true, - "dest": false, - "context_default": "nil", - "context_type": "[]string", - "parser": "(f.Value.(*StringSlice)).Value(), error(nil)", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", - "takes_file": true - }, - { - "name": "Uint64", - "type": "uint64", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)", - "takes_file": false - }, - { - "name": "Uint", - "type": "uint", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", - "parser_cast": "uint(parsed)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)", - "takes_file": false - } -] diff --git a/flag-gen/templates/altsrc_flags_generated.gotpl b/flag-gen/templates/altsrc_flags_generated.gotpl deleted file mode 100644 index 07f6e82..0000000 --- a/flag-gen/templates/altsrc_flags_generated.gotpl +++ /dev/null @@ -1,35 +0,0 @@ -// Code generated by fg; DO NOT EDIT. - -package {{ .PackageName }} - -import ( - "flag" - "github.com/urfave/cli" -) -{{ range $i, $flag := .Flags }} -// {{ $flag.Name }}Flag is the flag type that wraps cli.{{ $flag.Name }}Flag to allow -// for other values to be specified -type {{ $flag.Name }}Flag struct { - cli.{{ $flag.Name }}Flag - set *flag.FlagSet -} - -// New{{ $flag.Name }}Flag creates a new {{ $flag.Name }}Flag -func New{{ $flag.Name }}Flag(fl cli.{{ $flag.Name }}Flag) *{{ $flag.Name }}Flag { - return &{{ $flag.Name }}Flag{ {{ $flag.Name }}Flag: fl, set: nil } -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped {{ $flag.Name }}Flag.Apply -func (f *{{ $flag.Name }}Flag) Apply(set *flag.FlagSet) { - f.set = set - f.{{ $flag.Name }}Flag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped {{ $flag.Name }}Flag.ApplyWithError -func (f *{{ $flag.Name }}Flag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.{{ $flag.Name }}Flag.ApplyWithError(set) -} -{{ end }} \ No newline at end of file diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl deleted file mode 100644 index 61d06f0..0000000 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ /dev/null @@ -1,89 +0,0 @@ -// Code generated by fg; DO NOT EDIT. - -package {{ .PackageName }} - -import ( - "flag" - "fmt" - "strconv" - "time" -) -{{ range $i, $flag := .Flags }} -// {{ $flag.Name }}Flag is a flag with type {{ $flag.Type }}{{ $flag.Doctail }} -type {{ $flag.Name }}Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - {{- if eq $flag.TakesFile true }} - TakesFile bool - {{- end }} - {{- if eq $flag.Value true }} - Value {{ $flag.Type }} - {{- end }} - {{- if eq $flag.Destination true }} - Destination *{{ $flag.Type }} - {{- end }} -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f {{ $flag.Name }}Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f {{ $flag.Name }}Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f {{ $flag.Name }}Flag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f {{ $flag.Name }}Flag) TakesValue() bool { - return {{ $flag.Value }} -} - -// GetUsage returns the usage string for the flag -func (f {{ $flag.Name }}Flag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f {{ $flag.Name }}Flag) GetValue() string { - {{ $flag.ValueString }} -} - -// {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns -// {{ $flag.ContextDefault }} if not found -func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { - return lookup{{ $flag.Name }}(name, c.flagSet) -} - -// Global{{ $flag.Name }} looks up the value of a global {{ $flag.Name }}Flag, returns -// {{ $flag.ContextDefault }} if not found -func (c *Context) Global{{ $flag.Name }}(name string) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookup{{ $flag.Name }}(name, fs) - } - return {{ $flag.ContextDefault }} -} - -func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { - f := set.Lookup(name) - if f != nil { - {{ if ne .Parser "" }}parsed, err := {{ $flag.Parser }}{{ else }}parsed, err := f.Value, error(nil){{ end }} - if err != nil { - return {{ $flag.ContextDefault }} - } - {{ if ne .ParserCast "" }}return {{ $flag.ParserCast }}{{ else }}return parsed{{ end }} - } - return {{ $flag.ContextDefault }} -} -{{ end }} diff --git a/flag.go b/flag.go index be55a33..3c05270 100644 --- a/flag.go +++ b/flag.go @@ -9,7 +9,6 @@ import ( "strconv" "strings" "syscall" - "time" ) const defaultPlaceholder = "value" @@ -132,469 +131,6 @@ func eachName(longName string, fn func(string)) { } } -// Generic is a generic parseable type identified by a specific flag -type Generic interface { - Set(value string) error - String() string -} - -// Apply takes the flagset and calls Set on the generic flag with the value -// provided by the user for parsing by the flag -// Ignores parsing errors -func (f GenericFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError takes the flagset and calls Set on the generic flag with the value -// provided by the user for parsing by the flag -func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { - val := f.Value - if fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - if err := val.Set(fileEnvVal); err != nil { - return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err) - } - } - - eachName(f.Name, func(name string) { - set.Var(f.Value, name, f.Usage) - }) - - return nil -} - -// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter -type StringSlice []string - -// Set appends the string value to the list of values -func (f *StringSlice) Set(value string) error { - *f = append(*f, value) - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *StringSlice) String() string { - return fmt.Sprintf("%s", *f) -} - -// Value returns the slice of strings set by this flag -func (f *StringSlice) Value() []string { - return *f -} - -// Get returns the slice of strings set by this flag -func (f *StringSlice) Get() interface{} { - return *f -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f StringSliceFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - newVal := &StringSlice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - if err := newVal.Set(s); err != nil { - return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) - } - } - if f.Value == nil { - f.Value = newVal - } else { - *f.Value = *newVal - } - } - - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &StringSlice{} - } - set.Var(f.Value, name, f.Usage) - }) - - return nil -} - -// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter -type IntSlice []int - -// Set parses the value into an integer and appends it to the list of values -func (f *IntSlice) Set(value string) error { - tmp, err := strconv.Atoi(value) - if err != nil { - return err - } - *f = append(*f, tmp) - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *IntSlice) String() string { - return fmt.Sprintf("%#v", *f) -} - -// Value returns the slice of ints set by this flag -func (f *IntSlice) Value() []int { - return *f -} - -// Get returns the slice of ints set by this flag -func (f *IntSlice) Get() interface{} { - return *f -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f IntSliceFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - newVal := &IntSlice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - if err := newVal.Set(s); err != nil { - return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) - } - } - if f.Value == nil { - f.Value = newVal - } else { - *f.Value = *newVal - } - } - - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &IntSlice{} - } - set.Var(f.Value, name, f.Usage) - }) - - return nil -} - -// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter -type Int64Slice []int64 - -// Set parses the value into an integer and appends it to the list of values -func (f *Int64Slice) Set(value string) error { - tmp, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - *f = append(*f, tmp) - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *Int64Slice) String() string { - return fmt.Sprintf("%#v", *f) -} - -// Value returns the slice of ints set by this flag -func (f *Int64Slice) Value() []int64 { - return *f -} - -// Get returns the slice of ints set by this flag -func (f *Int64Slice) Get() interface{} { - return *f -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f Int64SliceFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - newVal := &Int64Slice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - if err := newVal.Set(s); err != nil { - return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) - } - } - if f.Value == nil { - f.Value = newVal - } else { - *f.Value = *newVal - } - } - - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &Int64Slice{} - } - set.Var(f.Value, name, f.Usage) - }) - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f BoolFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { - val := false - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - if envVal == "" { - val = false - } else { - envValBool, err := strconv.ParseBool(envVal) - if err != nil { - return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) - } - val = envValBool - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.BoolVar(f.Destination, name, val, f.Usage) - return - } - set.Bool(name, val, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f BoolTFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { - val := true - - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - if envVal == "" { - val = false - } else { - envValBool, err := strconv.ParseBool(envVal) - if err != nil { - return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) - } - val = envValBool - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.BoolVar(f.Destination, name, val, f.Usage) - return - } - set.Bool(name, val, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f StringFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - f.Value = envVal - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.StringVar(f.Destination, name, f.Value, f.Usage) - return - } - set.String(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f IntFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValInt, err := strconv.ParseInt(envVal, 0, 64) - if err != nil { - return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) - } - f.Value = int(envValInt) - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.IntVar(f.Destination, name, f.Value, f.Usage) - return - } - set.Int(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f Int64Flag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValInt, err := strconv.ParseInt(envVal, 0, 64) - if err != nil { - return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) - } - - f.Value = envValInt - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.Int64Var(f.Destination, name, f.Value, f.Usage) - return - } - set.Int64(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f UintFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValInt, err := strconv.ParseUint(envVal, 0, 64) - if err != nil { - return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) - } - - f.Value = uint(envValInt) - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.UintVar(f.Destination, name, f.Value, f.Usage) - return - } - set.Uint(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f Uint64Flag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValInt, err := strconv.ParseUint(envVal, 0, 64) - if err != nil { - return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) - } - - f.Value = envValInt - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.Uint64Var(f.Destination, name, f.Value, f.Usage) - return - } - set.Uint64(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f DurationFlag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValDuration, err := time.ParseDuration(envVal) - if err != nil { - return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) - } - - f.Value = envValDuration - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.DurationVar(f.Destination, name, f.Value, f.Usage) - return - } - set.Duration(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f Float64Flag) Apply(set *flag.FlagSet) { - _ = f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { - if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValFloat, err := strconv.ParseFloat(envVal, 10) - if err != nil { - return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) - } - - f.Value = envValFloat - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.Float64Var(f.Destination, name, f.Value, f.Usage) - return - } - set.Float64(name, f.Value, f.Usage) - }) - - return nil -} - func visibleFlags(fl []Flag) []Flag { var visible []Flag for _, f := range fl { diff --git a/flag_bool.go b/flag_bool.go new file mode 100644 index 0000000..2499b0b --- /dev/null +++ b/flag_bool.go @@ -0,0 +1,109 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// BoolFlag is a flag with type bool +type BoolFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Destination *bool +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f BoolFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f BoolFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f BoolFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f BoolFlag) TakesValue() bool { + return false +} + +// GetUsage returns the usage string for the flag +func (f BoolFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f BoolFlag) GetValue() string { + return "" +} + +// Bool looks up the value of a local BoolFlag, returns +// false if not found +func (c *Context) Bool(name string) bool { + return lookupBool(name, c.flagSet) +} + +// GlobalBool looks up the value of a global BoolFlag, returns +// false if not found +func (c *Context) GlobalBool(name string) bool { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupBool(name, fs) + } + return false +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f BoolFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { + val := false + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + if envVal == "" { + val = false + } else { + envValBool, err := strconv.ParseBool(envVal) + if err != nil { + return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) + } + val = envValBool + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.BoolVar(f.Destination, name, val, f.Usage) + return + } + set.Bool(name, val, f.Usage) + }) + + return nil +} + +func lookupBool(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return false + } + return parsed + } + return false +} diff --git a/flag_bool_t.go b/flag_bool_t.go new file mode 100644 index 0000000..cd0888f --- /dev/null +++ b/flag_bool_t.go @@ -0,0 +1,110 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// BoolTFlag is a flag with type bool that is true by default +type BoolTFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Destination *bool +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f BoolTFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f BoolTFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f BoolTFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f BoolTFlag) TakesValue() bool { + return false +} + +// GetUsage returns the usage string for the flag +func (f BoolTFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f BoolTFlag) GetValue() string { + return "" +} + +// BoolT looks up the value of a local BoolTFlag, returns +// false if not found +func (c *Context) BoolT(name string) bool { + return lookupBoolT(name, c.flagSet) +} + +// GlobalBoolT looks up the value of a global BoolTFlag, returns +// false if not found +func (c *Context) GlobalBoolT(name string) bool { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupBoolT(name, fs) + } + return false +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f BoolTFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { + val := true + + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + if envVal == "" { + val = false + } else { + envValBool, err := strconv.ParseBool(envVal) + if err != nil { + return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) + } + val = envValBool + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.BoolVar(f.Destination, name, val, f.Usage) + return + } + set.Bool(name, val, f.Usage) + }) + + return nil +} + +func lookupBoolT(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return false + } + return parsed + } + return false +} diff --git a/flag_duration.go b/flag_duration.go new file mode 100644 index 0000000..df4ade5 --- /dev/null +++ b/flag_duration.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "time" +) + +// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) +type DurationFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value time.Duration + Destination *time.Duration +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f DurationFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f DurationFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f DurationFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f DurationFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f DurationFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f DurationFlag) GetValue() string { + return f.Value.String() +} + +// Duration looks up the value of a local DurationFlag, returns +// 0 if not found +func (c *Context) Duration(name string) time.Duration { + return lookupDuration(name, c.flagSet) +} + +// GlobalDuration looks up the value of a global DurationFlag, returns +// 0 if not found +func (c *Context) GlobalDuration(name string) time.Duration { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupDuration(name, fs) + } + return 0 +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f DurationFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValDuration, err := time.ParseDuration(envVal) + if err != nil { + return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) + } + + f.Value = envValDuration + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.DurationVar(f.Destination, name, f.Value, f.Usage) + return + } + set.Duration(name, f.Value, f.Usage) + }) + + return nil +} + +func lookupDuration(name string, set *flag.FlagSet) time.Duration { + f := set.Lookup(name) + if f != nil { + parsed, err := time.ParseDuration(f.Value.String()) + if err != nil { + return 0 + } + return parsed + } + return 0 +} diff --git a/flag_float64.go b/flag_float64.go new file mode 100644 index 0000000..65398d3 --- /dev/null +++ b/flag_float64.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// Float64Flag is a flag with type float64 +type Float64Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value float64 + Destination *float64 +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Float64Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Float64Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Float64Flag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f Float64Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Float64Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Float64Flag) GetValue() string { + return fmt.Sprintf("%f", f.Value) +} + +// Float64 looks up the value of a local Float64Flag, returns +// 0 if not found +func (c *Context) Float64(name string) float64 { + return lookupFloat64(name, c.flagSet) +} + +// GlobalFloat64 looks up the value of a global Float64Flag, returns +// 0 if not found +func (c *Context) GlobalFloat64(name string) float64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupFloat64(name, fs) + } + return 0 +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f Float64Flag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValFloat, err := strconv.ParseFloat(envVal, 10) + if err != nil { + return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) + } + + f.Value = envValFloat + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.Float64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Float64(name, f.Value, f.Usage) + }) + + return nil +} + +func lookupFloat64(name string, set *flag.FlagSet) float64 { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseFloat(f.Value.String(), 64) + if err != nil { + return 0 + } + return parsed + } + return 0 +} diff --git a/flag_generated.go b/flag_generated.go deleted file mode 100644 index 9dcdfd6..0000000 --- a/flag_generated.go +++ /dev/null @@ -1,943 +0,0 @@ -// Code generated by fg; DO NOT EDIT. - -package cli - -import ( - "flag" - "fmt" - "strconv" - "time" -) - -// BoolFlag is a flag with type bool -type BoolFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Destination *bool -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f BoolFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f BoolFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f BoolFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f BoolFlag) TakesValue() bool { - return false -} - -// GetUsage returns the usage string for the flag -func (f BoolFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f BoolFlag) GetValue() string { - return "" -} - -// Bool looks up the value of a local BoolFlag, returns -// false if not found -func (c *Context) Bool(name string) bool { - return lookupBool(name, c.flagSet) -} - -// GlobalBool looks up the value of a global BoolFlag, returns -// false if not found -func (c *Context) GlobalBool(name string) bool { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupBool(name, fs) - } - return false -} - -func lookupBool(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return false - } - return parsed - } - return false -} - -// BoolTFlag is a flag with type bool that is true by default -type BoolTFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Destination *bool -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f BoolTFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f BoolTFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f BoolTFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f BoolTFlag) TakesValue() bool { - return false -} - -// GetUsage returns the usage string for the flag -func (f BoolTFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f BoolTFlag) GetValue() string { - return "" -} - -// BoolT looks up the value of a local BoolTFlag, returns -// false if not found -func (c *Context) BoolT(name string) bool { - return lookupBoolT(name, c.flagSet) -} - -// GlobalBoolT looks up the value of a global BoolTFlag, returns -// false if not found -func (c *Context) GlobalBoolT(name string) bool { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupBoolT(name, fs) - } - return false -} - -func lookupBoolT(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return false - } - return parsed - } - return false -} - -// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) -type DurationFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value time.Duration - Destination *time.Duration -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f DurationFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f DurationFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f DurationFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f DurationFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f DurationFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f DurationFlag) GetValue() string { - return f.Value.String() -} - -// Duration looks up the value of a local DurationFlag, returns -// 0 if not found -func (c *Context) Duration(name string) time.Duration { - return lookupDuration(name, c.flagSet) -} - -// GlobalDuration looks up the value of a global DurationFlag, returns -// 0 if not found -func (c *Context) GlobalDuration(name string) time.Duration { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupDuration(name, fs) - } - return 0 -} - -func lookupDuration(name string, set *flag.FlagSet) time.Duration { - f := set.Lookup(name) - if f != nil { - parsed, err := time.ParseDuration(f.Value.String()) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// Float64Flag is a flag with type float64 -type Float64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value float64 - Destination *float64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Float64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Float64Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Float64Flag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f Float64Flag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f Float64Flag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Float64Flag) GetValue() string { - return fmt.Sprintf("%f", f.Value) -} - -// Float64 looks up the value of a local Float64Flag, returns -// 0 if not found -func (c *Context) Float64(name string) float64 { - return lookupFloat64(name, c.flagSet) -} - -// GlobalFloat64 looks up the value of a global Float64Flag, returns -// 0 if not found -func (c *Context) GlobalFloat64(name string) float64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupFloat64(name, fs) - } - return 0 -} - -func lookupFloat64(name string, set *flag.FlagSet) float64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseFloat(f.Value.String(), 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// GenericFlag is a flag with type Generic -type GenericFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value Generic -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f GenericFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f GenericFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f GenericFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f GenericFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f GenericFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f GenericFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() - } - return "" -} - -// Generic looks up the value of a local GenericFlag, returns -// nil if not found -func (c *Context) Generic(name string) interface{} { - return lookupGeneric(name, c.flagSet) -} - -// GlobalGeneric looks up the value of a global GenericFlag, returns -// nil if not found -func (c *Context) GlobalGeneric(name string) interface{} { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupGeneric(name, fs) - } - return nil -} - -func lookupGeneric(name string, set *flag.FlagSet) interface{} { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value, error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Int64Flag is a flag with type int64 -type Int64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value int64 - Destination *int64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Int64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Int64Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Int64Flag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f Int64Flag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f Int64Flag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Int64Flag) GetValue() string { - return fmt.Sprintf("%d", f.Value) -} - -// Int64 looks up the value of a local Int64Flag, returns -// 0 if not found -func (c *Context) Int64(name string) int64 { - return lookupInt64(name, c.flagSet) -} - -// GlobalInt64 looks up the value of a global Int64Flag, returns -// 0 if not found -func (c *Context) GlobalInt64(name string) int64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt64(name, fs) - } - return 0 -} - -func lookupInt64(name string, set *flag.FlagSet) int64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// IntFlag is a flag with type int -type IntFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value int - Destination *int -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f IntFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f IntFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f IntFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f IntFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f IntFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f IntFlag) GetValue() string { - return fmt.Sprintf("%d", f.Value) -} - -// Int looks up the value of a local IntFlag, returns -// 0 if not found -func (c *Context) Int(name string) int { - return lookupInt(name, c.flagSet) -} - -// GlobalInt looks up the value of a global IntFlag, returns -// 0 if not found -func (c *Context) GlobalInt(name string) int { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt(name, fs) - } - return 0 -} - -func lookupInt(name string, set *flag.FlagSet) int { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return int(parsed) - } - return 0 -} - -// IntSliceFlag is a flag with type *IntSlice -type IntSliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *IntSlice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f IntSliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f IntSliceFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f IntSliceFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f IntSliceFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f IntSliceFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f IntSliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() - } - return "" -} - -// IntSlice looks up the value of a local IntSliceFlag, returns -// nil if not found -func (c *Context) IntSlice(name string) []int { - return lookupIntSlice(name, c.flagSet) -} - -// GlobalIntSlice looks up the value of a global IntSliceFlag, returns -// nil if not found -func (c *Context) GlobalIntSlice(name string) []int { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupIntSlice(name, fs) - } - return nil -} - -func lookupIntSlice(name string, set *flag.FlagSet) []int { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Int64SliceFlag is a flag with type *Int64Slice -type Int64SliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *Int64Slice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Int64SliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Int64SliceFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Int64SliceFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f Int64SliceFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f Int64SliceFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Int64SliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() - } - return "" -} - -// Int64Slice looks up the value of a local Int64SliceFlag, returns -// nil if not found -func (c *Context) Int64Slice(name string) []int64 { - return lookupInt64Slice(name, c.flagSet) -} - -// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns -// nil if not found -func (c *Context) GlobalInt64Slice(name string) []int64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt64Slice(name, fs) - } - return nil -} - -func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// StringFlag is a flag with type string -type StringFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value string - Destination *string -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f StringFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f StringFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f StringFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f StringFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f StringFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f StringFlag) GetValue() string { - return f.Value -} - -// String looks up the value of a local StringFlag, returns -// "" if not found -func (c *Context) String(name string) string { - return lookupString(name, c.flagSet) -} - -// GlobalString looks up the value of a global StringFlag, returns -// "" if not found -func (c *Context) GlobalString(name string) string { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupString(name, fs) - } - return "" -} - -func lookupString(name string, set *flag.FlagSet) string { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value.String(), error(nil) - if err != nil { - return "" - } - return parsed - } - return "" -} - -// StringSliceFlag is a flag with type *StringSlice -type StringSliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value *StringSlice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f StringSliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f StringSliceFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f StringSliceFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f StringSliceFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f StringSliceFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f StringSliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() - } - return "" -} - -// StringSlice looks up the value of a local StringSliceFlag, returns -// nil if not found -func (c *Context) StringSlice(name string) []string { - return lookupStringSlice(name, c.flagSet) -} - -// GlobalStringSlice looks up the value of a global StringSliceFlag, returns -// nil if not found -func (c *Context) GlobalStringSlice(name string) []string { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupStringSlice(name, fs) - } - return nil -} - -func lookupStringSlice(name string, set *flag.FlagSet) []string { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Uint64Flag is a flag with type uint64 -type Uint64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value uint64 - Destination *uint64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Uint64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Uint64Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Uint64Flag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f Uint64Flag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f Uint64Flag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Uint64Flag) GetValue() string { - return fmt.Sprintf("%d", f.Value) -} - -// Uint64 looks up the value of a local Uint64Flag, returns -// 0 if not found -func (c *Context) Uint64(name string) uint64 { - return lookupUint64(name, c.flagSet) -} - -// GlobalUint64 looks up the value of a global Uint64Flag, returns -// 0 if not found -func (c *Context) GlobalUint64(name string) uint64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupUint64(name, fs) - } - return 0 -} - -func lookupUint64(name string, set *flag.FlagSet) uint64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// UintFlag is a flag with type uint -type UintFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value uint - Destination *uint -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f UintFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f UintFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f UintFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f UintFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f UintFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f UintFlag) GetValue() string { - return fmt.Sprintf("%d", f.Value) -} - -// Uint looks up the value of a local UintFlag, returns -// 0 if not found -func (c *Context) Uint(name string) uint { - return lookupUint(name, c.flagSet) -} - -// GlobalUint looks up the value of a global UintFlag, returns -// 0 if not found -func (c *Context) GlobalUint(name string) uint { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupUint(name, fs) - } - return 0 -} - -func lookupUint(name string, set *flag.FlagSet) uint { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return uint(parsed) - } - return 0 -} - diff --git a/flag_generic.go b/flag_generic.go new file mode 100644 index 0000000..c43dae7 --- /dev/null +++ b/flag_generic.go @@ -0,0 +1,110 @@ +package cli + +import ( + "flag" + "fmt" +) + +// Generic is a generic parseable type identified by a specific flag +type Generic interface { + Set(value string) error + String() string +} + +// GenericFlag is a flag with type Generic +type GenericFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + TakesFile bool + Value Generic +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f GenericFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f GenericFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f GenericFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f GenericFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f GenericFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f GenericFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + +// Apply takes the flagset and calls Set on the generic flag with the value +// provided by the user for parsing by the flag +// Ignores parsing errors +func (f GenericFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError takes the flagset and calls Set on the generic flag with the value +// provided by the user for parsing by the flag +func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { + val := f.Value + if fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + if err := val.Set(fileEnvVal); err != nil { + return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err) + } + } + + eachName(f.Name, func(name string) { + set.Var(f.Value, name, f.Usage) + }) + + return nil +} + +// Generic looks up the value of a local GenericFlag, returns +// nil if not found +func (c *Context) Generic(name string) interface{} { + return lookupGeneric(name, c.flagSet) +} + +// GlobalGeneric looks up the value of a global GenericFlag, returns +// nil if not found +func (c *Context) GlobalGeneric(name string) interface{} { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupGeneric(name, fs) + } + return nil +} + +func lookupGeneric(name string, set *flag.FlagSet) interface{} { + f := set.Lookup(name) + if f != nil { + parsed, err := f.Value, error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} diff --git a/flag_int.go b/flag_int.go new file mode 100644 index 0000000..bae32e2 --- /dev/null +++ b/flag_int.go @@ -0,0 +1,105 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// IntFlag is a flag with type int +type IntFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value int + Destination *int +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f IntFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f IntFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f IntFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f IntFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f IntFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f IntFlag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f IntFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseInt(envVal, 0, 64) + if err != nil { + return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) + } + f.Value = int(envValInt) + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.IntVar(f.Destination, name, f.Value, f.Usage) + return + } + set.Int(name, f.Value, f.Usage) + }) + + return nil +} + +// Int looks up the value of a local IntFlag, returns +// 0 if not found +func (c *Context) Int(name string) int { + return lookupInt(name, c.flagSet) +} + +// GlobalInt looks up the value of a global IntFlag, returns +// 0 if not found +func (c *Context) GlobalInt(name string) int { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt(name, fs) + } + return 0 +} + +func lookupInt(name string, set *flag.FlagSet) int { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return int(parsed) + } + return 0 +} diff --git a/flag_int64.go b/flag_int64.go new file mode 100644 index 0000000..aaafbe9 --- /dev/null +++ b/flag_int64.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// Int64Flag is a flag with type int64 +type Int64Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value int64 + Destination *int64 +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Int64Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Int64Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Int64Flag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f Int64Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Int64Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Int64Flag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f Int64Flag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseInt(envVal, 0, 64) + if err != nil { + return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) + } + + f.Value = envValInt + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.Int64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Int64(name, f.Value, f.Usage) + }) + + return nil +} + +// Int64 looks up the value of a local Int64Flag, returns +// 0 if not found +func (c *Context) Int64(name string) int64 { + return lookupInt64(name, c.flagSet) +} + +// GlobalInt64 looks up the value of a global Int64Flag, returns +// 0 if not found +func (c *Context) GlobalInt64(name string) int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64(name, fs) + } + return 0 +} + +func lookupInt64(name string, set *flag.FlagSet) int64 { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return parsed + } + return 0 +} diff --git a/flag_int64_slice.go b/flag_int64_slice.go new file mode 100644 index 0000000..ed2e983 --- /dev/null +++ b/flag_int64_slice.go @@ -0,0 +1,141 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" + "strings" +) + +// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter +type Int64Slice []int64 + +// Set parses the value into an integer and appends it to the list of values +func (f *Int64Slice) Set(value string) error { + tmp, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + *f = append(*f, tmp) + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *Int64Slice) String() string { + return fmt.Sprintf("%#v", *f) +} + +// Value returns the slice of ints set by this flag +func (f *Int64Slice) Value() []int64 { + return *f +} + +// Get returns the slice of ints set by this flag +func (f *Int64Slice) Get() interface{} { + return *f +} + +// Int64SliceFlag is a flag with type *Int64Slice +type Int64SliceFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *Int64Slice +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Int64SliceFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Int64SliceFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Int64SliceFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f Int64SliceFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Int64SliceFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Int64SliceFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f Int64SliceFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + newVal := &Int64Slice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + if err := newVal.Set(s); err != nil { + return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) + } + } + if f.Value == nil { + f.Value = newVal + } else { + *f.Value = *newVal + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &Int64Slice{} + } + set.Var(f.Value, name, f.Usage) + }) + return nil +} + +// Int64Slice looks up the value of a local Int64SliceFlag, returns +// nil if not found +func (c *Context) Int64Slice(name string) []int64 { + return lookupInt64Slice(name, c.flagSet) +} + +// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns +// nil if not found +func (c *Context) GlobalInt64Slice(name string) []int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64Slice(name, fs) + } + return nil +} + +func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { + f := set.Lookup(name) + if f != nil { + parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} diff --git a/flag_int_slice.go b/flag_int_slice.go new file mode 100644 index 0000000..c38d010 --- /dev/null +++ b/flag_int_slice.go @@ -0,0 +1,142 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" + "strings" +) + +// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter +type IntSlice []int + +// Set parses the value into an integer and appends it to the list of values +func (f *IntSlice) Set(value string) error { + tmp, err := strconv.Atoi(value) + if err != nil { + return err + } + *f = append(*f, tmp) + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *IntSlice) String() string { + return fmt.Sprintf("%#v", *f) +} + +// Value returns the slice of ints set by this flag +func (f *IntSlice) Value() []int { + return *f +} + +// Get returns the slice of ints set by this flag +func (f *IntSlice) Get() interface{} { + return *f +} + +// IntSliceFlag is a flag with type *IntSlice +type IntSliceFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *IntSlice +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f IntSliceFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f IntSliceFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f IntSliceFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f IntSliceFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f IntSliceFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f IntSliceFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f IntSliceFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + newVal := &IntSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + if err := newVal.Set(s); err != nil { + return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) + } + } + if f.Value == nil { + f.Value = newVal + } else { + *f.Value = *newVal + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &IntSlice{} + } + set.Var(f.Value, name, f.Usage) + }) + + return nil +} + +// IntSlice looks up the value of a local IntSliceFlag, returns +// nil if not found +func (c *Context) IntSlice(name string) []int { + return lookupIntSlice(name, c.flagSet) +} + +// GlobalIntSlice looks up the value of a global IntSliceFlag, returns +// nil if not found +func (c *Context) GlobalIntSlice(name string) []int { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupIntSlice(name, fs) + } + return nil +} + +func lookupIntSlice(name string, set *flag.FlagSet) []int { + f := set.Lookup(name) + if f != nil { + parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} diff --git a/flag_string.go b/flag_string.go new file mode 100644 index 0000000..9f29da4 --- /dev/null +++ b/flag_string.go @@ -0,0 +1,98 @@ +package cli + +import "flag" + +// StringFlag is a flag with type string +type StringFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + TakesFile bool + Value string + Destination *string +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f StringFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f StringFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f StringFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f StringFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f StringFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f StringFlag) GetValue() string { + return f.Value +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f StringFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + f.Value = envVal + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.StringVar(f.Destination, name, f.Value, f.Usage) + return + } + set.String(name, f.Value, f.Usage) + }) + + return nil +} + +// String looks up the value of a local StringFlag, returns +// "" if not found +func (c *Context) String(name string) string { + return lookupString(name, c.flagSet) +} + +// GlobalString looks up the value of a global StringFlag, returns +// "" if not found +func (c *Context) GlobalString(name string) string { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupString(name, fs) + } + return "" +} + +func lookupString(name string, set *flag.FlagSet) string { + f := set.Lookup(name) + if f != nil { + parsed, err := f.Value.String(), error(nil) + if err != nil { + return "" + } + return parsed + } + return "" +} diff --git a/flag_string_slice.go b/flag_string_slice.go new file mode 100644 index 0000000..e865b2f --- /dev/null +++ b/flag_string_slice.go @@ -0,0 +1,138 @@ +package cli + +import ( + "flag" + "fmt" + "strings" +) + +// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter +type StringSlice []string + +// Set appends the string value to the list of values +func (f *StringSlice) Set(value string) error { + *f = append(*f, value) + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *StringSlice) String() string { + return fmt.Sprintf("%s", *f) +} + +// Value returns the slice of strings set by this flag +func (f *StringSlice) Value() []string { + return *f +} + +// Get returns the slice of strings set by this flag +func (f *StringSlice) Get() interface{} { + return *f +} + +// StringSliceFlag is a flag with type *StringSlice +type StringSliceFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + TakesFile bool + Value *StringSlice +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f StringSliceFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f StringSliceFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f StringSliceFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f StringSliceFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f StringSliceFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f StringSliceFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f StringSliceFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + newVal := &StringSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + if err := newVal.Set(s); err != nil { + return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) + } + } + if f.Value == nil { + f.Value = newVal + } else { + *f.Value = *newVal + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &StringSlice{} + } + set.Var(f.Value, name, f.Usage) + }) + + return nil +} + +// StringSlice looks up the value of a local StringSliceFlag, returns +// nil if not found +func (c *Context) StringSlice(name string) []string { + return lookupStringSlice(name, c.flagSet) +} + +// GlobalStringSlice looks up the value of a global StringSliceFlag, returns +// nil if not found +func (c *Context) GlobalStringSlice(name string) []string { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupStringSlice(name, fs) + } + return nil +} + +func lookupStringSlice(name string, set *flag.FlagSet) []string { + f := set.Lookup(name) + if f != nil { + parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} diff --git a/flag_uint.go b/flag_uint.go new file mode 100644 index 0000000..d6a04f4 --- /dev/null +++ b/flag_uint.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// UintFlag is a flag with type uint +type UintFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value uint + Destination *uint +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f UintFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f UintFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f UintFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f UintFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f UintFlag) GetUsage() string { + return f.Usage +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f UintFlag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + if err != nil { + return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) + } + + f.Value = uint(envValInt) + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.UintVar(f.Destination, name, f.Value, f.Usage) + return + } + set.Uint(name, f.Value, f.Usage) + }) + + return nil +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f UintFlag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + +// Uint looks up the value of a local UintFlag, returns +// 0 if not found +func (c *Context) Uint(name string) uint { + return lookupUint(name, c.flagSet) +} + +// GlobalUint looks up the value of a global UintFlag, returns +// 0 if not found +func (c *Context) GlobalUint(name string) uint { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint(name, fs) + } + return 0 +} + +func lookupUint(name string, set *flag.FlagSet) uint { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return uint(parsed) + } + return 0 +} diff --git a/flag_uint64.go b/flag_uint64.go new file mode 100644 index 0000000..ea6493a --- /dev/null +++ b/flag_uint64.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// Uint64Flag is a flag with type uint64 +type Uint64Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value uint64 + Destination *uint64 +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Uint64Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Uint64Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Uint64Flag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f Uint64Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Uint64Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Uint64Flag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + +// Apply populates the flag given the flag set and environment +// Ignores errors +func (f Uint64Flag) Apply(set *flag.FlagSet) { + _ = f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + if err != nil { + return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) + } + + f.Value = envValInt + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.Uint64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Uint64(name, f.Value, f.Usage) + }) + + return nil +} + +// Uint64 looks up the value of a local Uint64Flag, returns +// 0 if not found +func (c *Context) Uint64(name string) uint64 { + return lookupUint64(name, c.flagSet) +} + +// GlobalUint64 looks up the value of a global Uint64Flag, returns +// 0 if not found +func (c *Context) GlobalUint64(name string) uint64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint64(name, fs) + } + return 0 +} + +func lookupUint64(name string, set *flag.FlagSet) uint64 { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return parsed + } + return 0 +} diff --git a/runtests b/runtests deleted file mode 100755 index b073dc2..0000000 --- a/runtests +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -import argparse -import os -import sys -import tempfile - -from subprocess import check_call, check_output - - -PACKAGE_NAME = os.environ.get( - 'CLI_PACKAGE_NAME', 'github.com/urfave/cli' -) - - -def main(sysargs=sys.argv[:]): - targets = { - 'vet': _vet, - 'test': _test, - 'gfmrun': _gfmrun, - 'toc': _toc, - 'gen': _gen, - } - - parser = argparse.ArgumentParser() - parser.add_argument( - 'target', nargs='?', choices=tuple(targets.keys()), default='test' - ) - args = parser.parse_args(sysargs[1:]) - - targets[args.target]() - return 0 - - -def _test(): - coverprofiles = [] - for subpackage in ['', 'altsrc']: - coverprofile = 'cli.coverprofile' - if subpackage != '': - coverprofile = '{}.coverprofile'.format(subpackage) - - coverprofiles.append(coverprofile) - - _run('go test -v'.split() + [ - '-coverprofile={}'.format(coverprofile), - ('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') - ]) - - combined_name = _combine_coverprofiles(coverprofiles) - _run('go tool cover -func={}'.format(combined_name)) - os.remove(combined_name) - - -def _gfmrun(): - _run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) - - -def _vet(): - _run('go vet ./...') - - -def _toc(): - _run('node_modules/.bin/markdown-toc -i README.md') - _run('git diff --exit-code') - - -def _gen(): - _run('go generate ./...') - _run('git diff --exit-code') - - -def _run(command): - if hasattr(command, 'split'): - command = command.split() - print('runtests: {}'.format(' '.join(command)), file=sys.stderr) - check_call(command) - - -def _gfmrun_count(): - with open('README.md') as infile: - lines = infile.read().splitlines() - return len(filter(_is_go_runnable, lines)) - - -def _is_go_runnable(line): - return line.startswith('package main') - - -def _combine_coverprofiles(coverprofiles): - combined = tempfile.NamedTemporaryFile( - suffix='.coverprofile', delete=False - ) - combined.write('mode: set\n') - - for coverprofile in coverprofiles: - with open(coverprofile, 'r') as infile: - for line in infile.readlines(): - if not line.startswith('mode: '): - combined.write(line) - - combined.flush() - name = combined.name - combined.close() - return name - - -if __name__ == '__main__': - sys.exit(main()) From 5c81af9f10b974cecbec6e20e4976574e86fc78b Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 07:47:18 +0530 Subject: [PATCH 05/35] Remove generate script from travis flow Remove unused dependencies from travis script --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce2a96b..f401fa8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,6 @@ cache: - node_modules before_script: - - go get github.com/shurcooL/vfsgen || true - - go get github.com/shurcooL/httpfs/union || true - go get github.com/urfave/gfmrun/... || true - go get golang.org/x/tools/cmd/goimports - if [ ! -f node_modules/.bin/markdown-toc ] ; then @@ -25,7 +23,6 @@ before_script: fi script: - - go run build.go generate - go run build.go vet - go run build.go test - go run build.go gfmrun From 4a9e440503f4113a351322da93f8630df14dfcaa Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 08:12:09 +0530 Subject: [PATCH 06/35] Fix AppVeyor build --- appveyor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ed74366..6d2dcee 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,13 +14,10 @@ install: - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% - go version - go env - - go get github.com/shurcooL/vfsgen - - go get github.com/shurcooL/httpfs/union - go get github.com/urfave/gfmrun/... - go get -v -t ./... build_script: - - go run build.go generate - go run build.go vet - go run build.go test - go run build.go gfmrun From afd0ecbbf2fbda2f9459046228ccc9e8d2693258 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 08:19:06 +0530 Subject: [PATCH 07/35] Add support for Go 1.13 Drop support for Go 1.11 Use md2man v2 to avoid dependency issues when building with Go Modules Enabled Update TravisCI build environment images (trusty was deprecated) Add optional Windows build --- .travis.yml | 16 +++++++++------- docs.go | 2 +- go.mod | 2 +- go.sum | 12 ++++++++---- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce2a96b..7eca52c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,19 @@ language: go sudo: false -dist: trusty -osx_image: xcode8.3 +dist: bionic +osx_image: xcode10 go: - - 1.10.x - 1.11.x - 1.12.x + - 1.13.x os: - linux - osx + - windows + +env: + GO111MODULE=on cache: directories: @@ -20,14 +24,12 @@ before_script: - go get github.com/shurcooL/httpfs/union || true - go get github.com/urfave/gfmrun/... || true - go get golang.org/x/tools/cmd/goimports - - if [ ! -f node_modules/.bin/markdown-toc ] ; then - npm install markdown-toc ; - fi + - npm install markdown-toc script: - go run build.go generate - go run build.go vet - - go run build.go test + - go run build.go testm - go run build.go gfmrun - go run build.go toc diff --git a/docs.go b/docs.go index 0beadf2..ed79b44 100644 --- a/docs.go +++ b/docs.go @@ -8,7 +8,7 @@ import ( "strings" "text/template" - "github.com/cpuguy83/go-md2man/md2man" + "github.com/cpuguy83/go-md2man/v2/md2man" ) // ToMarkdown creates a markdown string for the `*App` diff --git a/go.mod b/go.mod index 1b94cfe..38803d6 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,6 @@ go 1.10 require ( github.com/BurntSushi/toml v0.3.1 - github.com/cpuguy83/go-md2man v1.0.10 + github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index a8e436a..ef121ff 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,13 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= From edbf66c25cf83541faee77d0064fdb5ac35a51b1 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 08:23:04 +0530 Subject: [PATCH 08/35] Update gfmrun import command to suite Go Modules pattern Fix test command typo in travis script --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7eca52c..86394e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,16 +20,14 @@ cache: - node_modules before_script: - - go get github.com/shurcooL/vfsgen || true - - go get github.com/shurcooL/httpfs/union || true - - go get github.com/urfave/gfmrun/... || true + - go get github.com/urfave/gfmrun/cmd/gfmrun - go get golang.org/x/tools/cmd/goimports - npm install markdown-toc script: - go run build.go generate - go run build.go vet - - go run build.go testm + - go run build.go test - go run build.go gfmrun - go run build.go toc From 39bd6176649871817d1966b6b91f042be4b62fe9 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 08:37:16 +0530 Subject: [PATCH 09/35] Cleanup after before_script to avoid git diff errors remove windows build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 86394e5..e978823 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,6 @@ go: os: - linux - osx - - windows env: GO111MODULE=on @@ -23,6 +22,7 @@ before_script: - go get github.com/urfave/gfmrun/cmd/gfmrun - go get golang.org/x/tools/cmd/goimports - npm install markdown-toc + - go mod tidy script: - go run build.go generate From 426e21c150d9a33e4d8c13c2a13c5234e85f3a0e Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 13:15:47 +0530 Subject: [PATCH 10/35] Update .travis.yml Set GOPROXY in Travis environment --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e978823..bfd07ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ os: env: GO111MODULE=on + GOPROXY=https://proxy.golang.org cache: directories: From 534d60bb9bb0476141540ec77c5a3d51e176d162 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Tue, 10 Sep 2019 06:40:45 +0530 Subject: [PATCH 11/35] Bump minimum supported version of Go to 1.11 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 38803d6..7d04d20 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/urfave/cli -go 1.10 +go 1.11 require ( github.com/BurntSushi/toml v0.3.1 From 054fbefec36cad7425dc6f4cfb6d2963c2710751 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Tue, 10 Sep 2019 06:42:34 +0530 Subject: [PATCH 12/35] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a30fcf..b49ab5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ ## [Unreleased] +## [1.22.1] - 2019-09-10 + +### Changed + +* Remove flag code generation logic, legacy python test runner in [urfave/cli/pull/883](https://github.com/urfave/cli/pull/883) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) +* Enable Go Modules support, drop support for `Go 1.10` add support for `Go 1.13` in [urfave/cli/pull/885](https://github.com/urfave/cli/pull/885) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) + ## [1.22.0] - 2019-09-07 ### Fixed From 487be14dceb185a3321cacd9bf302f5e811f5ee8 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 10 Sep 2019 13:49:11 -0400 Subject: [PATCH 13/35] Don't output hidden commands for man pages Signed-off-by: Michael Crosby --- docs.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs.go b/docs.go index ed79b44..5b94566 100644 --- a/docs.go +++ b/docs.go @@ -57,6 +57,9 @@ func prepareCommands(commands []Command, level int) []string { coms := []string{} for i := range commands { command := &commands[i] + if command.Hidden { + continue + } usage := "" if command.Usage != "" { usage = command.Usage From 5c019b10ca37c1a0b74b637d3b0aa25dfeb0110f Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 05:33:46 +0530 Subject: [PATCH 14/35] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b49ab5f..8b22624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ ## [1.22.1] - 2019-09-10 +### Fixed + +* Hide output of hidden commands on man pages in [urfave/cli/pull/889](https://github.com/urfave/cli/pull/889) via [@crosbymichael](https://github.com/crosbymichael) + ### Changed * Remove flag code generation logic, legacy python test runner in [urfave/cli/pull/883](https://github.com/urfave/cli/pull/883) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) From 0aee120c32003fff6f320c2a00a41d03285fdde0 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 05:37:41 +0530 Subject: [PATCH 15/35] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b22624..1d734c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -468,7 +468,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. ### Added - Initial implementation. -[Unreleased]: https://github.com/urfave/cli/compare/v1.22.0...HEAD +[Unreleased]: https://github.com/urfave/cli/compare/v1.22.1...HEAD +[1.22.1]: https://github.com/urfave/cli/compare/v1.22.0...v1.22.1 [1.22.0]: https://github.com/urfave/cli/compare/v1.21.0...v1.22.0 [1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0 [1.20.0]: https://github.com/urfave/cli/compare/v1.19.1...v1.20.0 From 7d6a604106e44732edc0a76f4a4800c8c27ddfbe Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 08:59:51 +0530 Subject: [PATCH 16/35] Fix #878 --- context.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index db7cd69..485f529 100644 --- a/context.go +++ b/context.go @@ -313,9 +313,17 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { - key := strings.Split(f.GetName(), ",")[0] - if !context.IsSet(key) { - missingFlags = append(missingFlags, key) + key := strings.Split(f.GetName(), ",") + if len(key) > 1 { + // has short name + if !context.IsSet(strings.TrimSpace(key[0])) && !context.IsSet(strings.TrimSpace(key[1])) { + missingFlags = append(missingFlags, key[0]) + } + } else { + // does not have short name + if !context.IsSet(strings.TrimSpace(key[0])) { + missingFlags = append(missingFlags, key[0]) + } } } } From cbb9e015b89225aa090c41085bdb0933f6290d96 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 09:21:45 +0530 Subject: [PATCH 17/35] Improve Code and Add Test Case --- context.go | 6 ++++-- context_test.go | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 485f529..8f1dcd8 100644 --- a/context.go +++ b/context.go @@ -314,14 +314,16 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { key := strings.Split(f.GetName(), ",") + shortName := strings.TrimSpace(key[0]) if len(key) > 1 { // has short name - if !context.IsSet(strings.TrimSpace(key[0])) && !context.IsSet(strings.TrimSpace(key[1])) { + longName := strings.TrimSpace(key[1]) + if !context.IsSet(shortName) && !context.IsSet(longName) { missingFlags = append(missingFlags, key[0]) } } else { // does not have short name - if !context.IsSet(strings.TrimSpace(key[0])) { + if !context.IsSet(shortName) { missingFlags = append(missingFlags, key[0]) } } diff --git a/context_test.go b/context_test.go index 9e594dd..1d52921 100644 --- a/context_test.go +++ b/context_test.go @@ -517,6 +517,13 @@ func TestCheckRequiredFlags(t *testing.T) { }, parseInput: []string{"--requiredFlag", "myinput", "--requiredFlagTwo", "myinput"}, }, + { + testCase: "required_flag_with_short_name", + flags: []Flag{ + StringSliceFlag{Name: "names, N", Required: true}, + }, + parseInput: []string{"-N", "asd", "-N", "qwe"}, + }, } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { From 36cdaa9964df03e2b8f8d2147a99497536851ad9 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 10:34:00 +0530 Subject: [PATCH 18/35] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d734c4..4a3fd46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,12 @@ ## [Unreleased] -## [1.22.1] - 2019-09-10 +## [1.22.1] - 2019-09-11 ### Fixed * Hide output of hidden commands on man pages in [urfave/cli/pull/889](https://github.com/urfave/cli/pull/889) via [@crosbymichael](https://github.com/crosbymichael) +* Using short flag names for required flags throws an error in [urfave/cli/pull/890](https://github.com/urfave/cli/pull/890) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) ### Changed From bac5bde38c7725990645cf9b2bf2c824594f3963 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Wed, 11 Sep 2019 09:06:02 +0200 Subject: [PATCH 19/35] Don't generate fish completion for hidden commands Added the missing test case as well. Signed-off-by: Sascha Grunert --- docs_test.go | 3 +++ fish.go | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/docs_test.go b/docs_test.go index 6ffc995..f18d12d 100644 --- a/docs_test.go +++ b/docs_test.go @@ -53,6 +53,9 @@ func testApp() *App { Usage: "retrieve generic information", }, { Name: "some-command", + }, { + Name: "hidden-command", + Hidden: true, }} app.UsageText = "app [first_arg] [second_arg]" app.Usage = "Some app" diff --git a/fish.go b/fish.go index 1121a20..cf183af 100644 --- a/fish.go +++ b/fish.go @@ -69,6 +69,10 @@ func (a *App) prepareFishCommands(commands []Command, allCommands *[]string, pre for i := range commands { command := &commands[i] + if command.Hidden { + continue + } + var completion strings.Builder completion.WriteString(fmt.Sprintf( "complete -r -c %s -n '%s' -a '%s'", From 71eaf37e337d5daea12c6a137113c71056151530 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 14:09:50 +0530 Subject: [PATCH 20/35] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a3fd46..7c1a27e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixed * Hide output of hidden commands on man pages in [urfave/cli/pull/889](https://github.com/urfave/cli/pull/889) via [@crosbymichael](https://github.com/crosbymichael) +* Don't generate fish completion for hidden commands [urfave/cli/pull/891](https://github.com/urfave/891) via [@saschagrunert](https://github.com/saschagrunert) * Using short flag names for required flags throws an error in [urfave/cli/pull/890](https://github.com/urfave/cli/pull/890) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) ### Changed From 1547ac2f6a3d3d39fe4d49570c0d1c2401a8f20e Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 14:15:20 +0530 Subject: [PATCH 21/35] Modify variable names --- context.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/context.go b/context.go index 8f1dcd8..485f529 100644 --- a/context.go +++ b/context.go @@ -314,16 +314,14 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { key := strings.Split(f.GetName(), ",") - shortName := strings.TrimSpace(key[0]) if len(key) > 1 { // has short name - longName := strings.TrimSpace(key[1]) - if !context.IsSet(shortName) && !context.IsSet(longName) { + if !context.IsSet(strings.TrimSpace(key[0])) && !context.IsSet(strings.TrimSpace(key[1])) { missingFlags = append(missingFlags, key[0]) } } else { // does not have short name - if !context.IsSet(shortName) { + if !context.IsSet(strings.TrimSpace(key[0])) { missingFlags = append(missingFlags, key[0]) } } From c6ee3b4904ed76d34f277c315c2097ae7b22d38f Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 14:34:41 +0530 Subject: [PATCH 22/35] Use iterative logic to determine missing flag --- context.go | 21 ++++++++++++--------- context_test.go | 7 +++++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/context.go b/context.go index 485f529..2f18f3f 100644 --- a/context.go +++ b/context.go @@ -313,18 +313,21 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { - key := strings.Split(f.GetName(), ",") - if len(key) > 1 { - // has short name - if !context.IsSet(strings.TrimSpace(key[0])) && !context.IsSet(strings.TrimSpace(key[1])) { - missingFlags = append(missingFlags, key[0]) + var flagPresent bool + var flagName string + for _, key := range strings.Split(f.GetName(), ",") { + if len(key) > 1 { + flagName = key } - } else { - // does not have short name - if !context.IsSet(strings.TrimSpace(key[0])) { - missingFlags = append(missingFlags, key[0]) + + if context.IsSet(strings.TrimSpace(key)) { + flagPresent = true } } + + if !flagPresent { + missingFlags = append(missingFlags, flagName) + } } } diff --git a/context_test.go b/context_test.go index 1d52921..13c3701 100644 --- a/context_test.go +++ b/context_test.go @@ -524,6 +524,13 @@ func TestCheckRequiredFlags(t *testing.T) { }, parseInput: []string{"-N", "asd", "-N", "qwe"}, }, + { + testCase: "required_flag_with_short_name", + flags: []Flag{ + StringSliceFlag{Name: "names, N, n", Required: true}, + }, + parseInput: []string{"-n", "asd", "-n", "qwe"}, + }, } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { From f8bb66ae7d679973cf9b3f6f8c3dc6933404a31a Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 14:42:38 +0530 Subject: [PATCH 23/35] Fix Typo --- context_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context_test.go b/context_test.go index 13c3701..28f5e08 100644 --- a/context_test.go +++ b/context_test.go @@ -525,7 +525,7 @@ func TestCheckRequiredFlags(t *testing.T) { parseInput: []string{"-N", "asd", "-N", "qwe"}, }, { - testCase: "required_flag_with_short_name", + testCase: "required_flag_with_multiple_short_names", flags: []Flag{ StringSliceFlag{Name: "names, N, n", Required: true}, }, From fa858dcc260fb07c25aab13650d9fa0e64f851c7 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 15:10:14 +0530 Subject: [PATCH 24/35] Ensure flag is not blank --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 2f18f3f..ecfc032 100644 --- a/context.go +++ b/context.go @@ -325,7 +325,7 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { } } - if !flagPresent { + if !flagPresent && flagName != "" { missingFlags = append(missingFlags, flagName) } } From 7d0751fa1309a01b405cde327cc43275d13016a2 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 11 Sep 2019 19:25:51 -0700 Subject: [PATCH 25/35] add argIsFlag check --- command.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index d33435e..6471f10 100644 --- a/command.go +++ b/command.go @@ -238,7 +238,7 @@ func reorderArgs(commandFlags []Flag, args []string) []string { } readFlagValue = false - if arg != "-" && strings.HasPrefix(arg, "-") { + if arg != "-" && strings.HasPrefix(arg, "-") && argIsFlag(commandFlags, arg) { reorderedArgs = append(reorderedArgs, arg) readFlagValue = !strings.Contains(arg, "=") @@ -251,6 +251,18 @@ func reorderArgs(commandFlags []Flag, args []string) []string { return append(reorderedArgs, remainingArgs...) } +func argIsFlag(commandFlags []Flag, arg string) bool { + strippedArg := strings.ReplaceAll(arg, "-", "") + for _, flag := range commandFlags { + for _, key := range strings.Split(flag.GetName(), ",") { + if key == strippedArg { + return true + } + } + } + return false +} + // Names returns the names including short names and aliases. func (c Command) Names() []string { names := []string{c.Name} From 49dbeed6877737d97ec6e121763a41c617d7b272 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 11 Sep 2019 20:24:42 -0700 Subject: [PATCH 26/35] handle `=` input --- command.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/command.go b/command.go index 6471f10..d52080e 100644 --- a/command.go +++ b/command.go @@ -252,10 +252,11 @@ func reorderArgs(commandFlags []Flag, args []string) []string { } func argIsFlag(commandFlags []Flag, arg string) bool { - strippedArg := strings.ReplaceAll(arg, "-", "") + arg = strings.ReplaceAll(arg, "-", "") + arg = strings.Split(arg, "=")[0] for _, flag := range commandFlags { for _, key := range strings.Split(flag.GetName(), ",") { - if key == strippedArg { + if key == arg { return true } } From 10682fbde6f00b1008d6748d7ffb090bce421da7 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 11 Sep 2019 20:28:48 -0700 Subject: [PATCH 27/35] docs --- command.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command.go b/command.go index d52080e..af91dbc 100644 --- a/command.go +++ b/command.go @@ -252,8 +252,11 @@ func reorderArgs(commandFlags []Flag, args []string) []string { } func argIsFlag(commandFlags []Flag, arg string) bool { + // this line turns `--flag` into `flag` arg = strings.ReplaceAll(arg, "-", "") + // this line turns `flag=value` into `flag` arg = strings.Split(arg, "=")[0] + // look through all the flags, to see if the `arg` is one of our flags for _, flag := range commandFlags { for _, key := range strings.Split(flag.GetName(), ",") { if key == arg { From 09cdbbfe281fd79e92616a28f1fdf701e9007c22 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 11 Sep 2019 20:29:31 -0700 Subject: [PATCH 28/35] more docs --- command.go | 1 + 1 file changed, 1 insertion(+) diff --git a/command.go b/command.go index af91dbc..6e09a32 100644 --- a/command.go +++ b/command.go @@ -264,6 +264,7 @@ func argIsFlag(commandFlags []Flag, arg string) bool { } } } + // return false if this arg was not one of our flags return false } From 223e21796c35c3f4a456db83c0d450ef5fe4db01 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 11 Sep 2019 21:08:52 -0700 Subject: [PATCH 29/35] swap a test case --- command_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command_test.go b/command_test.go index 56698fe..47792e1 100644 --- a/command_test.go +++ b/command_test.go @@ -18,7 +18,7 @@ func TestCommandFlagParsing(t *testing.T) { UseShortOptionHandling bool }{ // Test normal "not ignoring flags" flow - {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break"), false}, + {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, nil, false}, // Test no arg reorder {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil, false}, From 6da413ad825dea1f74563bdfee1f8e5c23c1c650 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 11 Sep 2019 22:45:51 -0700 Subject: [PATCH 30/35] fix go version support? --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index 6e09a32..0a6b6ed 100644 --- a/command.go +++ b/command.go @@ -253,7 +253,7 @@ func reorderArgs(commandFlags []Flag, args []string) []string { func argIsFlag(commandFlags []Flag, arg string) bool { // this line turns `--flag` into `flag` - arg = strings.ReplaceAll(arg, "-", "") + arg = strings.Replace(arg, "-", "", -1) // this line turns `flag=value` into `flag` arg = strings.Split(arg, "=")[0] // look through all the flags, to see if the `arg` is one of our flags From bfdd794eb3d0f8d99af8939a3a8c24547ef262ca Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 11 Sep 2019 23:05:26 -0700 Subject: [PATCH 31/35] docs --- command.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/command.go b/command.go index 0a6b6ed..3993e0f 100644 --- a/command.go +++ b/command.go @@ -220,7 +220,7 @@ func reorderArgs(commandFlags []Flag, args []string) []string { var remainingArgs []string var reorderedArgs []string - readFlagValue := false + nextIndexMayContainValue := false for i, arg := range args { // dont reorder any args after a -- @@ -229,28 +229,30 @@ func reorderArgs(commandFlags []Flag, args []string) []string { if arg == "--" { remainingArgs = append(remainingArgs, args[i:]...) break - } - if readFlagValue && !strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") { - readFlagValue = false + // checks if this arg is a value that should be re-ordered next to its associated flag + } else if nextIndexMayContainValue && !strings.HasPrefix(arg, "-") { + nextIndexMayContainValue = false reorderedArgs = append(reorderedArgs, arg) - continue - } - readFlagValue = false - if arg != "-" && strings.HasPrefix(arg, "-") && argIsFlag(commandFlags, arg) { + // checks if this is an arg that should be re-ordered + } else if arg != "-" && strings.HasPrefix(arg, "-") && argIsFlag(commandFlags, arg) { + + // we have determined that this is a flag that we should re-order reorderedArgs = append(reorderedArgs, arg) + // if this arg does not contain a "=", then the next index may contain the value for this flag + nextIndexMayContainValue = !strings.Contains(arg, "=") - readFlagValue = !strings.Contains(arg, "=") - continue + // simply append any remaining args + } else { + remainingArgs = append(remainingArgs, arg) } - - remainingArgs = append(remainingArgs, arg) } return append(reorderedArgs, remainingArgs...) } +// argIsFlag checks if an arg is one of our command flags func argIsFlag(commandFlags []Flag, arg string) bool { // this line turns `--flag` into `flag` arg = strings.Replace(arg, "-", "", -1) From 06e3d38d8865db934e6b6bcf6de6c132e0d85d56 Mon Sep 17 00:00:00 2001 From: Robert Liebowitz Date: Fri, 13 Sep 2019 05:30:07 -0400 Subject: [PATCH 32/35] Avoid panic for missing flag value Currently, in cases where a flag value is required but not passed and short-option handling is enabled, a panic will occur due to a nil pointer dereference. This prevents that situation from occurring, instead propagating the appropriate error. --- app.go | 8 ++++---- app_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ command.go | 7 ++++++- command_test.go | 43 ++++++++++++++++++++++++------------------- parse.go | 22 ++++++++++++---------- 5 files changed, 91 insertions(+), 34 deletions(-) diff --git a/app.go b/app.go index 76e869d..95d2038 100644 --- a/app.go +++ b/app.go @@ -199,12 +199,12 @@ func (a *App) Run(arguments []string) (err error) { // always appends the completion flag at the end of the command shellComplete, arguments := checkShellCompleteFlag(a, arguments) - _, err = a.newFlagSet() + set, err := a.newFlagSet() if err != nil { return err } - set, err := parseIter(a, arguments[1:]) + err = parseIter(set, a, arguments[1:]) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, nil) if nerr != nil { @@ -322,12 +322,12 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } a.Commands = newCmds - _, err = a.newFlagSet() + set, err := a.newFlagSet() if err != nil { return err } - set, err := parseIter(a, ctx.Args().Tail()) + err = parseIter(set, a, ctx.Args().Tail()) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, ctx) diff --git a/app_test.go b/app_test.go index bccede4..33024ff 100644 --- a/app_test.go +++ b/app_test.go @@ -659,6 +659,17 @@ func TestApp_UseShortOptionHandling(t *testing.T) { expect(t, name, expected) } +func TestApp_UseShortOptionHandling_missing_value(t *testing.T) { + app := NewApp() + app.UseShortOptionHandling = true + app.Flags = []Flag{ + StringFlag{Name: "name, n"}, + } + + err := app.Run([]string{"", "-n"}) + expect(t, err, errors.New("flag needs an argument: -n")) +} + func TestApp_UseShortOptionHandlingCommand(t *testing.T) { var one, two bool var name string @@ -688,6 +699,21 @@ func TestApp_UseShortOptionHandlingCommand(t *testing.T) { expect(t, name, expected) } +func TestApp_UseShortOptionHandlingCommand_missing_value(t *testing.T) { + app := NewApp() + app.UseShortOptionHandling = true + command := Command{ + Name: "cmd", + Flags: []Flag{ + StringFlag{Name: "name, n"}, + }, + } + app.Commands = []Command{command} + + err := app.Run([]string{"", "cmd", "-n"}) + expect(t, err, errors.New("flag needs an argument: -n")) +} + func TestApp_UseShortOptionHandlingSubCommand(t *testing.T) { var one, two bool var name string @@ -722,6 +748,25 @@ func TestApp_UseShortOptionHandlingSubCommand(t *testing.T) { expect(t, name, expected) } +func TestApp_UseShortOptionHandlingSubCommand_missing_value(t *testing.T) { + app := NewApp() + app.UseShortOptionHandling = true + command := Command{ + Name: "cmd", + } + subCommand := Command{ + Name: "sub", + Flags: []Flag{ + StringFlag{Name: "name, n"}, + }, + } + command.Subcommands = []Command{subCommand} + app.Commands = []Command{command} + + err := app.Run([]string{"", "cmd", "sub", "-n"}) + expect(t, err, errors.New("flag needs an argument: -n")) +} + func TestApp_Float64Flag(t *testing.T) { var meters float64 diff --git a/command.go b/command.go index 44a90de..b10e4d4 100644 --- a/command.go +++ b/command.go @@ -193,7 +193,12 @@ func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { args = reorderArgs(args) } - set, err := parseIter(c, args) + set, err := c.newFlagSet() + if err != nil { + return nil, err + } + + err = parseIter(set, c, args) if err != nil { return nil, err } diff --git a/command_test.go b/command_test.go index 56698fe..6235b41 100644 --- a/command_test.go +++ b/command_test.go @@ -70,29 +70,34 @@ func TestParseAndRunShortOpts(t *testing.T) { {[]string{"foo", "test", "-af"}, nil, []string{}}, {[]string{"foo", "test", "-cf"}, nil, []string{}}, {[]string{"foo", "test", "-acf"}, nil, []string{}}, - {[]string{"foo", "test", "-invalid"}, errors.New("flag provided but not defined: -invalid"), []string{}}, + {[]string{"foo", "test", "-invalid"}, errors.New("flag provided but not defined: -invalid"), nil}, {[]string{"foo", "test", "-acf", "arg1", "-invalid"}, nil, []string{"arg1", "-invalid"}}, - } - - var args []string - cmd := Command{ - Name: "test", - Usage: "this is for testing", - Description: "testing", - Action: func(c *Context) error { - args = c.Args() - return nil - }, - SkipArgReorder: true, - UseShortOptionHandling: true, - Flags: []Flag{ - BoolFlag{Name: "abc, a"}, - BoolFlag{Name: "cde, c"}, - BoolFlag{Name: "fgh, f"}, - }, + {[]string{"foo", "test", "-acfi", "not-arg", "arg1", "-invalid"}, nil, []string{"arg1", "-invalid"}}, + {[]string{"foo", "test", "-i", "ivalue"}, nil, []string{}}, + {[]string{"foo", "test", "-i", "ivalue", "arg1"}, nil, []string{"arg1"}}, + {[]string{"foo", "test", "-i"}, errors.New("flag needs an argument: -i"), nil}, } for _, c := range cases { + var args []string + cmd := Command{ + Name: "test", + Usage: "this is for testing", + Description: "testing", + Action: func(c *Context) error { + args = c.Args() + return nil + }, + SkipArgReorder: true, + UseShortOptionHandling: true, + Flags: []Flag{ + BoolFlag{Name: "abc, a"}, + BoolFlag{Name: "cde, c"}, + BoolFlag{Name: "fgh, f"}, + StringFlag{Name: "ijk, i"}, + }, + } + app := NewApp() app.Commands = []Command{cmd} diff --git a/parse.go b/parse.go index 865accf..2c2005c 100644 --- a/parse.go +++ b/parse.go @@ -14,22 +14,17 @@ type iterativeParser interface { // iteratively catch parsing errors. This way we achieve LR parsing without // transforming any arguments. Otherwise, there is no way we can discriminate // combined short options from common arguments that should be left untouched. -func parseIter(ip iterativeParser, args []string) (*flag.FlagSet, error) { +func parseIter(set *flag.FlagSet, ip iterativeParser, args []string) error { for { - set, err := ip.newFlagSet() - if err != nil { - return nil, err - } - - err = set.Parse(args) + err := set.Parse(args) if !ip.useShortOptionHandling() || err == nil { - return set, err + return err } errStr := err.Error() trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ") if errStr == trimmed { - return nil, err + return err } // regenerate the initial args with the split short opts @@ -42,7 +37,7 @@ func parseIter(ip iterativeParser, args []string) (*flag.FlagSet, error) { shortOpts := splitShortOptions(set, trimmed) if len(shortOpts) == 1 { - return nil, err + return err } // add each short option and all remaining arguments @@ -50,6 +45,13 @@ func parseIter(ip iterativeParser, args []string) (*flag.FlagSet, error) { newArgs = append(newArgs, args[i+1:]...) args = newArgs } + + // Since custom parsing failed, replace the flag set before retrying + newSet, err := ip.newFlagSet() + if err != nil { + return err + } + *set = *newSet } } From 3f6f97754aa8af1b6bd7b9324bf1e4c72023a269 Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Fri, 13 Sep 2019 07:27:16 -0700 Subject: [PATCH 33/35] Update command.go Co-Authored-By: Sascha Grunert --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index 3993e0f..037abe9 100644 --- a/command.go +++ b/command.go @@ -217,7 +217,7 @@ func (c *Command) useShortOptionHandling() bool { // reorderArgs moves all flags (via reorderedArgs) before the rest of // the arguments (remainingArgs) as this is what flag expects. func reorderArgs(commandFlags []Flag, args []string) []string { - var remainingArgs []string + var remainingArgs, reorderedArgs []string var reorderedArgs []string nextIndexMayContainValue := false From 72c249389c590a6817f21cf21c68bc0a076823bf Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Fri, 13 Sep 2019 07:35:16 -0700 Subject: [PATCH 34/35] Update command.go --- command.go | 1 - 1 file changed, 1 deletion(-) diff --git a/command.go b/command.go index 037abe9..9724a3b 100644 --- a/command.go +++ b/command.go @@ -218,7 +218,6 @@ func (c *Command) useShortOptionHandling() bool { // the arguments (remainingArgs) as this is what flag expects. func reorderArgs(commandFlags []Flag, args []string) []string { var remainingArgs, reorderedArgs []string - var reorderedArgs []string nextIndexMayContainValue := false for i, arg := range args { From 9072b3f9a5c74dbf62adbc47424478314c57d0c2 Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Wed, 18 Sep 2019 00:15:07 -0700 Subject: [PATCH 35/35] Copy appveyor changes from the V2 PR Copied from https://github.com/urfave/cli/pull/892/ --- appveyor.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 6d2dcee..1f30f3b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,16 +6,21 @@ image: Visual Studio 2017 clone_folder: c:\gopath\src\github.com\urfave\cli +cache: + - node_modules + environment: GOPATH: C:\gopath GOVERSION: 1.11.x + GO111MODULE: on + GOPROXY: https://proxy.golang.org install: - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% - go version - go env - - go get github.com/urfave/gfmrun/... - - go get -v -t ./... + - go get github.com/urfave/gfmrun/cmd/gfmrun + - go mod vendor build_script: - go run build.go vet