Skip to content

Commit 54b3980

Browse files
committed
Use ordered maps
Signed-off-by: Pierre Fenoll <[email protected]>
1 parent 3be535f commit 54b3980

33 files changed

+553
-253
lines changed

.github/workflows/go.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
strategy:
1313
fail-fast: true
1414
matrix:
15-
go: ['1.16', '1.x']
15+
go: ['1.x']
1616
os:
1717
- ubuntu-latest
1818
- windows-latest
@@ -88,7 +88,7 @@ jobs:
8888
run: |
8989
! git grep -InE 'json:"' | grep -v _test.go | grep -v yaml:
9090
91-
- if: runner.os == 'Linux' && matrix.go != '1.16'
91+
- if: runner.os == 'Linux'
9292
name: nilness
9393
run: go run golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness@latest ./...
9494

go.mod

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
module github.com/getkin/kin-openapi
22

3-
go 1.16
3+
go 1.18
44

55
require (
66
github.com/go-openapi/jsonpointer v0.19.5
77
github.com/gorilla/mux v1.8.0
88
github.com/invopop/yaml v0.1.0
99
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
1010
github.com/stretchr/testify v1.8.1
11-
gopkg.in/yaml.v2 v2.4.0 // indirect
11+
github.com/wk8/go-ordered-map/v2 v2.1.5
1212
gopkg.in/yaml.v3 v3.0.1
1313
)
14+
15+
require (
16+
github.com/bahlo/generic-list-go v0.2.0 // indirect
17+
github.com/buger/jsonparser v1.1.1 // indirect
18+
github.com/davecgh/go-spew v1.1.1 // indirect
19+
github.com/go-openapi/swag v0.19.5 // indirect
20+
github.com/josharian/intern v1.0.0 // indirect
21+
github.com/mailru/easyjson v0.7.7 // indirect
22+
github.com/pmezard/go-difflib v1.0.0 // indirect
23+
gopkg.in/yaml.v2 v2.4.0 // indirect
24+
)

go.sum

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
2+
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
3+
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
4+
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
15
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
26
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
37
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -9,14 +13,17 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
913
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
1014
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
1115
github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
16+
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
17+
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
1218
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
1319
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
1420
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
1521
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
1622
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1723
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
18-
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
1924
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
25+
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
26+
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
2027
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
2128
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
2229
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -29,6 +36,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
2936
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
3037
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
3138
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
39+
github.com/wk8/go-ordered-map/v2 v2.1.5 h1:jLbYIFyWQMUwHLO20cImlCRBoNc5lp0nmE2dvwcxc7k=
40+
github.com/wk8/go-ordered-map/v2 v2.1.5/go.mod h1:9Xvgm2mV2kSq2SAm0Y608tBmu8akTzI7c2bz7/G7ZN4=
3241
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3342
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
3443
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

