Skip to content

Setting: wrapErrorsUsing

wrapErrorsUsing [PACKAGE] is a boolean setting and can be defined as CLI argument, converter 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 errors
  • func Key(any) ELEMENT: used as ELEMENT when the errors occurs for a value of a map
  • func Index(int) ELEMENT: used as ELEMENT when the errors occurs for an item of an array / slice
  • func Field(string) ELEMENT: used as ELEMENT 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:

go
err := originalErr
err = Wrap(err, Key("jmattheis"), Index(5), Key("abc"), Field("Name"))
go
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.

go
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
}
go
// 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
	var exampleOutputList []example.Output
	if source.Friends != nil {
		exampleOutputList = 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))
			}
			exampleOutputList[i] = exampleOutput2
		}
	}
	exampleOutput.Friends = exampleOutputList
	xint, err := strconv.Atoi(source.Age)
	if err != nil {
		return exampleOutput, patherr.Wrap(err, patherr.Field("Age"))
	}
	exampleOutput.Age = xint
	var mapStringInt map[string]int
	if source.Attributes != nil {
		mapStringInt = 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))
			}
			mapStringInt[key] = xint2
		}
	}
	exampleOutput.Attributes = mapStringInt
	return exampleOutput, nil
}
mod
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:

go
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 }
go
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)
}
go
// 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
	var exampleOutputList []example.Output
	if source.Friends != nil {
		exampleOutputList = 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))
			}
			exampleOutputList[i] = exampleOutput2
		}
	}
	exampleOutput.Friends = exampleOutputList
	xint, err := strconv.Atoi(source.Age)
	if err != nil {
		return exampleOutput, patherr.Wrap(err, patherr.Field("Age"))
	}
	exampleOutput.Age = xint
	var mapStringInt map[string]int
	if source.Attributes != nil {
		mapStringInt = 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))
			}
			mapStringInt[key] = xint2
		}
	}
	exampleOutput.Attributes = mapStringInt
	return exampleOutput, nil
}