Giter Club home page Giter Club logo

enumer's Introduction

Enumer GoDoc Go Report Card GitHub Release

Enumer is a tool to generate Go code that adds useful methods to Go enums (constants with a specific type). It started as a fork of Rob Pike’s Stringer tool maintained by Álvaro López Espinosa. This was again forked here as (https://github.com/dmarkham/enumer) picking up where Álvaro left off.

$ ./enumer --help
Enumer is a tool to generate Go code that adds useful methods to Go enums (constants with a specific type).
Usage of ./enumer:
        Enumer [flags] -type T [directory]
        Enumer [flags] -type T files... # Must be a single package
For more information, see:
        http://godoc.org/github.com/dmarkham/enumer
Flags:
  -addprefix string
        transform each item name by adding a prefix. Default: ""
  -comment value
        comments to include in generated code, can repeat. Default: ""
  -gqlgen
        if true, GraphQL marshaling methods for gqlgen will be generated. Default: false
  -json
        if true, json marshaling methods will be generated. Default: false
  -linecomment
        use line comment text as printed text when present
  -output string
        output file name; default srcdir/<type>_string.go
  -sql
        if true, the Scanner and Valuer interface will be implemented.
  -text
        if true, text marshaling methods will be generated. Default: false
  -transform string
        enum item name transformation method. Default: noop (default "noop")
  -trimprefix string
        transform each item name by removing a prefix. Default: ""
  -type string
        comma-separated list of type names; must be set
  -values
    	if true, alternative string values method will be generated. Default: false
  -yaml
        if true, yaml marshaling methods will be generated. Default: false

Generated functions and methods

When Enumer is applied to a type, it will generate:

  • The following basic methods/functions:

    • Method String(): returns the string representation of the enum value. This makes the enum conform the Stringer interface, so whenever you print an enum value, you'll get the string name instead of a number.
    • Function <Type>String(s string): returns the enum value from its string representation. This is useful when you need to read enum values from command line arguments, from a configuration file, or from a REST API request... In short, from those places where using the real enum value (an integer) would be almost meaningless or hard to trace or use by a human. s string is Case Insensitive.
    • Function <Type>Values(): returns a slice with all the values of the enum
    • Function <Type>Strings(): returns a slice with all the Strings of the enum
    • Method IsA<Type>(): returns true only if the current value is among the values of the enum. Useful for validations.
  • When the flag json is provided, two additional methods will be generated, MarshalJSON() and UnmarshalJSON(). These make the enum conform to the json.Marshaler and json.Unmarshaler interfaces. Very useful to use it in JSON APIs.

  • When the flag text is provided, two additional methods will be generated, MarshalText() and UnmarshalText(). These make the enum conform to the encoding.TextMarshaler and encoding.TextUnmarshaler interfaces. Note: If you use your enum values as keys in a map and you encode the map as JSON, 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

  • When the flag yaml is provided, two additional methods will be generated, MarshalYAML() and UnmarshalYAML(). These make the enum conform to the gopkg.in/yaml.v2.Marshaler and gopkg.in/yaml.v2.Unmarshaler interfaces.

  • When the flag sql is provided, the methods for implementing the Scanner and Valuer interfaces. Useful when storing the enum in a database.

For example, if we have an enum type called Pill,

type Pill int

const (
	Placebo Pill = iota
	Aspirin
	Ibuprofen
	Paracetamol
	Acetaminophen = Paracetamol
)

executing enumer -type=Pill -json will generate a new file with four basic methods and two extra for JSON:

func (i Pill) String() string {
	//...
}

func PillString(s string) (Pill, error) {
	//...
}

func PillValues() []Pill {
	//...
}

func PillStrings() []string {
	//...
}

func (i Pill) IsAPill() bool {
	//...
}

func (i Pill) MarshalJSON() ([]byte, error) {
	//...
}

func (i *Pill) UnmarshalJSON(data []byte) error {
	//...
}

From now on, we can:

// Convert any Pill value to string
var aspirinString string = Aspirin.String()
// (or use it in any place where a Stringer is accepted)
fmt.Println("I need ", Paracetamol) // Will print "I need Paracetamol"

// Convert a string with the enum name to the corresponding enum value
pill, err := PillString("Ibuprofen") // "ibuprofen" will also work.
if err != nil {
    fmt.Println("Unrecognized pill: ", err)
    return
}
// Now pill == Ibuprofen

// Get all the values of the string
allPills := PillValues()
fmt.Println(allPills) // Will print [Placebo Aspirin Ibuprofen Paracetamol]

// Check if a value belongs to the Pill enum values
var notAPill Pill = 42
if (notAPill.IsAPill()) {
	fmt.Println(notAPill, "is not a value of the Pill enum")
}

// Marshal/unmarshal to/from json strings, either directly or automatically when
// the enum is a field of a struct
pillJSON := Aspirin.MarshalJSON()
// Now pillJSON == `"Aspirin"`

The generated code is exactly the same as the Stringer tool plus the mentioned additions, so you can use Enumer where you are already using Stringer without any code change.

Transforming the string representation of the enum value

By default, Enumer uses the same name of the enum value for generating the string representation (usually CamelCase in Go).

type MyType int

 ...

name := MyTypeValue.String() // name => "MyTypeValue"

Sometimes you need to use some other string representation format than CamelCase (i.e. in JSON).

To transform it from CamelCase to another format, you can use the transform flag.

For example, the command enumer -type=MyType -json -transform=snake would generate the following string representation:

name := MyTypeValue.String() // name => "my_type_value"

Note: The transformation only works from CamelCase to snake_case or kebab-case, not the other way around.

Transformers

  • snake
  • snake-upper
  • kebab
  • kebab-upper
  • lower (lowercase)
  • upper (UPPERCASE)
  • title (TitleCase)
  • title-lower (titleCase)
  • first (Use first character of string)
  • first-lower (same as first only lower case)
  • first-upper (same as first only upper case)
  • whitespace

How to use

For a module-aware repo with enumer in the go.mod file, generation can be called by adding the following to a .go source file:

//go:generate go run github.com/dmarkham/enumer -type=YOURTYPE

There are four boolean flags: json, text, yaml and sql. You can use any combination of them (i.e. enumer -type=Pill -json -text),

For enum string representation transformation the transform and trimprefix flags were added (i.e. enumer -type=MyType -json -transform=snake). Possible transform values are listed above in the transformers section. The default value for transform flag is noop which means no transformation will be performed.

If a prefix is provided via the trimprefix flag, it will be trimmed from the start of each name (before it is transformed). If a name doesn't have the prefix it will be passed unchanged.

If a prefix is provided via the addprefix flag, it will be added to the start of each name (after trimming and after transforming).

The boolean flag values will additionally create an alternative string values method Values() []string to fullfill the EnumValues interface of ent.

Inspiring projects

enumer's People

Contributors

alvaroloes avatar amanbolat avatar boromisp avatar carlsverre avatar dmarkham avatar dterei avatar eleduardo avatar gjrtimmer avatar godsboss avatar ltpquang avatar marcobeierer avatar mgaffney avatar mieubrisse avatar moritamori avatar mrgossett avatar mvrahden avatar mvrhov avatar nirhaas avatar pascaldekloe avatar pdf avatar prashantv avatar samiam2013 avatar serjlee avatar smlx avatar techmexdev avatar vladimiroff avatar wlbr avatar wttw avatar xescugc avatar xopherus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

enumer's Issues

Fork force use for go generate

Now we have two libraries with similar features but not the same. And when I add annotations like

//go:generate enumer -type=From -json -transform=snake

Binary can be from any of them and result can be different. Is there any way to make sure that code will be generated only by your binary? For example, add different binary name for ensure or some other tricks that provide incompatibility with original version?

Transformer and other operations when 'linecomment'

When using the -linecomment=true I think that operations that change the text should not apply, for example -transform= -trimprefix= -addprefix= as you are saying how do you want it to look like:

  -linecomment
        use line comment text as printed text when present

We had an error due to that (but was our fault) as we had a type like:

package enumer

type DeltaType int

//go:generate enumer -type=DeltaType -transform=snake -output=delta_type_string.go -linecomment=true
const (
	Common    DeltaType = iota + 1 // =
	LeftOnly                       // -
	RightOnly                      // +
	Unknown                        // ?
)

It was a copy paste error but it ended up having a .String() that always returned "" as the linecomment was not "valid" to snake (which could potentially rise errors if a type is gonna be transformed to just ""? 🤔 ).

If we want to change this behavior I'm open to do a PR which will also solve #27 😄

Feature Request: Different transformation methods for different marshaling types

I propose enhancing enumer to allow specifying different transformation methods for different marshaling types.

Our team currently implements custom GraphQL and JSON marshaling methods based on UPPER_SNAKE_CASE and snake_case representations, respectively. It'd be extremely helpful if enumer can generate both for us while keeping the current representations.

I suppose the feature will accept a transformation method via the value of each marshaling flag as shown below.

-gqlgen=snake-upper -json=snake

If this sounds like a good feature to add, I am ready and willing to contribute to its implementation.

feature request: integration with validator/gin bindings

Firstly, I'd like to thank you for great work, I found enumer really helpful!

I am not sure about proper solution, however I think that may be doable by another flag/parameter.

In perfect scenario it would either implement validator interface, or return validator.ValidationErrors.
If not, maybe there is possibility to generate helper validator functions to bind for API models.

Optimize IsAXXX method

we can optimize IsAXXX method to use switch statement instead of for loop.
with this, we dont need stringBelongsMethodSet

Feature req: support string enums

Hi! I have a use-case where I want to use enumer to map postgres enums with the -sql flag. There may be conflicts in my enums, hence I cannot declare multiple enums in the same package.

Example:

//go:generate ...
type EnumA int
const (
    Test EnumA = iota
    Cool EnumA
)

//go:generate ...
type EnumB int
const (
    VeryCool EnumB = iota
    Cool EnumB // this will cause issues!
)

It would be neat if we could instead declare the values as strings, e.g.:

//go:generate ...
type EnumA string
const (
    ATest EnumA = "TEST"
    ACool EnumA = "COOL"
)

//go:generate ...
type EnumB string
const (
    BVeryCool EnumB = "VERY_COOL"
    BCool EnumB = "COOL"
)

BSON support

Hi @dmarkham

While digging in the previous PR of the project I noticed that you had made some work on BSON support back in 2019 but did not finish it.

If I was to open a PR to make a -bson flag again would you be willing to merge it ?
I understood that you did not like the fact that the generated code would be tied to the external mongodb package but I don't think there is any alternative way to implement a BSON marshaller for now.

The err of func %[1]sString(s string) print a lower case of string

Hi team,

I was trying to do some unhappy path test case using
//go:generate enumer -type=BusinessType -json -transform upper

And when I test a "does not belong to BusinessType values" case:
I entered ABC, and expect to get "ABC does not belong to BusinessType values",
but what I get is "abc does not belong to BusinessType values"

I think it's because the generated function BusinessTypeString transformed it into lower case, it's not impacting the code logic, but my test case becomes a little weird:

Input: "ABC"
Output: "abc does not belong to BusinessType values"

The question is, is there any way to generate without the toLower() transform in function BusinessTypeString?

Many thanks!

Best regards,

Edit Readme for Ease of Use

The readme does a good job of explaining the use of enumer, comprehensive enough that you might not need more, but the actual use of the package is sort of below-the-fold or below what would be my first scroll. I'd like to add a very succinct example and the simplest instructions to install at the very start without removing any of the existing explanation(s). This would hopefully lower the bar for new and returning devs.

generate ToString() method

I sometimes want to convert an int item specified by enum to a string, e.g. to specify RESTFullAPI request parameters, etc.
It would be nice to have an option to generate a ToString() method.

Add build constraints to the exported file

First of all i'm a happy user of the enumer :) But i've an situation in which i need a bit of help.

When my enum is in code that is only build when certain constraints are met. (See: here) i'd like to have the constraints also in the generated code. If the build constraints not get exported the compile would generated errors as the generate file is build without the source containing the enum base definition.

I wrote a small PR in which i added a command line argument to add the build constraints. This works fine.

I was thinking whether it would be possible to deduct the build tags from the original source file and copy them into the generated one. But could not think of an easy way to that.

Note the comment option does not work as it adds a space between // and the first char of the comment

generate failed on go 1.18 beta2

hi, just got an issue with go.18 beta2, reproduce steps:

  1. build enumer from source with go 1.18beta2
  2. use enumer from step1 to generate code
  3. got error: enumer: internal error: package "bytes" without types was imported from ...."

enumer unit test also failed on go1.18

Make PillString case insensitive

It would be useful to perform string -> enum conversion in a case-insensitive fashion. While I could convert my string to lowercase and have all of my values lowercase as well, some of them are acronyms which looks ugly if not written with capital letters.

Enable interoperability with ent

Hi, to improve interoperability with ent and support its enum type-safety, I'd propose to add the Values() []string method (from the EnumValues interface) to the generated enum types, next to String() string.
This feature should likely be opt-in, so it would be beneficial to add a flag for it. I'd propose -values (or -valuesFunc) flag.

I've seen, there is already a generated ***Strings() []string-func, which would just need to be wrapped by the Values() []string method in question.

@dmarkham Would you be open for a PR for this?

An Example:

func (Pill) Values() []string {
  return PillStrings()
}

P.S.: I understand there's a potential for confusion, due to the pre-existing PillValues() []Pill function. But to have typesafety with enums in ent, this proposal is the only way to go from what I see.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.