8000 Use go code to describe mappings instead of comments · Issue #196 · jmattheis/goverter · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Use go code to describe mappings instead of comments #196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
YasserRabee opened this issue Apr 4, 2025 · 2 comments
Open

Use go code to describe mappings instead of comments #196

YasserRabee opened this issue Apr 4, 2025 · 2 comments
Labels
feature New feature or request

Comments

@YasserRabee
Copy link
Contributor
YasserRabee commented Apr 4, 2025

Is your feature request related to a problem? Please describe.

Writing comments on functions was good enough when I had one or two converters. But now, with 10s of converters and after using goverter in our mainstream toolchains, writing the mapping comments became very hard, error prune, and time consuming.

I may summarize our problems in the following points:

  • New comers need to learn new DSL (comments) and usage hacks to get around corner cases in our case.
  • IDE don't help with autocomplete for available actions (map, autoMap, default, ...etc)
  • We can't share reusable code mappings between different converters while the mapping is exactly the same. Similar to Support interface embedding for seperation of concerns #189 . But one big different from Support interface embedding for seperation of concerns #189 is that in our case the mapping itself is the same but the mapped types are not (for example, SourceA -> TargetA and SourceB -> TargetB should have the same comments to map fields, but they are different types).
  • No ability to build dynamic mappings, one use case is described in Support regex for mapping field names #122
  • typos in comments are hard to discover specially when the typo is in // goverter: prefix.

Describe the solution you'd like

Well, I don't have a full solution in mind. I was just thinking if we can use something like https://github.com/goadesign/goa or https://gorm.io/gen/database_to_structs.html (which I already use 😁).

At the moment, I have couple of approaches in mind:

  1. Keep the interface/vars definitions and write code in the same file to define mappings
  2. Like goa and gorm/gen, completely abstract the converter definition and generate everything.

I prefer the second approach for better abstraction and I gain no value of managing the interface definition myself.

Maybe something like the following:

Details

package def

// Below is very basic illustration of definition builder.

type Converter struct {
	// config holds all configurations that normally added as interface comments
	config any
	// methods holds definitions for all methods for that converter interface
	methods []*Method
}

// NewConverter creates a new converter instance.
// config may be omitted as AFAIK there is no required configurations that must be provided in the converter level
func NewConverter(config any) *Converter {
	return &Converter{config: config}
}

func (c *Converter) Extend(path string) *Converter {
	// ...
	return c
}

// Method creates a new method on this converter instance.
// config may be replaced by multiple arguments to properly model the method Signature.
func (c *Converter) Method(config any, builders ...func(*Method) *Method) *Converter {
	c.methods = append(c.methods, NewMethod(config).Builders(builders...))
	return c
}

type MethodMaps struct {
	source, target string
}

func NewMethodMaps(source string, target string) *MethodMaps {
	return &MethodMaps{source: source, target: target}
}

type Method struct {
	config any
	maps   []*MethodMaps
}

func NewMethod(config any) *Method {
	return &Method{config: config}
}

func (m *Method) Map(source, target string) *Method {
	m.maps = append(m.maps, NewMethodMaps(source, target))
	return m
}

func (m *Method) Builders(fns ...func(*Method) *Method) *Method {
	mm := m
	for _, fn := range fns {
		mm = fn(mm)
	}
	return mm
}

func (m *Method) AutoMap(source, target string) *Method {
	// ...
	return m
}

func (m *Method) Ignore(fields ...string) *Method {
	// ...
	return m
}

func Generate(converters []*Converter) error {
	// do the magic
	return nil
}

// In my code, I should have an entrypoint that constructs the converters and pass them to Generate func.
// goverter may also support this mode somehow in its CLI.

func main() {
	converters := []*Converter{
		NewConverter("converterA").
			Method("ConvertAToB", aToBMapField),
		NewConverter("converterB").
			Method("ConvertAToB", aToBMapField),
	}
	err := Generate(converters)
	if err != nil {
		panic(err)
	}
}

func aToBMapField(m *Method) *Method {
	mappings := map[string]string{
		"id":         "ID",
		"created_at": "Audit.CreatedAt",
	}

	for source, target := range mappings {
		m.Map(source, target)
	}

	return m
}

Describe alternatives you've considered

None

Please 👍 this issue if you like this functionality. If you have a specific use-case in mind, feel free to comment it.

@jmattheis
Copy link
Owner
jmattheis commented Apr 5, 2025

Goverter internally has a method that is similar to the one you propose.

func Generate(converters []*config.Converter, c Config) (map[string][]byte, error)

https://pkg.go.dev/github.com/jmattheis/goverter@v1.8.2/generator#Generate

It may not be easily usable because it the data is structured in a way that makes it easy to use in the implementation, and not to instantiate it. There are probably some private fields that must be set, so I don't think it's usable without modifying goverter. Also keep in mind that this api is not versionized.

Have you considered building a small pre code generator that uses your go code dsl and produces an interface usable by goverter? This likely solves most of your issues and shouldn't be too much effort.

I'll see this supported in goverter, but native support will be quite some effort and needs some refactoring. So I'd wait for more user feedback for native support.

@jmattheis jmattheis added the feature New feature or request label Apr 5, 2025
@YasserRabee
Copy link
Contributor Author

Thanks @jmattheis. I'm not familiar with goverter internals, but I took a quick look and yeah that seems very close.

Have you considered building a small pre code generator that uses your go code dsl and produces an interface usable by goverter? This likely solves most of your issues and shouldn't be too much effort.

Nope. We're into simplifying our toolchain here. Adding another generation step that our team manages directly doesn't seem a good fit for us.

I'll see this supported in goverter, but native support will be quite some effort and needs some refactoring. So I'd wait for more user feedback for native support.

Awesome! Totally agree, no need to put an effort without a clear value. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants
0