jmattheis / goverter

Generate type-safe Go converters by simply defining an interface
https://goverter.jmattheis.de/
MIT License
496 stars 46 forks source link

Customize enum transformer by package #156

Closed sublee closed 1 month ago

sublee commented 1 month ago

Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

I need a custom enum transformer that can convert PascalCase to UPPER_SNAKE_CASE. This kind of case conversion is a typical pattern between Go enums and Protobuf enums. It cannot be done only with the built-in regex transformer. I have two options. The first one is to map each value manually:

// goverter:enum:unknown ENUM_UNSPECIFIED
// goverter:enum:map EnumFooBar ENUM_FOO_BAR
// goverter:enum:map EnumBarBaz ENUM_BAR_BAZ
func ConvertEnum(my.Enum) pb.Enum

The second one is to extend the whole goverter CLI for a custom enum transformer. It's quite heavy compared to other customization settings.

Describe the solution you'd like A clear and concise description of what you want to happen.

goverter has wrapErrorUsing, which provides extensibility with an external package. Like this, custom enum transformers could be defined in an external package. I request a new configuring setting goverter:enum:transformUsing PACKAGE SEARCH REPLACE whose design is a combination of goverter:enum:transform regex SEARCH REPLACE and goverter:wrapErrorUsing PACKAGE:

// goverter:enum:unknown ENUM_UNSPECIFIED
// goverter:enum:transformUsing github.com/sublee/pascal-to-snake Enum* ENUM_*
func ConvertEnum(my.Enum) pb.Enum

Another solution is to provide a new built-in enum transformer called pkg. goverter:enum:transform pkg PACKAGE SEARCH REPLACE has the same functionality as the above one. It could be better because it fully respects the existing goverter architecture:

// goverter:enum:unknown ENUM_UNSPECIFIED
// goverter:enum:transform pkg github.com/sublee/pascal-to-snake Enum* ENUM_*
func ConvertEnum(my.Enum) pb.Enum

The external package may have to export some functions for goverter, such as:

func Transform(name, search, replace string) string

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

I'm currently trying to extend the whole goverter CLI for a custom enum transformer.

jmattheis commented 1 month ago

I'd love to support this but sadly I can't think of a good solution without spawning a new process or loading a shared library. There is a difference between wrapErrorsUsing and enum:transform. wrapErrorsUsing is basically just a package in the code gen output. Goverter doesn't care what the actual implementation is.

enum:transform on the other hand has to be executed inside goverter. Go is a statically compiled language without an eval to dynamically execute code. So from my point of view it's impossible to dynamically call functions from a package at runtime without any other changes.

A possibility could be https://pkg.go.dev/plugin but I'd say the ergonomics are even worse than the current way of setting enum transformers.

Another one would be to add something like enum:transformExec where the user could provide an executable that can transform enums. Tho, I dislike the idea of goverter executing external binaries. Adding another scripting language to goverter for this seems to be overkill too, so I'd say the current supported way is the best way to configure this.

sublee commented 1 month ago

It makes sense. Now I understand why the custom enum transformer should be injected into the Goverter CLI and why it's the best way. Thanks for explaining the design decision behind goverter:enum:transform.