Skip to content

Commit 564799a

Browse files
committed
doc: improved documentation
* added examples for structs and the usage of supported interfaces * detailed how type mappings work and limitations * README: made the badges zone more compact * README: fixed go.pkg.doc badge, disabled the Slack badge (not working) * lint: enabled doc-related linter (godot) * fixed missing license mark in recently added source Signed-off-by: Frédéric BIDON <[email protected]>
1 parent c653a59 commit 564799a

File tree

10 files changed

+432
-50
lines changed

10 files changed

+432
-50
lines changed

.golangci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ linters:
66
- errorlint
77
- exhaustruct
88
- funlen
9-
- godot
109
- godox
1110
- gosmopolitan
1211
- ireturn

README.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
<!-- Badges: status -->
44
[![Tests][test-badge]][test-url] [![Coverage][cov-badge]][cov-url] [![CI vuln scan][vuln-scan-badge]][vuln-scan-url] [![CodeQL][codeql-badge]][codeql-url]
55
<!-- Badges: release & docker images -->
6-
[![Release][release-badge]][release-url]
76
<!-- Badges: code quality -->
8-
[![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url]
97
<!-- Badges: license & compliance -->
10-
[![License][license-badge]][license-url]
8+
[![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url]
119
<!-- Badges: documentation & support -->
1210
<!-- Badges: others & stats -->
13-
[![GoDoc][godoc-badge]][godoc-url] [![Slack Channel][slack-badge]][slack-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge]
11+
<!-- Slack badge disabled until I am able to restore a valid link to the chat -->
12+
[![GoDoc][godoc-badge]][godoc-url] <!-- [![Slack Channel][slack-badge]][slack-url] -->[![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge]
1413

1514
---
1615

@@ -28,13 +27,18 @@ go get github.com/go-openapi/jsonpointer
2827

2928
## Basic usage
3029

31-
See [examples](./examples_test.go)
30+
See also some [examples](./examples_test.go)
31+
32+
### Retrieving a value
3233

3334
```go
3435
import (
3536
"github.com/go-openapi/jsonpointer"
3637
)
3738

39+
40+
var doc any
41+
3842
...
3943

4044
pointer, err := jsonpointer.New("/foo/1")
@@ -50,6 +54,23 @@ See [examples](./examples_test.go)
5054
...
5155
```
5256

57+
### Setting a value
58+
59+
```go
60+
...
61+
var doc any
62+
...
63+
pointer, err := jsonpointer.New("/foo/1")
64+
if err != nil {
65+
... // error: e.g. invalid JSON pointer specification
66+
}
67+
68+
doc, err = p.Set(doc, "value")
69+
if err != nil {
70+
... // error: e.g. key not found, index out of bounds, etc.
71+
}
72+
```
73+
5374
## Change log
5475

5576
See <https://github.com/go-openapi/jsonpointer/releases>
@@ -96,7 +117,7 @@ using the special trailing character "-" is not implemented.
96117
<!-- Badges: documentation & support -->
97118
[doc-badge]: https://img.shields.io/badge/doc-site-blue?link=https%3A%2F%2Fgoswagger.io%2Fgo-openapi%2F
98119
[doc-url]: https://goswagger.io/go-openapi
99-
[godoc-badge]: https://pkg.go.dev/github.com/go-openapi/jsonpointer?status.svg
120+
[godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer
100121
[godoc-url]: http://pkg.go.dev/github.com/go-openapi/jsonpointer
101122
[slack-badge]: https://slackin.goswagger.io/badge.svg
102123
[slack-url]: https://slackin.goswagger.io

errors.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,33 @@
33

44
package jsonpointer
55

6+
import "fmt"
7+
68
type pointerError string
79

810
func (e pointerError) Error() string {
911
return string(e)
1012
}
1113

1214
const (
13-
// ErrPointer is an error raised by the jsonpointer package
15+
// ErrPointer is a sentinel error raised by all errors from this package.
1416
ErrPointer pointerError = "JSON pointer error"
1517

16-
// ErrInvalidStart states that a JSON pointer must start with a separator ("/")
18+
// ErrInvalidStart states that a JSON pointer must start with a separator ("/").
1719
ErrInvalidStart pointerError = `JSON pointer must be empty or start with a "` + pointerSeparator
1820

19-
// ErrUnsupportedValueType indicates that a value of the wrong type is being set
21+
// ErrUnsupportedValueType indicates that a value of the wrong type is being set.
2022
ErrUnsupportedValueType pointerError = "only structs, pointers, maps and slices are supported for setting values"
2123
)
24+
25+
func errNoKey(key string) error {
26+
return fmt.Errorf("object has no key %q: %w", key, ErrPointer)
27+
}
28+
29+
func errOutOfBounds(length, idx int) error {
30+
return fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", length-1, idx, ErrPointer)
31+
}
32+
33+
func errInvalidReference(token string) error {
34+
return fmt.Errorf("invalid token reference %q: %w", token, ErrPointer)
35+
}

examples_test.go

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,88 @@ package jsonpointer
55

66
import (
77
"encoding/json"
8+
"errors"
89
"fmt"
910
)
1011

12+
var ErrExampleStruct = errors.New("example error")
13+
1114
type exampleDocument struct {
1215
Foo []string `json:"foo"`
1316
}
1417

18+
func ExampleNew() {
19+
empty, err := New("")
20+
if err != nil {
21+
fmt.Println(err)
22+
23+
return
24+
}
25+
fmt.Printf("empty pointer: %q\n", empty.String())
26+
27+
key, err := New("/foo")
28+
if err != nil {
29+
fmt.Println(err)
30+
31+
return
32+
}
33+
fmt.Printf("pointer to object key: %q\n", key.String())
34+
35+
elem, err := New("/foo/1")
36+
if err != nil {
37+
fmt.Println(err)
38+
39+
return
40+
}
41+
fmt.Printf("pointer to array element: %q\n", elem.String())
42+
43+
escaped0, err := New("/foo~0")
44+
if err != nil {
45+
fmt.Println(err)
46+
47+
return
48+
}
49+
// key contains "~"
50+
fmt.Printf("pointer to key %q: %q\n", Unescape("foo~0"), escaped0.String())
51+
52+
escaped1, err := New("/foo~1")
53+
if err != nil {
54+
fmt.Println(err)
55+
56+
return
57+
}
58+
// key contains "/"
59+
fmt.Printf("pointer to key %q: %q\n", Unescape("foo~1"), escaped1.String())
60+
61+
// output:
62+
// empty pointer: ""
63+
// pointer to object key: "/foo"
64+
// pointer to array element: "/foo/1"
65+
// pointer to key "foo~": "/foo~0"
66+
// pointer to key "foo/": "/foo~1"
67+
}
68+
1569
func ExamplePointer_Get() {
1670
var doc exampleDocument
1771

1872
if err := json.Unmarshal(testDocumentJSONBytes, &doc); err != nil { // populates doc
19-
panic(err)
73+
fmt.Println(err)
74+
75+
return
2076
}
2177

2278
pointer, err := New("/foo/1")
2379
if err != nil {
24-
panic(err)
80+
fmt.Println(err)
81+
82+
return
2583
}
2684

2785
value, kind, err := pointer.Get(doc)
2886
if err != nil {
29-
panic(err)
87+
fmt.Println(err)
88+
89+
return
3090
}
3191

3292
fmt.Printf(
@@ -43,17 +103,23 @@ func ExamplePointer_Set() {
43103
var doc exampleDocument
44104

45105
if err := json.Unmarshal(testDocumentJSONBytes, &doc); err != nil { // populates doc
46-
panic(err)
106+
fmt.Println(err)
107+
108+
return
47109
}
48110

49111
pointer, err := New("/foo/1")
50112
if err != nil {
51-
panic(err)
113+
fmt.Println(err)
114+
115+
return
52116
}
53117

54118
result, err := pointer.Set(&doc, "hey my")
55119
if err != nil {
56-
panic(err)
120+
fmt.Println(err)
121+
122+
return
57123
}
58124

59125
fmt.Printf("result: %#v\n", result)

fuzz_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers
2+
// SPDX-License-Identifier: Apache-2.0
3+
14
package jsonpointer
25

36
import (

iface_example_test.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package jsonpointer_test
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/go-openapi/jsonpointer"
7+
)
8+
9+
var (
10+
_ jsonpointer.JSONPointable = CustomDoc{}
11+
_ jsonpointer.JSONSetable = &CustomDoc{}
12+
)
13+
14+
// CustomDoc accepts 2 preset properties "propA" and "propB", plus any number of extra properties.
15+
//
16+
// All values are strings.
17+
type CustomDoc struct {
18+
a string
19+
b string
20+
c map[string]string
21+
}
22+
23+
// JSONLookup implements [jsonpointer.JSONPointable].
24+
func (d CustomDoc) JSONLookup(key string) (any, error) {
25+
switch key {
26+
case "propA":
27+
return d.a, nil
28+
case "propB":
29+
return d.b, nil
30+
default:
31+
if len(d.c) == 0 {
32+
return nil, fmt.Errorf("key %q not found: %w", key, ErrExampleIface)
33+
}
34+
extra, ok := d.c[key]
35+
if !ok {
36+
return nil, fmt.Errorf("key %q not found: %w", key, ErrExampleIface)
37+
}
38+
39+
return extra, nil
40+
}
41+
}
42+
43+
// JSONSet implements [jsonpointer.JSONSetable].
44+
func (d *CustomDoc) JSONSet(key string, value any) error {
45+
asString, ok := value.(string)
46+
if !ok {
47+
return fmt.Errorf("a CustomDoc only access strings as values, but got %T: %w", value, ErrExampleIface)
48+
}
49+
50+
switch key {
51+
case "propA":
52+
d.a = asString
53+
54+
return nil
55+
case "propB":
56+
d.b = asString
57+
58+
return nil
59+
default:
60+
if len(d.c) == 0 {
61+
d.c = make(map[string]string)
62+
}
63+
d.c[key] = asString
64+
65+
return nil
66+
}
67+
}
68+
69+
func Example_iface() {
70+
doc := CustomDoc{
71+
a: "initial value for a",
72+
b: "initial value for b",
73+
// no extra values
74+
}
75+
76+
pointerA, err := jsonpointer.New("/propA")
77+
if err != nil {
78+
fmt.Println(err)
79+
80+
return
81+
}
82+
83+
// get the initial value for a
84+
propA, kind, err := pointerA.Get(doc)
85+
if err != nil {
86+
fmt.Println(err)
87+
88+
return
89+
}
90+
fmt.Printf("propA (%v): %v\n", kind, propA)
91+
92+
pointerB, err := jsonpointer.New("/propB")
93+
if err != nil {
94+
fmt.Println(err)
95+
96+
return
97+
}
98+
99+
// get the initial value for b
100+
propB, kind, err := pointerB.Get(doc)
101+
if err != nil {
102+
fmt.Println(err)
103+
104+
return
105+
}
106+
fmt.Printf("propB (%v): %v\n", kind, propB)
107+
108+
pointerC, err := jsonpointer.New("/extra")
109+
if err != nil {
110+
fmt.Println(err)
111+
112+
return
113+
}
114+
115+
// not found yet
116+
_, _, err = pointerC.Get(doc)
117+
fmt.Printf("propC: %v\n", err)
118+
119+
_, err = pointerA.Set(&doc, "new value for a") // doc is updated in place
120+
if err != nil {
121+
fmt.Println(err)
122+
123+
return
124+
}
125+
126+
_, err = pointerB.Set(&doc, "new value for b")
127+
if err != nil {
128+
fmt.Println(err)
129+
130+
return
131+
}
132+
133+
_, err = pointerC.Set(&doc, "new extra value")
134+
if err != nil {
135+
fmt.Println(err)
136+
137+
return
138+
}
139+
140+
fmt.Printf("updated doc: %v", doc)
141+
142+
// output:
143+
// propA (string): initial value for a
144+
// propB (string): initial value for b
145+
// propC: key "extra" not found: example error
146+
// updated doc: {new value for a new value for b map[extra:new extra value]}
147+
}

0 commit comments

Comments
 (0)