openapi2/openapi2.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type T struct {
2020
Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
2121
Host string `json:"host,omitempty" yaml:"host,omitempty"`
2222
BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"`
23-
Paths map[string]*PathItem `json:"paths,omitempty" yaml:"paths,omitempty"`
23+
Paths *Paths `json:"paths,omitempty" yaml:"paths,omitempty"`
2424
Definitions map[string]*openapi3.SchemaRef `json:"definitions,omitempty" yaml:"definitions,omitempty"`
2525
Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
2626
Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"`
@@ -40,15 +40,13 @@ func (doc *T) UnmarshalJSON(data []byte) error {
4040
}
4141

4242
func (doc *T) AddOperation(path string, method string, operation *Operation) {
43-
paths := doc.Paths
44-
if paths == nil {
45-
paths = make(map[string]*PathItem)
46-
doc.Paths = paths
43+
if doc.Paths == nil {
44+
doc.Paths = NewPaths()
4745
}
48-
pathItem := paths[path]
46+
pathItem := doc.Paths.Value(path)
4947
if pathItem == nil {
5048
pathItem = &PathItem{}
51-
paths[path] = pathItem
49+
doc.Paths.Set(path, pathItem)
5250
}
5351
pathItem.SetOperation(method, operation)
5452
}

openapi2/paths.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package openapi2
2+
3+
import (
4+
"encoding/json"
5+
6+
orderedmap "github.com/wk8/go-ordered-map/v2"
7+
)
8+
9+
type Paths struct {
10+
om *orderedmap.OrderedMap[string, *PathItem]
11+
}
12+
13+
// MarshalJSON returns the JSON encoding of Paths.
14+
func (paths *Paths) MarshalJSON() ([]byte, error) {
15+
if paths == nil || paths.om == nil {
16+
return []byte("{}"), nil
17+
}
18+
return paths.om.MarshalJSON()
19+
}
20+
21+
// UnmarshalJSON sets Paths to a copy of data.
22+
func (paths *Paths) UnmarshalJSON(data []byte) error {
23+
return json.Unmarshal(data, &paths.om)
24+
}
25+
26+
func (paths *Paths) Value(key string) *PathItem {
27+
// if paths == nil || paths.om == nil {
28+
// return nil
29+
// }
30+
return paths.om.Value(key)
31+
}
32+
33+
func (paths *Paths) Set(key string, value *PathItem) {
34+
// if paths != nil || paths.om != nil {
35+
_, _ = paths.om.Set(key, value)
36+
// }
37+
}
38+
39+
func (paths *Paths) Len() int {
40+
if paths == nil || paths.om == nil {
41+
return 0
42+
}
43+
return paths.om.Len()
44+
}
45+
46+
func (paths *Paths) Iter() *pathsKV {
47+
if paths == nil || paths.om == nil {
48+
return nil
49+
}
50+
return (*pathsKV)(paths.om.Oldest())
51+
}
52+
53+
type pathsKV orderedmap.Pair[string, *PathItem] //FIXME: pub?
54+
55+
func (pair *pathsKV) Next() *pathsKV {
56+
ompair := (*orderedmap.Pair[string, *PathItem])(pair)
57+
return (*pathsKV)(ompair.Next())
58+
}
59+
60+
// NewPathsWithCapacity builds a paths object of the given capacity.
61+
func NewPathsWithCapacity(cap int) *Paths {
62+
return &Paths{om: orderedmap.New[string, *PathItem](cap)}
63+
}
64+
65+
// NewPaths builds a paths object with path items in insertion order.
66+
func NewPaths(opts ...NewPathsOption) *Paths {
67+
paths := NewPathsWithCapacity(len(opts))
68+
for _, opt := range opts {
69+
opt(paths)
70+
}
71+
return paths
72+
}
73+
74+
// NewPathsOption describes options to NewPaths func
75+
type NewPathsOption func(*Paths)
76+
77+
// WithPath adds paths as an option to NewPaths
78+
func WithPath(path string, pathItem *PathItem) NewPathsOption {
79+
return func(paths *Paths) { paths.Set(path, pathItem) }
80+
}

openapi2conv/issue558_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ paths:
2727
`
2828
doc3, err := v2v3YAML([]byte(spec))
2929
require.NoError(t, err)
30-
require.NotEmpty(t, doc3.Paths["/test"].Get.Deprecated)
30+
require.NotEmpty(t, doc3.Paths.Value("/test").Get.Deprecated)
3131
_, err = yaml.Marshal(doc3)
3232
require.NoError(t, err)
3333

3434
doc2, err := FromV3(doc3)
3535
require.NoError(t, err)
36-
require.NotEmpty(t, doc2.Paths["/test"].Get.Deprecated)
36+
require.NotEmpty(t, doc2.Paths.Value("/test").Get.Deprecated)
3737
}

openapi2conv/issue573_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ func TestIssue573(t *testing.T) {
3636

3737
// Make sure the response content appears for each mime-type originally
3838
// appeared in "produces".
39-
pingGetContent := v3.Paths["/ping"].Get.Responses["200"].Value.Content
39+
pingGetContent := v3.Paths.Value("/ping").Get.Responses["200"].Value.Content
4040
require.Len(t, pingGetContent, 2)
4141
require.Contains(t, pingGetContent, "application/toml")
4242
require.Contains(t, pingGetContent, "application/xml")
4343

4444
// Is "produces" is not explicitly specified, default to "application/json".
45-
pingPostContent := v3.Paths["/ping"].Post.Responses["200"].Value.Content
45+
pingPostContent := v3.Paths.Value("/ping").Post.Responses["200"].Value.Content
4646
require.Len(t, pingPostContent, 1)
4747
require.Contains(t, pingPostContent, "application/json")
4848
}

openapi2conv/openapi2_conv.go

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"sort"
99
"strings"
1010

11+
orderedmap "github.com/wk8/go-ordered-map/v2"
12+
1113
"github.com/getkin/kin-openapi/openapi2"
1214
"github.com/getkin/kin-openapi/openapi3"
1315
)
@@ -69,16 +71,14 @@ func ToV3(doc2 *openapi2.T) (*openapi3.T, error) {
6971
}
7072
}
7173

72-
if paths := doc2.Paths; len(paths) != 0 {
73-
doc3Paths := make(map[string]*openapi3.PathItem, len(paths))
74-
for path, pathItem := range paths {
75-
r, err := ToV3PathItem(doc2, &doc3.Components, pathItem, doc2.Consumes)
76-
if err != nil {
77-
return nil, err
78-
}
79-
doc3Paths[path] = r
74+
doc3.Paths = openapi3.NewPathsWithCapacity(doc2.Paths.Len())
75+
for pair := doc2.Paths.Iter(); pair != nil; pair = pair.Next() {
76+
path, pathItem := pair.Key, pair.Value
77+
r, err := ToV3PathItem(doc2, &doc3.Components, pathItem, doc2.Consumes)
78+
if err != nil {
79+
return nil, err
8080
}
81-
doc3.Paths = doc3Paths
81+
doc3.Paths.Set(path, r)
8282
}
8383

8484
if responses := doc2.Responses; len(responses) != 0 {
@@ -556,9 +556,9 @@ func ToV3SecurityScheme(securityScheme *openapi2.SecurityScheme) (*openapi3.Secu
556556
result.Type = "oauth2"
557557
flows := &openapi3.OAuthFlows{}
558558
result.Flows = flows
559-
scopesMap := make(map[string]string)
559+
scopesMap := orderedmap.New[string, string](len(securityScheme.Scopes))
560560
for scope, desc := range securityScheme.Scopes {
561-
scopesMap[scope] = desc
561+
scopesMap.Set(scope, desc)
562562
}
563563
flow := &openapi3.OAuthFlow{
564564
AuthorizationURL: securityScheme.AuthorizationURL,
@@ -628,7 +628,8 @@ func FromV3(doc3 *openapi3.T) (*openapi2.T, error) {
628628
if isHTTP {
629629
doc2.Schemes = append(doc2.Schemes, "http")
630630
}
631-
for path, pathItem := range doc3.Paths {
631+
for pair := doc3.Paths.Iter(); pair != nil; pair = pair.Next() {
632+
path, pathItem := pair.Key, pair.Value
632633
if pathItem == nil {
633634
continue
634635
}
@@ -654,7 +655,7 @@ func FromV3(doc3 *openapi3.T) (*openapi2.T, error) {
654655
params = append(params, p)
655656
}
656657
sort.Sort(params)
657-
doc2.Paths[path].Parameters = params
658+
doc2.Paths.Value(path).Parameters = params
658659
}
659660

660661
for name, param := range doc3.Components.Parameters {
@@ -1179,9 +1180,9 @@ func FromV3SecurityScheme(doc3 *openapi3.T, ref *openapi3.SecuritySchemeRef) (*o
11791180
return nil, nil
11801181
}
11811182

1182-
result.Scopes = make(map[string]string, len(flow.Scopes))
1183-
for scope, desc := range flow.Scopes {
1184-
result.Scopes[scope] = desc
1183+
result.Scopes = make(map[string]string, flow.Scopes.Len())
1184+
for pair := flow.Scopes.Oldest(); pair != nil; pair = pair.Next() {
1185+
result.Scopes[pair.Key] = pair.Value
11851186
}
11861187
}
11871188
default:
@@ -1204,15 +1205,13 @@ func stripNonCustomExtensions(extensions map[string]interface{}) {
12041205
}
12051206

12061207
func addPathExtensions(doc2 *openapi2.T, path string, extensionProps openapi3.ExtensionProps) {
1207-
paths := doc2.Paths
1208-
if paths == nil {
1209-
paths = make(map[string]*openapi2.PathItem)
1210-
doc2.Paths = paths
1208+
if doc2.Paths == nil {
1209+
doc2.Paths = openapi2.NewPaths()
12111210
}
1212-
pathItem := paths[path]
1211+
pathItem := doc2.Paths.Value(path)
12131212
if pathItem == nil {
12141213
pathItem = &openapi2.PathItem{}
1215-
paths[path] = pathItem
1214+
doc2.Paths.Set(path, pathItem)
12161215
}
12171216
pathItem.ExtensionProps = extensionProps
12181217
}

0 commit comments

Comments
 (0)