diff icon indicating copy to clipboard operation
diff copied to clipboard

net.IP comparable as String.

Open s3rj1k opened this issue 3 years ago • 2 comments

How do I force differ function so it would compare net.IP as a string and not as a slice?

https://go.dev/play/p/0VMlM5RQ9WO

package main

import (
	"log"
	"net"

	"github.com/davecgh/go-spew/spew"
	"github.com/r3labs/diff/v2"
)

type LoadBalancer struct {
	IP []net.IP
}

func main() {
	x := LoadBalancer{
		IP: []net.IP{
			net.ParseIP("192.0.2.1"),
			net.ParseIP("192.0.2.2"),
		},
	}

	y := LoadBalancer{
		IP: []net.IP{
			net.ParseIP("192.0.2.1"),
			net.ParseIP("192.0.2.3"),
		},
	}

	changelog, err := diff.Diff(x, y)
	if err != nil {
		log.Fatal(err)
	}

	spew.Dump(changelog)
}
(diff.Changelog) (len=1 cap=1) {
 (diff.Change) {
  Type: (string) (len=6) "update",
  Path: ([]string) (len=3 cap=3) {
   (string) (len=2) "IP",
   (string) (len=1) "1",
   (string) (len=2) "15"
  },
  From: (uint8) 2,
  To: (uint8) 3,
  parent: (interface {}) <nil>
 }
}

s3rj1k avatar Jun 16 '22 21:06 s3rj1k

To accomplish this, I implemented a custom ValueDiffer which looks like the following:

Instantiation

type NetIPDiffer struct {
	DiffFunc (func(path []string, a, b reflect.Value, p interface{}) error)
}

func (differ NetIPDiffer) Match(a, b reflect.Value) bool {
	return diff.AreType(a, b, reflect.TypeOf(net.IP{}))
}

func (differ NetIPDiffer) Diff(dt diff.DiffType, df diff.DiffFunc, cl *diff.Changelog, path []string, a, b reflect.Value, p interface{}) error {
	if a.Kind() == reflect.Invalid {
		cl.Add(diff.CREATE, path, nil, b.Interface())
		return nil
	}
	if b.Kind() == reflect.Invalid {
		cl.Add(diff.DELETE, path, a.Interface(), nil)
		return nil
	}

	aString := a.Interface().(net.IP).String()
	bString := b.Interface().(net.IP).String()

	if aString != bString {
		cl.Add(diff.UPDATE, path, aString, bString)
	}

	return nil
}

func (differ NetIPDiffer) InsertParentDiffer(dfunc func(path []string, a, b reflect.Value, p interface{}) error) {
	differ.DiffFunc = dfunc
}

Use

d, err := diff.NewDiffer(
    diff.DisableStructValues(),
    diff.SliceOrdering(true),
    diff.CustomValueDiffers(&NetIPDiffer{}),
)

For reference, I looked at the internal diff_test.go file to put this stuff together.

tyliec avatar Mar 06 '24 03:03 tyliec

@tyliec nice, I think this should be in docs at least

s3rj1k avatar Mar 06 '24 08:03 s3rj1k