godiff

package module
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 5, 2025 License: Apache-2.0 Imports: 13 Imported by: 3

README

godiff (Data structure diff for GoLang)

GoReportCard GoDoc

This library is compatible with Go 1.17+

Please refer to CHANGELOG.md if you encounter breaking changes.

Motivation

The goal of this library is to both produce diff and patch for arbitrary data structure in GoLang in performant manner

Other project for diffing golang structures and values:

See 3rd party differ performance comparison.

Usage

package godiff_test

import (
	"fmt"
	"github.com/viant/godiff"
	"log"
	"reflect"
	"testing"
)

type Flag struct {
	Value int
}
type Record struct {
	Id        int
	Name      string
	Dep       *Record
	Transient string `diff:"-"`
	Flags     []Flag
}

//ExampleNew shows basic differ usage
func ExampleNew() {

    record1 := &Record{
    Id:    1,
    Name:  "Rec1",
    Flags: []Flag{{Value: 3}, {Value: 15}},
    }
    
    record2 := &Record{
    Id:    2,
    Name:  "Rec1",
    Dep:   &Record{Id: 10},
    Flags: []Flag{{Value: 12}},
    }
    
    diff, err := godiff.New(reflect.TypeOf(record1), reflect.TypeOf(record2))
    if err != nil {
    log.Fatal(err)
    }
    changeLog := diff.Diff(record1, record2)
    fmt.Println(changeLog.String())
}

Supported tags:

  • name - optional name in the change log
  • indexBy - index elements before comparing
  • sort - sort elements before comparing
  • whitespace - remove specified whitespace chars when converting string to list or map
  • pairSeparator - pair separator to convert string to a map comparison
  • pairDelimiter - pair delimiter
  • itemSeparator - item separator to convert string to a slice comparison
  • '-' (ignore)

Work in progress tag:

  • timeLayout
  • precision

Config option

  • WithTagName
  • WithRegistry
  • NullifyEmpty
  • WithConfig

Diff option

  • WithPresence
  • WithShallow

Benchmark

GoDiff is around 5x faster than s3lab diff.

pkg: github.com/viant/godiff/bench
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
Benchmark_GoDiff
Benchmark_GoDiff-16       	 1432188	       815.9 ns/op	     768 B/op	      14 allocs/op
Benchmark_S3LabDiff
Benchmark_S3LabDiff-16    	  288633	      4531 ns/op	    2262 B/op	      42 allocs/op

Contributing to godiff

godiff is an open source project and contributors are welcome!

See TODO list

License

The source code is made available under the terms of the Apache License, Version 2, as stated in the file LICENSE.

Individual files may be made available under their own specific license, all compatible with Apache License, Version 2. Please see individual files for details.

Credits and Acknowledgements

Library Author: Adrian Witas

Documentation

Index

Examples

Constants

