diff --git a/cuetools/Earthfile b/cuetools/Earthfile deleted file mode 100644 index e3f3bdc..0000000 --- a/cuetools/Earthfile +++ /dev/null @@ -1,35 +0,0 @@ -VERSION 0.8 - -deps: - FROM golang:1.23.0-alpine3.19 - - WORKDIR /work - - RUN mkdir -p /go/cache && mkdir -p /go/modcache - ENV GOCACHE=/go/cache - ENV GOMODCACHE=/go/modcache - CACHE --persist --sharing shared /go - - COPY go.mod go.sum . - RUN go mod download - -src: - FROM +deps - - CACHE --persist --sharing shared /go - - COPY --dir pkg . - RUN go generate ./... - - SAVE ARTIFACT . src - -check: - FROM +src - - RUN gofmt -l . | grep . && exit 1 || exit 0 - RUN go vet ./... - -test: - FROM +src - - RUN go test ./... \ No newline at end of file diff --git a/cuetools/README.md b/cuetools/README.md deleted file mode 100644 index f756ece..0000000 --- a/cuetools/README.md +++ /dev/null @@ -1,123 +0,0 @@ -# Cue Tools - -The `cuetools` package provides common utilities for interacting with the [CUE language](https://cuelang.org/). -The functions contained within this package are used across multiple packages within Catalyst Forge. -However, all functions are self-contained, and they may prove vaulable even outside the context of Catalyst Forge. - -## Loading and Validation - -The contents of a CUE file can be loaded with: - -```go -package pkg - -import ( - "fmt" - "log" - "os" - - "cuelang.org/go/cue/cuecontext" - cuetools "github.com/input-output-hk/catalyst-forge/cuetools/pkg" -) - -func main() { - b, err := os.ReadFile("file.cue") - if err != nil { - log.Fatal(err) - } - - ctx := cuecontext.New() - v, err := cuetools.Compile(ctx, b) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("Value: %v\n", v) -} -``` - -The `Compile` function will only return an error when a syntax error is present in the given file. -It does not validate whether the file is logically valid (i.e., non-concrete values are acceptable). -To further validate the file: - -```go -func main() { - // ... - err = cuetools.Validate(v, cue.Concrete(true)) - if err != nil { - log.Fatal(err) - } -} -``` - -By default, the validation method provided by the CUE API will ellide error messages when more than one error exists. -The `Validate` method handles this by building a proper error string that includes all errors encountered while validating. -Each error is placed on a new line in order to improve readability. - -## Mutating Values - -By default, CUE is immutable and it's not possible to arbitrarily delete and/or replace fields within a CUE value. -This constraint exists at the language level and cannot be easily broken via the Go API. -While respecting language boundaries is often the best solution, in some cases it may be overwhelmingly apparent that a field needs -to be mutated and that it can be done safely. -For those cases, this package provides functions for both deleting and replacing arbitrary fields. - -To delete a field: - -```go -package main - -import ( - "fmt" - "log" - - "cuelang.org/go/cue/cuecontext" - cuetools "github.com/input-output-hk/catalyst-forge/cuetools/pkg" -) - -func main() { - ctx := cuecontext.New() - v := ctx.CompileString(`{a: 1, b: 2}`) - v, err := cuetools.Delete(ctx, v, "a") - if err != nil { - log.Fatalf("failed to delete field: %v", err) - } - - fmt.Println(v) // { b: 2 } -} -``` - -To replace a field with a new value: - -```go -func main() { - // ... - v = ctx.CompileString(`{a: 1, b: 2}`) - v, err := cuetools.Replace(ctx, v, "a", ctx.CompileString("3")) - if err != nil { - log.Fatalf("failed to delete field: %v", err) - } - - fmt.Println(v) // { a: 3, b: 2} -} -``` - -The `path` argument for both functions can be nested: - -``` -a.b.c -``` - -And can also index into lists: - -``` -a.b[0].c -``` - -## Testing - -Tests can be run with: - -``` -go test ./... -``` \ No newline at end of file diff --git a/cuetools/go.mod b/cuetools/go.mod deleted file mode 100644 index 7b25a38..0000000 --- a/cuetools/go.mod +++ /dev/null @@ -1,14 +0,0 @@ -module github.com/input-output-hk/catalyst-forge/cuetools - -go 1.22.3 - -require cuelang.org/go v0.10.0 - -require ( - github.com/cockroachdb/apd/v3 v3.2.1 // indirect - github.com/google/uuid v1.6.0 // indirect - golang.org/x/mod v0.20.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/text v0.17.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/cuetools/go.sum b/cuetools/go.sum deleted file mode 100644 index 1faf651..0000000 --- a/cuetools/go.sum +++ /dev/null @@ -1,49 +0,0 @@ -cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79 h1:EceZITBGET3qHneD5xowSTY/YHbNybvMWGh62K2fG/M= -cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg= -cuelang.org/go v0.10.0 h1:Y1Pu4wwga5HkXfLFK1sWAYaSWIBdcsr5Cb5AWj2pOuE= -cuelang.org/go v0.10.0/go.mod h1:HzlaqqqInHNiqE6slTP6+UtxT9hN6DAzgJgdbNxXvX8= -github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= -github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= -github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY= -github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 h1:sadMIsgmHpEOGbUs6VtHBXRR1OHevnj7hLx9ZcdNGW4= -github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= -github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 h1:igWZJluD8KtEtAgRyF4x6lqcxDry1ULztksMJh2mnQE= -github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21/go.mod h1:RMRJLmBOqWacUkmJHRMiPKh1S1m3PA7Zh4W80/kWPpg= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -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/cuetools/pkg/load.go b/cuetools/pkg/load.go deleted file mode 100644 index d607abe..0000000 --- a/cuetools/pkg/load.go +++ /dev/null @@ -1,40 +0,0 @@ -package pkg - -import ( - "fmt" - - "cuelang.org/go/cue" - "cuelang.org/go/cue/errors" -) - -// Compile compiles the given CUE contents and returns the resulting value. -// If the contents are invalid, an error is returned. -func Compile(ctx *cue.Context, contents []byte) (cue.Value, error) { - v := ctx.CompileBytes(contents) - if v.Err() != nil { - return cue.Value{}, v.Err() - } - - return v, nil -} - -// Validate validates the given CUE value. If the value is invalid, an error -// is returned. -func Validate(c cue.Value, opts ...cue.Option) error { - if err := c.Validate(opts...); err != nil { - var errStr string - errs := errors.Errors(err) - - if len(errs) == 1 { - errStr = errs[0].Error() - } else { - errStr = "\n" - for _, e := range errs { - errStr += e.Error() + "\n" - } - } - return fmt.Errorf("failed to validate: %s", errStr) - } - - return nil -} diff --git a/cuetools/pkg/load_test.go b/cuetools/pkg/load_test.go deleted file mode 100644 index 71031ee..0000000 --- a/cuetools/pkg/load_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package pkg - -import ( - "fmt" - "testing" - - "cuelang.org/go/cue" - "cuelang.org/go/cue/cuecontext" -) - -func TestCompile(t *testing.T) { - ctx := cuecontext.New() - tests := []struct { - name string - contents string - expectedVal cue.Value - expectedErr string - }{ - { - name: "valid contents", - contents: "{}", - expectedVal: ctx.CompileString("{}"), - expectedErr: "", - }, - { - name: "invalid contents", - contents: "{a: b}", - expectedVal: cue.Value{}, - expectedErr: "a: reference \"b\" not found", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - v, err := Compile(ctx, []byte(tt.contents)) - if err != nil && err.Error() != tt.expectedErr { - t.Errorf("unexpected error: %v", err) - return - } else if err == nil && tt.expectedErr != "" { - t.Errorf("expected error %q but got nil", tt.expectedErr) - return - } else if err != nil && err.Error() == tt.expectedErr { - return - } - - if !v.Equals(tt.expectedVal) { - t.Errorf("expected value %v, got %v", tt.expectedVal, v) - } - }) - } -} - -func TestValidate(t *testing.T) { - ctx := cuecontext.New() - tests := []struct { - name string - v cue.Value - expectedErr string - }{ - { - name: "valid value", - v: ctx.CompileString("{}"), - expectedErr: "", - }, - { - name: "invalid value", - v: ctx.CompileString("{a: 1}").FillPath(cue.ParsePath("a"), fmt.Errorf("invalid value")), - expectedErr: "failed to validate: invalid value", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := Validate(tt.v) - if err != nil && err.Error() != tt.expectedErr { - t.Errorf("unexpected error: %v", err) - } else if err == nil && tt.expectedErr != "" { - t.Errorf("expected error %q but got nil", tt.expectedErr) - } - }) - } -} diff --git a/cuetools/pkg/mutate.go b/cuetools/pkg/mutate.go deleted file mode 100644 index 753d81f..0000000 --- a/cuetools/pkg/mutate.go +++ /dev/null @@ -1,143 +0,0 @@ -package pkg - -import ( - "fmt" - - "cuelang.org/go/cue" -) - -// delete deletes the field at the given path from the given value. -// The path must point to either a struct field or a list index. -func Delete(ctx *cue.Context, v cue.Value, path string) (cue.Value, error) { - // final holds the final value after the delete operation - var final cue.Value - - refPath := cue.ParsePath(path) - refSels := refPath.Selectors() - - // Make sure the target path exists - if !v.LookupPath(refPath).Exists() { - return v, fmt.Errorf("path %q does not exist", path) - } - - // Isolate the last selector in the target path, which is the field to delete - deletedSel, parentSels := refSels[len(refSels)-1], refSels[:len(refSels)-1] - parentPath := cue.MakePath(parentSels...) // Path to the parent of the field to delete - - var err error - final, err = deleteFrom(ctx, v.LookupPath(parentPath), deletedSel) - if err != nil { - return v, fmt.Errorf("failed to delete field: %v", err) - } - - // Replace the parent struct in the given value with the new struct that has the target field removed - final, err = Replace(ctx, v, parentPath.String(), final) - if err != nil { - return v, fmt.Errorf("failed to rebuild struct: %v", err) - } - - return final, nil -} - -// replace replaces the value at the given path with the given value. -// The path must point to either a struct field or a list index. -func Replace(ctx *cue.Context, v cue.Value, path string, replace cue.Value) (cue.Value, error) { - cpath := cue.ParsePath(path) - if !v.LookupPath(cpath).Exists() { - return v, fmt.Errorf("path %q does not exist", path) - } - - final := replace - sels := cpath.Selectors() - for len(sels) > 0 { - var lastSel cue.Selector - curIndex := len(sels) - 1 - lastSel, sels = sels[curIndex], sels[:curIndex] - - switch lastSel.Type() { - case cue.IndexLabel: - new := ctx.CompileString("[...]") - curList, err := v.LookupPath(cue.MakePath(sels...)).List() - if err != nil { - return cue.Value{}, fmt.Errorf("expected list at path %s: %v", path, err) - } - - for i := 0; curList.Next(); i++ { - var val cue.Value - if curList.Selector() == lastSel { - val = final - } else { - val = curList.Value() - } - - new = new.FillPath(cue.MakePath(cue.Index(i)), val) - } - - final = new - case cue.StringLabel: - new := ctx.CompileString("{}") - curFields, err := v.LookupPath(cue.MakePath(sels...)).Fields() - if err != nil { - return cue.Value{}, fmt.Errorf("expected struct at path %s: %v", path, err) - } - - for curFields.Next() { - fieldPath := cue.MakePath(curFields.Selector()) - if curFields.Selector() == lastSel { - new = new.FillPath(fieldPath, final) - } else { - new = new.FillPath(fieldPath, curFields.Value()) - } - } - - final = new - default: - return cue.Value{}, fmt.Errorf("unknown selector type %s", lastSel.Type()) - } - } - - return final, nil -} - -// deleteFrom deletes the field at the given selector from the given value. -// The value must be a struct or a list. -func deleteFrom(ctx *cue.Context, v cue.Value, targetSel cue.Selector) (cue.Value, error) { - switch targetSel.Type() { - case cue.IndexLabel: - new := ctx.CompileString("[...]") - list, err := v.List() - if err != nil { - return v, fmt.Errorf("expected list: %v", err) - } - - var i int - for list.Next() { - if list.Selector() == targetSel { - continue - } - - new = new.FillPath(cue.MakePath(cue.Index(i)), list.Value()) - i++ - } - - return new, nil - case cue.StringLabel: - new := ctx.CompileString("{}") - fields, err := v.Fields() - if err != nil { - return v, fmt.Errorf("expected struct: %v", err) - } - - for fields.Next() { - if fields.Selector() == targetSel { - continue - } - - new = new.FillPath(cue.MakePath(fields.Selector()), fields.Value()) - } - - return new, nil - default: - return v, fmt.Errorf("unsupported selector type %s", targetSel.Type().String()) - } -} diff --git a/cuetools/pkg/mutate_test.go b/cuetools/pkg/mutate_test.go deleted file mode 100644 index fb56e17..0000000 --- a/cuetools/pkg/mutate_test.go +++ /dev/null @@ -1,283 +0,0 @@ -package pkg - -import ( - "fmt" - "testing" - - "cuelang.org/go/cue/cuecontext" - "cuelang.org/go/cue/format" -) - -func TestDelete(t *testing.T) { - tests := []struct { - name string - value string - path string - expected string - expectErr bool - }{ - { - name: "delete field", - value: ` -{ - a: 1 - b: 2 -}`, - path: "a", - expected: ` -{ - b: 2 -}`, - }, - { - name: "delete nested field", - value: ` -{ - a: { - b: 1 - c: 2 - } -}`, - path: "a.b", - expected: ` -{ - a: { - c: 2 - } -}`, - expectErr: false, - }, - { - name: "delete list element", - value: ` -{ - a: [1, 2, 3] -}`, - path: "a[1]", - expected: ` -{ - a: [1, ...] & [_, 3, ...] & { - [...] - } -}`, - expectErr: false, - }, - { - name: "delete nested list element", - value: ` -{ - a: [ - { - b: [1, 2, 3] - } - ] -}`, - path: "a[0].b[1]", - expected: ` -{ - a: [{ - b: [1, ...] & [_, 3, ...] & { - [...] - } - }, ...] & { - [...] - } -}`, - expectErr: false, - }, - { - name: "delete non-existent field", - value: ` -{ - a: 1 -}`, - path: "b", - expected: "", - expectErr: true, - }, - { - name: "delete non-existent index", - value: ` -{ - a: [1, 2, 3] -}`, - path: "a[3]", - expected: "", - expectErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctx := cuecontext.New() - v, err := Compile(ctx, []byte(tt.value)) - if err != nil { - t.Fatalf("failed to compile value: %v", err) - } - - final, err := Delete(ctx, v, tt.path) - if err != nil { - if !tt.expectErr { - t.Fatalf("unexpected error: %v", err) - } - return - } else if tt.expectErr { - t.Fatalf("expected error, got none") - } - - src, err := format.Node(final.Syntax()) - if err != nil { - t.Fatalf("failed to format node: %v", err) - } - - if fmt.Sprintf("\n%s", src) != tt.expected { - t.Errorf("expected %q, got %q", tt.expected, string(src)) - } - }) - } -} - -func TestReplace(t *testing.T) { - tests := []struct { - name string - value string - path string - replace string - expected string - expectErr bool - }{ - { - name: "replace field", - value: ` -{ - a: 1 - b: 2 -}`, - path: "a", - replace: "3", - expected: ` -{ - a: 3 - b: 2 -}`, - expectErr: false, - }, - { - name: "replace nested field", - value: ` -{ - a: { - b: 1 - c: 2 - } -}`, - path: "a.b", - replace: "3", - expected: ` -{ - a: { - b: 3 - c: 2 - } -}`, - expectErr: false, - }, - { - name: "replace list element", - value: ` -{ - a: [1, 2, 3] -}`, - path: "a[1]", - replace: "4", - expected: ` -{ - a: [1, ...] & [_, 4, ...] & [_, _, 3, ...] & { - [...] - } -}`, - expectErr: false, - }, - { - name: "replace nested list element", - value: ` -{ - a: [ - { - b: [1, 2, 3] - } - ] -}`, - path: "a[0].b[1]", - replace: "4", - expected: ` -{ - a: [{ - b: [1, ...] & [_, 4, ...] & [_, _, 3, ...] & { - [...] - } - }, ...] & { - [...] - } -}`, - expectErr: false, - }, - { - name: "replace non-existent field", - value: ` -{ - a: 1 -}`, - path: "b", - replace: "2", - expected: "", - expectErr: true, - }, - { - name: "replace non-existent index", - value: ` -{ - a: [1, 2, 3] -}`, - path: "a[3]", - replace: "4", - expected: "", - expectErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctx := cuecontext.New() - v, err := Compile(ctx, []byte(tt.value)) - if err != nil { - t.Fatalf("failed to compile value: %v", err) - } - - replace, err := Compile(ctx, []byte(tt.replace)) - if err != nil { - t.Fatalf("failed to compile replace value: %v", err) - } - - final, err := Replace(ctx, v, tt.path, replace) - - if err != nil { - if !tt.expectErr { - t.Fatalf("unexpected error: %v", err) - } - return - } else if tt.expectErr { - t.Fatalf("expected error, got none") - } - - src, err := format.Node(final.Syntax()) - if err != nil { - t.Fatalf("failed to format node: %v", err) - } - - if fmt.Sprintf("\n%s", src) != tt.expected { - t.Errorf("expected %q, got %q", tt.expected, string(src)) - } - }) - } -}