{"id":31815583,"url":"https://github.com/mvrahden/go-enumer","last_synced_at":"2025-10-11T09:18:17.892Z","repository":{"id":87889598,"uuid":"442907251","full_name":"mvrahden/go-enumer","owner":"mvrahden","description":"✨ Go-Enums with Magic ✨","archived":false,"fork":false,"pushed_at":"2024-03-31T07:40:53.000Z","size":2474,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-06-21T15:49:25.285Z","etag":null,"topics":["enum","enums","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":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mvrahden.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":"2021-12-29T22:40:41.000Z","updated_at":"2024-04-25T10:51:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"8860fb7d-f123-4707-a445-916b5366bed8","html_url":"https://github.com/mvrahden/go-enumer","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/mvrahden/go-enumer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mvrahden%2Fgo-enumer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mvrahden%2Fgo-enumer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mvrahden%2Fgo-enumer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mvrahden%2Fgo-enumer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mvrahden","download_url":"https://codeload.github.com/mvrahden/go-enumer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mvrahden%2Fgo-enumer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006751,"owners_count":26084177,"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","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["enum","enums","go","golang"],"created_at":"2025-10-11T09:18:13.661Z","updated_at":"2025-10-11T09:18:17.885Z","avatar_url":"https://github.com/mvrahden.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-enumer \u003c!-- omit in toc --\u003e\n\n[![Go](https://github.com/mvrahden/go-enumer/actions/workflows/go.yaml/badge.svg)](https://github.com/mvrahden/go-enumer/actions/workflows/go.yaml)\n\n`go-enumer` is a tool to generate Go code upgrading Go constants to enums.\nIt can also extract an enum definition from CSV files and turn it into Go enums.\nIt adds useful methods to these types, such as validation and various (de-)serialization.\nIt is an opinionated remake of the long existing [enumer](https://github.com/dmarkham/enumer) package and therefore behaves different in practically all aspects.\n\n`go-enumer` is intended to be:\n\n- strict by default (principle of least surprise)\n- flexible via opt-in\n\n\u003e E.g. it prevents `undefined` (or empty) values from being (de-)serialized by default.\n\u003e But you can always choose to configure your enums to permit (de-)serialization from such values.\n\n## Table of Contents \u003c!-- omit in toc --\u003e\n\n1. [Why `go-enumer`?](#why-go-enumer)\n2. [What's it all about?](#whats-it-all-about)\n   1. [Conventions](#conventions)\n   2. [Defaults and Zero-Values](#defaults-and-zero-values)\n   3. [Comment directive `//go:enum`](#comment-directive-goenum)\n   4. [Validation](#validation)\n   5. [Supported features](#supported-features)\n      1. [The \"undefined\" feature](#the-undefined-feature)\n      2. [Other supported features](#other-supported-features)\n3. [Simple Block Spec](#simple-block-spec)\n   1. [String Case Transformations](#string-case-transformations)\n   2. [Handling of Name Prefixes](#handling-of-name-prefixes)\n   3. [Alternative values](#alternative-values)\n4. [Filebased Spec](#filebased-spec)\n   1. [CSV-File sources](#csv-file-sources)\n5. [Generated functions and methods](#generated-functions-and-methods)\n6. [Configuration Options](#configuration-options)\n7. [Caveats](#caveats)\n8. [Inspiring projects](#inspiring-projects)\n\n## Why `go-enumer`?\n\nFirst and foremost enums are a common way of handling sets of distinct and customizable (and eventually human-readable) values.\nMany projects have use cases where enums might be useful, but Go has no built-in support for enums.\nTherefore this project aims to fill the gap, by making use of Go's features for type declaration and code generation.\n\nIn regards to the aforementioned ancestor project [enumer](https://github.com/dmarkham/enumer), I found some unfortunate obstacles like the configuration, strict case handling, validation and feature extensibility.\nIts scope, ambitions and constraints were clearly different, since it tried to be a \"drop-in\" replacement for the known \"Stringer\" utility.\n\nStarting from scratch, I decided it's best that `go-enumer` breaks with those restrictions while borrowing some of the neat ideas.\n\n## What's it all about?\n\n`go-enumer` understands an enum as individual type with their sets of custom-defined, distinct values - let's refer to such sets of enum values as the **enum's spec** from now on.\nWith `go-enumer` there are two possible ways to define enum specs.\nThe first one we'll refer from now on as the *simple block spec*, which is straight forward and a suitable choice for small and uncomplicated enum sets.\nThe second one we'll refer to as the *filebased spec*, which is much more advanced in its options and useful when you're reaching the *simple block spec's* limits.\n\n### Conventions\n\nTo define an enum spec, one first needs to define its distinct type – the **enum type**.\nTo define an Enum type, you must follow the listed rules.\nFailing to do so will either lead to an unsuccessful detection or to a code generation error.\n\n- Every enum type must derive from an unsigned integer (`uint`, `uint8`, ...) type.\n- Every enum type must be marked with a comment directive `//go:enum` ([read here for more](#comment-directive-goenum)).\n- Every enum type must either have a *simple block spec* or a *filebased spec* associated.\n\nNot only the enum type, but also its spec must follow a set of basic rules.\nBy convention, enum sets must:\n\n- start with **index** of `1` and in those cases with a **default value** at `0`.\n- consist of continuous linear increments of at most `1`.\n- contain unique **values**.\n\nDue to the nature of enums being distinct values, the majority of enum sequences don't require a default value and thus start at `1`.\nThis is demonstrated in the following snippet with an enum type of a *simple block spec*.\n\n```go\n//go:enum -transform=upper\ntype Color uint\n\nconst (\n  ColorRed Color = iota + 1\n  ColorBlue\n  ColorGreen\n)\n/* yields:\nenum type: Color\nenum spec:\nValue \"RED\"   at Index 1\nValue \"BLUE\"  at Index 2\nValue \"GREEN\" at Index 3\nis invalid    at Index 0 and 4,...,max\n*/\n```\n\n### Defaults and Zero-Values\n\n`go-enumer` gives a special semantic meaning to specs starting with the value `0`.\nThe **[Zero Value](https://go.dev/tour/basics/12)** of native integer types in Go is `0`.\nFor enums it likewise means, that an enum of value `0` is by definition the zero value.\nIf the enum spec does not define a **default** (starts at `0`) an enum field is in an invalid state when its assigned value is zero.\nIn some situations you may find yourself in the need of such a **default** value.\nDepending on whether your spec needs a default value, you will chose your sequence to start at value `0` with the your default value being `0`.\n(You may have multiple defaults, see section for [alternative values](#alternative-values))\n\n```go\n//go:enum\ntype UserRole uint\n\nconst (\n  UserRoleAnonymous UserRole = iota // \u003c- this is your default\n  UserRoleStandard\n  UserRoleAdmin\n  UserRoleUnknown = UserRoleAnonymous // \u003c- this is your alternative default\n)\n```\n\nThe previous snippet defines an enum spec which can be deserialized from the following set of values `[\"Anonymous\",\"Standard\",\"Admin\",\"Unknown\"]` after successful code generation.\nIf you want your default value to be robust against deserialization from `undefined` (resp. zero values), then rest assured you do not need to do anything.\n`go-enum` will naturally fail any attempt of unmarshalling from empty strings or `nil` if it was not explicitly instructed to do otherwise.\nIn these cases the returned error will be of type `ErrNoValidEnum` which is part of the generated file and can be used via Go's unwrapping mechanism `errors.Is(err, mypkg.ErrNoValidEnum)`.\n\nIf you need to also enable deserialization for your **default** enum value from a zero values please check out the section for [\"undefined\"-value](#the-undefined-feature).\n\n### Comment directive `//go:enum`\n\n`go-enumer` only needs one single `//go:generate` directive per package to screen the entire package thanks to the introduction of `//go:enum` comment directive.\nIt acts as a marker of all enums.\nNow `go-enumer` can determine all enums of a package in a single pass, reducing redundant scans and therefore making the code generation (and your CI process) much more efficient.\n\nBut the `//go:enum` directive does not exclusively serve as a marker to detect enums, it also enables config **mixins**.\nBy supplying it with a finegrained configuration on an enum type level, we have the ability to overwrite the global `generate` configuration.\n\nThe following example will generate `json` interfaces for practically all enums it will detect, except for this specific `Greeting` enum, where it will generate both `json` and `yaml` interfaces.\n\n\u003e The comment directive currently offers support for all configuration options, which are available on a global configuration level.\n\n```go\npackage mypackage\n\n//go:generate go run github.com/mvrahden/go-enumer -serializers=json\n\n/* ... */\n\n//go:enum -serializers=json,yaml\ntype Greeting uint\n\nconst (\n  GreetingWorld Greeting = iota\n  GreetingMars\n)\n```\n\n### Validation\n\nEach enum type will support two validation Methods `Validate() error` and `IsValid() bool`.\n\nBy defining enum specs on a linear scale, the validation complexity is reduced to a simple check of whether or not the value is within the extent of the scale.\nTo ensure (de-)serialization success the type validation will be performed on every (de-)serialization operation.\n\n### Supported features\n\nSupported features are targeted with the `-support=arg1,arg2,...` flag and can be used globally via `go:generate` or as a mixin via `go:enum`.\n\n#### The \"undefined\" feature\n\n\u003e how to use? `-support=undefined`\n\n`go-enumer` returns with an error upon (de-)serialization of any value which is not explicitly defined in your set of enums with an `error` – this rule covers empty values as well.\nTherefore those sequences, that neither contain a default value nor an empty value must start with the integer value of `1`.\n\nThe `undefined` feature is an opt-in, which enables the (de-)serialization of zero values.\nIn case you applied the `undefined` feature, lookups with an empty value will resolve as an \"undefined\" constant, representing an empty string.\nAny undefined value upon deserialization will be considered **valid** now and pass the [Validation](#validation).\nHowever it will not be represented in the list of possible values upon serialization.\n\nIn the case of `undefined` values, there are 4 cases that we can distinguish:\n\n| # | Has Default Value | Supports Undefined | Enum Type can deserialize from undefined |\n|:-:|:-----------------:|:------------------:|:----------------------------------------:|\n| 1 |        no         |         no         |                    no                    |\n| 2 |        yes        |         no         |                    no                    |\n| 3 |        yes        |        yes         |                   yes                    |\n| 4 |        no         |        yes         |                   yes                    |\n\n\u003e Read the table as follows (e.g. for row `4`): \"If my enum *has NO default* and it *DOES support undefined*\n\u003e then it *CAN deserialize from undefined*\"\n\n#### Other supported features\n\n\u003e how to use? `-support=ent`\n\nWith `ent` a method will be generated to return all valid Value strings. This allows you to use your enum type with the ent framework.\n\n## Simple Block Spec\n\nThe simple block spec is a very primitive and intuitive way to generate enums.\nSimply define a `const`-block with the enums you want your enum spec to contain.\nThe enum values will be derived from the constant names (less their type prefix).\nTo allow some degree of customization while keeping your constant names readable\nyou can apply [string case transformations](#string-case-transformations) to the values.\n\n### String Case Transformations\n\n`go-enumer` supports a range of string case transformations.\nThese transformations are a feature exclusive to the *simple block spec*.\nYou may configure a global transformation via the `generate` command and you may mixin deviating transformations case by case via `go:enum` comment directive.\nHere is an example with a `kebab` transformation.\n\n```go\n//go:enum -transform=kebab\ntype MyType uint\n\nconst (\n  MyTypeFoo MyType = iota + 1\n  MyTypeBar\n  MyTypeFooBar\n  // ...\n)\n// yields --\u003e [\"foo\", \"bar\", \"foo-bar\"]\n```\n\nPlease take examplary transformations from the following table:\n\n| Transformation Name | Resulting enum values     |\n|---------------------|---------------------------|\n| `noop` (default)    | [\"Foo\", \"Bar\", \"FooBar\"]  |\n| `camel`             | [\"foo\", \"bar\", \"fooBar\"]  |\n| `pascal`            | [\"Foo\", \"Bar\", \"FooBar\"]  |\n| `kebab`             | [\"foo\", \"bar\", \"foo-bar\"] |\n| `snake`             | [\"foo\", \"bar\", \"foo_bar\"] |\n| `lower`             | [\"foo\", \"bar\", \"foobar\"]  |\n| `upper`             | [\"FOO\", \"BAR\", \"FOOBAR\"]  |\n| `upper-kebab`       | [\"FOO\", \"BAR\", \"FOO-BAR\"] |\n| `upper-snake`       | [\"FOO\", \"BAR\", \"FOO_BAR\"] |\n| `whitespace`        | [\"Foo\", \"Bar\", \"Foo Bar\"] |\n\n**Note**: If you are missing a transformation please raise an issue and/or open a Pull Request.\n\n### Handling of Name Prefixes\n\nIt is good practice with `go-enumer` to prefix enum constant names with their corresponding type name and `go-enumer` will automatically detect these prefixes and strip them off their values.\nMeaning: it will turn `GreetingMars` (with enum type `Greeting`) into e.g. `\"MARS\"` (assuming an `upper` transformation was applied).\nIt does not support arbitrary prefixes and we do not encourage that due to a resulting noisyness of code.\nThis rule is in place to keep your source code concise.\n\n### Alternative values\n\nEnum based on the *simple block spec* can contain indeces (enum IDs) that are assigned multiple times – such values resemble **Alternative values**.\nThese alternative values are shadowed by the dominant value, which is always the constant which was assigned first to the index in the block.\n\n## Filebased Spec\n\nThe *filebased spec* allows code generation for the enum values from a file source.\nThe following sections describe the usage of the *filebased spec*.\n\n### CSV-File sources\n\n`go-enumer` can extract your enum definitions from CSV file sources if you target `-from=path/to.csv` in your enum's comment directive.\nFilebased specs are taken as given and will not undergo any string case transformation.\n\nFilebased specs allow you also to augment your enums with additional data columns.\n`go-enumer` can parse data and add typed Getter-funcs based on a column annotation syntax.\nIt supports Go's built-in data types via the following syntax `\u003cdatatype\u003e(your-column-name)`, e.g. `uint(area-in-square-meter)` or `float64(tolerance)`.\nIf there's no explicit type annotated, `go-enumer` will assume a basic `string` type as a fallback.\n\nHave a look at [the Booking, Color or Project examples](examples/README.md) for further info.\n\n## Generated functions and methods\n\nWhen `go-enumer` is applied to a type, it will generate:\n\n- The following basic methods/functions:\n\n  - Method `String()`: returns the string representation of the enum value. This makes the enum conform\n    to the `Stringer` interface, so whenever you print an enum value, you'll get the string name instead of its numeric counterpart.\n  - Function `\u003cEnumType\u003eFromString(raw string)`: returns the enum value from its string representation. This is useful\n    when you need to read enum values from command line arguments, from a configuration file, or\n    from a REST API request... In short, from those places where using the real enum value (an integer) would\n    be almost meaningless or hard to trace or use by a human. `raw` string is case sensitive.\n  - Function `\u003cEnumType\u003eFromStringIgnoreCase(raw string)`: we can not always guarantee the case matching because some systems out of our reach\n    are insensitive to exact case matching. In these situations `\u003cEnumType\u003eFromStringIgnoreCase(raw string)` comes in handy.\n    It acts the same as `\u003cEnumType\u003eFromString(raw string)` with the little difference of `raw` being case insensitive.\n  - Function `\u003cEnumType\u003eValues()`: returns a slice with all the numeric values of the enum, ignoring any alternative values.\n  - Function `\u003cEnumType\u003eStrings()`: returns a slice with all the string representations of the enum.\n  - Method `IsValid()`: returns true if the current value is a value of the defined enum set.\n  - Method `Validate()`: returns a wrapped error `ErrNoValidEnum` if the current value is not a valid value of the defined enum set.\n    It is being used upon serialization and deserialization, allowing for detecting enum errors via `errors.Is(err, ErrNoValidEnum)`.\n\n- The flag `serializers` in addition with any of the following values, additional methods for serialization are added.\n  Valid values are:\n\n  - `binary` makes the enum conform to the `encoding.BinaryMarshaler` and `encoding.BinaryUnmarshaler` interfaces.\n  - `bson` makes the enum conform to the `bson.MarshalBSONValue` and `bson.UnmarshalBSONValue` interfaces.\n  - `graphql` makes the enum conform to the `graphql.Marshaler` and `graphql.Unmarshaler` interfaces.\n  - `json` makes the enum conform to the `json.Marshaler` and `json.Unmarshaler` interfaces.\n  - `sql` makes the enum conform to the `sql.Scanner` and `sql.Valuer` interfaces.\n  - `text` makes the enum conform to the `encoding.TextMarshaler` and `encoding.TextUnmarshaler` interfaces.\n    **Note:** If you use your enum values as keys in a map and you encode the map as *JSON*,\n    you need this flag set to true to properly convert the map keys to json (strings). If not, the numeric values will be used instead\n  - `yaml` makes the enum conform to the `gopkg.in/yaml.v2.Marshaler` and `gopkg.in/yaml.v2.Unmarshaler` interfaces.\n  - `yaml.v3` makes the enum conform to the `gopkg.in/yaml.v3.Marshaler` and `gopkg.in/yaml.v3.Unmarshaler` interfaces.\n    **Note:** Supplying both yaml values (`yaml` and `yaml.v3`) will fail due to interface incompatibility.\n\n## Configuration Options\n\nYou can add:\n\n- transformation with `transform` option, e.g. `transform=kebab`.\n- serializers with `serializers` option, e.g. `serializers=json,sql,...`.\n- supported features via `support` option, e.g. `support=undefined,ent`\n  - `undefined`, see [\"undefined\"-value](#the-undefined-feature)\n  - `ignore-case`, adds support for case-insensitive lookup\n  - `ent`, adds interface support for [entgo.io](https://github.com/ent/ent)\n\n## Caveats\n\nFollowing is a list of known issues:\n\n- **Skipped calls to `UnmarshalJSON`**:  \n  When using `encoding/json` the enum unmarshaling will not be triggered if there's no `key`-`value` pair for the enum within the JSON payload.\n  This will lead to a zero value enum instead of a deserialized enum.\n  If no subsequent validation is performed and no default value is defined this will cause a failing validation upon subsequent serialization.\n\n## Inspiring projects\n\n- [enumer](https://github.com/dmarkham/enumer)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmvrahden%2Fgo-enumer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmvrahden%2Fgo-enumer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmvrahden%2Fgo-enumer/lists"}