jmattheis / goverter

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

mapping consts #12

Closed soniah closed 1 year ago

soniah commented 2 years ago

Hi Jannis, this is part question, part feature request, part "should I write this?".

If my target field should be set to a const, how would I do that? For example:

       const ageOfConsent = 18 

        type Input struct {
            Name string
        }

        type Output struct {
            Name2 string
            Age int
        }

Desired field mappings:

Input.Name -> OutPut.Name2
ageOfConsent -> Output.Age

I was wondering if I should attempt to write this (as a feature), but I'd be interested in your ideas. Here's a possible syntax:

        // goverter:converter
        type Converter interface {
            // goverter:const ageOfConsent:Age
            Convert(source Input) Output
        }
jmattheis commented 2 years ago

Hey Sonia,

currently it should be possible to extend with a custom the converter with a custom method and set all the required fields and the missing one.

Regardless, I think a similar feature could improve the usage of this library a lot. I feel like a constant is too restricting.I propose something similar to the existing goverter:extend, but instead it targets the property of a struct.

type Input struct { Name string }

type Output struct { Name string; Age int }

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

func DefaultAge() int {
    return 18
}

At first glance, this may seem like a more verbose version of your proposal, but the benefit is that is can cover much more use-cases. F.ex, we could allow passing the source object into this function, then the /example/error could be improved. Currently, it works like this:

// goverter:converter
// goverter:extend ToAPIPerson
type Converter interface {
    ToAPIApartment(source DBApartment) APIApartment
}

func ToAPIPerson(value DBPerson) APIPerson {
    return APIPerson{
        ID:       value.ID,
        FullName: fmt.Sprintf("%s %s", value.FirstName, value.LastName),
    }
}

We define an extension function for the whole *Person object. With the new goverter:mapExtend it could be written like this:

// goverter:converter
type Converter interface {
    ToAPIApartment(source DBApartment) APIApartment
    // goverter:mapExtend FullName GetFullName
    ToAPIPerson(source DBPerson) APIPerson
}

func GetFullName(value DBPerson) string {
    return fmt.Sprintf("%s %s", value.FirstName, value.LastName)
}

This looks much better.

You could even use it to convert values, without the need to write the entire custom extension method.

type Input struct {
    Percent100Based float64
    // ... other fields
}

type Output struct {
    Percent1Based float64
    // ... other fields
}

// goverter:converter
type Converter interface {
    // goverter:mapExtend Percent1Based DivideBy100
    ToOutput(source Input) Output
}

func DivideBy100(source Input) float64 {
    return source.Percent100Based / 100
}

The first step would be to add goverter:mapExtend with support for only defining a method without arguments and one return value, which must be the same as the return type of the parameter that is defined inside goverter:mapExtend. The optional passing of the source value as first parameter should be done later. Maybe we could even allow passing a Converter instance, like it can be done in goverter:extend (https://github.com/jmattheis/goverter#reuse-generated-converter)

Also, I'm not fully satisfied with the name goverter:mapExtend maybe you've something better?

switchupcb commented 2 years ago

@soniah This is possible to do in copygen:

soniah commented 2 years ago

Thanks @switchupcb that looks good. Sorry @jmattheis I haven't had time to work on the stuff above - work, family, etc...