diff --git a/Taskfile.yml b/Taskfile.yml index 28ad8da..6643c63 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -4,4 +4,6 @@ tasks: generate: dir: './cmd/generate_testdata' cmds: + - rm -rf ../../testdata/generated + - mkdir -p ../../testdata/generated - go generate . diff --git a/cmd/generate_testdata/main.go b/cmd/generate_testdata/main.go index e274c43..8413ace 100644 --- a/cmd/generate_testdata/main.go +++ b/cmd/generate_testdata/main.go @@ -9,9 +9,17 @@ import ( "log" "os" "path/filepath" + "strings" + + mianxiang "github.com/520MianXiangDuiXiang520/json-diff" + victorlowther "github.com/VictorLowther/jsonpatch2" + cameront "github.com/cameront/go-jsonpatch" + herkyl "github.com/herkyl/patchwerk" + mattbaird "github.com/mattbaird/jsonpatch" + snorwin "github.com/snorwin/jsonpatch" + wI2L "github.com/wI2L/jsondiff" "github.com/qri-io/jsonpointer" - "github.com/wI2L/jsondiff" "golang.org/x/tools/txtar" ) @@ -31,6 +39,7 @@ type metadata struct { } `json:"terraform,omitempty"` Metadata map[string]map[string]any `json:"metadata,omitempty"` JSONInJSON []string `json:"jsonInJSON,omitempty"` + PatchLib *string `json:"patchLib,omitempty"` } func main() { @@ -50,24 +59,22 @@ func main() { err = json.Unmarshal(txtarchive.Comment, &metadata) die(err) - var before, after interface{} - err = json.Unmarshal(txtarchive.Files[0].Data, &before) - die(err) + beforeJSON := txtarchive.Files[0].Data + afterJSON := txtarchive.Files[1].Data - err = json.Unmarshal(txtarchive.Files[1].Data, &after) + var before, after interface{} + err = json.Unmarshal(beforeJSON, &before) die(err) - patch, err := jsondiff.Compare(before, after) + err = json.Unmarshal(afterJSON, &after) die(err) - buf := bytes.Buffer{} - encoder := json.NewEncoder(&buf) - encoder.SetIndent("", " ") - encoder.SetEscapeHTML(false) - err = encoder.Encode(patch) - die(err) + patchLib := "wI2L" + if metadata.PatchLib != nil { + patchLib = *metadata.PatchLib + } - txtarchive.Files[1].Data = buf.Bytes() + txtarchive.Files[1].Data = compare(patchLib, beforeJSON, afterJSON) txtarchive.Files[1].Name = "patch.json" for i, pointer := range metadata.JSONInJSON { @@ -80,18 +87,7 @@ func main() { afterStr, err := ptr.Eval(after) die(err) - var before, after interface{} - err = json.Unmarshal([]byte(beforeStr.(string)), &before) - die(err) - - err = json.Unmarshal([]byte(afterStr.(string)), &after) - die(err) - - patch, err := jsondiff.Compare(before, after) - die(err) - - patchData, err := json.MarshalIndent(patch, "", " ") - die(err) + patchData := compare(patchLib, []byte(beforeStr.(string)), []byte(afterStr.(string))) patchFile := txtar.File{ Name: fmt.Sprintf("jsonInJSON.%d.json", i), @@ -102,6 +98,7 @@ func main() { } metadata.JSONInJSON = nil + metadata.PatchLib = nil txtarchive.Comment, err = json.MarshalIndent(metadata, "", " ") die(err) @@ -118,6 +115,65 @@ func main() { } } +func compare(patchLib string, beforeJSON, afterJSON []byte) []byte { + var before, after interface{} + err := json.Unmarshal(beforeJSON, &before) + die(err) + + err = json.Unmarshal(afterJSON, &after) + die(err) + + var marshal bool + var patch any + switch strings.ToLower(patchLib) { + case "cameront": + patch, err = cameront.MakePatch(before, after) + marshal = true + case "herkyl": + patch, err = herkyl.Diff(beforeJSON, afterJSON) + marshal = true + case "mattbaird": + patch, err = mattbaird.CreatePatch(beforeJSON, afterJSON) + marshal = true + case "mianxiang": + patch, err = mianxiang.AsDiffs(beforeJSON, afterJSON) + case "snorwin": + var patchList snorwin.JSONPatchList + patchList, err = snorwin.CreateJSONPatch(after, before) + patch = patchList.Raw() + case "victorlowther": + patch, err = victorlowther.Generate(beforeJSON, afterJSON, false) + marshal = true + case "victorlowther-paranoid": + patch, err = victorlowther.Generate(beforeJSON, afterJSON, true) + marshal = true + case "wi2l": + patch, err = wI2L.Compare(before, after) + marshal = true + default: + fmt.Fprintf(os.Stderr, `Unknown patch lib %q, default to "wI2L"`, patchLib) + patch, err = wI2L.Compare(before, after) + marshal = true + } + die(err) + + var patchData []byte + if marshal { + buf := bytes.Buffer{} + encoder := json.NewEncoder(&buf) + encoder.SetIndent("", " ") + encoder.SetEscapeHTML(false) + err = encoder.Encode(patch) + die(err) + patchData = buf.Bytes() + } else { + patchData = patch.([]byte) + patchData = append(patchData, '\n') + } + + return patchData +} + func die(err error) { if err != nil { log.Panic(err) diff --git a/cmd/go.mod b/cmd/go.mod index 6aca20a..1386f29 100644 --- a/cmd/go.mod +++ b/cmd/go.mod @@ -1,15 +1,29 @@ module github.com/breml/jsondiffprinter/cmd -go 1.21.7 +go 1.22.3 require ( github.com/breml/jsondiffprinter v0.0.5 github.com/qri-io/jsonpointer v0.1.1 + github.com/snorwin/jsonpatch v1.5.0 github.com/wI2L/jsondiff v0.5.2 golang.org/x/tools v0.21.0 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/evanphx/json-patch v0.5.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect +) + +require ( + github.com/520MianXiangDuiXiang520/json-diff v0.2.2 + github.com/VictorLowther/jsonpatch2 v1.0.1 + github.com/cameront/go-jsonpatch v0.0.0-20180223123257-a8710867776e + github.com/herkyl/patchwerk v0.0.0-20190629103337-f0ea77068152 + github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38 github.com/tidwall/gjson v1.17.1 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect diff --git a/cmd/go.sum b/cmd/go.sum index b580fe7..a49ed10 100644 --- a/cmd/go.sum +++ b/cmd/go.sum @@ -1,5 +1,44 @@ +github.com/520MianXiangDuiXiang520/json-diff v0.2.2 h1:F9LvnoP8OXuOcEJDK7YKbFwGaQfZZrsstgSaEYpq3xA= +github.com/520MianXiangDuiXiang520/json-diff v0.2.2/go.mod h1:CvZu4GzZOS8w/et7AeljQUa/O2mplywH0gfc04ZOhKs= +github.com/VictorLowther/jsonpatch2 v1.0.1 h1:+r9GAjpCFyIDvv/xnqqlWURFF1FcQpYQXF9ezLZYkEI= +github.com/VictorLowther/jsonpatch2 v1.0.1/go.mod h1:MagdKGtUJ6bwyDgLk502ME/LDMKy9Rqh9iOf41iG5ds= +github.com/cameront/go-jsonpatch v0.0.0-20180223123257-a8710867776e h1:6c3+GQuYUWljNcReOg4gxMUss9Gjll+5Y9vqDM+ILy8= +github.com/cameront/go-jsonpatch v0.0.0-20180223123257-a8710867776e/go.mod h1:kdPJxKAfR3ZdD+MWYorN1oTdV9+qwJy9jO/0meJmcxU= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/go-faker/faker/v4 v4.4.1 h1:LY1jDgjVkBZWIhATCt+gkl0x9i/7wC61gZx73GTFb+Q= +github.com/go-faker/faker/v4 v4.4.1/go.mod h1:HRLrjis+tYsbFtIHufEPTAIzcZiRu0rS9EYl2Ccwme4= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/herkyl/patchwerk v0.0.0-20190629103337-f0ea77068152 h1:GITUy7r2Eijl7u/Xe5AOs3HBV3Gt/3+l0j2bTh2UO5Y= +github.com/herkyl/patchwerk v0.0.0-20190629103337-f0ea77068152/go.mod h1:bRtCxY0f88xmUw+Y0Xfl/uGaCWMUy04wUGSKbnQdsJs= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38 h1:hQWBtNqRYrI7CWIaUSXXtNKR90KzcUA5uiuxFVWw7sU= +github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0= +github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= +github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA= github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64= +github.com/snorwin/jsonpatch v1.5.0 h1:0m56YSt9cHiJOn8U+OcqdPGcDQZmhPM/zsG7Dv5QQP0= +github.com/snorwin/jsonpatch v1.5.0/go.mod h1:e0IDKlyFBLTFPqM0wa79dnMwjMs3XFvmKcrgCRpDqok= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -10,7 +49,19 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/wI2L/jsondiff v0.5.2 h1:f68drsfk/Xgvt3BpLVDlGkQzOC4o+qUCl9jtGr0sbfE= github.com/wI2L/jsondiff v0.5.2/go.mod h1:96+qu+Fhb323v//55RjkiTWYaGkiNWUqRV/w670bTAE= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cmd/jd/main.go b/cmd/jd/main.go index b0cdbb0..73aae13 100644 --- a/cmd/jd/main.go +++ b/cmd/jd/main.go @@ -5,13 +5,25 @@ import ( "flag" "fmt" "os" + "strings" - "github.com/wI2L/jsondiff" + mianxiang "github.com/520MianXiangDuiXiang520/json-diff" + victorlowther "github.com/VictorLowther/jsonpatch2" + cameront "github.com/cameront/go-jsonpatch" + herkyl "github.com/herkyl/patchwerk" + mattbaird "github.com/mattbaird/jsonpatch" + snorwin "github.com/snorwin/jsonpatch" + wI2L "github.com/wI2L/jsondiff" "github.com/breml/jsondiffprinter" ) -var format = flag.String("format", "ascii", "output format to use (ascii, terraform)") +var ( + // Call it showPatch? + debug = flag.Bool("debug", false, "enable debug output") + format = flag.String("format", "ascii", "output format to use (ascii, terraform)") + patchLib = flag.String("patchlib", "wI2L", "patch library to use (cameront, herkyl, mattbaird, MianXiang, snorwin, VictorLowther, VictorLowther-paranoid, wI2L)") +) func main() { flag.Parse() @@ -49,11 +61,34 @@ func run(args []string) error { return err } - patch, err := jsondiff.Compare(before, after) + var patch any + switch strings.ToLower(*patchLib) { + case "cameront": + patch, err = cameront.MakePatch(before, after) + case "herkyl": + patch, err = herkyl.Diff(beforeJSON, afterJSON) + case "mattbaird": + patch, err = mattbaird.CreatePatch(beforeJSON, afterJSON) + case "mianxiang": + // TODO: consider options offered by 520MianXiangDuiXiang520/json-diff + patch, err = mianxiang.AsDiffs(beforeJSON, afterJSON) + case "snorwin": + var patchList snorwin.JSONPatchList + patchList, err = snorwin.CreateJSONPatch(after, before) + patch = patchList.Raw() + case "victorlowther": + patch, err = victorlowther.Generate(beforeJSON, afterJSON, false) + case "victorlowther-paranoid": + patch, err = victorlowther.Generate(beforeJSON, afterJSON, true) + default: // "wI2L" + patch, err = wI2L.Compare(before, after) + } if err != nil { return err } + printPatch(patch) + switch *format { case "ascii": err = jsondiffprinter.NewJSONFormatter(os.Stdout).Format(before, patch) @@ -65,3 +100,22 @@ func run(args []string) error { return err } + +func printPatch(patch any) { + if !*debug { + return + } + + switch p := patch.(type) { + case []byte: + fmt.Println(string(p)) + default: + patchJSON, err := json.MarshalIndent(patch, "", " ") + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + return + } + + fmt.Println(string(patchJSON)) + } +} diff --git a/cmd/jd/testdata/after.json b/cmd/jd/testdata/extended/after.json similarity index 100% rename from cmd/jd/testdata/after.json rename to cmd/jd/testdata/extended/after.json diff --git a/cmd/jd/testdata/before.json b/cmd/jd/testdata/extended/before.json similarity index 100% rename from cmd/jd/testdata/before.json rename to cmd/jd/testdata/extended/before.json diff --git a/cmd/jd/testdata/simple_array/after.json b/cmd/jd/testdata/simple_array/after.json new file mode 100644 index 0000000..db525eb --- /dev/null +++ b/cmd/jd/testdata/simple_array/after.json @@ -0,0 +1,7 @@ +{ + "append": [1, 2, 3, 4], + "insert": [1, 2, 3], + "prepend": [0, 1, 2, 3], + "remove": [1, 3], + "update": [1, 22, 3] +} diff --git a/cmd/jd/testdata/simple_array/before.json b/cmd/jd/testdata/simple_array/before.json new file mode 100644 index 0000000..1175f82 --- /dev/null +++ b/cmd/jd/testdata/simple_array/before.json @@ -0,0 +1,7 @@ +{ + "append": [1, 2, 3], + "insert": [1, 3], + "prepend": [1, 2, 3], + "remove": [1, 2, 3], + "update": [1, 2, 3] +} diff --git a/cmd/jd/testdata/simple_object/after.json b/cmd/jd/testdata/simple_object/after.json new file mode 100644 index 0000000..f47538b --- /dev/null +++ b/cmd/jd/testdata/simple_object/after.json @@ -0,0 +1,8 @@ +{ + "a": 2, + "b": { + "b1": "a", + "b2": "b1" + }, + "c": true +} diff --git a/cmd/jd/testdata/simple_object/before.json b/cmd/jd/testdata/simple_object/before.json new file mode 100644 index 0000000..4c5ac58 --- /dev/null +++ b/cmd/jd/testdata/simple_object/before.json @@ -0,0 +1,7 @@ +{ + "a": 1, + "b": { + "b1": "a", + "b2": "b" + } +} diff --git a/cmd/jd/testdata/type_change/after.json b/cmd/jd/testdata/type_change/after.json new file mode 100644 index 0000000..ebf70f0 --- /dev/null +++ b/cmd/jd/testdata/type_change/after.json @@ -0,0 +1,5 @@ +{ + "object_to_array": [ + 1 + ] +} diff --git a/cmd/jd/testdata/type_change/before.json b/cmd/jd/testdata/type_change/before.json new file mode 100644 index 0000000..f9d7ddb --- /dev/null +++ b/cmd/jd/testdata/type_change/before.json @@ -0,0 +1,5 @@ +{ + "object_to_array": { + "a": 1 + } +} diff --git a/formatter.go b/formatter.go index 817db18..1854d2e 100644 --- a/formatter.go +++ b/formatter.go @@ -366,6 +366,8 @@ func (f Formatter) printOp(cfg printOpConfig) { if cfg.withKey { fmt.Fprintf(f.w, "%s%s %s%s%s", cfg.preDiffMarkerIndent, opTypeIndicator, cfg.indent, cfg.key, eol) + } else { + fmt.Fprint(f.w, " ") } if cfg.valueOld != "" { fmt.Fprintf(f.w, "%s %s ", cfg.valueOld, f.c.yellow(f.singleLineReplaceTransitionIndicator)) @@ -616,13 +618,14 @@ func asPatchTestSeries(value any, path jsonpointer.Pointer) jsonpatch.Patch { } func compileDiffPatchSeries(src jsonpatch.Patch, patch jsonpatch.Patch) (jsonpatch.Patch, error) { - deletePath := jsonpointer.Pointer{} + var deletePath *jsonpointer.Pointer res := make(jsonpatch.Patch, 0, len(src)+len(patch)) - for _, op := range src { - if !deletePath.IsEmpty() && deletePath.IsParentOf(op.Path) { + for opIndex := 0; opIndex < len(src); opIndex++ { + op := src[opIndex] + if deletePath != nil && deletePath.IsParentOf(op.Path) { continue } - deletePath = jsonpointer.Pointer{} + deletePath = nil // Search patch for operation with the same path. // If none is found, keep the operation from the source document. @@ -675,16 +678,31 @@ func compileDiffPatchSeries(src jsonpatch.Patch, patch jsonpatch.Patch) (jsonpat switch patchop.Operation { case jsonpatch.OperationTest: // If the patch operation is a test operation, skip it. + opIndex-- continue case jsonpatch.OperationReplace, jsonpatch.OperationRemove: // If the patch operation is a replace or delete operation, preserve the // old value and we mark all child operations for removal. patchop.OldValue = op.Value - deletePath = op.Path + deletePath = &op.Path } res = append(res, patchop) + + if patchop.Operation == jsonpatch.OperationAdd { + res = append(res, op) + } + + if patchop.Operation == jsonpatch.OperationRemove && parentIsArray(src, patchop.Path) { + for j := opIndex + 1; j < len(src); j++ { + if src[j].Path.HasSameAncestorsAs(patchop.Path) { + src[j].Path.DecrementIndex() + continue + } + break + } + } } for i := 0; i < len(patch); i++ { @@ -704,13 +722,23 @@ func compileDiffPatchSeries(src jsonpatch.Patch, patch jsonpatch.Patch) (jsonpat return nil, fmt.Errorf("patch is not empty after it has been applied") } - sort.Slice(res, func(i, j int) bool { + sort.SliceStable(res, func(i, j int) bool { return res[i].Path.LessThan(res[j].Path) }) return res, nil } +func parentIsArray(patch jsonpatch.Patch, path jsonpointer.Pointer) bool { + for i := range patch { + if patch[i].Path.IsParentOf(path) { + _, ok := patch[i].Value.([]any) + return ok + } + } + return false +} + func findPatchIndex(patch jsonpatch.Patch, path jsonpointer.Pointer) (int, bool) { for i := range patch { if patch[i].Path.Equals(path) { diff --git a/formatter_internal_test.go b/formatter_internal_test.go index 9d45916..dda7b4e 100644 --- a/formatter_internal_test.go +++ b/formatter_internal_test.go @@ -9,9 +9,10 @@ import ( ) func Test_compileDiffPatchSeries(t *testing.T) { - tt := []struct { + tests := []struct { name string + src jsonpatch.Patch patch jsonpatch.Patch assertErr require.ErrorAssertionFunc @@ -62,11 +63,136 @@ func Test_compileDiffPatchSeries(t *testing.T) { assertErr: require.Error, }, + + { + name: "array append with index", + + src: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 0}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 1}, + }, + patch: jsonpatch.Patch{ + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/2"), Value: 2}, + }, + + assertErr: require.NoError, + want: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 0}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 1}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/2"), Value: 2}, + }, + }, + { + name: "array insert", + + src: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 0}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 2}, + }, + patch: jsonpatch.Patch{ + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 1}, + }, + + assertErr: require.NoError, + want: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 0}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 1}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 2}, + }, + }, + { + name: "array insert without LCS", + + src: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 0}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 2}, + }, + patch: jsonpatch.Patch{ + {Operation: jsonpatch.OperationReplace, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 1}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/-"), Value: 2}, + }, + + assertErr: require.NoError, + want: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 0}, + {Operation: jsonpatch.OperationReplace, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 1, OldValue: 2}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/-"), Value: 2}, + }, + }, + { + name: "array multi change", + + src: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 5}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 6}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/2"), Value: 7}, + }, + patch: jsonpatch.Patch{ + {Operation: jsonpatch.OperationRemove, Path: jsonpointer.NewPointerFromPath("/array/1")}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/2"), Value: 8}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/3"), Value: 9}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/4"), Value: 10}, + }, + + assertErr: require.NoError, + want: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 5}, + {Operation: jsonpatch.OperationRemove, Path: jsonpointer.NewPointerFromPath("/array/1"), OldValue: 6}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 7}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/2"), Value: 8}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/3"), Value: 9}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/4"), Value: 10}, + }, + }, + { + name: "array multi change without LCS", + + src: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 5}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 6}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/2"), Value: 7}, + }, + patch: jsonpatch.Patch{ + {Operation: jsonpatch.OperationReplace, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 7}, + {Operation: jsonpatch.OperationReplace, Path: jsonpointer.NewPointerFromPath("/array/2"), Value: 8}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/-"), Value: 9}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/-"), Value: 10}, + }, + + assertErr: require.NoError, + want: jsonpatch.Patch{ + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointer(), Value: map[string]any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array"), Value: []any{}}, + {Operation: jsonpatch.OperationTest, Path: jsonpointer.NewPointerFromPath("/array/0"), Value: 5}, + {Operation: jsonpatch.OperationReplace, Path: jsonpointer.NewPointerFromPath("/array/1"), Value: 7, OldValue: 6}, + {Operation: jsonpatch.OperationReplace, Path: jsonpointer.NewPointerFromPath("/array/2"), Value: 8, OldValue: 7}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/-"), Value: 9}, + {Operation: jsonpatch.OperationAdd, Path: jsonpointer.NewPointerFromPath("/array/-"), Value: 10}, + }, + }, } - for _, tc := range tt { + for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - got, err := compileDiffPatchSeries(nil, tc.patch) + got, err := compileDiffPatchSeries(tc.src, tc.patch) tc.assertErr(t, err) require.Equal(t, tc.want, got) }) diff --git a/formatter_test.go b/formatter_test.go index 3a67293..aaea478 100644 --- a/formatter_test.go +++ b/formatter_test.go @@ -122,13 +122,16 @@ func TestFormatter(t *testing.T) { for _, formatter := range formatters { formatter := formatter t.Run(formatter.name, func(t *testing.T) { + if txtarFileByName(t, txtar, formatter.wantFilename) == nil { + t.Skip("no want file found") + } jsonInJSONInvocation = 0 buf.Reset() err := formatter.formatter.Format(before, txtarFileByName(t, txtar, "patch.json").Data) require.NoError(t, err) - require.Equal(t, string(txtarFileByName(t, txtar, formatter.wantFilename).Data), buf.String()) + require.EqualStringWithTabwriter(t, string(txtarFileByName(t, txtar, formatter.wantFilename).Data), buf.String()) }) } }) @@ -144,7 +147,6 @@ func txtarFileByName(t *testing.T, txtar *txtar.Archive, name string) *txtar.Fil } } - t.Fatalf("file %q not found", name) return nil } diff --git a/go.mod b/go.mod index 681c5a5..1752512 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/breml/jsondiffprinter -go 1.21.7 +go 1.22.3 require golang.org/x/tools v0.21.0 diff --git a/go.work b/go.work index c57c3d3..91756f3 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.21.9 +go 1.22.3 use ( . diff --git a/go.work.sum b/go.work.sum index 1eb0853..778bba4 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,10 +1,28 @@ +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/go-faker/faker/v4 v4.4.1 h1:LY1jDgjVkBZWIhATCt+gkl0x9i/7wC61gZx73GTFb+Q= +github.com/go-faker/faker/v4 v4.4.1/go.mod h1:HRLrjis+tYsbFtIHufEPTAIzcZiRu0rS9EYl2Ccwme4= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= +github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/internal/jsonpointer/jsonpointer.go b/internal/jsonpointer/jsonpointer.go index f2ba8ec..049979e 100644 --- a/internal/jsonpointer/jsonpointer.go +++ b/internal/jsonpointer/jsonpointer.go @@ -48,6 +48,17 @@ func (p Pointer) AppendIndex(i int) Pointer { return pp } +func (p *Pointer) DecrementIndex() { + if len(*p) == 0 { + return + } + i, err := strconv.ParseInt((*p)[len(*p)-1], 10, 64) + if err != nil { + return + } + (*p)[len(*p)-1] = strconv.Itoa(int(i - 1)) +} + func (p Pointer) LessThan(alt Pointer) (b bool) { if p.HasSameAncestorsAs(alt) && (p[len(p)-1] == "-" || alt[len(alt)-1] == "-") { return p[len(p)-1] != "-" diff --git a/internal/require/require.go b/internal/require/require.go index fc410ca..0a54989 100644 --- a/internal/require/require.go +++ b/internal/require/require.go @@ -1,8 +1,12 @@ package require import ( + "bytes" + "fmt" "reflect" + "strings" "testing" + "text/tabwriter" ) type ErrorAssertionFunc func(t *testing.T, err error) @@ -24,6 +28,38 @@ func Error(t *testing.T, err error) { func Equal(t *testing.T, want, got any) { t.Helper() if !reflect.DeepEqual(want, got) { - t.Fatalf("unexpected value: want:\n%v, got:\n%v", want, got) + t.Fatalf("unexpected value: want:\n%v, got:\n%v\n%s", want, got, withTabwriter(fmt.Sprintf("%#v", want), fmt.Sprintf("%#v", got))) } } + +func EqualStringWithTabwriter(t *testing.T, want, got string) { + t.Helper() + if want == got { + return + } + + t.Fatal(withTabwriter(want, got)) +} + +func withTabwriter(want, got string) string { + buf := bytes.NewBufferString("\n") + w := tabwriter.NewWriter(buf, 0, 0, 3, ' ', 0) + fmt.Fprintln(w, "want:\tgot:\n=====\t====\n\t") + + wantLines := strings.Split(want, "\n") + gotLines := strings.Split(got, "\n") + + minLens := min(len(wantLines), len(gotLines)) + for i := 0; i < minLens; i++ { + fmt.Fprintf(w, "%s\t%s\n", wantLines[i], gotLines[i]) + } + for i := minLens; i < len(wantLines); i++ { + fmt.Fprintf(w, "%s\n", wantLines[i]) + } + for i := minLens; i < len(gotLines); i++ { + fmt.Fprintf(w, "\t%s\n", gotLines[i]) + } + w.Flush() + + return buf.String() +} diff --git a/testdata/advanced_mianxiang.txtar b/testdata/advanced_mianxiang.txtar new file mode 100644 index 0000000..23ab833 --- /dev/null +++ b/testdata/advanced_mianxiang.txtar @@ -0,0 +1,84 @@ +{ + "jsonInJSON": [ + "/array", + "/object" + ], + "patchLib": "mianxiang" +} +-- before.json -- +{ + "array": "[\"foo\",\"bar\"]", + "object": "{\"array_changed\":[\"foo\",\"bar\",\"baz\"],\"array_item_added\":[\"foo\",\"bar\",\"baz\"],\"array_item_removed\":[\"foo\",\"bar\",\"baz\"],\"array_removed\":[\"foo\",\"bar\",\"baz\"],\"array_unchanged\":[\"foo\",\"bar\",\"baz\"],\"number_changed\":10,\"number_removed\":10,\"number_unchanged\":10,\"object_changed\":{\"key\":\"value\"},\"object_removed\":{\"key\":\"value\"},\"object_unchanged\":{\"key\":\"value\"},\"string_changed\":\"bar\",\"string_removed\":\"removed\",\"string_unchanged\":\"foo\"}", + "key": "value" +} +-- after.json -- +{ + "array": "[\"new foo\",\"bar\",\"baz\"]", + "object": "{\"array_changed\":[\"foo2\",\"bar\",\"baz2\"],\"array_item_added\":[\"foo\",\"bar\",\"baz\",\"biz\"],\"array_item_removed\":[\"foo\",\"baz\"],\"array_new\":[\"foo\",\"bar\",\"baz\"],\"array_unchanged\":[\"foo\",\"bar\",\"baz\"],\"number_changed\":14,\"number_new\":14,\"number_unchanged\":10,\"object_changed\":{\"key\":\"new value\"},\"object_new\":{\"key\":\"value\"},\"object_unchanged\":{\"key\":\"value\"},\"string_changed\":\"bar changed\",\"string_new\":\"new\",\"string_unchanged\":\"foo\"}", + "key": "value" +} +-- diff.json -- + { +- "array": "[\"foo\",\"bar\"]", ++ "array": "[\"new foo\",\"bar\",\"baz\"]", + "key": "value", +- "object": "{\"array_changed\":[\"foo\",\"bar\",\"baz\"],\"array_item_added\":[\"foo\",\"bar\",\"baz\"],\"array_item_removed\":[\"foo\",\"bar\",\"baz\"],\"array_removed\":[\"foo\",\"bar\",\"baz\"],\"array_unchanged\":[\"foo\",\"bar\",\"baz\"],\"number_changed\":10,\"number_removed\":10,\"number_unchanged\":10,\"object_changed\":{\"key\":\"value\"},\"object_removed\":{\"key\":\"value\"},\"object_unchanged\":{\"key\":\"value\"},\"string_changed\":\"bar\",\"string_removed\":\"removed\",\"string_unchanged\":\"foo\"}" ++ "object": "{\"array_changed\":[\"foo2\",\"bar\",\"baz2\"],\"array_item_added\":[\"foo\",\"bar\",\"baz\",\"biz\"],\"array_item_removed\":[\"foo\",\"baz\"],\"array_new\":[\"foo\",\"bar\",\"baz\"],\"array_unchanged\":[\"foo\",\"bar\",\"baz\"],\"number_changed\":14,\"number_new\":14,\"number_unchanged\":10,\"object_changed\":{\"key\":\"new value\"},\"object_new\":{\"key\":\"value\"},\"object_unchanged\":{\"key\":\"value\"},\"string_changed\":\"bar changed\",\"string_new\":\"new\",\"string_unchanged\":\"foo\"}" + } +-- diff.tf -- + { + ~ array = jsonencode( + [ + ~ "foo" -> "new foo" + "bar" + + "baz" + ] + ) + ~ object = jsonencode( + { + array_changed = [ + ~ "foo" -> "foo2" + "bar" + ~ "baz" -> "baz2" + ] + array_item_added = [ + "foo" + "bar" + "baz" + + "biz" + ] + array_item_removed = [ + "foo" + - "bar" + "baz" + ] + + array_new = [ + + "foo" + + "bar" + + "baz" + ] + - array_removed = [ + - "foo" + - "bar" + - "baz" + ] + ~ number_changed = 10 -> 14 + + number_new = 14 + - number_removed = 10 + object_changed = { + ~ key = "value" -> "new value" + } + + object_new = { + + key = "value" + } + - object_removed = { + - key = "value" + } + ~ string_changed = "bar" -> "bar changed" + + string_new = "new" + - string_removed = "removed" + # (4 unchanged attribute hidden) + } + ) + # (1 unchanged attribute hidden) + } diff --git a/testdata/array.txtar b/testdata/array.txtar new file mode 100644 index 0000000..c4bf2f5 --- /dev/null +++ b/testdata/array.txtar @@ -0,0 +1,63 @@ +-- before.json -- +{ + "append": [1, 2, 3], + "insert": [1, 3], + "multi_change": [5, 6, 7], + "prepend": [1, 2, 3], + "remove": [1, 2, 3], + "update": [1, 2, 3] +} +-- after.json -- +{ + "append": [1, 2, 3, 4], + "insert": [1, 2, 3], + "multi_change": [5, 7, 8, 9, 10], + "prepend": [0, 1, 2, 3], + "remove": [1, 3], + "update": [1, 22, 3] +} +-- diff.json -- + { + "append": [ + 1, + 2, + 3, ++ 4 + ], + "insert": [ + 1, +- 3, ++ 2, ++ 3 + ], + "multi_change": [ + 5, +- 6, ++ 7, +- 7, ++ 8, ++ 9, ++ 10 + ], + "prepend": [ +- 1, ++ 0, +- 2, ++ 1, +- 3, ++ 2, ++ 3 + ], + "remove": [ + 1, +- 2, ++ 3, +- 3 + ], + "update": [ + 1, +- 2, ++ 22, + 3 + ] + } diff --git a/testdata/array_mianxiang.txtar b/testdata/array_mianxiang.txtar new file mode 100644 index 0000000..d26b335 --- /dev/null +++ b/testdata/array_mianxiang.txtar @@ -0,0 +1,60 @@ +{ + "patchLib": "mianxiang" +} +-- before.json -- +{ + "append": [1, 2, 3], + "insert": [1, 3], + "multi_change": [5, 6, 7], + "prepend": [1, 2, 3], + "remove": [1, 2, 3], + "update": [1, 2, 3] +} +-- after.json -- +{ + "append": [1, 2, 3, 4], + "insert": [1, 2, 3], + "multi_change": [5, 7, 8, 9, 10], + "prepend": [0, 1, 2, 3], + "remove": [1, 3], + "update": [1, 22, 3] +} +-- diff.json -- + { + "append": [ + 1, + 2, + 3, ++ 4 + ], + "insert": [ + 1, ++ 2, + 3 + ], + "multi_change": [ + 5, +- 6, + 7, ++ 8, ++ 9, ++ 10 + ], + "prepend": [ ++ 0, + 1, + 2, + 3 + ], + "remove": [ + 1, +- 2, + 3 + ], + "update": [ + 1, +- 2, ++ 22, + 3 + ] + } diff --git a/testdata/base_example_cameront.txtar.off b/testdata/base_example_cameront.txtar.off new file mode 100644 index 0000000..f28cda3 --- /dev/null +++ b/testdata/base_example_cameront.txtar.off @@ -0,0 +1,134 @@ +{ + "patchLib": "cameront" +} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- after.json -- +{ + "a": 1, + "a1": { + "1": [ + 1, + 2, + 3, + true + ] + }, + "g": 14, + "h": [ + 1, + 2, + 3 + ], + "b": { + "c": 3 + }, + "d": 4, + "f": [ + 5, + 7, + 8, + 9, + 10 + ], + "j": [ + true, + false + ] +} +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, + "f": [ + 5, +- 6, + 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + f = [ + 5 + - 6 + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/base_example_herkyl.txtar b/testdata/base_example_herkyl.txtar new file mode 100644 index 0000000..d554448 --- /dev/null +++ b/testdata/base_example_herkyl.txtar @@ -0,0 +1,156 @@ +{ + "patchLib": "herkyl" +} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- after.json -- +{ + "a": 1, + "a1": { + "1": [ + 1, + 2, + 3, + true + ] + }, + "g": 14, + "h": [ + 1, + 2, + 3 + ], + "b": { + "c": 3 + }, + "d": 4, + "f": [ + 5, + 7, + 8, + 9, + 10 + ], + "j": [ + true, + false + ] +} +-- diff.json -- +- { +- "a": 1, +- "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 + }, +- "d": 4, +- "e": null, +- "f": [ +- 5, +- 6, +- 7 + ], +- "j": { +- "a": 1 + } + } ++ { ++ "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, ++ "b": { ++ "c": 3 + }, ++ "d": 4, ++ "f": [ ++ 5, ++ 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + - a = 1 + - b = { + - b1 = "a" + - b2 = "b" + - c = 2 + } + - d = 4 + - e = null + - f = [ + - 5 + - 6 + - 7 + ] + - j = { + - a = 1 + } + } -> { + + a = 1 + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + + b = { + + c = 3 + } + + d = 4 + + f = [ + + 5 + + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + + j = [ + + true + + false + ] + } diff --git a/testdata/base_example_mattbaird.txtar b/testdata/base_example_mattbaird.txtar new file mode 100644 index 0000000..f6cfb17 --- /dev/null +++ b/testdata/base_example_mattbaird.txtar @@ -0,0 +1,134 @@ +{ + "patchLib": "mattbaird" +} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- after.json -- +{ + "a": 1, + "a1": { + "1": [ + 1, + 2, + 3, + true + ] + }, + "g": 14, + "h": [ + 1, + 2, + 3 + ], + "b": { + "c": 3 + }, + "d": 4, + "f": [ + 5, + 7, + 8, + 9, + 10 + ], + "j": [ + true, + false + ] +} +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, + "f": [ + 5, +- 6, + 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + f = [ + 5 + - 6 + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/base_example_mianxiang.txtar b/testdata/base_example_mianxiang.txtar new file mode 100644 index 0000000..0e4be3c --- /dev/null +++ b/testdata/base_example_mianxiang.txtar @@ -0,0 +1,134 @@ +{ + "patchLib": "mianxiang" +} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- after.json -- +{ + "a": 1, + "a1": { + "1": [ + 1, + 2, + 3, + true + ] + }, + "g": 14, + "h": [ + 1, + 2, + 3 + ], + "b": { + "c": 3 + }, + "d": 4, + "f": [ + 5, + 7, + 8, + 9, + 10 + ], + "j": [ + true, + false + ] +} +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, + "f": [ + 5, +- 6, + 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + f = [ + 5 + - 6 + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/base_example_snorwin.txtar.off b/testdata/base_example_snorwin.txtar.off new file mode 100644 index 0000000..42b0bbf --- /dev/null +++ b/testdata/base_example_snorwin.txtar.off @@ -0,0 +1,134 @@ +{ + "patchLib": "snorwin" +} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- after.json -- +{ + "a": 1, + "a1": { + "1": [ + 1, + 2, + 3, + true + ] + }, + "g": 14, + "h": [ + 1, + 2, + 3 + ], + "b": { + "c": 3 + }, + "d": 4, + "f": [ + 5, + 7, + 8, + 9, + 10 + ], + "j": [ + true, + false + ] +} +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, + "f": [ + 5, +- 6, + 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + f = [ + 5 + - 6 + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/base_example_victorlowther-paranoid.txtar b/testdata/base_example_victorlowther-paranoid.txtar new file mode 100644 index 0000000..39c58bf --- /dev/null +++ b/testdata/base_example_victorlowther-paranoid.txtar @@ -0,0 +1,141 @@ +{ + "patchLib": "victorlowther-paranoid" +} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- after.json -- +{ + "a": 1, + "a1": { + "1": [ + 1, + 2, + 3, + true + ] + }, + "g": 14, + "h": [ + 1, + 2, + 3 + ], + "b": { + "c": 3 + }, + "d": 4, + "f": [ + 5, + 7, + 8, + 9, + 10 + ], + "j": [ + true, + false + ] +} +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, +- "f": [ +- 5, +- 6, +- 7 + ], ++ "f": [ ++ 5, ++ 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + ~ f = [ + - 5 + - 6 + - 7 + ] -> [ + + 5 + + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/base_example_victorlowther.txtar b/testdata/base_example_victorlowther.txtar new file mode 100644 index 0000000..4530412 --- /dev/null +++ b/testdata/base_example_victorlowther.txtar @@ -0,0 +1,141 @@ +{ + "patchLib": "victorlowther" +} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- after.json -- +{ + "a": 1, + "a1": { + "1": [ + 1, + 2, + 3, + true + ] + }, + "g": 14, + "h": [ + 1, + 2, + 3 + ], + "b": { + "c": 3 + }, + "d": 4, + "f": [ + 5, + 7, + 8, + 9, + 10 + ], + "j": [ + true, + false + ] +} +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, +- "f": [ +- 5, +- 6, +- 7 + ], ++ "f": [ ++ 5, ++ 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + ~ f = [ + - 5 + - 6 + - 7 + ] -> [ + + 5 + + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/generated/advanced.txtar b/testdata/generated/advanced.txtar index da59cf2..fa7ed36 100644 --- a/testdata/generated/advanced.txtar +++ b/testdata/generated/advanced.txtar @@ -96,6 +96,7 @@ "path": "/-" } ] + -- jsonInJSON.1.json -- [ { @@ -180,3 +181,4 @@ "path": "/string_removed" } ] + diff --git a/testdata/generated/advanced_mianxiang.txtar b/testdata/generated/advanced_mianxiang.txtar new file mode 100644 index 0000000..dfe2c1d --- /dev/null +++ b/testdata/generated/advanced_mianxiang.txtar @@ -0,0 +1,80 @@ +{} +-- before.json -- +{ + "array": "[\"foo\",\"bar\"]", + "object": "{\"array_changed\":[\"foo\",\"bar\",\"baz\"],\"array_item_added\":[\"foo\",\"bar\",\"baz\"],\"array_item_removed\":[\"foo\",\"bar\",\"baz\"],\"array_removed\":[\"foo\",\"bar\",\"baz\"],\"array_unchanged\":[\"foo\",\"bar\",\"baz\"],\"number_changed\":10,\"number_removed\":10,\"number_unchanged\":10,\"object_changed\":{\"key\":\"value\"},\"object_removed\":{\"key\":\"value\"},\"object_unchanged\":{\"key\":\"value\"},\"string_changed\":\"bar\",\"string_removed\":\"removed\",\"string_unchanged\":\"foo\"}", + "key": "value" +} +-- patch.json -- +[{"op":"replace","path":"/array","value":"[\"new foo\",\"bar\",\"baz\"]"},{"op":"replace","path":"/object","value":"{\"array_changed\":[\"foo2\",\"bar\",\"baz2\"],\"array_item_added\":[\"foo\",\"bar\",\"baz\",\"biz\"],\"array_item_removed\":[\"foo\",\"baz\"],\"array_new\":[\"foo\",\"bar\",\"baz\"],\"array_unchanged\":[\"foo\",\"bar\",\"baz\"],\"number_changed\":14,\"number_new\":14,\"number_unchanged\":10,\"object_changed\":{\"key\":\"new value\"},\"object_new\":{\"key\":\"value\"},\"object_unchanged\":{\"key\":\"value\"},\"string_changed\":\"bar changed\",\"string_new\":\"new\",\"string_unchanged\":\"foo\"}"}] +-- diff.json -- + { +- "array": "[\"foo\",\"bar\"]", ++ "array": "[\"new foo\",\"bar\",\"baz\"]", + "key": "value", +- "object": "{\"array_changed\":[\"foo\",\"bar\",\"baz\"],\"array_item_added\":[\"foo\",\"bar\",\"baz\"],\"array_item_removed\":[\"foo\",\"bar\",\"baz\"],\"array_removed\":[\"foo\",\"bar\",\"baz\"],\"array_unchanged\":[\"foo\",\"bar\",\"baz\"],\"number_changed\":10,\"number_removed\":10,\"number_unchanged\":10,\"object_changed\":{\"key\":\"value\"},\"object_removed\":{\"key\":\"value\"},\"object_unchanged\":{\"key\":\"value\"},\"string_changed\":\"bar\",\"string_removed\":\"removed\",\"string_unchanged\":\"foo\"}" ++ "object": "{\"array_changed\":[\"foo2\",\"bar\",\"baz2\"],\"array_item_added\":[\"foo\",\"bar\",\"baz\",\"biz\"],\"array_item_removed\":[\"foo\",\"baz\"],\"array_new\":[\"foo\",\"bar\",\"baz\"],\"array_unchanged\":[\"foo\",\"bar\",\"baz\"],\"number_changed\":14,\"number_new\":14,\"number_unchanged\":10,\"object_changed\":{\"key\":\"new value\"},\"object_new\":{\"key\":\"value\"},\"object_unchanged\":{\"key\":\"value\"},\"string_changed\":\"bar changed\",\"string_new\":\"new\",\"string_unchanged\":\"foo\"}" + } +-- diff.tf -- + { + ~ array = jsonencode( + [ + ~ "foo" -> "new foo" + "bar" + + "baz" + ] + ) + ~ object = jsonencode( + { + array_changed = [ + ~ "foo" -> "foo2" + "bar" + ~ "baz" -> "baz2" + ] + array_item_added = [ + "foo" + "bar" + "baz" + + "biz" + ] + array_item_removed = [ + "foo" + - "bar" + "baz" + ] + + array_new = [ + + "foo" + + "bar" + + "baz" + ] + - array_removed = [ + - "foo" + - "bar" + - "baz" + ] + ~ number_changed = 10 -> 14 + + number_new = 14 + - number_removed = 10 + object_changed = { + ~ key = "value" -> "new value" + } + + object_new = { + + key = "value" + } + - object_removed = { + - key = "value" + } + ~ string_changed = "bar" -> "bar changed" + + string_new = "new" + - string_removed = "removed" + # (4 unchanged attribute hidden) + } + ) + # (1 unchanged attribute hidden) + } +-- jsonInJSON.0.json -- +[{"op":"replace","path":"/0","value":"new foo"},{"op":"add","path":"/2","value":"baz"}] + +-- jsonInJSON.1.json -- +[{"op":"remove","path":"/object_removed"},{"op":"remove","path":"/array_item_removed/1"},{"op":"remove","path":"/array_removed"},{"op":"replace","path":"/object_changed/key","value":"new value"},{"op":"replace","path":"/number_changed","value":14},{"op":"remove","path":"/number_removed"},{"op":"replace","path":"/string_changed","value":"bar changed"},{"op":"remove","path":"/string_removed"},{"op":"replace","path":"/array_changed/0","value":"foo2"},{"op":"replace","path":"/array_changed/2","value":"baz2"},{"op":"add","path":"/array_item_added/3","value":"biz"},{"op":"add","path":"/number_new","value":14},{"op":"add","path":"/string_new","value":"new"},{"op":"add","path":"/object_new","value":{"key":"value"}},{"op":"add","path":"/array_new","value":["foo","bar","baz"]}] + diff --git a/testdata/generated/array.txtar b/testdata/generated/array.txtar new file mode 100644 index 0000000..b801b63 --- /dev/null +++ b/testdata/generated/array.txtar @@ -0,0 +1,127 @@ +{} +-- before.json -- +{ + "append": [1, 2, 3], + "insert": [1, 3], + "multi_change": [5, 6, 7], + "prepend": [1, 2, 3], + "remove": [1, 2, 3], + "update": [1, 2, 3] +} +-- patch.json -- +[ + { + "value": 4, + "op": "add", + "path": "/append/-" + }, + { + "value": 2, + "op": "replace", + "path": "/insert/1" + }, + { + "value": 3, + "op": "add", + "path": "/insert/-" + }, + { + "value": 7, + "op": "replace", + "path": "/multi_change/1" + }, + { + "value": 8, + "op": "replace", + "path": "/multi_change/2" + }, + { + "value": 9, + "op": "add", + "path": "/multi_change/-" + }, + { + "value": 10, + "op": "add", + "path": "/multi_change/-" + }, + { + "value": 0, + "op": "replace", + "path": "/prepend/0" + }, + { + "value": 1, + "op": "replace", + "path": "/prepend/1" + }, + { + "value": 2, + "op": "replace", + "path": "/prepend/2" + }, + { + "value": 3, + "op": "add", + "path": "/prepend/-" + }, + { + "op": "remove", + "path": "/remove/2" + }, + { + "value": 3, + "op": "replace", + "path": "/remove/1" + }, + { + "value": 22, + "op": "replace", + "path": "/update/1" + } +] +-- diff.json -- + { + "append": [ + 1, + 2, + 3, ++ 4 + ], + "insert": [ + 1, +- 3, ++ 2, ++ 3 + ], + "multi_change": [ + 5, +- 6, ++ 7, +- 7, ++ 8, ++ 9, ++ 10 + ], + "prepend": [ +- 1, ++ 0, +- 2, ++ 1, +- 3, ++ 2, ++ 3 + ], + "remove": [ + 1, +- 2, ++ 3, +- 3 + ], + "update": [ + 1, +- 2, ++ 22, + 3 + ] + } diff --git a/testdata/generated/array_mianxiang.txtar b/testdata/generated/array_mianxiang.txtar new file mode 100644 index 0000000..5b24a2c --- /dev/null +++ b/testdata/generated/array_mianxiang.txtar @@ -0,0 +1,51 @@ +{} +-- before.json -- +{ + "append": [1, 2, 3], + "insert": [1, 3], + "multi_change": [5, 6, 7], + "prepend": [1, 2, 3], + "remove": [1, 2, 3], + "update": [1, 2, 3] +} +-- patch.json -- +[{"op":"remove","path":"/multi_change/1"},{"op":"add","path":"/multi_change/2","value":8},{"op":"add","path":"/multi_change/3","value":9},{"op":"add","path":"/multi_change/4","value":10},{"op":"add","path":"/prepend/0","value":0},{"op":"remove","path":"/remove/1"},{"op":"replace","path":"/update/1","value":22},{"op":"add","path":"/append/3","value":4},{"op":"add","path":"/insert/1","value":2}] +-- diff.json -- + { + "append": [ + 1, + 2, + 3, ++ 4 + ], + "insert": [ + 1, ++ 2, + 3 + ], + "multi_change": [ + 5, +- 6, + 7, ++ 8, ++ 9, ++ 10 + ], + "prepend": [ ++ 0, + 1, + 2, + 3 + ], + "remove": [ + 1, +- 2, + 3 + ], + "update": [ + 1, +- 2, ++ 22, + 3 + ] + } diff --git a/testdata/generated/base_example_herkyl.txtar b/testdata/generated/base_example_herkyl.txtar new file mode 100644 index 0000000..9007662 --- /dev/null +++ b/testdata/generated/base_example_herkyl.txtar @@ -0,0 +1,160 @@ +{} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- patch.json -- +[ + { + "op": "replace", + "path": "", + "value": { + "a": 1, + "a1": { + "1": [ + 1, + 2, + 3, + true + ] + }, + "b": { + "c": 3 + }, + "d": 4, + "f": [ + 5, + 7, + 8, + 9, + 10 + ], + "g": 14, + "h": [ + 1, + 2, + 3 + ], + "j": [ + true, + false + ] + } + } +] +-- diff.json -- +- { +- "a": 1, +- "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 + }, +- "d": 4, +- "e": null, +- "f": [ +- 5, +- 6, +- 7 + ], +- "j": { +- "a": 1 + } + } ++ { ++ "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, ++ "b": { ++ "c": 3 + }, ++ "d": 4, ++ "f": [ ++ 5, ++ 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + - a = 1 + - b = { + - b1 = "a" + - b2 = "b" + - c = 2 + } + - d = 4 + - e = null + - f = [ + - 5 + - 6 + - 7 + ] + - j = { + - a = 1 + } + } -> { + + a = 1 + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + + b = { + + c = 3 + } + + d = 4 + + f = [ + + 5 + + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + + j = [ + + true + + false + ] + } diff --git a/testdata/generated/base_example_mattbaird.txtar b/testdata/generated/base_example_mattbaird.txtar new file mode 100644 index 0000000..ad18e01 --- /dev/null +++ b/testdata/generated/base_example_mattbaird.txtar @@ -0,0 +1,172 @@ +{} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- patch.json -- +[ + { + "op": "remove", + "path": "/f/1" + }, + { + "op": "add", + "path": "/f/2", + "value": 8 + }, + { + "op": "add", + "path": "/f/3", + "value": 9 + }, + { + "op": "add", + "path": "/f/4", + "value": 10 + }, + { + "op": "replace", + "path": "/j", + "value": [ + true, + false + ] + }, + { + "op": "add", + "path": "/a1", + "value": { + "1": [ + 1, + 2, + 3, + true + ] + } + }, + { + "op": "add", + "path": "/g", + "value": 14 + }, + { + "op": "add", + "path": "/h", + "value": [ + 1, + 2, + 3 + ] + }, + { + "op": "replace", + "path": "/b/c", + "value": 3 + }, + { + "op": "remove", + "path": "/b/b1" + }, + { + "op": "remove", + "path": "/b/b2" + }, + { + "op": "remove", + "path": "/e" + } +] +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, + "f": [ + 5, +- 6, + 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + f = [ + 5 + - 6 + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/generated/base_example_mianxiang.txtar b/testdata/generated/base_example_mianxiang.txtar new file mode 100644 index 0000000..c5b535f --- /dev/null +++ b/testdata/generated/base_example_mianxiang.txtar @@ -0,0 +1,101 @@ +{} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- patch.json -- +[{"op":"remove","path":"/e"},{"op":"remove","path":"/f/1"},{"op":"add","path":"/f/2","value":8},{"op":"add","path":"/f/3","value":9},{"op":"add","path":"/f/4","value":10},{"op":"replace","path":"/j","value":[true,false]},{"op":"remove","path":"/b/b2"},{"op":"replace","path":"/b/c","value":3},{"op":"remove","path":"/b/b1"},{"op":"add","path":"/a1","value":{"1":[1,2,3,true]}},{"op":"add","path":"/g","value":14},{"op":"add","path":"/h","value":[1,2,3]}] +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, + "f": [ + 5, +- 6, + 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + f = [ + 5 + - 6 + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/generated/base_example_victorlowther-paranoid.txtar b/testdata/generated/base_example_victorlowther-paranoid.txtar new file mode 100644 index 0000000..bb6254f --- /dev/null +++ b/testdata/generated/base_example_victorlowther-paranoid.txtar @@ -0,0 +1,225 @@ +{} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- patch.json -- +[ + { + "op": "test", + "path": "/b/c", + "from": "", + "value": 2 + }, + { + "op": "replace", + "path": "/b/c", + "from": "", + "value": 3 + }, + { + "op": "test", + "path": "/b/b1", + "from": "", + "value": "a" + }, + { + "op": "remove", + "path": "/b/b1", + "from": "", + "value": null + }, + { + "op": "test", + "path": "/b/b2", + "from": "", + "value": "b" + }, + { + "op": "remove", + "path": "/b/b2", + "from": "", + "value": null + }, + { + "op": "test", + "path": "/e", + "from": "", + "value": null + }, + { + "op": "remove", + "path": "/e", + "from": "", + "value": null + }, + { + "op": "test", + "path": "/f", + "from": "", + "value": [ + 5, + 6, + 7 + ] + }, + { + "op": "replace", + "path": "/f", + "from": "", + "value": [ + 5, + 7, + 8, + 9, + 10 + ] + }, + { + "op": "test", + "path": "/j", + "from": "", + "value": { + "a": 1 + } + }, + { + "op": "replace", + "path": "/j", + "from": "", + "value": [ + true, + false + ] + }, + { + "op": "add", + "path": "/h", + "from": "", + "value": [ + 1, + 2, + 3 + ] + }, + { + "op": "add", + "path": "/a1", + "from": "", + "value": { + "1": [ + 1, + 2, + 3, + true + ] + } + }, + { + "op": "add", + "path": "/g", + "from": "", + "value": 14 + } +] +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, +- "f": [ +- 5, +- 6, +- 7 + ], ++ "f": [ ++ 5, ++ 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + ~ f = [ + - 5 + - 6 + - 7 + ] -> [ + + 5 + + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/generated/base_example_victorlowther.txtar b/testdata/generated/base_example_victorlowther.txtar new file mode 100644 index 0000000..34982a4 --- /dev/null +++ b/testdata/generated/base_example_victorlowther.txtar @@ -0,0 +1,183 @@ +{} +-- before.json -- +{ + "a": 1, + "b": { + "c": 2, + "b1": "a", + "b2": "b" + }, + "d": 4, + "e": null, + "f": [ + 5, + 6, + 7 + ], + "j": { + "a": 1 + } +} +-- patch.json -- +[ + { + "op": "remove", + "path": "/e", + "from": "", + "value": null + }, + { + "op": "replace", + "path": "/f", + "from": "", + "value": [ + 5, + 7, + 8, + 9, + 10 + ] + }, + { + "op": "replace", + "path": "/j", + "from": "", + "value": [ + true, + false + ] + }, + { + "op": "replace", + "path": "/b/c", + "from": "", + "value": 3 + }, + { + "op": "remove", + "path": "/b/b1", + "from": "", + "value": null + }, + { + "op": "remove", + "path": "/b/b2", + "from": "", + "value": null + }, + { + "op": "add", + "path": "/g", + "from": "", + "value": 14 + }, + { + "op": "add", + "path": "/h", + "from": "", + "value": [ + 1, + 2, + 3 + ] + }, + { + "op": "add", + "path": "/a1", + "from": "", + "value": { + "1": [ + 1, + 2, + 3, + true + ] + } + } +] +-- diff.json -- + { + "a": 1, ++ "a1": { ++ "1": [ ++ 1, ++ 2, ++ 3, ++ true + ] + }, + "b": { +- "b1": "a", +- "b2": "b", +- "c": 2 ++ "c": 3 + }, + "d": 4, +- "e": null, +- "f": [ +- 5, +- 6, +- 7 + ], ++ "f": [ ++ 5, ++ 7, ++ 8, ++ 9, ++ 10 + ], ++ "g": 14, ++ "h": [ ++ 1, ++ 2, ++ 3 + ], +- "j": { +- "a": 1 + } ++ "j": [ ++ true, ++ false + ] + } +-- diff.tf -- + { + + a1 = { + + 1 = [ + + 1 + + 2 + + 3 + + true + ] + } + b = { + - b1 = "a" + - b2 = "b" + ~ c = 2 -> 3 + } + - e = null + ~ f = [ + - 5 + - 6 + - 7 + ] -> [ + + 5 + + 7 + + 8 + + 9 + + 10 + ] + + g = 14 + + h = [ + + 1 + + 2 + + 3 + ] + ~ j = { + - a = 1 + } -> [ + + true + + false + ] + # (2 unchanged attribute hidden) + } diff --git a/testdata/generated/null_2_string.txtar b/testdata/generated/null_2_string.txtar index d416c31..2913647 100644 --- a/testdata/generated/null_2_string.txtar +++ b/testdata/generated/null_2_string.txtar @@ -12,4 +12,4 @@ null -- diff.json -- + "foobar" -- diff.tf -- -"foobar" + "foobar" diff --git a/testdata/generated/string_2_null.txtar b/testdata/generated/string_2_null.txtar index 61b7d07..f8982b8 100644 --- a/testdata/generated/string_2_null.txtar +++ b/testdata/generated/string_2_null.txtar @@ -12,4 +12,4 @@ -- diff.json -- - "foobar" -- diff.tf -- -"foobar" + "foobar" diff --git a/testdata/generated/string_2_string.txtar b/testdata/generated/string_2_string.txtar index 0e98cb4..37786c5 100644 --- a/testdata/generated/string_2_string.txtar +++ b/testdata/generated/string_2_string.txtar @@ -13,4 +13,4 @@ - "" + "" -- diff.tf -- -"" -> "" + "" -> "" diff --git a/testdata/null_2_string.txtar b/testdata/null_2_string.txtar index 06344b1..7dae19a 100644 --- a/testdata/null_2_string.txtar +++ b/testdata/null_2_string.txtar @@ -5,4 +5,4 @@ null -- diff.json -- + "foobar" -- diff.tf -- -"foobar" + "foobar" diff --git a/testdata/string_2_null.txtar b/testdata/string_2_null.txtar index 4320b35..2fdf807 100644 --- a/testdata/string_2_null.txtar +++ b/testdata/string_2_null.txtar @@ -5,4 +5,4 @@ null -- diff.json -- - "foobar" -- diff.tf -- -"foobar" + "foobar" diff --git a/testdata/string_2_string.txtar b/testdata/string_2_string.txtar index 8dba40a..6b86b06 100644 --- a/testdata/string_2_string.txtar +++ b/testdata/string_2_string.txtar @@ -6,4 +6,4 @@ - "" + "" -- diff.tf -- -"" -> "" + "" -> ""