View Source
const (
	//ChangeTypeCreate defines create change type
	ChangeTypeCreate = ChangeType("create")
	//ChangeTypeUpdate defines update change type
	ChangeTypeUpdate = ChangeType("update")
	//ChangeTypeDelete defines delete change type
	ChangeTypeDelete = ChangeType("delete")
)
View Source
const (
	//PathKindRoot defines root path kind
	PathKindRoot = PathKind(iota)
	//PathKinField defines field path kind
	PathKinField
	//PathKindKey defines key path kind
	PathKindKey
	//PathKindIndex defines index path kind
	PathKindIndex
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Change

type Change struct {
	Type  ChangeType
	Path  *Path
	From  interface{}
	To    interface{}
	Error string `json:",omitempty"`
}

Change represents a change

type ChangeLog

type ChangeLog struct {
	Changes []*Change
}

ChangeLog represents a change log

func (*ChangeLog) Add

func (l *ChangeLog) Add(change *Change)

Add adds change log

func (*ChangeLog) AddCreate

func (l *ChangeLog) AddCreate(path *Path, value interface{})

AddCreate adds create change

func (*ChangeLog) AddDelete

func (l *ChangeLog) AddDelete(path *Path, value interface{})

AddDelete adds delete change

func (*ChangeLog) AddError

func (l *ChangeLog) AddError(path *Path, err error)

AddError adds an error

func (*ChangeLog) AddUpdate

func (l *ChangeLog) AddUpdate(path *Path, from, to interface{})

AddUpdate adds update change

func (*ChangeLog) Size

func (l *ChangeLog) Size() int

Size returns change log size

func (*ChangeLog) String

func (l *ChangeLog) String() string

String stringify change

func (*ChangeLog) ToChangeRecords

func (l *ChangeLog) ToChangeRecords(source, id, userID string) []*ChangeRecord

ToChangeRecords converts changeLog to change records

type ChangeRecord

type ChangeRecord struct {
	Source   string `json:",omitempty"`
	SourceID string `json:",omitempty"`
	UserID   string `json:",omitempty"`
	Path     string
	Change   string
	From     interface{} `json:",omitempty"`
	To       interface{} `json:",omitempty"`
	Error    string      `json:",omitempty"`
}

ChangeRecord defines a change record

type ChangeType

type ChangeType string

ChangeType defines a change type

type Comparator

type Comparator interface {
	Matches(from, to interface{}, tag *Tag) (bool, error)
}

Comparator an interface for comparison customization

type Config

type Config struct {
	TimeLayout   string
	NullifyEmpty *bool
	TagName      string //diff by default
	StrictMode   bool   //non-strict mode allows string with non-string matches
	// contains filtered or unexported fields
}

Config represents a config

func (*Config) Init

func (c *Config) Init()

Init init config

type ConfigOption added in v0.3.0

type ConfigOption func(config *Config)

ConfigOption represents an option

func NullifyEmpty

func NullifyEmpty(flag bool) ConfigOption

NullifyEmpty updated config option

func WithConfig

func WithConfig(cfg *Config) ConfigOption

WithConfig updated config tag

func WithRegistry

func WithRegistry(registry *Registry) ConfigOption

WithRegistry updated config with registry

func WithTag

func WithTag(tag *Tag) ConfigOption

WithTag updated config tag

func WithTagName

func WithTagName(name string) ConfigOption

WithTagName updated config tag

type Differ

type Differ struct {
	// contains filtered or unexported fields
}

Differ represents a differ

func New

func New(from, to reflect.Type, opts ...ConfigOption) (*Differ, error)

New creates a differ

Example

ExampleNew shows basic differ usage

package main

import (
	"fmt"
	"github.com/viant/godiff"
	"log"
	"reflect"
)

func main() {

	type Flag struct {
		Value int
	}
	type Record struct {
		Id        int
		Name      string
		Dep       *Record
		Transient string `diff:"-"`
		Flags     []Flag
	}
	record1 := &Record{
		Id:    1,
		Name:  "Rec1",
		Flags: []Flag{{Value: 3}, {Value: 15}},
	}

	record2 := &Record{
		Id:    2,
		Name:  "Rec1",
		Dep:   &Record{Id: 10},
		Flags: []Flag{{Value: 12}},
	}

	diff, err := godiff.New(reflect.TypeOf(record1), reflect.TypeOf(record2))
	if err != nil {
		log.Fatal(err)
	}
	changeLog := diff.Diff(record1, record2)
	fmt.Println(changeLog.String())
}

func (*Differ) Diff

func (d *Differ) Diff(from, to interface{}, opts ...Option) *ChangeLog

Diff creates change log based on comparison from and to values

type Option

type Option func(c *Options)

func WithSetMarker added in v0.4.1

func WithSetMarker(f bool) Option

func WithShallow added in v0.3.0

func WithShallow(f bool) Option

type Options added in v0.3.0

type Options struct {
	// contains filtered or unexported fields
}

func (*Options) Apply added in v0.3.0

func (o *Options) Apply(options []Option)

type Path

type Path struct {
	Kind  PathKind    `json:",omitempty"`
	Path  *Path       `json:",omitempty"`
	Name  string      `json:",omitempty"`
	Index int         `json:",omitempty"`
	Key   interface{} `json:",omitempty"`
}

Path represents an arbitrary data structure path

func (*Path) Element

func (p *Path) Element(index int) *Path

Element adds slice element node

func (*Path) Entry

func (p *Path) Entry(name string) *Path

Entry adds map entry node

func (*Path) Field

func (p *Path) Field(name string) *Path

Field add fields node

func (*Path) String

func (p *Path) String() string

String stringifies a path

type PathKind

type PathKind int

PathKind defines patch kind

type Registry

type Registry struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

Registry represents differ registry

func NewRegistry

func NewRegistry() *Registry

func (*Registry) Get

func (r *Registry) Get(from, to reflect.Type, tag *Tag, options ...ConfigOption) (*Differ, error)

type Tag

type Tag struct {
	Name           string
	PresenceMarker bool
	PairSeparator  string
	PairDelimiter  string

	ItemSeparator string

	Whitespace   string
	IndexBy      string
	Sort         bool
	TimeLayout   string
	Precision    *int
	Ignore       bool
	NullifyEmpty *bool
	// contains filtered or unexported fields
}

Tag represents a tag

func ParseTag

func ParseTag(tagString string) (*Tag, error)

ParseTag parses tag

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL