jmattheis / goverter

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

Is it possible to provide default values to converters? #93

Closed lampajr closed 9 months ago

lampajr commented 9 months ago

Hi everyone :wave:,

First of all I am a new user of this tool and this is pretty amazing!!

Have you read the project readme?

Describe your question I apologize if I am asking for something stupid or simple but I am struggling finding how I can provide some defaults to be applied during the mapping, here a possible use case:

package example

// goverter:converter
type Converter interface {
    // goverter:autoMap Address
    Convert(Person) FlatPerson
}

type Person struct {
    Name    string
    Age     int
    Address Address
}
type Address struct {
    Street  string
    ZipCode string
}
type FlatPerson struct {
    Name    string
    Age     int
    Street  string
    ZipCode string
}

Use case:

Let's assume that we have a default for Name whenever the source, in this case Person object, does not provide it.

Q1: Is there a way to accomplish this goal without having to create a custom function converter?

// goverter:converter
type Converter interface {
    // goverter:autoMap Address
    // goverter:map Name Name <DEFAULT>
    Convert(Person) FlatPerson
}

Q2: In my specific case I do have an autogenerated constructor that automatically sets all defaults for a specific struct, is there a way to provide that custom constructor to goverter such that the autogenerated code initialize the struct using that constructor instead of simple FlatPerson{} initialization?

// goverter:converter
type Converter interface {
    // goverter:autoMap Address
    // goverter:init <MY-FLATPERSON_CONSTRUCTOR>
    Convert(Person) FlatPerson
}
jmattheis commented 9 months ago

Q1: Is there a way to accomplish this goal without having to create a custom function converter?

You can use a method mapping like so

// goverter:converter
type Converter interface {
    // goverter:map Age | DefaultAge
    Convert(Input) Output
}

type Input struct {
    Name string
}
type Output struct {
    Name string
    Age  int
}

func DefaultAge() int {
    return 42
}

Q2: In my specific case I do have an autogenerated constructor that automatically sets all defaults for a specific struct, is there a way to provide that custom constructor to goverter such that the autogenerated code initialize the struct using that constructor instead of simple FlatPerson{} initialization?

This is currently not possible but sounds like a useful addition. I'll keep this as a feature request.

lampajr commented 9 months ago

Many thanks for the prompt answer!

You can use a method mapping like so

// goverter:converter
type Converter interface {
  // goverter:map Age | DefaultAge
  Convert(Input) Output
}

type Input struct {
  Name string
}
type Output struct {
  Name string
  Age  int
}

func DefaultAge() int {
  return 42
}

Yeah exactly, but in that case I think it will be set to 42 even if provided in the Input obj right? I mean I was curious if there was a way to set a default mapping only if the source object does not have it or if it is nil.

This is currently not possible but sounds like a useful addition. I'll keep this as a feature request.

I would be happy to contribute, or at least to try contributing, if that sounds good for you!

jmattheis commented 9 months ago

Yeah exactly, but in that case I think it will be set to 42 even if provided in the Input obj right?

Correct.

if there was a way to set a default mapping only if the source object does not have it

You could do it like this, but there is no way around the function definition

// goverter:converter
type Converter interface {
    // goverter:map Age | DefaultAge
    Convert(Input) Output
}

type Input struct {
    Name string
    Age  *int
}
type Output struct {
    Name string
    Age  int
}

func DefaultAge(source *int) int {
    if source == nil {
        return 42
    }
    return *source
}

or if it is nil.

this isn't currently possible.

I would be happy to contribute, or at least to try contributing, if that sounds good for you!

Sure. I'm not sure yet about the config name yet, but we can decide this later and then rename it in the PR.