mergo

repository·master·Indexed 25 days ago

https://github.com/darccio/mergo

A Go helper library for merging structs and maps, specifically designed for applying default values to configuration structs by filling in zero-value fields. It supports recursive merging of exported fields, custom merge behavior via Transformers, and functional options such as WithOverride, WithAppendSlice, and WithoutDereference. It also provides functionality to map map[string]interface{} to structs and vice versa using mergo.Map().

Tokens
2K
Snippets
9
Records
11
Agent score
83%

What's inside mergo

  1. Migrate from github.com/imdario/mergo to dario.cat/mergo

    master

    As of version 1.0.0, Mergo uses the vanity URL dario.cat/mergo. If your project or its dependencies are having issues with this change, you can use a Go replace directive to pin the version to the last one using the old import URL:

    replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16
  2. Customize merge behavior with Transformers

    master

    Transformers allow you to define custom logic for how specific types are merged. You must implement the Transformer interface, which returns a function that handles the reflect.Value of the destination and source.

    package main
    
    import (
    	"fmt"
    	"dario.cat/mergo"
        "reflect"
        "time"
    )
    
    type timeTransformer struct {
    }
    
    func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
    	if typ == reflect.TypeOf(time.Time{}) {
    		return func(dst, src reflect.Value) error {
    			if dst.CanSet() {
    				isZero := dst.MethodByName("IsZero")
    				result := isZero.Call([]reflect.Value{})
    				if result[0].Bool() {
    					dst.Set(src)
    				}
    			}
    			return nil
    		}
    	}
    	return nil
    }
    
    type Snapshot struct {
    	Time time.Time
    	// ...
    }
    
    func main() {
    	src := Snapshot{time.Now()}
    	dest := Snapshot{}
    	mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{}))
    	fmt.Println(dest)
    }
  3. Merge structs and maps with Merge()

    master

    Use mergo.Merge(&dst, src) to merge src into dst.

    Constraints:

    • Only same-type structs and maps can be merged.
    • Only exported fields are merged (unexported/private fields are ignored).
    • Merging is recursive for exported fields.
    • Empty structs are treated as zero values and won't be merged.
    • Maps are merged recursively, except for structs inside maps (which are not addressable via reflection).

    If dst has a non-zero value in a field, src will only overwrite it if you use the mergo.WithOverride transformer.

    if err := mergo.Merge(&dst, src); err != nil {
        // ...
    }
  4. Override pointers using WithoutDereference

    master

    If you need to override pointers such that the source pointer's value is assigned to the destination's pointer (rather than merging the values the pointers point to), use the mergo.WithoutDereference transformer.

    package main
    
    import (
    	"fmt"
    
    	"dario.cat/mergo"
    )
    
    type Foo struct {
    	A *string
    	B int64
    }
    
    func main() {
    	first := "first"
    	second := "second"
    	src := Foo{
    		A: &first,
    		B: 2,
    	}
    
    	dest := Foo{
    		A: &second,
    		B: 1,
    	}
    
    	mergo.Merge(&dest, src, mergo.WithOverride, mergo.WithoutDereference)
    }
  5. Overwrite values using WithOverride

    master

    By default, Mergo only sets values in dst if they are currently the zero value. To force src values to overwrite existing non-zero values in dst, use the mergo.WithOverride transformer.

    if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
        // ...
    }
  6. Map a map to a struct with Map()

    master

    You can map a map[string]interface{} to a struct (or vice versa) using mergo.Map(&dst, srcMap).

    Notes:

    • Keys in the map are capitalized to find corresponding exported fields in the struct.
    • Mapping a struct to a map is not recursive; struct members will be assigned as values rather than being converted to map[string]interface{}.
    if err := mergo.Map(&dst, srcMap); err != nil {
        // ...
    }
  7. Configure the Config struct

    master

    The Config struct defines the internal settings for the merge operation. While typically configured via functional options, you can interact with these fields directly if building custom extensions:

    FieldDescription
    TransformersCustom type-specific merging logic
    OverwriteIf true, non-empty dst values are replaced by src values
    ShouldNotDereferenceIf true, pointers are not dereferenced when checking for empty values
    AppendSliceIf true, slices are appended instead of replaced
    TypeCheckIf true, validates that types match during an overwrite
    overwriteWithEmptyValue(Internal) Overwrites dst even if src is empty
    overwriteSliceWithEmptyValue(Internal) Overwrites slices even if src is empty
    sliceDeepCopy(Internal) Performs a deep merge on slice elements
  8. Configure merge behavior with functional options

    master

    mergo uses functional options to configure the Config object passed to the merge process. Common options include:

    • WithOverride(): Overrides non-empty dst attributes with non-empty src values.
    • WithOverwriteWithEmptyValue(): Overrides non-empty dst attributes even with empty src values.
    • WithAppendSlice(): Appends src slices to dst slices instead of overwriting them.
    • WithTypeCheck(): Ensures types match when overwriting (must be used with WithOverride).
    • WithSliceDeepCopy(): Merges slice elements one-by-one using the Overwrite logic.
    • WithoutDereference(): Prevents dereferencing pointers when checking if they are empty (a non-nil pointer is never considered empty).
    • WithTransformers(transformers): Allows custom merging logic for specific types via the Transformers interface.
  9. Mergo Error Codes

    master

    Mergo returns the following errors when encountering invalid arguments or unsupported types during a merge operation:

    var (
    	ErrNilArguments                = errors.New("src and dst must not be nil")
    	ErrDifferentArgumentsTypes     = errors.New("src and dst must be of same type")
    	ErrNotSupported                = errors.New("only structs, maps, and slices are supported")
    	ErrExpectedMapAsDestination    = errors.New("dst was expected to be a map")
    	ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct")
    	ErrNonPointerArgument          = errors.New("dst must be a pointer")
    )
  10. Customize merging logic with Transformers

    master

    The Transformers interface allows you to define custom merging logic for specific types. You implement the Transformer method, which returns a function that takes the destination and source reflect.Value and returns an error.

    type Transformers interface {
    	Transformer(reflect.Type) func(dst, src reflect.Value) error
    }