diff --git a/.golangci.yaml b/.golangci.yaml index a3e942d..e884f8d 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -12,6 +12,8 @@ linters: - testpackage - varnamelen - wsl + - wsl_v5 + - noinlineerr settings: dupword: ignore: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b17bef..17d98dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,30 @@ All notable changes to this project will be documented in this file. It uses the [Semantic Versioning]: https://semver.org/spec/v2.0.0.html "Semantic Versioning 2.0.0" -## [v0.9.0] — Unreleased +## [v0.10.0] — Unreleased + +### ⚡ Improvements + +* Added text and binary encoding interface methods to `Path`: + * [encoding.TextMarshaler] + * [encoding.TextUnmarshaler] + * [encoding.BinaryMarshaler] + * [encoding.BinaryUnmarshaler] + Thanks to @rkosegi for the suggestion ([#20]) + +### 📔 Notes + +* Upgraded to `golangci-lint` v2.2.2. + + [v0.10.0]: https://github.com/theory/jsonpath/compare/v0.9.0...v0.10.0 + [encoding.TextMarshaler]: https://pkg.go.dev/encoding#TextMarshaler + [encoding.TextUnmarshaler]: https://pkg.go.dev/encoding#TextUnmarshaler + [encoding.BinaryMarshaler]: https://pkg.go.dev/encoding#BinaryMarshaler + [encoding.BinaryUnmarshaler]: https://pkg.go.dev/encoding#BinaryUnmarshaler + [#20]: https://github.com/theory/jsonpath/issues/20 + "theory/jsonpath#20: Implement encoding.TextUnmarshaler in Path" + +## [v0.9.0] — 2025-05-05 ### ⚡ Improvements diff --git a/Makefile b/Makefile index 218ba4a..5ceb49d 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ brew-lint-depends: .PHONY: debian-lint-depends # Install linting tools on Debian debian-lint-depends: - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b /usr/bin v2.1.5 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b /usr/bin v2.2.2 .PHONY: install-generators # Install Go code generators install-generators: diff --git a/path.go b/path.go index 81d6b6c..a13b4c8 100644 --- a/path.go +++ b/path.go @@ -37,6 +37,35 @@ func MustParse(path string) *Path { return NewParser().MustParse(path) } +// MarshalText encodes p into UTF-8-encoded text and returns the result. +// Implements [encoding.TextMarshaler]. +func (p *Path) MarshalText() ([]byte, error) { + return []byte(p.q.String()), nil +} + +// UnmarshalText decodes UTF-8-encoded text into p. Implements +// [encoding.TextUnmarshaler]. +func (p *Path) UnmarshalText(data []byte) error { + parsed, err := NewParser().Parse(string(data)) + if err != nil { + return err + } + p.q = parsed.q + return nil +} + +// MarshalBinary encodes p into UTF-8-encoded bytes and returns the result. +// Implements [encoding.BinaryMarshaler]. +func (p *Path) MarshalBinary() ([]byte, error) { + return p.MarshalText() +} + +// UnmarshalBinary decodes UTF-8-encoded bytes into p. Implements +// [encoding.BinaryUnmarshaler]. +func (p *Path) UnmarshalBinary(data []byte) error { + return p.UnmarshalText(data) +} + // String returns a string representation of p. func (p *Path) String() string { return p.q.String() diff --git a/path_test.go b/path_test.go index 066c300..689287a 100644 --- a/path_test.go +++ b/path_test.go @@ -1,6 +1,7 @@ package jsonpath import ( + "encoding" "encoding/json" "fmt" "os" @@ -21,6 +22,7 @@ func book(idx int) spec.NormalizedPath { func TestParseSpecExamples(t *testing.T) { t.Parallel() a := assert.New(t) + r := require.New(t) val := specExampleJSON(t) store, _ := val["store"].(map[string]any) books, _ := store["book"].([]any) @@ -126,9 +128,35 @@ func TestParseSpecExamples(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { t.Parallel() + + // Test parsing. p := MustParse(tc.path) a.Equal(p.q, p.Query()) a.Equal(p.q.String(), p.String()) + + // Test text encoding. + a.Implements((*encoding.TextMarshaler)(nil), p) + a.Implements((*encoding.TextUnmarshaler)(nil), p) + text, err := p.MarshalText() + r.NoError(err) + a.Equal(p.q.String(), string(text)) + p.q = nil + r.NoError(p.UnmarshalText(text)) + a.Equal(p.q, p.Query()) + a.Equal(p.q.String(), p.String()) + + // Test binary encoding. + a.Implements((*encoding.BinaryMarshaler)(nil), p) + a.Implements((*encoding.BinaryUnmarshaler)(nil), p) + data, err := p.MarshalBinary() + r.NoError(err) + a.Equal(p.q.String(), string(data)) + p.q = nil + r.NoError(p.UnmarshalBinary(text)) + a.Equal(p.q, p.Query()) + a.Equal(p.q.String(), p.String()) + + // Test execution. res := p.Select(val) loc := p.SelectLocated(val) @@ -333,6 +361,20 @@ func TestParser(t *testing.T) { a.PanicsWithError(tc.err, func() { parser.MustParse(tc.path) }) } } + + // Test text and binary decoding. + if tc.err == "" { + p = &Path{} + r.NoError(p.UnmarshalText([]byte(tc.path))) + a.Equal(tc.exp, p) + p = &Path{} + r.NoError(p.UnmarshalBinary([]byte(tc.path))) + a.Equal(tc.exp, p) + } else { + p = &Path{} + r.EqualError(p.UnmarshalText([]byte(tc.path)), tc.err) + r.EqualError(p.UnmarshalBinary([]byte(tc.path)), tc.err) + } }) } }