{"id":16921361,"url":"https://github.com/thediveo/enumflag","last_synced_at":"2025-04-06T19:11:18.224Z","repository":{"id":54612664,"uuid":"254700073","full_name":"thediveo/enumflag","owner":"thediveo","description":"Typed enumeration flags (single and slice) for spf13/pflag, Go's flag drop-in package.","archived":false,"fork":false,"pushed_at":"2025-01-05T20:19:53.000Z","size":123,"stargazers_count":30,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-30T18:08:31.107Z","etag":null,"topics":["cli","cli-flags","enum","go","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thediveo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-10T17:52:54.000Z","updated_at":"2025-03-21T17:22:52.000Z","dependencies_parsed_at":"2022-08-13T21:31:12.529Z","dependency_job_id":"904d38ac-a019-48f4-9efa-79e6f230233d","html_url":"https://github.com/thediveo/enumflag","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thediveo%2Fenumflag","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thediveo%2Fenumflag/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thediveo%2Fenumflag/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thediveo%2Fenumflag/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thediveo","download_url":"https://codeload.github.com/thediveo/enumflag/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247535516,"owners_count":20954576,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","cli-flags","enum","go","golang"],"created_at":"2024-10-13T19:51:28.658Z","updated_at":"2025-04-06T19:11:18.203Z","avatar_url":"https://github.com/thediveo.png","language":"Go","readme":"# CLI Enumeration Flags\n[![Go Reference](https://pkg.go.dev/badge/github.com/thediveo/enumflag.svg)](https://pkg.go.dev/github.com/thediveo/enumflag/v2)\n[![GitHub](https://img.shields.io/github/license/thediveo/enumflag)](https://img.shields.io/github/license/thediveo/enumflag)\n![build and test](https://github.com/thediveo/enumflag/actions/workflows/buildandtest.yaml/badge.svg?branch=master)\n[![Go Report Card](https://goreportcard.com/badge/github.com/thediveo/enumflag/v2)](https://goreportcard.com/report/github.com/thediveo/enumflag/v2)\n![Coverage](https://img.shields.io/badge/Coverage-100.0%25-brightgreen)\n\n`enumflag/v2` is a Golang package which supplements the Golang CLI flag packages\n[spf13/cobra](https://github.com/spf13/cobra) and\n[spf13/pflag](https://github.com/spf13/pflag) with enumeration flags, including\nsupport for enumeration slices. Thanks to Go generics, `enumflag/v2` now\nprovides type-safe enumeration flags (and thus requires Go 1.18 or later).\n\n\u003e The v2 API is source-compatible with v0 unless you've used the `Get()` method\n\u003e in the past. However, since the use of Go generics might be a breaking change\n\u003e to downstream projects the semantic major version of `enumflag` thus went from\n\u003e v0 straight to v2.\n\nFor instance, users can specify enum flags as `--mode=foo` or `--mode=bar`,\nwhere `foo` and `bar` are valid enumeration values. Other values which are not\npart of the set of allowed enumeration values cannot be set and raise CLI flag\nerrors. In case of an enumeration _slice_ flag users can specify multiple\nenumeration values either with a single flag `--mode=foo,bar` or multiple flag\ncalls, such as `--mode=foo --mode=bar`.\n\nApplication programmers then simply deal with enumeration values in form of\nuints (or ints, _erm_, anything that satisfies `constraints.Integer`s),\nliberated from parsing strings and validating enumeration flags.\n\nFor devcontainer instructions, please see the [section \"DevContainer\"\nbelow](#devcontainer).\n\n## Alternatives\n\nIn case you are just interested in string-based one-of-a-set flags, then the\nfollowing packages offer you a minimalist approach:\n\n- [hashicorp/packer/helper/enumflag](https://godoc.org/github.com/hashicorp/packer/helper/enumflag)\n  really is a reduced-to-the-max version without any whistles and bells.\n- [creachadair/goflags/enumflag](https://godoc.org/github.com/creachadair/goflags/enumflag)\n  has a similar, but slightly more elaborate API with additional \"indices\" for\n  enumeration values.\n\nBut if you instead want to handle one-of-a-set flags as properly typed\nenumerations instead of strings, or if you need (multiple-of-a-set) slice\nsupport, then please read on.\n\n## Installation\n\nTo add `enumflag/v2` as a dependency, in your Go module issue:\n\n```bash\ngo get github.com/thediveo/enumflag/v2\n```\n\n## How To Use\n\n- [start with your own enum types](#start-with-your-own-enum-types),\n- optional: [shell completion](#shell-completion),\n- optional: [use existing enum types and non-zero defaults](#use-existing-enum-types),\n- optional: [CLI flag with default](#cli-flag-with-default),\n- optional: [CLI flag without a default value](#cli-flag-without-default),\n- optional: [slice of enums](#slice-of-enums).\n\n### Start With Your Own Enum Types\n\nWithout further ado, here's how to define and use enum flags in your own\napplications...\n\n```go\nimport (\n    \"fmt\"\n\n    \"github.com/spf13/cobra\"\n    \"github.com/thediveo/enumflag/v2\"\n)\n\n// ① Define your new enum flag type. It can be derived from enumflag.Flag,\n// but it doesn't need to be as long as it satisfies constraints.Integer.\ntype FooMode enumflag.Flag\n\n// ② Define the enumeration values for FooMode.\nconst (\n    Foo FooMode = iota\n    Bar\n)\n\n// ③ Map enumeration values to their textual representations (value\n// identifiers).\nvar FooModeIds = map[FooMode][]string{\n    Foo: {\"foo\"},\n    Bar: {\"bar\"},\n}\n\n// ④ Now use the FooMode enum flag. If you want a non-zero default, then\n// simply set it here, such as in \"foomode = Bar\".\nvar foomode FooMode\n\nfunc main() {\n    rootCmd := \u0026cobra.Command{\n        Run: func(cmd *cobra.Command, _ []string) {\n            fmt.Printf(\"mode is: %d=%q\\n\",\n                foomode,\n                cmd.PersistentFlags().Lookup(\"mode\").Value.String())\n        },\n    }\n    // ⑤ Define the CLI flag parameters for your wrapped enum flag.\n    rootCmd.PersistentFlags().VarP(\n        enumflag.New(\u0026foomode, \"mode\", FooModeIds, enumflag.EnumCaseInsensitive),\n        \"mode\", \"m\",\n        \"foos the output; can be 'foo' or 'bar'\")\n\n    rootCmd.SetArgs([]string{\"--mode\", \"bAr\"})\n    _ = rootCmd.Execute()\n}\n```\n\nThe boilerplate pattern is always the same:\n\n1. Define your own new enumeration type, such as `type FooMode enumflag.Flag`.\n2. Define the constants in your enumeration.\n3. Define the mapping of the constants onto enum values (textual\n   representations).\n4. Somewhere, declare a flag variable of your enum flag type.\n   - If you want to use a non-zero default enum value, just go ahead and set\n     it: `var foomode = Bar`. It will be used correctly.\n5. Wire up your flag variable to its flag long and short names, et cetera.\n\n### Shell Completion\n\nDynamic flag completion can be enabled by calling the `RegisterCompletion(...)`\nreceiver of an enum flag (more precise: flag value) created using\n`enumflag.New(...)`. `enumflag` supports dynamic flag completion for both scalar\nand slice enum flags. Unfortunately, due to the cobra API design it isn't\npossible for `enumflag` to offer a fluent API. Instead, creation, adding, and\nregistering have to be carried out as separate instructions.\n\n```go\n    // ⑤ Define the CLI flag parameters for your wrapped enum flag.\n    ef := enumflag.New(\u0026foomode, \"mode\", FooModeIds, enumflag.EnumCaseInsensitive)\n    rootCmd.PersistentFlags().VarP(\n        ef,\n        \"mode\", \"m\",\n        \"foos the output; can be 'foo' or 'bar'\")\n    // ⑥ register completion\n    ef.RegisterCompletion(rootCmd, \"mode\", enumflag.Help[FooMode]{\n\t\tFoo: \"foos the output\",\n\t\tBar: \"bars the output\",\n\t})\n```\n\nPlease note for shell completion to work, your root command needs to have at\nleast one (explicit) sub command. Otherwise, `cobra` won't automatically add an\nadditional `completion` sub command. For more details, please refer to cobra's\ndocumentation on [Generating shell\ncompletions](https://github.com/spf13/cobra/blob/main/shell_completions.md).\n\n### Use Existing Enum Types\n\nA typical example might be your application using a 3rd party logging package\nand you want to offer a `-v` log level CLI flag. Here, we use the existing 3rd\nparty enum values and set a non-zero default for our logging CLI flag.\n\nConsidering the boiler plate shown above, we can now leave out steps ① and ②,\nbecause these definitions come from a 3rd party package. We only need to\nsupply the textual enum names as ③.\n\n```go\nimport (\n    \"fmt\"\n    \"os\"\n\n    log \"github.com/sirupsen/logrus\"\n    \"github.com/spf13/cobra\"\n    \"github.com/thediveo/enumflag/v2\"\n)\n\nfunc main() {\n    // ①+② skip \"define your own enum flag type\" and enumeration values, as we\n    // already have a 3rd party one.\n\n    // ③ Map 3rd party enumeration values to their textual representations\n    var LoglevelIds = map[log.Level][]string{\n        log.TraceLevel: {\"trace\"},\n        log.DebugLevel: {\"debug\"},\n        log.InfoLevel:  {\"info\"},\n        log.WarnLevel:  {\"warning\", \"warn\"},\n        log.ErrorLevel: {\"error\"},\n        log.FatalLevel: {\"fatal\"},\n        log.PanicLevel: {\"panic\"},\n    }\n\n    // ④ Define your enum flag value and set the your logging default value.\n    var loglevel log.Level = log.WarnLevel\n\n    rootCmd := \u0026cobra.Command{\n        Run: func(cmd *cobra.Command, _ []string) {\n            fmt.Printf(\"logging level is: %d=%q\\n\",\n                loglevel,\n                cmd.PersistentFlags().Lookup(\"log\").Value.String())\n        },\n    }\n\n    // ⑤ Define the CLI flag parameters for your wrapped enum flag.\n    rootCmd.PersistentFlags().Var(\n        enumflag.New(\u0026loglevel, \"log\", LoglevelIds, enumflag.EnumCaseInsensitive),\n        \"log\",\n        \"sets logging level; can be 'trace', 'debug', 'info', 'warn', 'error', 'fatal', 'panic'\")\n\n    // Defaults to what we set above: warn level.\n    _ = rootCmd.Execute()\n\n    // User specifies a specific level, such as log level. \n    rootCmd.SetArgs([]string{\"--log\", \"debug\"})\n    _ = rootCmd.Execute()\n}\n```\n\n### CLI Flag With Default\n\nSometimes you might want a CLI enum flag to have a default value when the user\njust specifies the CLI flag **without its value**. A good example is the\n`--color` flag of the `ls` command:\n\n- if just specified as `--color` without a value, it\nwill default to the value of `auto`;\n- otherwise, as specific value can be given, such as\n  - `--color=always`,\n  - `--color=never`,\n  - or even `--color=auto`.\n\nIn such situations, use spf13/pflags's\n[`NoOptDefVal`](https://godoc.org/github.com/spf13/pflag#Flag) to set the\nflag's default value *as text*, if the flag is on the command line without any\noptions.\n\nThe gist here is as follows, please see also\n[colormode.go](https://github.com/TheDiveO/lxkns/blob/master/cmd/internal/pkg/style/colormode.go)\nfrom my [lxkns](https://github.com/TheDiveO/lxkns) Linux namespaces discovery\nproject:\n\n```go\nrootCmd.PersistentFlags().VarP(\n    enumflag.New(\u0026colorize, \"color\", colorModeIds, enumflag.EnumCaseSensitive),\n    \"color\", \"c\",\n    \"colorize the output; can be 'always' (default if omitted), 'auto',\\n\"+\n        \"or 'never'\")\nrootCmd.PersistentFlags().Lookup(\"color\").NoOptDefVal = \"always\"\n```\n\n### CLI Flag Without Default\n\nIn other situations you might _not_ want to have a default value set, because a\nparticular CLI flag is mandatory (using cobra's\n[MarkFlagRequired](https://pkg.go.dev/github.com/spf13/cobra#MarkFlagRequired)).\nHere, cobra's help should not show a (useless) default enum flag setting but\nonly the available enum values.\n\n**Don't assign the zero value** of your enum type to any value, except the\n\"non-existing\" default.\n\n```go\n// ② Define the enumeration values for FooMode; do not assign the zero value to\n// any enum value except for the \"no default\" default.\nconst (\n    NoDefault FooMode = iota // optional; must be the zero value.\n    Foo                      // make sure to not use the zero value.\n    Bar\n)\n```\n\nAlso, **don't map the zero value** of your enum type.\n\n```go\n// ③ Map enumeration values to their textual representations (value\n// identifiers).\nvar FooModeIds = map[FooMode][]string{\n    // ...do NOT include/map the \"no default\" zero value!\n    Foo: {\"foo\"},\n    Bar: {\"bar\"},\n}\n```\n\nFinally, simply use `enumflag.NewWithoutDefault` instead of `enumflag.New` –\nthat's all.\n\n```go\n// ⑤ Define the CLI flag parameters for your wrapped enum flag.\nrootCmd.PersistentFlags().VarP(\n    enumflag.NewWithoutDefault(\u0026foomode, \"mode\", FooModeIds, enumflag.EnumCaseInsensitive),\n    \"mode\", \"m\",\n    \"foos the output; can be 'foo' or 'bar'\")\n```\n\n### Slice of Enums\n\nFor a slice of enumerations, simply declare your variable to be a slice of your\nenumeration type and then use `enumflag.NewSlice(...)` instead of\n`enumflag.New(...)`.\n\n```go\nimport (\n    \"fmt\"\n\n    \"github.com/spf13/cobra\"\n    \"github.com/thediveo/enumflag/v2\"\n)\n\n// ① Define your new enum flag type. It can be derived from enumflag.Flag,\n// but it doesn't need to be as long as it satisfies constraints.Integer.\ntype MooMode enumflag.Flag\n\n// ② Define the enumeration values for FooMode.\nconst (\n    Moo MooMode = (iota + 1) * 111\n    Møø\n    Mimimi\n)\n\n// ③ Map enumeration values to their textual representations (value\n// identifiers).\nvar MooModeIds = map[MooMode][]string{\n    Moo:    {\"moo\"},\n    Møø:    {\"møø\"},\n    Mimimi: {\"mimimi\"},\n}\n\nfunc Example_slice() {\n    // ④ Define your enum slice flag value.\n    var moomode []MooMode\n    rootCmd := \u0026cobra.Command{\n        Run: func(cmd *cobra.Command, _ []string) {\n            fmt.Printf(\"mode is: %d=%q\\n\",\n                moomode,\n                cmd.PersistentFlags().Lookup(\"mode\").Value.String())\n        },\n    }\n    // ⑤ Define the CLI flag parameters for your wrapped enumm slice flag.\n    rootCmd.PersistentFlags().VarP(\n        enumflag.NewSlice(\u0026moomode, \"mode\", MooModeIds, enumflag.EnumCaseInsensitive),\n        \"mode\", \"m\",\n        \"can be any combination of 'moo', 'møø', 'mimimi'\")\n\n    rootCmd.SetArgs([]string{\"--mode\", \"Moo,møø\"})\n    _ = rootCmd.Execute()\n}\n```\n\n## DevContainer\n\n\u003e [!CAUTION]\n\u003e\n\u003e Do **not** use VSCode's \"~~Dev Containers: Clone Repository in Container\n\u003e Volume~~\" command, as it is utterly broken by design, ignoring\n\u003e `.devcontainer/devcontainer.json`.\n\n1. `git clone https://github.com/thediveo/enumflag`\n2. in VSCode: Ctrl+Shift+P, \"Dev Containers: Open Workspace in Container...\"\n3. select `enumflag.code-workspace` and off you go...\n\n## VSCode Tasks\n\nThe included `enumflag.code-workspace` defines the following tasks:\n\n- **Build workspace** task: builds all, including the shared library test\n  plugin.\n\n- **Run all tests with coverage** task: does what it says on the tin and runs\n  all tests with coverage.\n\n## Make Targets\n\n- `make`: lists available targets.\n- `make test`: runs all tests.\n- `make coverage`: deprecated, use the `gocover` CLI command in the devcontainer\n  instead.\n- `make report`: deprecated, use the `goreportcard-cli` CLI command in the\n  devcontainer instead.\n\n## Contributing\n\nPlease see [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Copyright and License\n\n`lxkns` is Copyright 2020, 2025 Harald Albrecht, and licensed under the Apache\nLicense, Version 2.0.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthediveo%2Fenumflag","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthediveo%2Fenumflag","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthediveo%2Fenumflag/lists"}