Goverter Documentation

website·Indexed 32 days ago

https://goverter.jmattheis.de

Goverter is a code generator for Go that simplifies the conversion of one type to another, specifically focusing on struct conversion. It provides features for mapping fields, handling unexported fields, managing pointer inconsistencies, and configuring generation via CLI arguments or conversion comments.

Tokens
17.7K
Snippets
95
Records
132
Agent score
58%

What's inside Goverter

  1. Overview of Goverter type-safe converter

    Goverter is a tool for creating type-safe converters in Go. It generates conversion code based on defined interfaces, serving as an alternative to reflection-based libraries like jinzhu/copier. Key features include:

    • Performance: No reflection is used at runtime, ensuring fast execution.
    • Automatic Conversion: Supports builtin types including slices, maps, named types, primitive types, pointers, and structs with matching fields.
    • Copying Behavior: Performs deep copies by default, with support for shallow copying.
    • Extensibility: Allows the implementation of custom converter methods.
    • Validation: Provides clear errors during generation if the target struct has unmapped fields or if types cannot be converted without data loss.
  2. Update an existing struct instance using goverter

    To create a conversion that updates an existing struct instance rather than returning a new one, use the // goverter:update target annotation on the conversion method. The target instance must be passed as a pointer argument to the method.
    package update
    
    // goverter:converter
    type Converter interface{
    	// goverter:update target
    	Convert(source Input, target *Output)
    }
    
    type Input struct {
    	Name    *string
    	Aliases []string
    	Age     int
    }
    
    type Output struct {
    	Name    *string
    	Aliases []string
    	Age     int
    }
  3. Use a constructor to handle unexported fields

    When a target struct with unexported fields must be instantiated via a constructor, use // goverter:extend to make the constructor available globally or // goverter:map to link a specific source field to the constructor method.
    // goverter:converter
    // goverter:extend ReaderForBytes
    type ConverterGlobalMethod interface {
    	// goverter:map Content Reader
    	Convert(source *Input) *Output
    }
    
    func ReaderForBytes(content []byte) io.Reader {
    	return bytes.NewReader(content)
    }
    
    // Or specify the constructor directly in the map:
    // goverter:map Content Reader | bytes:NewReader
  4. Define a converter interface with Goverter

    To generate mapping code, create a Go interface and mark it with the // goverter:converter comment. You can use specific comments to control the generation: use // goverter:ignore to skip a method or // goverter:map to map a nested source field to a destination field.
    package example
    
    // goverter:converter
    type Converter interface{
      ConvertItems(source []Input) []Output
      
      // goverter:ignore Irrelevant
      // goverter:map Nested.AgeInYears Age
      Convert(source Input) Output
    }
    
    type Input struct {
      Name string
      Nested InputNested
    }
    
    type InputNested struct {
      AgeInYears int
    }
    
    type Output struct {
      Name string
      Age int
      Irrelevant bool
    }
  5. Understand Goverter's automatic conversion error behavior

    Goverter employs an 'error early' approach: if the tool cannot automatically map a source type to a target type, it will trigger a generation error rather than producing potentially incorrect code. This ensures that missing fields or incompatible types are identified at compile/generation time. For example, if a target struct contains a field (e.g., Age) that does not exist in the source struct, Goverter will fail with a message stating: Cannot match the target field with the source entry: "Age" does not exist.
    package example
    
    // goverter:converter
    type Converter interface {
        Convert([]Input) []Output
    }
    
    type Input struct {
        Name string
    }
    
    type Output struct {
        Name string
        Age int // This field does not exist in Input, causing Goverter to error
    }
  6. Run Goverter without installation using go run

    You can execute Goverter without installing a permanent binary on your system by using go run. This is the simplest method, although the first execution may be slower as Go compiles the tool before running it.
    $ go run github.com/jmattheis/goverter/cmd/goverter@v1.10.0 --help
  7. Migrate Goverter converter comments from v0.x.x to v1.0.x

    When upgrading from Goverter v0.x.x to v1.0.x, update the following converter comments to their new syntax:
    // Old: goverter:mapExtend FIELD METHOD
    // New: goverter:map . FIELD | METHOD
    
    // Old: goverter:mapIdentity FIELD
    // New: goverter:map . FIELD
  8. Perform deep copies of instances using Goverter

    You can implement a deep copy of a type by defining a Goverter conversion where the source and target types are identical. This allows you to modify a copy of an instance without affecting the original. Compared to runtime libraries like copier, using Goverter provides better performance through generated code and a compile-time guarantee that the types are convertible.
  9. Understand the Goverter type conversion generation logic

    Goverter follows a specific sequence of rules to determine how to convert a source type (CS) to a target type (CT). It attempts to simplify types until they reach primitives that can be copied by value. The generation process follows these priority steps:

    1. Custom Extend Methods: If an extend method func(CS) CT exists, it is used and generation finishes.
    2. Underlying Types: If useUnderlyingTypeMethods is enabled and an extend method exists for an underlying type, it is used.
    3. Same Type Skip: If skipCopySameType is enabled and CS == CT, the source value is used directly.
    4. Primitive Copy: If both CS and CT are the same primitive type, the value is copied.
    5. Pointer to Pointer: If both are pointers (*NS to *NT), it checks for nil and recursively calls generate(NS, NT).
    6. Value to Pointer: If CS is not a pointer but CT is *NT, it generates a pointer to the result of generate(CS, NT).
    7. Pointer to Value: If CS is *NS and CT is not a pointer, and useZeroValueOnPointerInconsistency is enabled: it uses the zero value of CT if CS is nil, otherwise it calls generate(NS, CT).
    8. Slices: If both are slices ([]NS to []NT), it iterates over the slice and converts each item via generate(NS, NT).
    9. Maps: If both are maps, it iterates over the map and converts both keys and values recursively.
    10. Structs: If both are structs, Goverter iterates through each field in the target struct (CT):
      • Skips fields marked as ignore.
      • Errors on unexported fields unless ignoreUnexported is enabled.
      • Locates the source field (SF) via explicit map definitions, same-name matching, or autoMap paths (case-insensitive if matchIgnoreCase is enabled).
      • If the source field is a method, it calls that method.
      • If a mapping method is defined, it executes the mapping function and ensures the result matches the target field type.
      • Otherwise, it recursively calls generate(SF, TF).
    11. Fallback: If none of the above rules apply, an error is thrown stating CS cannot be automatically converted to CT.
  10. Scan packages with build constraints

    Goverter supports scanning packages that utilize build constraints, allowing for more flexible code generation across different environments.
  11. Add Goverter as a project dependency in go.mod

    To ensure all developers on a project use the same version of Goverter, add it as a dependency in your go.mod file. This requires a Go modules project.
    # 1. Initialize the module if not already done
    $ go mod init module-name
    
    # 2. Add goverter as a dependency
    $ go get github.com/jmattheis/goverter@v1.10.0
    
    # 3. Run the binary
    $ go run github.com/jmattheis/goverter/cmd/goverter --help