r3labs / diff

A library for diffing golang structures
Mozilla Public License 2.0
884 stars 79 forks source link

net.IP comparable as String. #88

Open s3rj1k opened 2 years ago

s3rj1k commented 2 years ago

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>
 }
}
tyliec commented 6 months ago

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.

s3rj1k commented 6 months ago

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