jmattheis / goverter

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

Support one to many mappings with slices #125

Closed hermanschaaf closed 5 months ago

hermanschaaf commented 6 months ago

As best I can tell, it is currently not possible to use goverter to map the Input to the Output in this case:

package example

// goverter:converter
type Converter interface {
    Convert(Input) (Output, error)
}

type Input struct {
    Envs []Env
}
type Env struct {
    Key string
    Value string
}
type Output struct {
    EnvKeys []string
    EnvValues []string
}

Is there a known solution to this?

If not, one solution I would like to suggest is a post-hook function where the user can receive the Input and Output structs and make any final alterations to it, if necessary.

jmattheis commented 6 months ago

If it's an simple example it could define the complete conversion function yourself via extend otherwise there are two ways to do this with generation support.

  1. Via default, this works similar to a before hook. It normally is used to instantiate the type. Here you have to ignore the already defined fields.

    // goverter:converter
    type ConverterWithDefault interface {
       // goverter:default PreProcess
       // goverter:ignore EnvKeys EnvValues
       Convert(Input) (Output, error)
    }
  2. Via map [SOURCE-PATH] TARGET | METHOD by defining the field mapping and giving a custom conversion function.

    // goverter:converter
    type ConverterWithMapCustom interface {
    // goverter:map Envs EnvKeys | ExtractKeys
    // goverter:map Envs EnvValues | ExtractValues
    Convert(Input) (Output, error)
    }

I'm not against an after hook, but I'd need a good example that's not fixable via default.

Full Example (click me)

```go // goverter:converter type ConverterWithMapCustom interface { // goverter:map Envs EnvKeys | ExtractKeys // goverter:map Envs EnvValues | ExtractValues Convert(Input) (Output, error) } // goverter:converter type ConverterWithDefault interface { // goverter:default PreProcess // goverter:ignore EnvKeys EnvValues Convert(Input) (Output, error) } func PreProcess(i Input) Output { return Output{EnvKeys: ExtractKeys(i.Envs), EnvValues: ExtractKeys(i.Envs)} } func ExtractKeys(envs []Env) []string { var result []string for _, env := range envs { result = append(result, env.Key) } return result } func ExtractValues(envs []Env) []string { var result []string for _, env := range envs { result = append(result, env.Value) } return result } type Input struct { Envs []Env Name string } type Env struct { Key string Value string } type Output struct { EnvKeys []string EnvValues []string Name string } ```

hermanschaaf commented 5 months ago

Ah got it, thanks! The trick I was missing was that you can actually map the same input field twice, as in:

// goverter:map Envs EnvKeys | ExtractKeys
// goverter:map Envs EnvValues | ExtractValues
Convert(Input) (Output, error)