alvaroloes / enumer

A Go tool to auto generate methods for your enums
Other
478 stars 111 forks source link

Add support for BSON marshaling #49

Open KatamariJr opened 5 years ago

KatamariJr commented 5 years ago

Add the funcs MarshalBSONValue and UnmarshalBSONValue, allowing enums to be inserted and retrieved from MongoDB which uses a BSON store.

alvaroloes commented 5 years ago

Thank you for the PR.

I haven't gone deep into the details, but I don't feel comfortable with adding a dependency to an external library. If we go down this path, then Enumer would end up depending on a lot of external libraries and would need to "track" their versions. Also, What if other people use a different BSON library?

Maybe there is a way to be able to be compatible with external marshalers without depending on them

KatamariJr commented 5 years ago

I understand the concern. The main problem lies with mongo-driver requiring an interface method with prototype MarshalBSONValue() (bsontype.Type, []byte, error), immediately including a package dependency. There is also a MashalBSON func that doesn't have this, however I spent most of yesterday trying to get that to work with mashaling a simple string and found that you must use MashalBSONValue. Not really sure where to go from there.

I don't think the slippery-slope argument necessarily applies here, as there would still need to be a major reason to want to serialize to whatever format is specified, which leads me to...

MongoDB is extremely popular with Go and I feel that this will come up as a request sooner rather than later. The official mongo-driver package (the only mongo / BSON package being actively worked on, mgo is dead) is at a stable 1.0.+ release and should not have any breaking changes until 2.0 comes out, and even then it would be surprising if they change the interface for un/marshaling.

Any thoughts on a middle ground?

alvaroloes commented 5 years ago

I see. Here is a workaround that I think it is a good trade-off: You can autogenerate all the methods Enumer support for your enum type, and then add any extra method you want manually. At least this will work for your project and you won't notice any difference in behavior if we decide to eventually autogenerate methods with external dependencies. For example:

//go:generate enumer -type=EntityType -json
type EntityType int

const (
    EntityTypeUser EntityType = iota
    EntityTypeCustomer
    EntityTypeProduct
)

// This is added manually
func (et *EntityType) MarshalBSONValue() (bsontype.Type, []byte, error) {
    // ...
    return nil, nil, nil
}

// In another file...
func main() {
    const x EntityType = 3
    x.String() // Correct. It's been autogenerated
    x.MarshalJSON() // Correct. It's been autogenerated
    x.MarshalBSONValue() // Correct. It's the method you defined above, but it is at the same level as the autogenerated ones
}

Running go generate in the folder this file is placed will generate a file called entitytype_enumer.go with all the autogenerated methods. Then you add any custom methods you want to your enum in the same file where it is defined (or in another, but in the same package). As all the methods are in the same package, they all belong to the enum method set and you can marshal it to JSON or BSON when needed.

Let me know if this helped you.