Setting: wrapErrorsUsing
wrapErrorsUsing [PACKAGE]
can be defined as CLI argument, conversion comment or method comment. This setting is inheritable.
Enable wrapErrorsUsing
to instruct goverter to wrap errors returned by extend
and map [SOURCE-PATH] TARGET | METHOD
using the implementation specified in PACKAGE
.
The configured PACKAGE
is required to have four callable identifiers:
func Wrap(error, ELEMENT...) error
: used to wrap conversion errorsfunc Key(any) ELEMENT
: used asELEMENT
when the errors occurs for a value of a mapfunc Index(int) ELEMENT
: used asELEMENT
when the errors occurs for an item of an array / slicefunc Field(string) ELEMENT
: used asELEMENT
when the errors occurs for field inside a struct
The type of ELEMENT
can be anything that is applicable to the Wrap
signature, it must be consistent between all four methonds. When goverter returns errors in a conversion function it will Wrap
the original error and also provide the target path for the failed conversion.
Goverter creates submethods for code reuse, this means that Wrap
may be called multiple times. For your implementation it shouldn't matter if the path is given in one call or multiple. Both these examples should have the same end result:
err := originalErr
err = Wrap(err, Key("jmattheis"), Index(5), Key("abc"), Field("Name"))
err := originalErr
err = Wrap(err, Key("abc"), Field("Name"))
err = Wrap(err, Index(5))
err = Wrap(err, Key("jmattheis"))
Here is an example using github.com/goverter/patherr. This is an implementation for satisfying the requirements from above. You can use it directly or as a template for creating your own implementation.
package example
// goverter:converter
// goverter:wrapErrorsUsing github.com/goverter/patherr
// goverter:extend strconv:Atoi
type Converter interface {
Convert(source map[int]Input) (map[int]Output, error)
}
type Input struct {
Friends []Input
Age string
Attributes map[string]string
}
type Output struct {
Friends []Output
Age int
Attributes map[string]int
}
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter
package generated
import (
patherr "github.com/goverter/patherr"
example "goverter/example"
"strconv"
)
type ConverterImpl struct{}
func (c *ConverterImpl) Convert(source map[int]example.Input) (map[int]example.Output, error) {
var mapIntExampleOutput map[int]example.Output
if source != nil {
mapIntExampleOutput = make(map[int]example.Output, len(source))
for key, value := range source {
exampleOutput, err := c.exampleInputToExampleOutput(value)
if err != nil {
return nil, patherr.Wrap(err, patherr.Key(key))
}
mapIntExampleOutput[key] = exampleOutput
}
}
return mapIntExampleOutput, nil
}
func (c *ConverterImpl) exampleInputToExampleOutput(source example.Input) (example.Output, error) {
var exampleOutput example.Output
if source.Friends != nil {
exampleOutput.Friends = make([]example.Output, len(source.Friends))
for i := 0; i < len(source.Friends); i++ {
exampleOutput2, err := c.exampleInputToExampleOutput(source.Friends[i])
if err != nil {
return exampleOutput, patherr.Wrap(err, patherr.Field("Friends"), patherr.Index(i))
}
exampleOutput.Friends[i] = exampleOutput2
}
}
xint, err := strconv.Atoi(source.Age)
if err != nil {
return exampleOutput, patherr.Wrap(err, patherr.Field("Age"))
}
exampleOutput.Age = xint
if source.Attributes != nil {
exampleOutput.Attributes = make(map[string]int, len(source.Attributes))
for key, value := range source.Attributes {
xint2, err := strconv.Atoi(value)
if err != nil {
return exampleOutput, patherr.Wrap(err, patherr.Field("Attributes"), patherr.Key(key))
}
exampleOutput.Attributes[key] = xint2
}
}
return exampleOutput, nil
}
module goverter/example
go 1.18.0
require github.com/goverter/patherr v1.0.0
Minimal impl
Here is an example minimal implementation of the requirements above:
package patherr
import "fmt"
type Error struct {
Path []any
Inner error
}
func (e *Error) Error() string {
return fmt.Sprintf("error at path %s: %s", e.Path, e.Inner)
}
func Wrap(err error, path ...any) error {
if err, ok := err.(*Error); ok {
err.Path = append(path, err.Path...)
return err
}
return &Error{Inner: err, Path: path}
}
func Key(v any) any { return v }
func Index(v int) any { return v }
func Field(v string) any { return v }
package example
// goverter:converter
// goverter:wrapErrorsUsing goverter/example/patherr
// goverter:output:file ./generated/minimal.go
// goverter:extend strconv:Atoi
type Minimal interface {
Convert(source map[int]Input) (map[int]Output, error)
}
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter
package generated
import (
example "goverter/example"
patherr "goverter/example/patherr"
"strconv"
)
type MinimalImpl struct{}
func (c *MinimalImpl) Convert(source map[int]example.Input) (map[int]example.Output, error) {
var mapIntExampleOutput map[int]example.Output
if source != nil {
mapIntExampleOutput = make(map[int]example.Output, len(source))
for key, value := range source {
exampleOutput, err := c.exampleInputToExampleOutput(value)
if err != nil {
return nil, patherr.Wrap(err, patherr.Key(key))
}
mapIntExampleOutput[key] = exampleOutput
}
}
return mapIntExampleOutput, nil
}
func (c *MinimalImpl) exampleInputToExampleOutput(source example.Input) (example.Output, error) {
var exampleOutput example.Output
if source.Friends != nil {
exampleOutput.Friends = make([]example.Output, len(source.Friends))
for i := 0; i < len(source.Friends); i++ {
exampleOutput2, err := c.exampleInputToExampleOutput(source.Friends[i])
if err != nil {
return exampleOutput, patherr.Wrap(err, patherr.Field("Friends"), patherr.Index(i))
}
exampleOutput.Friends[i] = exampleOutput2
}
}
xint, err := strconv.Atoi(source.Age)
if err != nil {
return exampleOutput, patherr.Wrap(err, patherr.Field("Age"))
}
exampleOutput.Age = xint
if source.Attributes != nil {
exampleOutput.Attributes = make(map[string]int, len(source.Attributes))
for key, value := range source.Attributes {
xint2, err := strconv.Atoi(value)
if err != nil {
return exampleOutput, patherr.Wrap(err, patherr.Field("Attributes"), patherr.Key(key))
}
exampleOutput.Attributes[key] = xint2
}
}
return exampleOutput, nil
}