mirror of https://github.com/k3s-io/k3s
Merge pull request #52793 from nikhita/crd-validation-conversion-tests
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. apiextensions: add round trip tests for CRD schema conversion Follow up test for https://github.com/kubernetes/kubernetes/pull/52281. Need to update go-openapi to get this - https://github.com/go-openapi/spec/pull/31 - in. **Special notes for your reviewer**: The tests won't pass until https://github.com/kubernetes/kubernetes/pull/52281 is merged. **Release note**: ```release-note NONE ``` /cc @stttspull/6/head
commit
e339400f6f
|
@ -1188,7 +1188,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/loads",
|
"ImportPath": "github.com/go-openapi/loads",
|
||||||
"Rev": "18441dfa706d924a39a030ee2c3b1d8d81917b38"
|
"Rev": "a80dea3052f00e5f032e860dd7355cd0cc67e24d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/runtime",
|
"ImportPath": "github.com/go-openapi/runtime",
|
||||||
|
@ -1196,7 +1196,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/strfmt",
|
"ImportPath": "github.com/go-openapi/strfmt",
|
||||||
|
@ -1204,7 +1204,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/validate",
|
"ImportPath": "github.com/go-openapi/validate",
|
||||||
|
@ -1856,7 +1856,6 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/inconshreveable/mousetrap",
|
"ImportPath": "github.com/inconshreveable/mousetrap",
|
||||||
"Comment": "v1.0",
|
|
||||||
"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1978,15 +1977,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||||
|
|
|
@ -32,11 +32,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||||
|
@ -56,15 +56,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/spf13/pflag",
|
"ImportPath": "github.com/spf13/pflag",
|
||||||
|
@ -134,6 +134,10 @@
|
||||||
"ImportPath": "gopkg.in/inf.v0",
|
"ImportPath": "gopkg.in/inf.v0",
|
||||||
"Rev": "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
|
"Rev": "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "gopkg.in/yaml.v2",
|
||||||
|
"Rev": "53feefa2559fb8dfa8d81baad31be332c97d6c77"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
|
||||||
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
|
|
@ -132,7 +132,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/loads",
|
"ImportPath": "github.com/go-openapi/loads",
|
||||||
"Rev": "18441dfa706d924a39a030ee2c3b1d8d81917b38"
|
"Rev": "a80dea3052f00e5f032e860dd7355cd0cc67e24d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/runtime",
|
"ImportPath": "github.com/go-openapi/runtime",
|
||||||
|
@ -140,7 +140,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/strfmt",
|
"ImportPath": "github.com/go-openapi/strfmt",
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/validate",
|
"ImportPath": "github.com/go-openapi/validate",
|
||||||
|
@ -264,15 +264,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||||
|
|
|
@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
|
||||||
load(
|
load(
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
"go_library",
|
"go_library",
|
||||||
|
"go_test",
|
||||||
)
|
)
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
|
@ -27,3 +28,20 @@ filegroup(
|
||||||
srcs = [":package-srcs"],
|
srcs = [":package-srcs"],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["validation_test.go"],
|
||||||
|
library = ":go_default_library",
|
||||||
|
deps = [
|
||||||
|
"//vendor/github.com/go-openapi/spec:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/api/testing/fuzzer:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-openapi/spec"
|
||||||
|
|
||||||
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
|
"k8s.io/apimachinery/pkg/api/testing/fuzzer"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
"k8s.io/apimachinery/pkg/util/json"
|
||||||
|
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||||
|
apiextensionsfuzzer "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer"
|
||||||
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestRoundTrip checks the conversion to go-openapi types.
|
||||||
|
// internal -> go-openapi -> JSON -> external -> internal
|
||||||
|
func TestRoundTrip(t *testing.T) {
|
||||||
|
scheme := runtime.NewScheme()
|
||||||
|
codecs := serializer.NewCodecFactory(scheme)
|
||||||
|
|
||||||
|
// add internal and external types to scheme
|
||||||
|
if err := apiextensions.AddToScheme(scheme); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := apiextensionsv1beta1.AddToScheme(scheme); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
seed := rand.Int63()
|
||||||
|
fuzzerFuncs := fuzzer.MergeFuzzerFuncs(apiextensionsfuzzer.Funcs)
|
||||||
|
f := fuzzer.FuzzerFor(fuzzerFuncs, rand.NewSource(seed), codecs)
|
||||||
|
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
// fuzz internal types
|
||||||
|
internal := &apiextensions.JSONSchemaProps{}
|
||||||
|
f.Fuzz(internal)
|
||||||
|
|
||||||
|
// internal -> go-openapi
|
||||||
|
openAPITypes := &spec.Schema{}
|
||||||
|
if err := convertJSONSchemaProps(internal, openAPITypes); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// go-openapi -> JSON
|
||||||
|
openAPIJSON, err := json.Marshal(openAPITypes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON -> external
|
||||||
|
external := &apiextensionsv1beta1.JSONSchemaProps{}
|
||||||
|
if err := json.Unmarshal(openAPIJSON, external); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// external -> internal
|
||||||
|
internalRoundTripped := &apiextensions.JSONSchemaProps{}
|
||||||
|
if err := scheme.Convert(external, internalRoundTripped, nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !apiequality.Semantic.DeepEqual(internal, internalRoundTripped) {
|
||||||
|
t.Fatalf("expected\n\t%#v, got \n\t%#v", internal, internalRoundTripped)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,11 +56,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||||
|
@ -100,15 +100,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mxk/go-flowrate/flowrate",
|
"ImportPath": "github.com/mxk/go-flowrate/flowrate",
|
||||||
|
|
|
@ -340,11 +340,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||||
|
@ -488,15 +488,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||||
|
|
|
@ -112,11 +112,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||||
|
@ -240,15 +240,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/peterbourgon/diskv",
|
"ImportPath": "github.com/peterbourgon/diskv",
|
||||||
|
|
|
@ -32,11 +32,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gogo/protobuf/gogoproto",
|
"ImportPath": "github.com/gogo/protobuf/gogoproto",
|
||||||
|
@ -144,15 +144,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/spf13/pflag",
|
"ImportPath": "github.com/spf13/pflag",
|
||||||
|
@ -206,6 +206,10 @@
|
||||||
"ImportPath": "golang.org/x/text/width",
|
"ImportPath": "golang.org/x/text/width",
|
||||||
"Rev": "b19bf474d317b857955b12035d2c5acb57ce8b01"
|
"Rev": "b19bf474d317b857955b12035d2c5acb57ce8b01"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "gopkg.in/yaml.v2",
|
||||||
|
"Rev": "53feefa2559fb8dfa8d81baad31be332c97d6c77"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/gengo/args",
|
"ImportPath": "k8s.io/gengo/args",
|
||||||
"Rev": "70ad626ed2d7a483d89d2c4c56364d60b48ee8fc"
|
"Rev": "70ad626ed2d7a483d89d2c4c56364d60b48ee8fc"
|
||||||
|
|
|
@ -128,11 +128,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||||
|
@ -244,15 +244,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||||
|
|
|
@ -44,11 +44,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||||
|
@ -120,15 +120,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/peterbourgon/diskv",
|
"ImportPath": "github.com/peterbourgon/diskv",
|
||||||
|
|
|
@ -120,11 +120,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/spec",
|
"ImportPath": "github.com/go-openapi/spec",
|
||||||
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
|
"Rev": "7abd5745472fff5eb3685386d5fb8bf38683154d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-openapi/swag",
|
"ImportPath": "github.com/go-openapi/swag",
|
||||||
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||||
|
@ -236,15 +236,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/buffer",
|
"ImportPath": "github.com/mailru/easyjson/buffer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
"ImportPath": "github.com/mailru/easyjson/jlexer",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
"ImportPath": "github.com/mailru/easyjson/jwriter",
|
||||||
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
"Rev": "2f5df55504ebc322e4d52d34df6a1f5b503bf26d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
# Set default charset
|
||||||
|
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# Tab indentation (no size specified)
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
# Matches the exact files either package.json or .travis.yml
|
||||||
|
[{package.json,.travis.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
|
@ -1,13 +0,0 @@
|
||||||
approve_by_comment: true
|
|
||||||
approve_regex: '^(:shipit:|:\+1:|\+1|LGTM|lgtm|Approved)'
|
|
||||||
reject_regex: ^[Rr]ejected
|
|
||||||
reset_on_push: false
|
|
||||||
reviewers:
|
|
||||||
members:
|
|
||||||
- casualjim
|
|
||||||
- chancez
|
|
||||||
- frapposelli
|
|
||||||
- vburenin
|
|
||||||
- pytlesk4
|
|
||||||
name: pullapprove
|
|
||||||
required: 1
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.8
|
||||||
|
install:
|
||||||
|
- go get -u github.com/stretchr/testify
|
||||||
|
- go get -u github.com/go-openapi/analysis
|
||||||
|
- go get -u github.com/go-openapi/spec
|
||||||
|
- go get -u github.com/go-openapi/swag
|
||||||
|
- go get -u gopkg.in/yaml.v2
|
||||||
|
script:
|
||||||
|
- ./hack/coverage
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
notifications:
|
||||||
|
slack:
|
||||||
|
secure: OxkPwVp35qBTUilgWC8xykSj+sGMcj0h8IIOKD+Rflx2schZVlFfdYdyVBM+s9OqeOfvtuvnR9v1Ye2rPKAvcjWdC4LpRGUsgmItZaI6Um8Aj6+K9udCw5qrtZVfOVmRu8LieH//XznWWKdOultUuniW0MLqw5+II87Gd00RWbCGi0hk0PykHe7uK+PDA2BEbqyZ2WKKYCvfB3j+0nrFOHScXqnh0V05l2E83J4+Sgy1fsPy+1WdX58ZlNBG333ibaC1FS79XvKSmTgKRkx3+YBo97u6ZtUmJa5WZjf2OdLG3KIckGWAv6R5xgxeU31N0Ng8L332w/Edpp2O/M2bZwdnKJ8hJQikXIAQbICbr+lTDzsoNzMdEIYcHpJ5hjPbiUl3Bmd+Jnsjf5McgAZDiWIfpCKZ29tPCEkVwRsOCqkyPRMNMzHHmoja495P5jR+ODS7+J8RFg5xgcnOgpP9D4Wlhztlf5WyZMpkLxTUD+bZq2SRf50HfHFXTkfq22zPl3d1eq0yrLwh/Z/fWKkfb6SyysROL8y6s8u3dpFX1YHSg0BR6i913h4aoZw9B2BG27cafLLTwKYsp2dFo1PWl4O6u9giFJIeqwloZHLKKrwh0cBFhB7RH0I58asxkZpCH6uWjJierahmHe7iS+E6i+9oCHkOZ59hmCYNimIs3hM=
|
|
@ -1,4 +1,4 @@
|
||||||
# Loads OAI specs [![Build Status](https://ci.vmware.run/api/badges/go-openapi/loads/status.svg)](https://ci.vmware.run/go-openapi/loads) [![Coverage](https://coverage.vmware.run/badges/go-openapi/loads/coverage.svg)](https://coverage.vmware.run/go-openapi/loads) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
|
# Loads OAI specs [![Build Status](https://travis-ci.org/go-openapi/loads.svg?branch=master)](https://travis-ci.org/go-openapi/loads) [![codecov](https://codecov.io/gh/go-openapi/loads/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/loads) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
|
||||||
|
|
||||||
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/loads/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/loads?status.svg)](http://godoc.org/github.com/go-openapi/loads)
|
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/loads/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/loads?status.svg)](http://godoc.org/github.com/go-openapi/loads)
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,13 @@
|
||||||
package loads
|
package loads
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/go-openapi/analysis"
|
"github.com/go-openapi/analysis"
|
||||||
"github.com/go-openapi/spec"
|
"github.com/go-openapi/spec"
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
|
@ -39,7 +42,17 @@ type DocLoader func(string) (json.RawMessage, error)
|
||||||
// DocMatcher represents a predicate to check if a loader matches
|
// DocMatcher represents a predicate to check if a loader matches
|
||||||
type DocMatcher func(string) bool
|
type DocMatcher func(string) bool
|
||||||
|
|
||||||
var loaders = &loader{Match: func(_ string) bool { return true }, Fn: JSONDoc}
|
var (
|
||||||
|
loaders *loader
|
||||||
|
defaultLoader *loader
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
defaultLoader = &loader{Match: func(_ string) bool { return true }, Fn: JSONDoc}
|
||||||
|
loaders = defaultLoader
|
||||||
|
spec.PathLoader = loaders.Fn
|
||||||
|
AddLoader(swag.YAMLMatcher, swag.YAMLDoc)
|
||||||
|
}
|
||||||
|
|
||||||
// AddLoader for a document
|
// AddLoader for a document
|
||||||
func AddLoader(predicate DocMatcher, load DocLoader) {
|
func AddLoader(predicate DocMatcher, load DocLoader) {
|
||||||
|
@ -49,7 +62,7 @@ func AddLoader(predicate DocMatcher, load DocLoader) {
|
||||||
Fn: load,
|
Fn: load,
|
||||||
Next: prev,
|
Next: prev,
|
||||||
}
|
}
|
||||||
|
spec.PathLoader = loaders.Fn
|
||||||
}
|
}
|
||||||
|
|
||||||
type loader struct {
|
type loader struct {
|
||||||
|
@ -71,11 +84,12 @@ func JSONSpec(path string) (*Document, error) {
|
||||||
// Document represents a swagger spec document
|
// Document represents a swagger spec document
|
||||||
type Document struct {
|
type Document struct {
|
||||||
// specAnalyzer
|
// specAnalyzer
|
||||||
Analyzer *analysis.Spec
|
Analyzer *analysis.Spec
|
||||||
spec *spec.Swagger
|
spec *spec.Swagger
|
||||||
origSpec *spec.Swagger
|
specFilePath string
|
||||||
schema *spec.Schema
|
origSpec *spec.Swagger
|
||||||
raw json.RawMessage
|
schema *spec.Schema
|
||||||
|
raw json.RawMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spec loads a new spec document
|
// Spec loads a new spec document
|
||||||
|
@ -84,23 +98,39 @@ func Spec(path string) (*Document, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var lastErr error
|
||||||
for l := loaders.Next; l != nil; l = l.Next {
|
for l := loaders.Next; l != nil; l = l.Next {
|
||||||
if loaders.Match(specURL.Path) {
|
if loaders.Match(specURL.Path) {
|
||||||
b, err2 := loaders.Fn(path)
|
b, err2 := loaders.Fn(path)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return nil, err2
|
lastErr = err2
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
return Analyzed(b, "")
|
doc, err := Analyzed(b, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if doc != nil {
|
||||||
|
doc.specFilePath = path
|
||||||
|
}
|
||||||
|
return doc, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b, err := loaders.Fn(path)
|
if lastErr != nil {
|
||||||
|
return nil, lastErr
|
||||||
|
}
|
||||||
|
b, err := defaultLoader.Fn(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return Analyzed(b, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
var swag20Schema = spec.MustLoadSwagger20Schema()
|
document, err := Analyzed(b, "")
|
||||||
|
if document != nil {
|
||||||
|
document.specFilePath = path
|
||||||
|
}
|
||||||
|
|
||||||
|
return document, err
|
||||||
|
}
|
||||||
|
|
||||||
// Analyzed creates a new analyzed spec document
|
// Analyzed creates a new analyzed spec document
|
||||||
func Analyzed(data json.RawMessage, version string) (*Document, error) {
|
func Analyzed(data json.RawMessage, version string) (*Document, error) {
|
||||||
|
@ -111,40 +141,66 @@ func Analyzed(data json.RawMessage, version string) (*Document, error) {
|
||||||
return nil, fmt.Errorf("spec version %q is not supported", version)
|
return nil, fmt.Errorf("spec version %q is not supported", version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
raw := data
|
||||||
|
trimmed := bytes.TrimSpace(data)
|
||||||
|
if len(trimmed) > 0 {
|
||||||
|
if trimmed[0] != '{' && trimmed[0] != '[' {
|
||||||
|
yml, err := swag.BytesToYAMLDoc(trimmed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("analyzed: %v", err)
|
||||||
|
}
|
||||||
|
d, err := swag.YAMLToJSON(yml)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("analyzed: %v", err)
|
||||||
|
}
|
||||||
|
raw = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
swspec := new(spec.Swagger)
|
swspec := new(spec.Swagger)
|
||||||
if err := json.Unmarshal(data, swspec); err != nil {
|
if err := json.Unmarshal(raw, swspec); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
origsqspec := new(spec.Swagger)
|
origsqspec := new(spec.Swagger)
|
||||||
if err := json.Unmarshal(data, origsqspec); err != nil {
|
if err := json.Unmarshal(raw, origsqspec); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d := &Document{
|
d := &Document{
|
||||||
Analyzer: analysis.New(swspec),
|
Analyzer: analysis.New(swspec),
|
||||||
schema: swag20Schema,
|
schema: spec.MustLoadSwagger20Schema(),
|
||||||
spec: swspec,
|
spec: swspec,
|
||||||
raw: data,
|
raw: raw,
|
||||||
origSpec: origsqspec,
|
origSpec: origsqspec,
|
||||||
}
|
}
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expanded expands the ref fields in the spec document and returns a new spec document
|
// Expanded expands the ref fields in the spec document and returns a new spec document
|
||||||
func (d *Document) Expanded() (*Document, error) {
|
func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) {
|
||||||
swspec := new(spec.Swagger)
|
swspec := new(spec.Swagger)
|
||||||
if err := json.Unmarshal(d.raw, swspec); err != nil {
|
if err := json.Unmarshal(d.raw, swspec); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := spec.ExpandSpec(swspec); err != nil {
|
|
||||||
|
var expandOptions *spec.ExpandOptions
|
||||||
|
if len(options) > 0 {
|
||||||
|
expandOptions = options[1]
|
||||||
|
} else {
|
||||||
|
expandOptions = &spec.ExpandOptions{
|
||||||
|
RelativeBase: filepath.Dir(d.specFilePath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := spec.ExpandSpec(swspec, expandOptions); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dd := &Document{
|
dd := &Document{
|
||||||
Analyzer: analysis.New(swspec),
|
Analyzer: analysis.New(swspec),
|
||||||
spec: swspec,
|
spec: swspec,
|
||||||
schema: swag20Schema,
|
schema: spec.MustLoadSwagger20Schema(),
|
||||||
raw: d.raw,
|
raw: d.raw,
|
||||||
origSpec: d.origSpec,
|
origSpec: d.origSpec,
|
||||||
}
|
}
|
||||||
|
@ -201,3 +257,8 @@ func (d *Document) Pristine() *Document {
|
||||||
dd, _ := Analyzed(d.Raw(), d.Version())
|
dd, _ := Analyzed(d.Raw(), d.Version())
|
||||||
return dd
|
return dd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpecFilePath returns the file path of the spec if one is defined
|
||||||
|
func (d *Document) SpecFilePath() string {
|
||||||
|
return d.specFilePath
|
||||||
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.Epk8dDFH8U1RPYIPDpajZO26L5zFJ1wnQNGWxVHHo5cXrWF148kENoZzh35FT9cAxxPS_4CeVVpf59EgvCc8bem1puuj0gBZptn-lYa7iXZdI-ESN2Te7nF5VbZfwbnI62nEikYGyxz-ozL_IFuMl-qWek4iLerF8Z_xh0MZOJ_w8Nog7qb2WQov72d997TJv5ZKjWcRYPbnsAy1q60-Cqxq3a6enhcSPXqpK46nYSXGKfHvognWBJ_pxwkEqIBPN6hE4EfNtJjMf2LFKEdYy02nbHz78d-2YZ8wIUSJ-IWIwn3GTzObdGqRed20Qf3JtWTsOespmexDrLSeo3HW6A.7XaHW-Y1jjRAWt_W.S1Adut62RLOYZc-lN02M0MGczEucch3zIr4J1UPBPnZooWzntiE5UaUz0UdhjHVszQE5hTfG-yocKD1rDQGER6qrLtnJVrCm9J3n4lHglM-xOz1eZln1XKrWcAgZnAKaKSzuAa5scPG4iTHW6RwbWi_PWm04tBJ1yazdjaVo3uvuhflwvU9if7uMPMtscrDesbBVvpG89xmeudiFjX-wjsV5oGBIjz6ukEBAMKzNDMqikNoG4SnGenpxUpjUjMkDXxiC3BC8oL2_myeIfFeEOF066DqEN3CLkqBVO25zdpWAF4Ou2jKv--mgGEb_E1aMgiSoAVBnybene0TKn2IJ8rtkyRdmWlLIRKZdDT3v775C1FPK6-tYzS7NVg9nnuvpta5PhzYNkqI1Ie74Sl0I-RFClhsdx9dLDhoFEKCx2etC4UDX9jhj2u0Y2MrL76dRGE9kEV1hL1fh6HMvS4ZAAWw3Qce4skCjcL-2YyIOHzKjgLGkZsR5cTUQwCJyacVkdHUOUKFdDGZaUzWkFyeZ1oyrlG2d52svaplpU5-vCOVbWkqUN9rOALGPTC51Ur0L7DFx29aDImhaxZqTe2t9mcdqY7VLcO3JgUiD3JKsEet7s2EDeN44MqITv9KBS8wqJW4.sRv4ov0wB0IxTHw90kJy-A
|
|
|
@ -1,35 +0,0 @@
|
||||||
clone:
|
|
||||||
path: github.com/go-openapi/spec
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
GO_VERSION:
|
|
||||||
- "1.6"
|
|
||||||
|
|
||||||
build:
|
|
||||||
integration:
|
|
||||||
image: golang:$$GO_VERSION
|
|
||||||
pull: true
|
|
||||||
commands:
|
|
||||||
- go get -u github.com/stretchr/testify/assert
|
|
||||||
- go get -u gopkg.in/yaml.v2
|
|
||||||
- go get -u github.com/go-openapi/swag
|
|
||||||
- go get -u github.com/go-openapi/jsonpointer
|
|
||||||
- go get -u github.com/go-openapi/jsonreference
|
|
||||||
- go test -race
|
|
||||||
- go test -v -cover -coverprofile=coverage.out -covermode=count ./...
|
|
||||||
|
|
||||||
notify:
|
|
||||||
slack:
|
|
||||||
channel: bots
|
|
||||||
webhook_url: $$SLACK_URL
|
|
||||||
username: drone
|
|
||||||
|
|
||||||
publish:
|
|
||||||
coverage:
|
|
||||||
server: https://coverage.vmware.run
|
|
||||||
token: $$GITHUB_TOKEN
|
|
||||||
# threshold: 70
|
|
||||||
# must_increase: true
|
|
||||||
when:
|
|
||||||
matrix:
|
|
||||||
GO_VERSION: "1.6"
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
# Set default charset
|
||||||
|
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# Tab indentation (no size specified)
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
# Matches the exact files either package.json or .travis.yml
|
||||||
|
[{package.json,.travis.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
|
@ -1,13 +0,0 @@
|
||||||
approve_by_comment: true
|
|
||||||
approve_regex: '^(:shipit:|:\+1:|\+1|LGTM|lgtm|Approved)'
|
|
||||||
reject_regex: ^[Rr]ejected
|
|
||||||
reset_on_push: false
|
|
||||||
reviewers:
|
|
||||||
members:
|
|
||||||
- casualjim
|
|
||||||
- chancez
|
|
||||||
- frapposelli
|
|
||||||
- vburenin
|
|
||||||
- pytlesk4
|
|
||||||
name: pullapprove
|
|
||||||
required: 1
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.7
|
||||||
|
install:
|
||||||
|
- go get -u github.com/stretchr/testify
|
||||||
|
- go get -u github.com/go-openapi/swag
|
||||||
|
- go get -u gopkg.in/yaml.v2
|
||||||
|
- go get -u github.com/go-openapi/jsonpointer
|
||||||
|
- go get -u github.com/go-openapi/jsonreference
|
||||||
|
script:
|
||||||
|
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
notifications:
|
||||||
|
slack:
|
||||||
|
secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E=
|
|
@ -1,5 +1,5 @@
|
||||||
# OAI object model [![Build Status](https://ci.vmware.run/api/badges/go-openapi/spec/status.svg)](https://ci.vmware.run/go-openapi/spec) [![Coverage](https://coverage.vmware.run/badges/go-openapi/spec/coverage.svg)](https://coverage.vmware.run/go-openapi/spec) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
|
# OAI object model [![Build Status](https://travis-ci.org/go-openapi/spec.svg?branch=master)](https://travis-ci.org/go-openapi/spec) [![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
|
||||||
|
|
||||||
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/spec?status.svg)](http://godoc.org/github.com/go-openapi/spec)
|
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/spec?status.svg)](http://godoc.org/github.com/go-openapi/spec)
|
||||||
|
|
||||||
The object model for OpenAPI specification documents
|
The object model for OpenAPI specification documents
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -17,7 +17,10 @@ package spec
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -26,6 +29,18 @@ import (
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Debug enables logging when SWAGGER_DEBUG env var is not empty
|
||||||
|
Debug = os.Getenv("SWAGGER_DEBUG") != ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExpandOptions provides options for expand.
|
||||||
|
type ExpandOptions struct {
|
||||||
|
RelativeBase string
|
||||||
|
SkipSchemas bool
|
||||||
|
ContinueOnError bool
|
||||||
|
}
|
||||||
|
|
||||||
// ResolutionCache a cache for resolving urls
|
// ResolutionCache a cache for resolving urls
|
||||||
type ResolutionCache interface {
|
type ResolutionCache interface {
|
||||||
Get(string) (interface{}, bool)
|
Get(string) (interface{}, bool)
|
||||||
|
@ -37,7 +52,11 @@ type simpleCache struct {
|
||||||
store map[string]interface{}
|
store map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var resCache = initResolutionCache()
|
var resCache ResolutionCache
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
resCache = initResolutionCache()
|
||||||
|
}
|
||||||
|
|
||||||
func initResolutionCache() ResolutionCache {
|
func initResolutionCache() ResolutionCache {
|
||||||
return &simpleCache{store: map[string]interface{}{
|
return &simpleCache{store: map[string]interface{}{
|
||||||
|
@ -47,8 +66,11 @@ func initResolutionCache() ResolutionCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *simpleCache) Get(uri string) (interface{}, bool) {
|
func (s *simpleCache) Get(uri string) (interface{}, bool) {
|
||||||
|
debugLog("getting %q from resolution cache", uri)
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
v, ok := s.store[uri]
|
v, ok := s.store[uri]
|
||||||
|
debugLog("got %q from resolution cache: %t", uri, ok)
|
||||||
|
|
||||||
s.lock.Unlock()
|
s.lock.Unlock()
|
||||||
return v, ok
|
return v, ok
|
||||||
}
|
}
|
||||||
|
@ -59,9 +81,9 @@ func (s *simpleCache) Set(uri string, data interface{}) {
|
||||||
s.lock.Unlock()
|
s.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveRef resolves a reference against a context root
|
// ResolveRefWithBase resolves a reference against a context root with preservation of base path
|
||||||
func ResolveRef(root interface{}, ref *Ref) (*Schema, error) {
|
func ResolveRefWithBase(root interface{}, ref *Ref, opts *ExpandOptions) (*Schema, error) {
|
||||||
resolver, err := defaultSchemaLoader(root, nil, nil)
|
resolver, err := defaultSchemaLoader(root, nil, opts, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -73,9 +95,19 @@ func ResolveRef(root interface{}, ref *Ref) (*Schema, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolveRef resolves a reference against a context root
|
||||||
|
func ResolveRef(root interface{}, ref *Ref) (*Schema, error) {
|
||||||
|
return ResolveRefWithBase(root, ref, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// ResolveParameter resolves a paramter reference against a context root
|
// ResolveParameter resolves a paramter reference against a context root
|
||||||
func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) {
|
func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) {
|
||||||
resolver, err := defaultSchemaLoader(root, nil, nil)
|
return ResolveParameterWithBase(root, ref, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveParameterWithBase resolves a paramter reference against a context root and base path
|
||||||
|
func ResolveParameterWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Parameter, error) {
|
||||||
|
resolver, err := defaultSchemaLoader(root, nil, opts, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -89,7 +121,12 @@ func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) {
|
||||||
|
|
||||||
// ResolveResponse resolves response a reference against a context root
|
// ResolveResponse resolves response a reference against a context root
|
||||||
func ResolveResponse(root interface{}, ref Ref) (*Response, error) {
|
func ResolveResponse(root interface{}, ref Ref) (*Response, error) {
|
||||||
resolver, err := defaultSchemaLoader(root, nil, nil)
|
return ResolveResponseWithBase(root, ref, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveResponseWithBase resolves response a reference against a context root and base path
|
||||||
|
func ResolveResponseWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Response, error) {
|
||||||
|
resolver, err := defaultSchemaLoader(root, nil, opts, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -101,23 +138,72 @@ func ResolveResponse(root interface{}, ref Ref) (*Response, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolveItems resolves header and parameter items reference against a context root and base path
|
||||||
|
func ResolveItems(root interface{}, ref Ref, opts *ExpandOptions) (*Items, error) {
|
||||||
|
resolver, err := defaultSchemaLoader(root, nil, opts, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(Items)
|
||||||
|
if err := resolver.Resolve(&ref, result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolvePathItem resolves response a path item against a context root and base path
|
||||||
|
func ResolvePathItem(root interface{}, ref Ref, opts *ExpandOptions) (*PathItem, error) {
|
||||||
|
resolver, err := defaultSchemaLoader(root, nil, opts, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(PathItem)
|
||||||
|
if err := resolver.Resolve(&ref, result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
type schemaLoader struct {
|
type schemaLoader struct {
|
||||||
loadingRef *Ref
|
loadingRef *Ref
|
||||||
startingRef *Ref
|
startingRef *Ref
|
||||||
currentRef *Ref
|
currentRef *Ref
|
||||||
root interface{}
|
root interface{}
|
||||||
|
options *ExpandOptions
|
||||||
cache ResolutionCache
|
cache ResolutionCache
|
||||||
loadDoc func(string) (json.RawMessage, error)
|
loadDoc func(string) (json.RawMessage, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var idPtr, _ = jsonpointer.New("/id")
|
var idPtr, _ = jsonpointer.New("/id")
|
||||||
var schemaPtr, _ = jsonpointer.New("/$schema")
|
|
||||||
var refPtr, _ = jsonpointer.New("/$ref")
|
var refPtr, _ = jsonpointer.New("/$ref")
|
||||||
|
|
||||||
func defaultSchemaLoader(root interface{}, ref *Ref, cache ResolutionCache) (*schemaLoader, error) {
|
// PathLoader function to use when loading remote refs
|
||||||
|
var PathLoader func(string) (json.RawMessage, error)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
PathLoader = func(path string) (json.RawMessage, error) {
|
||||||
|
data, err := swag.LoadFromFileOrHTTP(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.RawMessage(data), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultSchemaLoader(
|
||||||
|
root interface{},
|
||||||
|
ref *Ref,
|
||||||
|
expandOptions *ExpandOptions,
|
||||||
|
cache ResolutionCache) (*schemaLoader, error) {
|
||||||
|
|
||||||
if cache == nil {
|
if cache == nil {
|
||||||
cache = resCache
|
cache = resCache
|
||||||
}
|
}
|
||||||
|
if expandOptions == nil {
|
||||||
|
expandOptions = &ExpandOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
var ptr *jsonpointer.Pointer
|
var ptr *jsonpointer.Pointer
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
|
@ -127,18 +213,16 @@ func defaultSchemaLoader(root interface{}, ref *Ref, cache ResolutionCache) (*sc
|
||||||
currentRef := nextRef(root, ref, ptr)
|
currentRef := nextRef(root, ref, ptr)
|
||||||
|
|
||||||
return &schemaLoader{
|
return &schemaLoader{
|
||||||
root: root,
|
|
||||||
loadingRef: ref,
|
loadingRef: ref,
|
||||||
startingRef: ref,
|
startingRef: ref,
|
||||||
|
currentRef: currentRef,
|
||||||
|
root: root,
|
||||||
|
options: expandOptions,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
loadDoc: func(path string) (json.RawMessage, error) {
|
loadDoc: func(path string) (json.RawMessage, error) {
|
||||||
data, err := swag.LoadFromFileOrHTTP(path)
|
debugLog("fetching document at %q", path)
|
||||||
if err != nil {
|
return PathLoader(path)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return json.RawMessage(data), nil
|
|
||||||
},
|
},
|
||||||
currentRef: currentRef,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +243,7 @@ func nextRef(startingNode interface{}, startingRef *Ref, ptr *jsonpointer.Pointe
|
||||||
if startingRef == nil {
|
if startingRef == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ptr == nil {
|
if ptr == nil {
|
||||||
return startingRef
|
return startingRef
|
||||||
}
|
}
|
||||||
|
@ -184,32 +269,111 @@ func nextRef(startingNode interface{}, startingRef *Ref, ptr *jsonpointer.Pointe
|
||||||
|
|
||||||
refRef, _, _ := refPtr.Get(node)
|
refRef, _, _ := refPtr.Get(node)
|
||||||
if refRef != nil {
|
if refRef != nil {
|
||||||
rf, _ := NewRef(refRef.(string))
|
var rf Ref
|
||||||
|
switch value := refRef.(type) {
|
||||||
|
case string:
|
||||||
|
rf, _ = NewRef(value)
|
||||||
|
}
|
||||||
nw, err := ret.Inherits(rf)
|
nw, err := ret.Inherits(rf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
nwURL := nw.GetURL()
|
||||||
|
if nwURL.Scheme == "file" || (nwURL.Scheme == "" && nwURL.Host == "") {
|
||||||
|
nwpt := filepath.ToSlash(nwURL.Path)
|
||||||
|
if filepath.IsAbs(nwpt) {
|
||||||
|
_, err := os.Stat(nwpt)
|
||||||
|
if err != nil {
|
||||||
|
nwURL.Path = filepath.Join(".", nwpt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = nw
|
ret = nw
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func debugLog(msg string, args ...interface{}) {
|
||||||
|
if Debug {
|
||||||
|
log.Printf(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeFileRef(ref *Ref, relativeBase string) *Ref {
|
||||||
|
refURL := ref.GetURL()
|
||||||
|
debugLog("normalizing %s against %s (%s)", ref.String(), relativeBase, refURL.String())
|
||||||
|
if strings.HasPrefix(refURL.String(), "#") {
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if refURL.Scheme == "file" || (refURL.Scheme == "" && refURL.Host == "") {
|
||||||
|
filePath := refURL.Path
|
||||||
|
debugLog("normalizing file path: %s", filePath)
|
||||||
|
|
||||||
|
if !filepath.IsAbs(filepath.FromSlash(filePath)) && len(relativeBase) != 0 {
|
||||||
|
debugLog("joining %s with %s", relativeBase, filePath)
|
||||||
|
if fi, err := os.Stat(filepath.FromSlash(relativeBase)); err == nil {
|
||||||
|
if !fi.IsDir() {
|
||||||
|
relativeBase = filepath.Dir(filepath.FromSlash(relativeBase))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filePath = filepath.Join(filepath.FromSlash(relativeBase), filepath.FromSlash(filePath))
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(filepath.FromSlash(filePath)) {
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err == nil {
|
||||||
|
debugLog("joining cwd %s with %s", pwd, filePath)
|
||||||
|
filePath = filepath.Join(pwd, filepath.FromSlash(filePath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugLog("cleaning %s", filePath)
|
||||||
|
filePath = filepath.Clean(filepath.FromSlash(filePath))
|
||||||
|
_, err := os.Stat(filepath.FromSlash(filePath))
|
||||||
|
if err == nil {
|
||||||
|
debugLog("rewriting url %s to scheme \"\" path %s", refURL.String(), filePath)
|
||||||
|
slp := filepath.FromSlash(filePath)
|
||||||
|
if filepath.IsAbs(slp) && filepath.Separator == '\\' && len(slp) > 1 && slp[1] == ':' && ('a' <= slp[0] && slp[0] <= 'z' || 'A' <= slp[0] && slp[0] <= 'Z') {
|
||||||
|
slp = slp[2:]
|
||||||
|
}
|
||||||
|
refURL.Scheme = ""
|
||||||
|
refURL.Path = filepath.ToSlash(slp)
|
||||||
|
debugLog("new url with joined filepath: %s", refURL.String())
|
||||||
|
*ref = MustCreateRef(refURL.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugLog("refurl: %s", ref.GetURL().String())
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
func (r *schemaLoader) resolveRef(currentRef, ref *Ref, node, target interface{}) error {
|
func (r *schemaLoader) resolveRef(currentRef, ref *Ref, node, target interface{}) error {
|
||||||
|
|
||||||
tgt := reflect.ValueOf(target)
|
tgt := reflect.ValueOf(target)
|
||||||
if tgt.Kind() != reflect.Ptr {
|
if tgt.Kind() != reflect.Ptr {
|
||||||
return fmt.Errorf("resolve ref: target needs to be a pointer")
|
return fmt.Errorf("resolve ref: target needs to be a pointer")
|
||||||
}
|
}
|
||||||
|
|
||||||
oldRef := currentRef
|
oldRef := currentRef
|
||||||
|
|
||||||
if currentRef != nil {
|
if currentRef != nil {
|
||||||
|
debugLog("resolve ref current %s new %s", currentRef.String(), ref.String())
|
||||||
|
nextRef := nextRef(node, ref, currentRef.GetPointer())
|
||||||
|
if nextRef == nil || nextRef.GetURL() == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
currentRef, err = currentRef.Inherits(*nextRef(node, ref, currentRef.GetPointer()))
|
currentRef, err = currentRef.Inherits(*nextRef)
|
||||||
|
debugLog("resolved ref current %s", currentRef.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentRef == nil {
|
if currentRef == nil {
|
||||||
currentRef = ref
|
currentRef = ref
|
||||||
}
|
}
|
||||||
|
@ -245,42 +409,71 @@ func (r *schemaLoader) resolveRef(currentRef, ref *Ref, node, target interface{}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if refURL.Scheme != "" && refURL.Host != "" {
|
relativeBase := ""
|
||||||
// most definitely take the red pill
|
if r.options != nil && r.options.RelativeBase != "" {
|
||||||
data, _, _, err := r.load(refURL)
|
relativeBase = r.options.RelativeBase
|
||||||
|
}
|
||||||
|
normalizeFileRef(currentRef, relativeBase)
|
||||||
|
debugLog("current ref normalized file: %s", currentRef.String())
|
||||||
|
normalizeFileRef(ref, relativeBase)
|
||||||
|
debugLog("ref normalized file: %s", currentRef.String())
|
||||||
|
|
||||||
|
data, _, _, err := r.load(currentRef.GetURL())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((oldRef == nil && currentRef != nil) ||
|
||||||
|
(oldRef != nil && currentRef == nil) ||
|
||||||
|
oldRef.String() != currentRef.String()) &&
|
||||||
|
((oldRef == nil && ref != nil) ||
|
||||||
|
(oldRef != nil && ref == nil) ||
|
||||||
|
(oldRef.String() != ref.String())) {
|
||||||
|
|
||||||
|
return r.resolveRef(currentRef, ref, data, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
var res interface{}
|
||||||
|
if currentRef.String() != "" {
|
||||||
|
res, _, err = currentRef.GetPointer().Get(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
if strings.HasPrefix(ref.String(), "#") {
|
||||||
}
|
if r.loadingRef != nil {
|
||||||
|
rr, er := r.loadingRef.Inherits(*ref)
|
||||||
|
if er != nil {
|
||||||
|
return er
|
||||||
|
}
|
||||||
|
refURL = rr.GetURL()
|
||||||
|
|
||||||
if ((oldRef == nil && currentRef != nil) ||
|
data, _, _, err = r.load(refURL)
|
||||||
(oldRef != nil && currentRef == nil) ||
|
if err != nil {
|
||||||
oldRef.String() != currentRef.String()) &&
|
return err
|
||||||
((oldRef == nil && ref != nil) ||
|
}
|
||||||
(oldRef != nil && ref == nil) ||
|
} else {
|
||||||
(oldRef.String() != ref.String())) {
|
data = r.root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return r.resolveRef(currentRef, ref, data, target)
|
res, _, err = ref.GetPointer().Get(data)
|
||||||
}
|
|
||||||
|
|
||||||
var res interface{}
|
|
||||||
if currentRef.String() != "" {
|
|
||||||
res, _, err = currentRef.GetPointer().Get(data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
res = data
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if err := swag.DynamicJSONToStruct(res, target); err != nil {
|
res = data
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := swag.DynamicJSONToStruct(res, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.currentRef = currentRef
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error) {
|
func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error) {
|
||||||
|
debugLog("loading schema from url: %s", refURL)
|
||||||
toFetch := *refURL
|
toFetch := *refURL
|
||||||
toFetch.Fragment = ""
|
toFetch.Fragment = ""
|
||||||
|
|
||||||
|
@ -299,44 +492,51 @@ func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error)
|
||||||
|
|
||||||
return data, toFetch, fromCache, nil
|
return data, toFetch, fromCache, nil
|
||||||
}
|
}
|
||||||
func (r *schemaLoader) Resolve(ref *Ref, target interface{}) error {
|
|
||||||
if err := r.resolveRef(r.currentRef, ref, r.root, target); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
func (r *schemaLoader) Resolve(ref *Ref, target interface{}) error {
|
||||||
|
return r.resolveRef(r.currentRef, ref, r.root, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
type specExpander struct {
|
func (r *schemaLoader) reset() {
|
||||||
spec *Swagger
|
ref := r.startingRef
|
||||||
resolver *schemaLoader
|
|
||||||
|
var ptr *jsonpointer.Pointer
|
||||||
|
if ref != nil {
|
||||||
|
ptr = ref.GetPointer()
|
||||||
|
}
|
||||||
|
|
||||||
|
r.currentRef = nextRef(r.root, ref, ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpandSpec expands the references in a swagger spec
|
// ExpandSpec expands the references in a swagger spec
|
||||||
func ExpandSpec(spec *Swagger) error {
|
func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
|
||||||
resolver, err := defaultSchemaLoader(spec, nil, nil)
|
resolver, err := defaultSchemaLoader(spec, nil, options, nil)
|
||||||
if err != nil {
|
// Just in case this ever returns an error.
|
||||||
|
if shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, defintition := range spec.Definitions {
|
if options == nil || !options.SkipSchemas {
|
||||||
var def *Schema
|
for key, definition := range spec.Definitions {
|
||||||
var err error
|
var def *Schema
|
||||||
if def, err = expandSchema(defintition, []string{"#/definitions/" + key}, resolver); err != nil {
|
var err error
|
||||||
return err
|
if def, err = expandSchema(definition, []string{"#/definitions/" + key}, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resolver.reset()
|
||||||
|
spec.Definitions[key] = *def
|
||||||
}
|
}
|
||||||
spec.Definitions[key] = *def
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, parameter := range spec.Parameters {
|
for key, parameter := range spec.Parameters {
|
||||||
if err := expandParameter(¶meter, resolver); err != nil {
|
if err := expandParameter(¶meter, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
spec.Parameters[key] = parameter
|
spec.Parameters[key] = parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, response := range spec.Responses {
|
for key, response := range spec.Responses {
|
||||||
if err := expandResponse(&response, resolver); err != nil {
|
if err := expandResponse(&response, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
spec.Responses[key] = response
|
spec.Responses[key] = response
|
||||||
|
@ -344,7 +544,7 @@ func ExpandSpec(spec *Swagger) error {
|
||||||
|
|
||||||
if spec.Paths != nil {
|
if spec.Paths != nil {
|
||||||
for key, path := range spec.Paths.Paths {
|
for key, path := range spec.Paths.Paths {
|
||||||
if err := expandPathItem(&path, resolver); err != nil {
|
if err := expandPathItem(&path, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
spec.Paths.Paths[key] = path
|
spec.Paths.Paths[key] = path
|
||||||
|
@ -354,9 +554,25 @@ func ExpandSpec(spec *Swagger) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldStopOnError(err error, opts *ExpandOptions) bool {
|
||||||
|
if err != nil && !opts.ContinueOnError {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ExpandSchema expands the refs in the schema object
|
// ExpandSchema expands the refs in the schema object
|
||||||
func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error {
|
func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error {
|
||||||
|
return ExpandSchemaWithBasePath(schema, root, cache, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandSchemaWithBasePath expands the refs in the schema object, base path configured through expand options
|
||||||
|
func ExpandSchemaWithBasePath(schema *Schema, root interface{}, cache ResolutionCache, opts *ExpandOptions) error {
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -367,18 +583,17 @@ func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error
|
||||||
nrr, _ := NewRef(schema.ID)
|
nrr, _ := NewRef(schema.ID)
|
||||||
var rrr *Ref
|
var rrr *Ref
|
||||||
if nrr.String() != "" {
|
if nrr.String() != "" {
|
||||||
switch root.(type) {
|
switch rt := root.(type) {
|
||||||
case *Schema:
|
case *Schema:
|
||||||
rid, _ := NewRef(root.(*Schema).ID)
|
rid, _ := NewRef(rt.ID)
|
||||||
rrr, _ = rid.Inherits(nrr)
|
rrr, _ = rid.Inherits(nrr)
|
||||||
case *Swagger:
|
case *Swagger:
|
||||||
rid, _ := NewRef(root.(*Swagger).ID)
|
rid, _ := NewRef(rt.ID)
|
||||||
rrr, _ = rid.Inherits(nrr)
|
rrr, _ = rid.Inherits(nrr)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolver, err := defaultSchemaLoader(root, rrr, cache)
|
resolver, err := defaultSchemaLoader(root, rrr, opts, cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -389,7 +604,7 @@ func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error
|
||||||
}
|
}
|
||||||
var s *Schema
|
var s *Schema
|
||||||
if s, err = expandSchema(*schema, refs, resolver); err != nil {
|
if s, err = expandSchema(*schema, refs, resolver); err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
*schema = *s
|
*schema = *s
|
||||||
return nil
|
return nil
|
||||||
|
@ -400,7 +615,15 @@ func expandItems(target Schema, parentRefs []string, resolver *schemaLoader) (*S
|
||||||
if target.Items.Schema != nil {
|
if target.Items.Schema != nil {
|
||||||
t, err := expandSchema(*target.Items.Schema, parentRefs, resolver)
|
t, err := expandSchema(*target.Items.Schema, parentRefs, resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
if target.Items.Schema.ID == "" {
|
||||||
|
target.Items.Schema.ID = target.ID
|
||||||
|
if err != nil {
|
||||||
|
t, err = expandSchema(*target.Items.Schema, parentRefs, resolver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*target.Items.Schema = *t
|
*target.Items.Schema = *t
|
||||||
}
|
}
|
||||||
|
@ -415,137 +638,173 @@ func expandItems(target Schema, parentRefs []string, resolver *schemaLoader) (*S
|
||||||
return &target, nil
|
return &target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader) (schema *Schema, err error) {
|
func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader) (*Schema, error) {
|
||||||
defer func() {
|
|
||||||
schema = &target
|
|
||||||
}()
|
|
||||||
if target.Ref.String() == "" && target.Ref.IsRoot() {
|
if target.Ref.String() == "" && target.Ref.IsRoot() {
|
||||||
target = *resolver.root.(*Schema)
|
debugLog("skipping expand schema for no ref and root: %v", resolver.root)
|
||||||
return
|
|
||||||
|
return resolver.root.(*Schema), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// t is the new expanded schema
|
// t is the new expanded schema
|
||||||
var t *Schema
|
var t *Schema
|
||||||
|
|
||||||
for target.Ref.String() != "" {
|
for target.Ref.String() != "" {
|
||||||
// var newTarget Schema
|
if swag.ContainsStringsCI(parentRefs, target.Ref.String()) {
|
||||||
pRefs := strings.Join(parentRefs, ",")
|
return &target, nil
|
||||||
pRefs += ","
|
|
||||||
if strings.Contains(pRefs, target.Ref.String()+",") {
|
|
||||||
err = nil
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = resolver.Resolve(&target.Ref, &t); err != nil {
|
if err := resolver.Resolve(&target.Ref, &t); shouldStopOnError(err, resolver.options) {
|
||||||
return
|
return &target, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if swag.ContainsStringsCI(parentRefs, target.Ref.String()) {
|
||||||
|
debugLog("ref already exists in parent")
|
||||||
|
return &target, nil
|
||||||
}
|
}
|
||||||
parentRefs = append(parentRefs, target.Ref.String())
|
parentRefs = append(parentRefs, target.Ref.String())
|
||||||
|
if t != nil {
|
||||||
|
target = *t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := expandItems(target, parentRefs, resolver)
|
||||||
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
target = *t
|
target = *t
|
||||||
}
|
}
|
||||||
|
|
||||||
if t, err = expandItems(target, parentRefs, resolver); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
target = *t
|
|
||||||
|
|
||||||
for i := range target.AllOf {
|
for i := range target.AllOf {
|
||||||
if t, err = expandSchema(target.AllOf[i], parentRefs, resolver); err != nil {
|
t, err := expandSchema(target.AllOf[i], parentRefs, resolver)
|
||||||
return
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
target.AllOf[i] = *t
|
||||||
}
|
}
|
||||||
target.AllOf[i] = *t
|
|
||||||
}
|
}
|
||||||
for i := range target.AnyOf {
|
for i := range target.AnyOf {
|
||||||
if t, err = expandSchema(target.AnyOf[i], parentRefs, resolver); err != nil {
|
t, err := expandSchema(target.AnyOf[i], parentRefs, resolver)
|
||||||
return
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
}
|
}
|
||||||
target.AnyOf[i] = *t
|
target.AnyOf[i] = *t
|
||||||
}
|
}
|
||||||
for i := range target.OneOf {
|
for i := range target.OneOf {
|
||||||
if t, err = expandSchema(target.OneOf[i], parentRefs, resolver); err != nil {
|
t, err := expandSchema(target.OneOf[i], parentRefs, resolver)
|
||||||
return
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
target.OneOf[i] = *t
|
||||||
}
|
}
|
||||||
target.OneOf[i] = *t
|
|
||||||
}
|
}
|
||||||
if target.Not != nil {
|
if target.Not != nil {
|
||||||
if t, err = expandSchema(*target.Not, parentRefs, resolver); err != nil {
|
t, err := expandSchema(*target.Not, parentRefs, resolver)
|
||||||
return
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
*target.Not = *t
|
||||||
}
|
}
|
||||||
*target.Not = *t
|
|
||||||
}
|
}
|
||||||
for k, _ := range target.Properties {
|
for k := range target.Properties {
|
||||||
if t, err = expandSchema(target.Properties[k], parentRefs, resolver); err != nil {
|
t, err := expandSchema(target.Properties[k], parentRefs, resolver)
|
||||||
return
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
target.Properties[k] = *t
|
||||||
}
|
}
|
||||||
target.Properties[k] = *t
|
|
||||||
}
|
}
|
||||||
if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil {
|
if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil {
|
||||||
if t, err = expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver); err != nil {
|
t, err := expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver)
|
||||||
return
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
}
|
}
|
||||||
*target.AdditionalProperties.Schema = *t
|
if t != nil {
|
||||||
}
|
*target.AdditionalProperties.Schema = *t
|
||||||
for k, _ := range target.PatternProperties {
|
|
||||||
if t, err = expandSchema(target.PatternProperties[k], parentRefs, resolver); err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
target.PatternProperties[k] = *t
|
|
||||||
}
|
}
|
||||||
for k, _ := range target.Dependencies {
|
for k := range target.PatternProperties {
|
||||||
|
t, err := expandSchema(target.PatternProperties[k], parentRefs, resolver)
|
||||||
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
target.PatternProperties[k] = *t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k := range target.Dependencies {
|
||||||
if target.Dependencies[k].Schema != nil {
|
if target.Dependencies[k].Schema != nil {
|
||||||
if t, err = expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver); err != nil {
|
t, err := expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver)
|
||||||
return
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
*target.Dependencies[k].Schema = *t
|
||||||
}
|
}
|
||||||
*target.Dependencies[k].Schema = *t
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil {
|
if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil {
|
||||||
if t, err = expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver); err != nil {
|
t, err := expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver)
|
||||||
return
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
}
|
}
|
||||||
*target.AdditionalItems.Schema = *t
|
if t != nil {
|
||||||
}
|
*target.AdditionalItems.Schema = *t
|
||||||
for k, _ := range target.Definitions {
|
|
||||||
if t, err = expandSchema(target.Definitions[k], parentRefs, resolver); err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
target.Definitions[k] = *t
|
|
||||||
}
|
}
|
||||||
return
|
for k := range target.Definitions {
|
||||||
|
t, err := expandSchema(target.Definitions[k], parentRefs, resolver)
|
||||||
|
if shouldStopOnError(err, resolver.options) {
|
||||||
|
return &target, err
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
target.Definitions[k] = *t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func expandPathItem(pathItem *PathItem, resolver *schemaLoader) error {
|
func expandPathItem(pathItem *PathItem, resolver *schemaLoader) error {
|
||||||
if pathItem == nil {
|
if pathItem == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if pathItem.Ref.String() != "" {
|
if pathItem.Ref.String() != "" {
|
||||||
if err := resolver.Resolve(&pathItem.Ref, &pathItem); err != nil {
|
if err := resolver.Resolve(&pathItem.Ref, &pathItem); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
resolver.reset()
|
||||||
|
pathItem.Ref = Ref{}
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx := range pathItem.Parameters {
|
for idx := range pathItem.Parameters {
|
||||||
if err := expandParameter(&(pathItem.Parameters[idx]), resolver); err != nil {
|
if err := expandParameter(&(pathItem.Parameters[idx]), resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := expandOperation(pathItem.Get, resolver); err != nil {
|
if err := expandOperation(pathItem.Get, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := expandOperation(pathItem.Head, resolver); err != nil {
|
if err := expandOperation(pathItem.Head, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := expandOperation(pathItem.Options, resolver); err != nil {
|
if err := expandOperation(pathItem.Options, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := expandOperation(pathItem.Put, resolver); err != nil {
|
if err := expandOperation(pathItem.Put, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := expandOperation(pathItem.Post, resolver); err != nil {
|
if err := expandOperation(pathItem.Post, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := expandOperation(pathItem.Patch, resolver); err != nil {
|
if err := expandOperation(pathItem.Patch, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := expandOperation(pathItem.Delete, resolver); err != nil {
|
if err := expandOperation(pathItem.Delete, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -555,8 +814,9 @@ func expandOperation(op *Operation, resolver *schemaLoader) error {
|
||||||
if op == nil {
|
if op == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, param := range op.Parameters {
|
for i, param := range op.Parameters {
|
||||||
if err := expandParameter(¶m, resolver); err != nil {
|
if err := expandParameter(¶m, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
op.Parameters[i] = param
|
op.Parameters[i] = param
|
||||||
|
@ -564,11 +824,11 @@ func expandOperation(op *Operation, resolver *schemaLoader) error {
|
||||||
|
|
||||||
if op.Responses != nil {
|
if op.Responses != nil {
|
||||||
responses := op.Responses
|
responses := op.Responses
|
||||||
if err := expandResponse(responses.Default, resolver); err != nil {
|
if err := expandResponse(responses.Default, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for code, response := range responses.StatusCodeResponses {
|
for code, response := range responses.StatusCodeResponses {
|
||||||
if err := expandResponse(&response, resolver); err != nil {
|
if err := expandResponse(&response, resolver); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
responses.StatusCodeResponses[code] = response
|
responses.StatusCodeResponses[code] = response
|
||||||
|
@ -582,22 +842,29 @@ func expandResponse(response *Response, resolver *schemaLoader) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parentRefs []string
|
||||||
|
|
||||||
if response.Ref.String() != "" {
|
if response.Ref.String() != "" {
|
||||||
if err := resolver.Resolve(&response.Ref, response); err != nil {
|
parentRefs = append(parentRefs, response.Ref.String())
|
||||||
|
if err := resolver.Resolve(&response.Ref, response); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
resolver.reset()
|
||||||
|
response.Ref = Ref{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.Schema != nil {
|
if !resolver.options.SkipSchemas && response.Schema != nil {
|
||||||
parentRefs := []string{response.Schema.Ref.String()}
|
parentRefs = append(parentRefs, response.Schema.Ref.String())
|
||||||
if err := resolver.Resolve(&response.Schema.Ref, &response.Schema); err != nil {
|
debugLog("response ref: %s", response.Schema.Ref)
|
||||||
|
if err := resolver.Resolve(&response.Schema.Ref, &response.Schema); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if s, err := expandSchema(*response.Schema, parentRefs, resolver); err != nil {
|
s, err := expandSchema(*response.Schema, parentRefs, resolver)
|
||||||
|
if shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
*response.Schema = *s
|
|
||||||
}
|
}
|
||||||
|
resolver.reset()
|
||||||
|
*response.Schema = *s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -606,21 +873,28 @@ func expandParameter(parameter *Parameter, resolver *schemaLoader) error {
|
||||||
if parameter == nil {
|
if parameter == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parentRefs []string
|
||||||
|
|
||||||
if parameter.Ref.String() != "" {
|
if parameter.Ref.String() != "" {
|
||||||
if err := resolver.Resolve(¶meter.Ref, parameter); err != nil {
|
parentRefs = append(parentRefs, parameter.Ref.String())
|
||||||
|
if err := resolver.Resolve(¶meter.Ref, parameter); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
resolver.reset()
|
||||||
|
parameter.Ref = Ref{}
|
||||||
}
|
}
|
||||||
if parameter.Schema != nil {
|
if !resolver.options.SkipSchemas && parameter.Schema != nil {
|
||||||
parentRefs := []string{parameter.Schema.Ref.String()}
|
parentRefs = append(parentRefs, parameter.Schema.Ref.String())
|
||||||
if err := resolver.Resolve(¶meter.Schema.Ref, ¶meter.Schema); err != nil {
|
if err := resolver.Resolve(¶meter.Schema.Ref, ¶meter.Schema); shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if s, err := expandSchema(*parameter.Schema, parentRefs, resolver); err != nil {
|
s, err := expandSchema(*parameter.Schema, parentRefs, resolver)
|
||||||
|
if shouldStopOnError(err, resolver.options) {
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
*parameter.Schema = *s
|
|
||||||
}
|
}
|
||||||
|
resolver.reset()
|
||||||
|
*parameter.Schema = *s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@ package spec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-openapi/jsonpointer"
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,6 +32,7 @@ type HeaderProps struct {
|
||||||
type Header struct {
|
type Header struct {
|
||||||
CommonValidations
|
CommonValidations
|
||||||
SimpleSchema
|
SimpleSchema
|
||||||
|
VendorExtensible
|
||||||
HeaderProps
|
HeaderProps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,8 +161,35 @@ func (h *Header) UnmarshalJSON(data []byte) error {
|
||||||
if err := json.Unmarshal(data, &h.SimpleSchema); err != nil {
|
if err := json.Unmarshal(data, &h.SimpleSchema); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := json.Unmarshal(data, &h.VendorExtensible); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := json.Unmarshal(data, &h.HeaderProps); err != nil {
|
if err := json.Unmarshal(data, &h.HeaderProps); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JSONLookup look up a value by the json property name
|
||||||
|
func (p Header) JSONLookup(token string) (interface{}, error) {
|
||||||
|
if ex, ok := p.Extensions[token]; ok {
|
||||||
|
return &ex, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _, err := jsonpointer.GetForToken(p.CommonValidations, token)
|
||||||
|
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r != nil {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token)
|
||||||
|
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r != nil {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
r, _, err = jsonpointer.GetForToken(p.HeaderProps, token)
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@ package spec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-openapi/jsonpointer"
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,11 +62,12 @@ type CommonValidations struct {
|
||||||
// Items a limited subset of JSON-Schema's items object.
|
// Items a limited subset of JSON-Schema's items object.
|
||||||
// It is used by parameter definitions that are not located in "body".
|
// It is used by parameter definitions that are not located in "body".
|
||||||
//
|
//
|
||||||
// For more information: http://goo.gl/8us55a#items-object-
|
// For more information: http://goo.gl/8us55a#items-object
|
||||||
type Items struct {
|
type Items struct {
|
||||||
Refable
|
Refable
|
||||||
CommonValidations
|
CommonValidations
|
||||||
SimpleSchema
|
SimpleSchema
|
||||||
|
VendorExtensible
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewItems creates a new instance of items
|
// NewItems creates a new instance of items
|
||||||
|
@ -197,3 +200,20 @@ func (i Items) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
return swag.ConcatJSON(b3, b1, b2), nil
|
return swag.ConcatJSON(b3, b1, b2), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JSONLookup look up a value by the json property name
|
||||||
|
func (p Items) JSONLookup(token string) (interface{}, error) {
|
||||||
|
if token == "$ref" {
|
||||||
|
return &p.Ref, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _, err := jsonpointer.GetForToken(p.CommonValidations, token)
|
||||||
|
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r != nil {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token)
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ package spec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-openapi/jsonpointer"
|
"github.com/go-openapi/jsonpointer"
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
|
@ -100,15 +101,16 @@ func (p Parameter) JSONLookup(token string) (interface{}, error) {
|
||||||
if token == "$ref" {
|
if token == "$ref" {
|
||||||
return &p.Ref, nil
|
return &p.Ref, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
r, _, err := jsonpointer.GetForToken(p.CommonValidations, token)
|
r, _, err := jsonpointer.GetForToken(p.CommonValidations, token)
|
||||||
if err != nil {
|
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if r != nil {
|
if r != nil {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token)
|
r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token)
|
||||||
if err != nil {
|
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if r != nil {
|
if r != nil {
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (r *Ref) RemoteURI() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValidURI returns true when the url the ref points to can be found
|
// IsValidURI returns true when the url the ref points to can be found
|
||||||
func (r *Ref) IsValidURI() bool {
|
func (r *Ref) IsValidURI(basepaths ...string) bool {
|
||||||
if r.String() == "" {
|
if r.String() == "" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -81,14 +81,18 @@ func (r *Ref) IsValidURI() bool {
|
||||||
// check for local file
|
// check for local file
|
||||||
pth := v
|
pth := v
|
||||||
if r.HasURLPathOnly {
|
if r.HasURLPathOnly {
|
||||||
p, e := filepath.Abs(pth)
|
base := "."
|
||||||
|
if len(basepaths) > 0 {
|
||||||
|
base = filepath.Dir(filepath.Join(basepaths...))
|
||||||
|
}
|
||||||
|
p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth)))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
pth = p
|
pth = p
|
||||||
}
|
}
|
||||||
|
|
||||||
fi, err := os.Stat(pth)
|
fi, err := os.Stat(filepath.ToSlash(pth))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -116,25 +120,18 @@ func NewRef(refURI string) (Ref, error) {
|
||||||
return Ref{Ref: ref}, nil
|
return Ref{Ref: ref}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustCreateRef creates a ref object but
|
// MustCreateRef creates a ref object but panics when refURI is invalid.
|
||||||
|
// Use the NewRef method for a version that returns an error.
|
||||||
func MustCreateRef(refURI string) Ref {
|
func MustCreateRef(refURI string) Ref {
|
||||||
return Ref{Ref: jsonreference.MustCreateRef(refURI)}
|
return Ref{Ref: jsonreference.MustCreateRef(refURI)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// // NewResolvedRef creates a resolved ref
|
|
||||||
// func NewResolvedRef(refURI string, data interface{}) Ref {
|
|
||||||
// return Ref{
|
|
||||||
// Ref: jsonreference.MustCreateRef(refURI),
|
|
||||||
// Resolved: data,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MarshalJSON marshals this ref into a JSON object
|
// MarshalJSON marshals this ref into a JSON object
|
||||||
func (r Ref) MarshalJSON() ([]byte, error) {
|
func (r Ref) MarshalJSON() ([]byte, error) {
|
||||||
str := r.String()
|
str := r.String()
|
||||||
if str == "" {
|
if str == "" {
|
||||||
if r.IsRoot() {
|
if r.IsRoot() {
|
||||||
return []byte(`{"$ref":"#"}`), nil
|
return []byte(`{"$ref":""}`), nil
|
||||||
}
|
}
|
||||||
return []byte("{}"), nil
|
return []byte("{}"), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package spec
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/go-openapi/jsonpointer"
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,6 +35,19 @@ type ResponseProps struct {
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Refable
|
Refable
|
||||||
ResponseProps
|
ResponseProps
|
||||||
|
VendorExtensible
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONLookup look up a value by the json property name
|
||||||
|
func (p Response) JSONLookup(token string) (interface{}, error) {
|
||||||
|
if ex, ok := p.Extensions[token]; ok {
|
||||||
|
return &ex, nil
|
||||||
|
}
|
||||||
|
if token == "$ref" {
|
||||||
|
return &p.Ref, nil
|
||||||
|
}
|
||||||
|
r, _, err := jsonpointer.GetForToken(p.ResponseProps, token)
|
||||||
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON hydrates this items instance with the data from JSON
|
// UnmarshalJSON hydrates this items instance with the data from JSON
|
||||||
|
@ -44,6 +58,9 @@ func (r *Response) UnmarshalJSON(data []byte) error {
|
||||||
if err := json.Unmarshal(data, &r.Refable); err != nil {
|
if err := json.Unmarshal(data, &r.Refable); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +74,11 @@ func (r Response) MarshalJSON() ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return swag.ConcatJSON(b1, b2), nil
|
b3, err := json.Marshal(r.VendorExtensible)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return swag.ConcatJSON(b1, b2, b3), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewResponse creates a new response instance
|
// NewResponse creates a new response instance
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (r Responses) JSONLookup(token string) (interface{}, error) {
|
||||||
}
|
}
|
||||||
if i, err := strconv.Atoi(token); err == nil {
|
if i, err := strconv.Atoi(token); err == nil {
|
||||||
if scr, ok := r.StatusCodeResponses[i]; ok {
|
if scr, ok := r.StatusCodeResponses[i]; ok {
|
||||||
return &scr, nil
|
return scr, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("object has no field %q", token)
|
return nil, fmt.Errorf("object has no field %q", token)
|
||||||
|
|
|
@ -201,8 +201,8 @@ func (r *SchemaURL) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
type SchemaProps struct {
|
type SchemaProps struct {
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Ref Ref `json:"-,omitempty"`
|
Ref Ref `json:"-"`
|
||||||
Schema SchemaURL `json:"-,omitempty"`
|
Schema SchemaURL `json:"-"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
Type StringOrArray `json:"type,omitempty"`
|
Type StringOrArray `json:"type,omitempty"`
|
||||||
Format string `json:"format,omitempty"`
|
Format string `json:"format,omitempty"`
|
||||||
|
@ -269,7 +269,7 @@ func (s Schema) JSONLookup(token string) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
r, _, err := jsonpointer.GetForToken(s.SchemaProps, token)
|
r, _, err := jsonpointer.GetForToken(s.SchemaProps, token)
|
||||||
if r != nil || err != nil {
|
if r != nil || (err != nil && !strings.HasPrefix(err.Error(), "object has no field")) {
|
||||||
return r, err
|
return r, err
|
||||||
}
|
}
|
||||||
r, _, err = jsonpointer.GetForToken(s.SwaggerSchemaProps, token)
|
r, _, err = jsonpointer.GetForToken(s.SwaggerSchemaProps, token)
|
||||||
|
|
|
@ -16,6 +16,8 @@ package spec
|
||||||
|
|
||||||
import "encoding/json"
|
import "encoding/json"
|
||||||
|
|
||||||
|
//go:generate curl -L --progress -o ./schemas/v2/schema.json http://swagger.io/v2/schema.json
|
||||||
|
//go:generate curl -L --progress -o ./schemas/jsonschema-draft-04.json http://json-schema.org/draft-04/schema
|
||||||
//go:generate go-bindata -pkg=spec -prefix=./schemas -ignore=.*\.md ./schemas/...
|
//go:generate go-bindata -pkg=spec -prefix=./schemas -ignore=.*\.md ./schemas/...
|
||||||
//go:generate perl -pi -e s,Json,JSON,g bindata.go
|
//go:generate perl -pi -e s,Json,JSON,g bindata.go
|
||||||
|
|
||||||
|
@ -27,10 +29,15 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
jsonSchema = MustLoadJSONSchemaDraft04()
|
jsonSchema *Schema
|
||||||
swaggerSchema = MustLoadSwagger20Schema()
|
swaggerSchema *Schema
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
jsonSchema = MustLoadJSONSchemaDraft04()
|
||||||
|
swaggerSchema = MustLoadSwagger20Schema()
|
||||||
|
}
|
||||||
|
|
||||||
// MustLoadJSONSchemaDraft04 panics when Swagger20Schema returns an error
|
// MustLoadJSONSchemaDraft04 panics when Swagger20Schema returns an error
|
||||||
func MustLoadJSONSchemaDraft04() *Schema {
|
func MustLoadJSONSchemaDraft04() *Schema {
|
||||||
d, e := JSONSchemaDraft04()
|
d, e := JSONSchemaDraft04()
|
||||||
|
|
|
@ -77,7 +77,7 @@ type SwaggerProps struct {
|
||||||
Host string `json:"host,omitempty"`
|
Host string `json:"host,omitempty"`
|
||||||
BasePath string `json:"basePath,omitempty"` // must start with a leading "/"
|
BasePath string `json:"basePath,omitempty"` // must start with a leading "/"
|
||||||
Paths *Paths `json:"paths"` // required
|
Paths *Paths `json:"paths"` // required
|
||||||
Definitions Definitions `json:"definitions"`
|
Definitions Definitions `json:"definitions,omitempty"`
|
||||||
Parameters map[string]Parameter `json:"parameters,omitempty"`
|
Parameters map[string]Parameter `json:"parameters,omitempty"`
|
||||||
Responses map[string]Response `json:"responses,omitempty"`
|
Responses map[string]Response `json:"responses,omitempty"`
|
||||||
SecurityDefinitions SecurityDefinitions `json:"securityDefinitions,omitempty"`
|
SecurityDefinitions SecurityDefinitions `json:"securityDefinitions,omitempty"`
|
||||||
|
@ -156,7 +156,7 @@ func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) {
|
||||||
if s.Schema != nil {
|
if s.Schema != nil {
|
||||||
return json.Marshal(s.Schema)
|
return json.Marshal(s.Schema)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return []byte("null"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON converts this schema object or array from a JSON structure
|
// UnmarshalJSON converts this schema object or array from a JSON structure
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.darMHuSYnLhsknrnqjdXLGcyEJ5kM13yQbLGi2UWcKXpddHvPT3dMCnP5y7e27c76R2HFnvr56BqDMI-x0zyuQtjYKzSFUGjOcEhOH3OL1Hxu-Cm2z2443BS8asXNA3sEveAGQvG68jffm7CvtEtMo57wBpggI7UHbfIdLMK47s1EDpC4xanjiS1BJe5NC_Ikf0jfa6vf18oggbjxuoqSEvSNVdNRyZwG_npFaZFJzvdtehTG7GqunWjGiqBb81qNcEdzSdIZW7A_Esv4U-nOL5gGr55E9jKhv3bX4Z9ygGcSrJ3NCgR_3zRhYKkPEAOXIqQKfL6-h82BY--cHq9uw.NHL3X-1tjb8a8zF7.eRmLvOG32e7260K8rkI-HmUGG5Gb6Hu-obKKxBqHd-vVzsKnwTVJavLWktPqlXGMsnDt7MimSysNqsWemMUEviW2p3godnvjOOXTDb-RAtQ-39rvxnZ2bN8qwUVFrdiKTZD06l60yTeLW7L1psyLj50NxklFObhkpUcK5uukxLXT1SzGM9aY6_3dzW4HU9pZGQrIH1pj1UzvWIjz7iIzE1a37DHBN-FiYSASsw01v1SSIFr34gwlGcqzGfJBonffVrM4ordm3IiVm50Zvr25DrmYTKrQpJRB-KOvYxBNYDzjCaanHDyWGUGN44FUx38azHHEVBTaiOM7xwPeyCc-xTTv8WXGnL1xrhL3M_jNuwnbAjzL9X_li7KUSeYajwhGihdMZaHLYaqxh3NNnbPfYhR6sBxu8vaT1Sc4eE84QC4dV4OaAglPvrPdWL-DC7OYQyoPU8u9ggwUQHpFUzJyD549T_Tlgn-2Cw7kTe41VonH9HkoXGANDGtQCGTqTIEeFQJ3MDDucf5VteFP8_SJPfyJYxpStFt5U1AuULV9sXmpGQL_-GGFXowd0X0bHxFeo_eu1vm-oTqQQNbKRnyt5V3n4U9jhOUGnnIBy3JOG3DA2YhVJsHdlLZ9vaDpFYcxts4.SqYfES30FqVSufGbPZ6YXA
|
|
|
@ -1,32 +0,0 @@
|
||||||
clone:
|
|
||||||
path: github.com/go-openapi/swag
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
GO_VERSION:
|
|
||||||
- "1.6"
|
|
||||||
|
|
||||||
build:
|
|
||||||
integration:
|
|
||||||
image: golang:$$GO_VERSION
|
|
||||||
pull: true
|
|
||||||
commands:
|
|
||||||
- go get -u github.com/stretchr/testify
|
|
||||||
- go get -u github.com/mailru/easyjson
|
|
||||||
- go test -race
|
|
||||||
- go test -v -cover -coverprofile=coverage.out -covermode=count ./...
|
|
||||||
|
|
||||||
notify:
|
|
||||||
slack:
|
|
||||||
channel: bots
|
|
||||||
webhook_url: $$SLACK_URL
|
|
||||||
username: drone
|
|
||||||
|
|
||||||
publish:
|
|
||||||
coverage:
|
|
||||||
server: https://coverage.vmware.run
|
|
||||||
token: $$GITHUB_TOKEN
|
|
||||||
# threshold: 70
|
|
||||||
# must_increase: true
|
|
||||||
when:
|
|
||||||
matrix:
|
|
||||||
GO_VERSION: "1.6"
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
# Set default charset
|
||||||
|
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# Tab indentation (no size specified)
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
# Matches the exact files either package.json or .travis.yml
|
||||||
|
[{package.json,.travis.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
|
@ -1,13 +0,0 @@
|
||||||
approve_by_comment: true
|
|
||||||
approve_regex: '^(:shipit:|:\+1:|\+1|LGTM|lgtm|Approved)'
|
|
||||||
reject_regex: ^[Rr]ejected
|
|
||||||
reset_on_push: false
|
|
||||||
reviewers:
|
|
||||||
members:
|
|
||||||
- casualjim
|
|
||||||
- chancez
|
|
||||||
- frapposelli
|
|
||||||
- vburenin
|
|
||||||
- pytlesk4
|
|
||||||
name: pullapprove
|
|
||||||
required: 1
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.8
|
||||||
|
install:
|
||||||
|
- go get -u github.com/stretchr/testify
|
||||||
|
- go get -u github.com/mailru/easyjson
|
||||||
|
- go get -u gopkg.in/yaml.v2
|
||||||
|
script:
|
||||||
|
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
notifications:
|
||||||
|
slack:
|
||||||
|
secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E=
|
|
@ -10,11 +10,13 @@ go_library(
|
||||||
"net.go",
|
"net.go",
|
||||||
"path.go",
|
"path.go",
|
||||||
"util.go",
|
"util.go",
|
||||||
|
"yaml.go",
|
||||||
],
|
],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//vendor/github.com/mailru/easyjson/jlexer:go_default_library",
|
"//vendor/github.com/mailru/easyjson/jlexer:go_default_library",
|
||||||
"//vendor/github.com/mailru/easyjson/jwriter:go_default_library",
|
"//vendor/github.com/mailru/easyjson/jwriter:go_default_library",
|
||||||
|
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Swag [![Build Status](https://ci.vmware.run/api/badges/go-openapi/swag/status.svg)](https://ci.vmware.run/go-openapi/swag) [![Coverage](https://coverage.vmware.run/badges/go-openapi/swag/coverage.svg)](https://coverage.vmware.run/go-openapi/swag) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
|
# Swag [![Build Status](https://travis-ci.org/go-openapi/swag.svg?branch=master)](https://travis-ci.org/go-openapi/swag) [![codecov](https://codecov.io/gh/go-openapi/swag/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/swag) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
|
||||||
|
|
||||||
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/swag?status.svg)](http://godoc.org/github.com/go-openapi/swag)
|
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/swag?status.svg)](http://godoc.org/github.com/go-openapi/swag)
|
||||||
|
|
||||||
|
|
|
@ -159,7 +159,7 @@ func FormatInt16(value int16) string {
|
||||||
|
|
||||||
// FormatInt32 turns an int32 into a string
|
// FormatInt32 turns an int32 into a string
|
||||||
func FormatInt32(value int32) string {
|
func FormatInt32(value int32) string {
|
||||||
return strconv.FormatInt(int64(value), 10)
|
return strconv.Itoa(int(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatInt64 turns an int64 into a string
|
// FormatInt64 turns an int64 into a string
|
||||||
|
|
|
@ -17,6 +17,7 @@ package swag
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -110,28 +111,40 @@ func ConcatJSON(blobs ...[]byte) []byte {
|
||||||
|
|
||||||
if len(b) < 3 { // yep empty but also the last one, so closing this thing
|
if len(b) < 3 { // yep empty but also the last one, so closing this thing
|
||||||
if i == last && a > 0 {
|
if i == last && a > 0 {
|
||||||
buf.WriteByte(closing)
|
if err := buf.WriteByte(closing); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
if a > 0 { // we need to join with a comma for everything beyond the first non-empty item
|
if a > 0 { // we need to join with a comma for everything beyond the first non-empty item
|
||||||
buf.WriteByte(comma)
|
if err := buf.WriteByte(comma); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
idx = 1 // this is not the first or the last so we want to drop the leading bracket
|
idx = 1 // this is not the first or the last so we want to drop the leading bracket
|
||||||
}
|
}
|
||||||
|
|
||||||
if i != last { // not the last one, strip brackets
|
if i != last { // not the last one, strip brackets
|
||||||
buf.Write(b[idx : len(b)-1])
|
if _, err := buf.Write(b[idx : len(b)-1]); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
} else { // last one, strip only the leading bracket
|
} else { // last one, strip only the leading bracket
|
||||||
buf.Write(b[idx:])
|
if _, err := buf.Write(b[idx:]); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
a++
|
a++
|
||||||
}
|
}
|
||||||
// somehow it ended up being empty, so provide a default value
|
// somehow it ended up being empty, so provide a default value
|
||||||
if buf.Len() == 0 {
|
if buf.Len() == 0 {
|
||||||
buf.WriteByte(opening)
|
if err := buf.WriteByte(opening); err != nil {
|
||||||
buf.WriteByte(closing)
|
log.Println(err)
|
||||||
|
}
|
||||||
|
if err := buf.WriteByte(closing); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
@ -139,15 +152,23 @@ func ConcatJSON(blobs ...[]byte) []byte {
|
||||||
// ToDynamicJSON turns an object into a properly JSON typed structure
|
// ToDynamicJSON turns an object into a properly JSON typed structure
|
||||||
func ToDynamicJSON(data interface{}) interface{} {
|
func ToDynamicJSON(data interface{}) interface{} {
|
||||||
// TODO: convert straight to a json typed map (mergo + iterate?)
|
// TODO: convert straight to a json typed map (mergo + iterate?)
|
||||||
b, _ := json.Marshal(data)
|
b, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
var res interface{}
|
var res interface{}
|
||||||
json.Unmarshal(b, &res)
|
if err := json.Unmarshal(b, &res); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromDynamicJSON turns an object into a properly JSON typed structure
|
// FromDynamicJSON turns an object into a properly JSON typed structure
|
||||||
func FromDynamicJSON(data, target interface{}) error {
|
func FromDynamicJSON(data, target interface{}) error {
|
||||||
b, _ := json.Marshal(data)
|
b, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
return json.Unmarshal(b, target)
|
return json.Unmarshal(b, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +237,8 @@ func newNameIndex(tpe reflect.Type) nameIndex {
|
||||||
|
|
||||||
// GetJSONNames gets all the json property names for a type
|
// GetJSONNames gets all the json property names for a type
|
||||||
func (n *NameProvider) GetJSONNames(subject interface{}) []string {
|
func (n *NameProvider) GetJSONNames(subject interface{}) []string {
|
||||||
|
n.lock.Lock()
|
||||||
|
defer n.lock.Unlock()
|
||||||
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
||||||
names, ok := n.index[tpe]
|
names, ok := n.index[tpe]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -237,6 +260,8 @@ func (n *NameProvider) GetJSONName(subject interface{}, name string) (string, bo
|
||||||
|
|
||||||
// GetJSONNameForType gets the json name for a go property name on a given type
|
// GetJSONNameForType gets the json name for a go property name on a given type
|
||||||
func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) {
|
func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) {
|
||||||
|
n.lock.Lock()
|
||||||
|
defer n.lock.Unlock()
|
||||||
names, ok := n.index[tpe]
|
names, ok := n.index[tpe]
|
||||||
if !ok {
|
if !ok {
|
||||||
names = n.makeNameIndex(tpe)
|
names = n.makeNameIndex(tpe)
|
||||||
|
@ -246,8 +271,6 @@ func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex {
|
func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex {
|
||||||
n.lock.Lock()
|
|
||||||
defer n.lock.Unlock()
|
|
||||||
names := newNameIndex(tpe)
|
names := newNameIndex(tpe)
|
||||||
n.index[tpe] = names
|
n.index[tpe] = names
|
||||||
return names
|
return names
|
||||||
|
@ -261,6 +284,8 @@ func (n *NameProvider) GetGoName(subject interface{}, name string) (string, bool
|
||||||
|
|
||||||
// GetGoNameForType gets the go name for a given type for a json property name
|
// GetGoNameForType gets the go name for a given type for a json property name
|
||||||
func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) {
|
func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) {
|
||||||
|
n.lock.Lock()
|
||||||
|
defer n.lock.Unlock()
|
||||||
names, ok := n.index[tpe]
|
names, ok := n.index[tpe]
|
||||||
if !ok {
|
if !ok {
|
||||||
names = n.makeNameIndex(tpe)
|
names = n.makeNameIndex(tpe)
|
||||||
|
|
|
@ -17,13 +17,25 @@ package swag
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LoadHTTPTimeout the default timeout for load requests
|
||||||
|
var LoadHTTPTimeout = 30 * time.Second
|
||||||
|
|
||||||
// LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in
|
// LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in
|
||||||
func LoadFromFileOrHTTP(path string) ([]byte, error) {
|
func LoadFromFileOrHTTP(path string) ([]byte, error) {
|
||||||
return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes)(path)
|
return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes(LoadHTTPTimeout))(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFromFileOrHTTPWithTimeout loads the bytes from a file or a remote http server based on the path passed in
|
||||||
|
// timeout arg allows for per request overriding of the request timeout
|
||||||
|
func LoadFromFileOrHTTPWithTimeout(path string, timeout time.Duration) ([]byte, error) {
|
||||||
|
return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes(timeout))(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadStrategy returns a loader function for a given path or uri
|
// LoadStrategy returns a loader function for a given path or uri
|
||||||
|
@ -31,19 +43,32 @@ func LoadStrategy(path string, local, remote func(string) ([]byte, error)) func(
|
||||||
if strings.HasPrefix(path, "http") {
|
if strings.HasPrefix(path, "http") {
|
||||||
return remote
|
return remote
|
||||||
}
|
}
|
||||||
return local
|
return func(pth string) ([]byte, error) { return local(filepath.FromSlash(pth)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadHTTPBytes(path string) ([]byte, error) {
|
func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) {
|
||||||
resp, err := http.Get(path)
|
return func(path string) ([]byte, error) {
|
||||||
if err != nil {
|
client := &http.Client{Timeout: timeout}
|
||||||
return nil, err
|
req, err := http.NewRequest("GET", path, nil)
|
||||||
}
|
if err != nil {
|
||||||
defer resp.Body.Close()
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
defer func() {
|
||||||
|
if resp != nil {
|
||||||
|
if e := resp.Body.Close(); e != nil {
|
||||||
|
log.Println(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, fmt.Errorf("could not access document at %q [%s] ", path, resp.Status)
|
return nil, fmt.Errorf("could not access document at %q [%s] ", path, resp.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ioutil.ReadAll(resp.Body)
|
return ioutil.ReadAll(resp.Body)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,9 @@ func FindInGoSearchPath(pkg string) string {
|
||||||
// FullGoSearchPath gets the search paths for finding packages
|
// FullGoSearchPath gets the search paths for finding packages
|
||||||
func FullGoSearchPath() string {
|
func FullGoSearchPath() string {
|
||||||
allPaths := os.Getenv(GOPATHKey)
|
allPaths := os.Getenv(GOPATHKey)
|
||||||
|
if allPaths == "" {
|
||||||
|
allPaths = filepath.Join(os.Getenv("HOME"), "go")
|
||||||
|
}
|
||||||
if allPaths != "" {
|
if allPaths != "" {
|
||||||
allPaths = strings.Join([]string{allPaths, runtime.GOROOT()}, ":")
|
allPaths = strings.Join([]string{allPaths, runtime.GOROOT()}, ":")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -20,10 +20,12 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Taken from https://github.com/golang/lint/blob/1fab560e16097e5b69afb66eb93aab843ef77845/lint.go#L663-L698
|
// Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
|
||||||
var commonInitialisms = map[string]bool{
|
var commonInitialisms = map[string]bool{
|
||||||
|
"ACL": true,
|
||||||
"API": true,
|
"API": true,
|
||||||
"ASCII": true,
|
"ASCII": true,
|
||||||
"CPU": true,
|
"CPU": true,
|
||||||
|
@ -44,19 +46,21 @@ var commonInitialisms = map[string]bool{
|
||||||
"RPC": true,
|
"RPC": true,
|
||||||
"SLA": true,
|
"SLA": true,
|
||||||
"SMTP": true,
|
"SMTP": true,
|
||||||
|
"SQL": true,
|
||||||
"SSH": true,
|
"SSH": true,
|
||||||
"TCP": true,
|
"TCP": true,
|
||||||
"TLS": true,
|
"TLS": true,
|
||||||
"TTL": true,
|
"TTL": true,
|
||||||
"UDP": true,
|
"UDP": true,
|
||||||
"UUID": true,
|
|
||||||
"UID": true,
|
|
||||||
"UI": true,
|
"UI": true,
|
||||||
|
"UID": true,
|
||||||
|
"UUID": true,
|
||||||
"URI": true,
|
"URI": true,
|
||||||
"URL": true,
|
"URL": true,
|
||||||
"UTF8": true,
|
"UTF8": true,
|
||||||
"VM": true,
|
"VM": true,
|
||||||
"XML": true,
|
"XML": true,
|
||||||
|
"XMPP": true,
|
||||||
"XSRF": true,
|
"XSRF": true,
|
||||||
"XSS": true,
|
"XSS": true,
|
||||||
}
|
}
|
||||||
|
@ -246,6 +250,9 @@ func ToJSONName(name string) string {
|
||||||
// ToVarName camelcases a name which can be underscored or pascal cased
|
// ToVarName camelcases a name which can be underscored or pascal cased
|
||||||
func ToVarName(name string) string {
|
func ToVarName(name string) string {
|
||||||
res := ToGoName(name)
|
res := ToGoName(name)
|
||||||
|
if _, ok := commonInitialisms[res]; ok {
|
||||||
|
return lower(res)
|
||||||
|
}
|
||||||
if len(res) <= 1 {
|
if len(res) <= 1 {
|
||||||
return lower(res)
|
return lower(res)
|
||||||
}
|
}
|
||||||
|
@ -263,7 +270,18 @@ func ToGoName(name string) string {
|
||||||
}
|
}
|
||||||
out = append(out, uw)
|
out = append(out, uw)
|
||||||
}
|
}
|
||||||
return strings.Join(out, "")
|
|
||||||
|
result := strings.Join(out, "")
|
||||||
|
if len(result) > 0 {
|
||||||
|
ud := upper(result[:1])
|
||||||
|
ru := []rune(ud)
|
||||||
|
if unicode.IsUpper(ru[0]) {
|
||||||
|
result = ud + result[1:]
|
||||||
|
} else {
|
||||||
|
result = "X" + ud + result[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainsStringsCI searches a slice of strings for a case-insensitive match
|
// ContainsStringsCI searches a slice of strings for a case-insensitive match
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
// Copyright 2015 go-swagger maintainers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package swag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/mailru/easyjson/jlexer"
|
||||||
|
"github.com/mailru/easyjson/jwriter"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// YAMLMatcher matches yaml
|
||||||
|
func YAMLMatcher(path string) bool {
|
||||||
|
ext := filepath.Ext(path)
|
||||||
|
return ext == ".yaml" || ext == ".yml"
|
||||||
|
}
|
||||||
|
|
||||||
|
// YAMLToJSON converts YAML unmarshaled data into json compatible data
|
||||||
|
func YAMLToJSON(data interface{}) (json.RawMessage, error) {
|
||||||
|
jm, err := transformData(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, err := WriteJSON(jm)
|
||||||
|
return json.RawMessage(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func BytesToYAMLDoc(data []byte) (interface{}, error) {
|
||||||
|
var canary map[interface{}]interface{} // validate this is an object and not a different type
|
||||||
|
if err := yaml.Unmarshal(data, &canary); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var document yaml.MapSlice // preserve order that is present in the document
|
||||||
|
if err := yaml.Unmarshal(data, &document); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return document, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type JSONMapSlice []JSONMapItem
|
||||||
|
|
||||||
|
func (s JSONMapSlice) MarshalJSON() ([]byte, error) {
|
||||||
|
w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
|
||||||
|
s.MarshalEasyJSON(w)
|
||||||
|
return w.BuildBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) {
|
||||||
|
w.RawByte('{')
|
||||||
|
|
||||||
|
ln := len(s)
|
||||||
|
last := ln - 1
|
||||||
|
for i := 0; i < ln; i++ {
|
||||||
|
s[i].MarshalEasyJSON(w)
|
||||||
|
if i != last { // last item
|
||||||
|
w.RawByte(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.RawByte('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *JSONMapSlice) UnmarshalJSON(data []byte) error {
|
||||||
|
l := jlexer.Lexer{Data: data}
|
||||||
|
s.UnmarshalEasyJSON(&l)
|
||||||
|
return l.Error()
|
||||||
|
}
|
||||||
|
func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||||
|
if in.IsNull() {
|
||||||
|
in.Skip()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var result JSONMapSlice
|
||||||
|
in.Delim('{')
|
||||||
|
for !in.IsDelim('}') {
|
||||||
|
var mi JSONMapItem
|
||||||
|
mi.UnmarshalEasyJSON(in)
|
||||||
|
result = append(result, mi)
|
||||||
|
}
|
||||||
|
*s = result
|
||||||
|
}
|
||||||
|
|
||||||
|
type JSONMapItem struct {
|
||||||
|
Key string
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s JSONMapItem) MarshalJSON() ([]byte, error) {
|
||||||
|
w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
|
||||||
|
s.MarshalEasyJSON(w)
|
||||||
|
return w.BuildBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s JSONMapItem) MarshalEasyJSON(w *jwriter.Writer) {
|
||||||
|
w.String(s.Key)
|
||||||
|
w.RawByte(':')
|
||||||
|
w.Raw(WriteJSON(s.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||||
|
key := in.UnsafeString()
|
||||||
|
in.WantColon()
|
||||||
|
value := in.Interface()
|
||||||
|
in.WantComma()
|
||||||
|
s.Key = key
|
||||||
|
s.Value = value
|
||||||
|
}
|
||||||
|
func (s *JSONMapItem) UnmarshalJSON(data []byte) error {
|
||||||
|
l := jlexer.Lexer{Data: data}
|
||||||
|
s.UnmarshalEasyJSON(&l)
|
||||||
|
return l.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func transformData(input interface{}) (out interface{}, err error) {
|
||||||
|
switch in := input.(type) {
|
||||||
|
case yaml.MapSlice:
|
||||||
|
|
||||||
|
o := make(JSONMapSlice, len(in))
|
||||||
|
for i, mi := range in {
|
||||||
|
var nmi JSONMapItem
|
||||||
|
switch k := mi.Key.(type) {
|
||||||
|
case string:
|
||||||
|
nmi.Key = k
|
||||||
|
case int:
|
||||||
|
nmi.Key = strconv.Itoa(k)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("types don't match expect map key string or int got: %T", mi.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := transformData(mi.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nmi.Value = v
|
||||||
|
o[i] = nmi
|
||||||
|
}
|
||||||
|
return o, nil
|
||||||
|
case map[interface{}]interface{}:
|
||||||
|
o := make(JSONMapSlice, 0, len(in))
|
||||||
|
for ke, va := range in {
|
||||||
|
var nmi JSONMapItem
|
||||||
|
switch k := ke.(type) {
|
||||||
|
case string:
|
||||||
|
nmi.Key = k
|
||||||
|
case int:
|
||||||
|
nmi.Key = strconv.Itoa(k)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("types don't match expect map key string or int got: %T", ke)
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := transformData(va)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nmi.Value = v
|
||||||
|
o = append(o, nmi)
|
||||||
|
}
|
||||||
|
return o, nil
|
||||||
|
case []interface{}:
|
||||||
|
len1 := len(in)
|
||||||
|
o := make([]interface{}, len1)
|
||||||
|
for i := 0; i < len1; i++ {
|
||||||
|
o[i], err = transformData(in[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
return input, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// YAMLDoc loads a yaml document from either http or a file and converts it to json
|
||||||
|
func YAMLDoc(path string) (json.RawMessage, error) {
|
||||||
|
yamlDoc, err := YAMLData(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := YAMLToJSON(yamlDoc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.RawMessage(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// YAMLData loads a yaml document from either http or a file
|
||||||
|
func YAMLData(path string) (interface{}, error) {
|
||||||
|
data, err := LoadFromFileOrHTTP(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return BytesToYAMLDoc(data)
|
||||||
|
}
|
|
@ -179,18 +179,25 @@ func (b *Buffer) DumpTo(w io.Writer) (written int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildBytes creates a single byte slice with all the contents of the buffer. Data is
|
// BuildBytes creates a single byte slice with all the contents of the buffer. Data is
|
||||||
// copied if it does not fit in a single chunk.
|
// copied if it does not fit in a single chunk. You can optionally provide one byte
|
||||||
func (b *Buffer) BuildBytes() []byte {
|
// slice as argument that it will try to reuse.
|
||||||
|
func (b *Buffer) BuildBytes(reuse ...[]byte) []byte {
|
||||||
if len(b.bufs) == 0 {
|
if len(b.bufs) == 0 {
|
||||||
|
|
||||||
ret := b.Buf
|
ret := b.Buf
|
||||||
b.toPool = nil
|
b.toPool = nil
|
||||||
b.Buf = nil
|
b.Buf = nil
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := make([]byte, 0, b.Size())
|
var ret []byte
|
||||||
|
size := b.Size()
|
||||||
|
|
||||||
|
// If we got a buffer as argument and it is big enought, reuse it.
|
||||||
|
if len(reuse) == 1 && cap(reuse[0]) >= size {
|
||||||
|
ret = reuse[0][:0]
|
||||||
|
} else {
|
||||||
|
ret = make([]byte, 0, size)
|
||||||
|
}
|
||||||
for _, buf := range b.bufs {
|
for _, buf := range b.bufs {
|
||||||
ret = append(ret, buf...)
|
ret = append(ret, buf...)
|
||||||
putBuf(buf)
|
putBuf(buf)
|
||||||
|
@ -205,3 +212,59 @@ func (b *Buffer) BuildBytes() []byte {
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type readCloser struct {
|
||||||
|
offset int
|
||||||
|
bufs [][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *readCloser) Read(p []byte) (n int, err error) {
|
||||||
|
for _, buf := range r.bufs {
|
||||||
|
// Copy as much as we can.
|
||||||
|
x := copy(p[n:], buf[r.offset:])
|
||||||
|
n += x // Increment how much we filled.
|
||||||
|
|
||||||
|
// Did we empty the whole buffer?
|
||||||
|
if r.offset+x == len(buf) {
|
||||||
|
// On to the next buffer.
|
||||||
|
r.offset = 0
|
||||||
|
r.bufs = r.bufs[1:]
|
||||||
|
|
||||||
|
// We can release this buffer.
|
||||||
|
putBuf(buf)
|
||||||
|
} else {
|
||||||
|
r.offset += x
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == len(p) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No buffers left or nothing read?
|
||||||
|
if len(r.bufs) == 0 {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *readCloser) Close() error {
|
||||||
|
// Release all remaining buffers.
|
||||||
|
for _, buf := range r.bufs {
|
||||||
|
putBuf(buf)
|
||||||
|
}
|
||||||
|
// In case Close gets called multiple times.
|
||||||
|
r.bufs = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadCloser creates an io.ReadCloser with all the contents of the buffer.
|
||||||
|
func (b *Buffer) ReadCloser() io.ReadCloser {
|
||||||
|
ret := &readCloser{0, append(b.bufs, b.Buf)}
|
||||||
|
|
||||||
|
b.bufs = nil
|
||||||
|
b.toPool = nil
|
||||||
|
b.Buf = nil
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"bytestostr.go",
|
||||||
"error.go",
|
"error.go",
|
||||||
"lexer.go",
|
"lexer.go",
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// This file will only be included to the build if neither
|
||||||
|
// easyjson_nounsafe nor appengine build tag is set. See README notes
|
||||||
|
// for more details.
|
||||||
|
|
||||||
|
//+build !easyjson_nounsafe
|
||||||
|
//+build !appengine
|
||||||
|
|
||||||
|
package jlexer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// bytesToStr creates a string pointing at the slice to avoid copying.
|
||||||
|
//
|
||||||
|
// Warning: the string returned by the function should be used with care, as the whole input data
|
||||||
|
// chunk may be either blocked from being freed by GC because of a single string or the buffer.Data
|
||||||
|
// may be garbage-collected even when the string exists.
|
||||||
|
func bytesToStr(data []byte) string {
|
||||||
|
h := (*reflect.SliceHeader)(unsafe.Pointer(&data))
|
||||||
|
shdr := reflect.StringHeader{Data: h.Data, Len: h.Len}
|
||||||
|
return *(*string)(unsafe.Pointer(&shdr))
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// This file is included to the build if any of the buildtags below
|
||||||
|
// are defined. Refer to README notes for more details.
|
||||||
|
|
||||||
|
//+build easyjson_nounsafe appengine
|
||||||
|
|
||||||
|
package jlexer
|
||||||
|
|
||||||
|
// bytesToStr creates a string normally from []byte
|
||||||
|
//
|
||||||
|
// Note that this method is roughly 1.5x slower than using the 'unsafe' method.
|
||||||
|
func bytesToStr(data []byte) string {
|
||||||
|
return string(data)
|
||||||
|
}
|
|
@ -5,12 +5,14 @@
|
||||||
package jlexer
|
package jlexer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf16"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// tokenKind determines type of a token.
|
// tokenKind determines type of a token.
|
||||||
|
@ -45,11 +47,13 @@ type Lexer struct {
|
||||||
firstElement bool // Whether current element is the first in array or an object.
|
firstElement bool // Whether current element is the first in array or an object.
|
||||||
wantSep byte // A comma or a colon character, which need to occur before a token.
|
wantSep byte // A comma or a colon character, which need to occur before a token.
|
||||||
|
|
||||||
err error // Error encountered during lexing, if any.
|
UseMultipleErrors bool // If we want to use multiple errors.
|
||||||
|
fatalError error // Fatal error occurred during lexing. It is usually a syntax error.
|
||||||
|
multipleErrors []*LexerError // Semantic errors occurred during lexing. Marshalling will be continued after finding this errors.
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchToken scans the input for the next token.
|
// FetchToken scans the input for the next token.
|
||||||
func (r *Lexer) fetchToken() {
|
func (r *Lexer) FetchToken() {
|
||||||
r.token.kind = tokenUndef
|
r.token.kind = tokenUndef
|
||||||
r.start = r.pos
|
r.start = r.pos
|
||||||
|
|
||||||
|
@ -147,7 +151,7 @@ func (r *Lexer) fetchToken() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.err = io.EOF
|
r.fatalError = io.EOF
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,17 +203,6 @@ func (r *Lexer) fetchFalse() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// bytesToStr creates a string pointing at the slice to avoid copying.
|
|
||||||
//
|
|
||||||
// Warning: the string returned by the function should be used with care, as the whole input data
|
|
||||||
// chunk may be either blocked from being freed by GC because of a single string or the buffer.Data
|
|
||||||
// may be garbage-collected even when the string exists.
|
|
||||||
func bytesToStr(data []byte) string {
|
|
||||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&data))
|
|
||||||
shdr := reflect.StringHeader{h.Data, h.Len}
|
|
||||||
return *(*string)(unsafe.Pointer(&shdr))
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetchNumber scans a number literal token.
|
// fetchNumber scans a number literal token.
|
||||||
func (r *Lexer) fetchNumber() {
|
func (r *Lexer) fetchNumber() {
|
||||||
hasE := false
|
hasE := false
|
||||||
|
@ -265,6 +258,33 @@ func findStringLen(data []byte) (hasEscapes bool, length int) {
|
||||||
return false, len(data)
|
return false, len(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
|
||||||
|
// or it returns -1.
|
||||||
|
func getu4(s []byte) rune {
|
||||||
|
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
var val rune
|
||||||
|
for i := 2; i < len(s) && i < 6; i++ {
|
||||||
|
var v byte
|
||||||
|
c := s[i]
|
||||||
|
switch c {
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
|
v = c - '0'
|
||||||
|
case 'a', 'b', 'c', 'd', 'e', 'f':
|
||||||
|
v = c - 'a' + 10
|
||||||
|
case 'A', 'B', 'C', 'D', 'E', 'F':
|
||||||
|
v = c - 'A' + 10
|
||||||
|
default:
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
val <<= 4
|
||||||
|
val |= rune(v)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
// processEscape processes a single escape sequence and returns number of bytes processed.
|
// processEscape processes a single escape sequence and returns number of bytes processed.
|
||||||
func (r *Lexer) processEscape(data []byte) (int, error) {
|
func (r *Lexer) processEscape(data []byte) (int, error) {
|
||||||
if len(data) < 2 {
|
if len(data) < 2 {
|
||||||
|
@ -292,39 +312,28 @@ func (r *Lexer) processEscape(data []byte) (int, error) {
|
||||||
r.token.byteValue = append(r.token.byteValue, '\t')
|
r.token.byteValue = append(r.token.byteValue, '\t')
|
||||||
return 2, nil
|
return 2, nil
|
||||||
case 'u':
|
case 'u':
|
||||||
default:
|
rr := getu4(data)
|
||||||
return 0, fmt.Errorf("syntax error")
|
if rr < 0 {
|
||||||
}
|
return 0, errors.New("syntax error")
|
||||||
|
|
||||||
var val rune
|
|
||||||
|
|
||||||
for i := 2; i < len(data) && i < 6; i++ {
|
|
||||||
var v byte
|
|
||||||
c = data[i]
|
|
||||||
switch c {
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
||||||
v = c - '0'
|
|
||||||
case 'a', 'b', 'c', 'd', 'e', 'f':
|
|
||||||
v = c - 'a' + 10
|
|
||||||
case 'A', 'B', 'C', 'D', 'E', 'F':
|
|
||||||
v = c - 'A' + 10
|
|
||||||
default:
|
|
||||||
return 0, fmt.Errorf("syntax error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val <<= 4
|
read := 6
|
||||||
val |= rune(v)
|
if utf16.IsSurrogate(rr) {
|
||||||
|
rr1 := getu4(data[read:])
|
||||||
|
if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
|
||||||
|
read += 6
|
||||||
|
rr = dec
|
||||||
|
} else {
|
||||||
|
rr = unicode.ReplacementChar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var d [4]byte
|
||||||
|
s := utf8.EncodeRune(d[:], rr)
|
||||||
|
r.token.byteValue = append(r.token.byteValue, d[:s]...)
|
||||||
|
return read, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
l := utf8.RuneLen(val)
|
return 0, errors.New("syntax error")
|
||||||
if l == -1 {
|
|
||||||
return 0, fmt.Errorf("invalid unicode escape")
|
|
||||||
}
|
|
||||||
|
|
||||||
var d [4]byte
|
|
||||||
utf8.EncodeRune(d[:], val)
|
|
||||||
r.token.byteValue = append(r.token.byteValue, d[:l]...)
|
|
||||||
return 6, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchString scans a string literal token.
|
// fetchString scans a string literal token.
|
||||||
|
@ -368,11 +377,11 @@ func (r *Lexer) fetchString() {
|
||||||
|
|
||||||
// scanToken scans the next token if no token is currently available in the lexer.
|
// scanToken scans the next token if no token is currently available in the lexer.
|
||||||
func (r *Lexer) scanToken() {
|
func (r *Lexer) scanToken() {
|
||||||
if r.token.kind != tokenUndef || r.err != nil {
|
if r.token.kind != tokenUndef || r.fatalError != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
// consume resets the current token to allow scanning the next one.
|
// consume resets the current token to allow scanning the next one.
|
||||||
|
@ -383,20 +392,20 @@ func (r *Lexer) consume() {
|
||||||
|
|
||||||
// Ok returns true if no error (including io.EOF) was encountered during scanning.
|
// Ok returns true if no error (including io.EOF) was encountered during scanning.
|
||||||
func (r *Lexer) Ok() bool {
|
func (r *Lexer) Ok() bool {
|
||||||
return r.err == nil
|
return r.fatalError == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxErrorContextLen = 13
|
const maxErrorContextLen = 13
|
||||||
|
|
||||||
func (r *Lexer) errParse(what string) {
|
func (r *Lexer) errParse(what string) {
|
||||||
if r.err == nil {
|
if r.fatalError == nil {
|
||||||
var str string
|
var str string
|
||||||
if len(r.Data)-r.pos <= maxErrorContextLen {
|
if len(r.Data)-r.pos <= maxErrorContextLen {
|
||||||
str = string(r.Data)
|
str = string(r.Data)
|
||||||
} else {
|
} else {
|
||||||
str = string(r.Data[r.pos:r.pos+maxErrorContextLen-3]) + "..."
|
str = string(r.Data[r.pos:r.pos+maxErrorContextLen-3]) + "..."
|
||||||
}
|
}
|
||||||
r.err = &LexerError{
|
r.fatalError = &LexerError{
|
||||||
Reason: what,
|
Reason: what,
|
||||||
Offset: r.pos,
|
Offset: r.pos,
|
||||||
Data: str,
|
Data: str,
|
||||||
|
@ -409,36 +418,64 @@ func (r *Lexer) errSyntax() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) errInvalidToken(expected string) {
|
func (r *Lexer) errInvalidToken(expected string) {
|
||||||
if r.err == nil {
|
if r.fatalError != nil {
|
||||||
var str string
|
return
|
||||||
if len(r.token.byteValue) <= maxErrorContextLen {
|
|
||||||
str = string(r.token.byteValue)
|
|
||||||
} else {
|
|
||||||
str = string(r.token.byteValue[:maxErrorContextLen-3]) + "..."
|
|
||||||
}
|
|
||||||
r.err = &LexerError{
|
|
||||||
Reason: fmt.Sprintf("expected %s", expected),
|
|
||||||
Offset: r.pos,
|
|
||||||
Data: str,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if r.UseMultipleErrors {
|
||||||
|
r.pos = r.start
|
||||||
|
r.consume()
|
||||||
|
r.SkipRecursive()
|
||||||
|
switch expected {
|
||||||
|
case "[":
|
||||||
|
r.token.delimValue = ']'
|
||||||
|
r.token.kind = tokenDelim
|
||||||
|
case "{":
|
||||||
|
r.token.delimValue = '}'
|
||||||
|
r.token.kind = tokenDelim
|
||||||
|
}
|
||||||
|
r.addNonfatalError(&LexerError{
|
||||||
|
Reason: fmt.Sprintf("expected %s", expected),
|
||||||
|
Offset: r.start,
|
||||||
|
Data: string(r.Data[r.start:r.pos]),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var str string
|
||||||
|
if len(r.token.byteValue) <= maxErrorContextLen {
|
||||||
|
str = string(r.token.byteValue)
|
||||||
|
} else {
|
||||||
|
str = string(r.token.byteValue[:maxErrorContextLen-3]) + "..."
|
||||||
|
}
|
||||||
|
r.fatalError = &LexerError{
|
||||||
|
Reason: fmt.Sprintf("expected %s", expected),
|
||||||
|
Offset: r.pos,
|
||||||
|
Data: str,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Lexer) GetPos() int {
|
||||||
|
return r.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delim consumes a token and verifies that it is the given delimiter.
|
// Delim consumes a token and verifies that it is the given delimiter.
|
||||||
func (r *Lexer) Delim(c byte) {
|
func (r *Lexer) Delim(c byte) {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !r.Ok() || r.token.delimValue != c {
|
if !r.Ok() || r.token.delimValue != c {
|
||||||
|
r.consume() // errInvalidToken can change token if UseMultipleErrors is enabled.
|
||||||
r.errInvalidToken(string([]byte{c}))
|
r.errInvalidToken(string([]byte{c}))
|
||||||
|
} else {
|
||||||
|
r.consume()
|
||||||
}
|
}
|
||||||
r.consume()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDelim returns true if there was no scanning error and next token is the given delimiter.
|
// IsDelim returns true if there was no scanning error and next token is the given delimiter.
|
||||||
func (r *Lexer) IsDelim(c byte) bool {
|
func (r *Lexer) IsDelim(c byte) bool {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
return !r.Ok() || r.token.delimValue == c
|
return !r.Ok() || r.token.delimValue == c
|
||||||
}
|
}
|
||||||
|
@ -446,7 +483,7 @@ func (r *Lexer) IsDelim(c byte) bool {
|
||||||
// Null verifies that the next token is null and consumes it.
|
// Null verifies that the next token is null and consumes it.
|
||||||
func (r *Lexer) Null() {
|
func (r *Lexer) Null() {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
if !r.Ok() || r.token.kind != tokenNull {
|
if !r.Ok() || r.token.kind != tokenNull {
|
||||||
r.errInvalidToken("null")
|
r.errInvalidToken("null")
|
||||||
|
@ -457,7 +494,7 @@ func (r *Lexer) Null() {
|
||||||
// IsNull returns true if the next token is a null keyword.
|
// IsNull returns true if the next token is a null keyword.
|
||||||
func (r *Lexer) IsNull() bool {
|
func (r *Lexer) IsNull() bool {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
return r.Ok() && r.token.kind == tokenNull
|
return r.Ok() && r.token.kind == tokenNull
|
||||||
}
|
}
|
||||||
|
@ -465,7 +502,7 @@ func (r *Lexer) IsNull() bool {
|
||||||
// Skip skips a single token.
|
// Skip skips a single token.
|
||||||
func (r *Lexer) Skip() {
|
func (r *Lexer) Skip() {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
r.consume()
|
r.consume()
|
||||||
}
|
}
|
||||||
|
@ -476,7 +513,6 @@ func (r *Lexer) Skip() {
|
||||||
// Note: no syntax validation is performed on the skipped data.
|
// Note: no syntax validation is performed on the skipped data.
|
||||||
func (r *Lexer) SkipRecursive() {
|
func (r *Lexer) SkipRecursive() {
|
||||||
r.scanToken()
|
r.scanToken()
|
||||||
|
|
||||||
var start, end byte
|
var start, end byte
|
||||||
|
|
||||||
if r.token.delimValue == '{' {
|
if r.token.delimValue == '{' {
|
||||||
|
@ -505,7 +541,7 @@ func (r *Lexer) SkipRecursive() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case c == '\\' && inQuotes:
|
case c == '\\' && inQuotes:
|
||||||
wasEscape = true
|
wasEscape = !wasEscape
|
||||||
continue
|
continue
|
||||||
case c == '"' && inQuotes:
|
case c == '"' && inQuotes:
|
||||||
inQuotes = wasEscape
|
inQuotes = wasEscape
|
||||||
|
@ -515,7 +551,11 @@ func (r *Lexer) SkipRecursive() {
|
||||||
wasEscape = false
|
wasEscape = false
|
||||||
}
|
}
|
||||||
r.pos = len(r.Data)
|
r.pos = len(r.Data)
|
||||||
r.err = io.EOF
|
r.fatalError = &LexerError{
|
||||||
|
Reason: "EOF reached while skipping array/object or token",
|
||||||
|
Offset: r.pos,
|
||||||
|
Data: string(r.Data[r.pos:]),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raw fetches the next item recursively as a data slice
|
// Raw fetches the next item recursively as a data slice
|
||||||
|
@ -527,48 +567,107 @@ func (r *Lexer) Raw() []byte {
|
||||||
return r.Data[r.start:r.pos]
|
return r.Data[r.start:r.pos]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsStart returns whether the lexer is positioned at the start
|
||||||
|
// of an input string.
|
||||||
|
func (r *Lexer) IsStart() bool {
|
||||||
|
return r.pos == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consumed reads all remaining bytes from the input, publishing an error if
|
||||||
|
// there is anything but whitespace remaining.
|
||||||
|
func (r *Lexer) Consumed() {
|
||||||
|
if r.pos > len(r.Data) || !r.Ok() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range r.Data[r.pos:] {
|
||||||
|
if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
|
||||||
|
r.AddError(&LexerError{
|
||||||
|
Reason: "invalid character '" + string(c) + "' after top-level value",
|
||||||
|
Offset: r.pos,
|
||||||
|
Data: string(r.Data[r.pos:]),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.pos++
|
||||||
|
r.start++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Lexer) unsafeString() (string, []byte) {
|
||||||
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
|
r.FetchToken()
|
||||||
|
}
|
||||||
|
if !r.Ok() || r.token.kind != tokenString {
|
||||||
|
r.errInvalidToken("string")
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
bytes := r.token.byteValue
|
||||||
|
ret := bytesToStr(r.token.byteValue)
|
||||||
|
r.consume()
|
||||||
|
return ret, bytes
|
||||||
|
}
|
||||||
|
|
||||||
// UnsafeString returns the string value if the token is a string literal.
|
// UnsafeString returns the string value if the token is a string literal.
|
||||||
//
|
//
|
||||||
// Warning: returned string may point to the input buffer, so the string should not outlive
|
// Warning: returned string may point to the input buffer, so the string should not outlive
|
||||||
// the input buffer. Intended pattern of usage is as an argument to a switch statement.
|
// the input buffer. Intended pattern of usage is as an argument to a switch statement.
|
||||||
func (r *Lexer) UnsafeString() string {
|
func (r *Lexer) UnsafeString() string {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
ret, _ := r.unsafeString()
|
||||||
r.fetchToken()
|
return ret
|
||||||
}
|
}
|
||||||
if !r.Ok() || r.token.kind != tokenString {
|
|
||||||
r.errInvalidToken("string")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := bytesToStr(r.token.byteValue)
|
// UnsafeBytes returns the byte slice if the token is a string literal.
|
||||||
r.consume()
|
func (r *Lexer) UnsafeBytes() []byte {
|
||||||
|
_, ret := r.unsafeString()
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// String reads a string literal.
|
// String reads a string literal.
|
||||||
func (r *Lexer) String() string {
|
func (r *Lexer) String() string {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
if !r.Ok() || r.token.kind != tokenString {
|
if !r.Ok() || r.token.kind != tokenString {
|
||||||
r.errInvalidToken("string")
|
r.errInvalidToken("string")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
}
|
}
|
||||||
ret := string(r.token.byteValue)
|
ret := string(r.token.byteValue)
|
||||||
r.consume()
|
r.consume()
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bytes reads a string literal and base64 decodes it into a byte slice.
|
||||||
|
func (r *Lexer) Bytes() []byte {
|
||||||
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
|
r.FetchToken()
|
||||||
|
}
|
||||||
|
if !r.Ok() || r.token.kind != tokenString {
|
||||||
|
r.errInvalidToken("string")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ret := make([]byte, base64.StdEncoding.DecodedLen(len(r.token.byteValue)))
|
||||||
|
len, err := base64.StdEncoding.Decode(ret, r.token.byteValue)
|
||||||
|
if err != nil {
|
||||||
|
r.fatalError = &LexerError{
|
||||||
|
Reason: err.Error(),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.consume()
|
||||||
|
return ret[:len]
|
||||||
|
}
|
||||||
|
|
||||||
// Bool reads a true or false boolean keyword.
|
// Bool reads a true or false boolean keyword.
|
||||||
func (r *Lexer) Bool() bool {
|
func (r *Lexer) Bool() bool {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
if !r.Ok() || r.token.kind != tokenBool {
|
if !r.Ok() || r.token.kind != tokenBool {
|
||||||
r.errInvalidToken("bool")
|
r.errInvalidToken("bool")
|
||||||
return false
|
return false
|
||||||
|
|
||||||
}
|
}
|
||||||
ret := r.token.boolValue
|
ret := r.token.boolValue
|
||||||
r.consume()
|
r.consume()
|
||||||
|
@ -577,12 +676,11 @@ func (r *Lexer) Bool() bool {
|
||||||
|
|
||||||
func (r *Lexer) number() string {
|
func (r *Lexer) number() string {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
if !r.Ok() || r.token.kind != tokenNumber {
|
if !r.Ok() || r.token.kind != tokenNumber {
|
||||||
r.errInvalidToken("number")
|
r.errInvalidToken("number")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
}
|
}
|
||||||
ret := bytesToStr(r.token.byteValue)
|
ret := bytesToStr(r.token.byteValue)
|
||||||
r.consume()
|
r.consume()
|
||||||
|
@ -597,9 +695,11 @@ func (r *Lexer) Uint8() uint8 {
|
||||||
|
|
||||||
n, err := strconv.ParseUint(s, 10, 8)
|
n, err := strconv.ParseUint(s, 10, 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return uint8(n)
|
return uint8(n)
|
||||||
}
|
}
|
||||||
|
@ -612,9 +712,11 @@ func (r *Lexer) Uint16() uint16 {
|
||||||
|
|
||||||
n, err := strconv.ParseUint(s, 10, 16)
|
n, err := strconv.ParseUint(s, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return uint16(n)
|
return uint16(n)
|
||||||
}
|
}
|
||||||
|
@ -627,9 +729,11 @@ func (r *Lexer) Uint32() uint32 {
|
||||||
|
|
||||||
n, err := strconv.ParseUint(s, 10, 32)
|
n, err := strconv.ParseUint(s, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return uint32(n)
|
return uint32(n)
|
||||||
}
|
}
|
||||||
|
@ -642,9 +746,11 @@ func (r *Lexer) Uint64() uint64 {
|
||||||
|
|
||||||
n, err := strconv.ParseUint(s, 10, 64)
|
n, err := strconv.ParseUint(s, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
@ -661,9 +767,11 @@ func (r *Lexer) Int8() int8 {
|
||||||
|
|
||||||
n, err := strconv.ParseInt(s, 10, 8)
|
n, err := strconv.ParseInt(s, 10, 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return int8(n)
|
return int8(n)
|
||||||
}
|
}
|
||||||
|
@ -676,9 +784,11 @@ func (r *Lexer) Int16() int16 {
|
||||||
|
|
||||||
n, err := strconv.ParseInt(s, 10, 16)
|
n, err := strconv.ParseInt(s, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return int16(n)
|
return int16(n)
|
||||||
}
|
}
|
||||||
|
@ -691,9 +801,11 @@ func (r *Lexer) Int32() int32 {
|
||||||
|
|
||||||
n, err := strconv.ParseInt(s, 10, 32)
|
n, err := strconv.ParseInt(s, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return int32(n)
|
return int32(n)
|
||||||
}
|
}
|
||||||
|
@ -706,9 +818,11 @@ func (r *Lexer) Int64() int64 {
|
||||||
|
|
||||||
n, err := strconv.ParseInt(s, 10, 64)
|
n, err := strconv.ParseInt(s, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
@ -718,61 +832,69 @@ func (r *Lexer) Int() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) Uint8Str() uint8 {
|
func (r *Lexer) Uint8Str() uint8 {
|
||||||
s := r.UnsafeString()
|
s, b := r.unsafeString()
|
||||||
if !r.Ok() {
|
if !r.Ok() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := strconv.ParseUint(s, 10, 8)
|
n, err := strconv.ParseUint(s, 10, 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: string(b),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return uint8(n)
|
return uint8(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) Uint16Str() uint16 {
|
func (r *Lexer) Uint16Str() uint16 {
|
||||||
s := r.UnsafeString()
|
s, b := r.unsafeString()
|
||||||
if !r.Ok() {
|
if !r.Ok() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := strconv.ParseUint(s, 10, 16)
|
n, err := strconv.ParseUint(s, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: string(b),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return uint16(n)
|
return uint16(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) Uint32Str() uint32 {
|
func (r *Lexer) Uint32Str() uint32 {
|
||||||
s := r.UnsafeString()
|
s, b := r.unsafeString()
|
||||||
if !r.Ok() {
|
if !r.Ok() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := strconv.ParseUint(s, 10, 32)
|
n, err := strconv.ParseUint(s, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: string(b),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return uint32(n)
|
return uint32(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) Uint64Str() uint64 {
|
func (r *Lexer) Uint64Str() uint64 {
|
||||||
s := r.UnsafeString()
|
s, b := r.unsafeString()
|
||||||
if !r.Ok() {
|
if !r.Ok() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := strconv.ParseUint(s, 10, 64)
|
n, err := strconv.ParseUint(s, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: string(b),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
@ -782,61 +904,69 @@ func (r *Lexer) UintStr() uint {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) Int8Str() int8 {
|
func (r *Lexer) Int8Str() int8 {
|
||||||
s := r.UnsafeString()
|
s, b := r.unsafeString()
|
||||||
if !r.Ok() {
|
if !r.Ok() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := strconv.ParseInt(s, 10, 8)
|
n, err := strconv.ParseInt(s, 10, 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: string(b),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return int8(n)
|
return int8(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) Int16Str() int16 {
|
func (r *Lexer) Int16Str() int16 {
|
||||||
s := r.UnsafeString()
|
s, b := r.unsafeString()
|
||||||
if !r.Ok() {
|
if !r.Ok() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := strconv.ParseInt(s, 10, 16)
|
n, err := strconv.ParseInt(s, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: string(b),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return int16(n)
|
return int16(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) Int32Str() int32 {
|
func (r *Lexer) Int32Str() int32 {
|
||||||
s := r.UnsafeString()
|
s, b := r.unsafeString()
|
||||||
if !r.Ok() {
|
if !r.Ok() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := strconv.ParseInt(s, 10, 32)
|
n, err := strconv.ParseInt(s, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: string(b),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return int32(n)
|
return int32(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) Int64Str() int64 {
|
func (r *Lexer) Int64Str() int64 {
|
||||||
s := r.UnsafeString()
|
s, b := r.unsafeString()
|
||||||
if !r.Ok() {
|
if !r.Ok() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := strconv.ParseInt(s, 10, 64)
|
n, err := strconv.ParseInt(s, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: string(b),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
@ -853,9 +983,11 @@ func (r *Lexer) Float32() float32 {
|
||||||
|
|
||||||
n, err := strconv.ParseFloat(s, 32)
|
n, err := strconv.ParseFloat(s, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return float32(n)
|
return float32(n)
|
||||||
}
|
}
|
||||||
|
@ -868,27 +1000,53 @@ func (r *Lexer) Float64() float64 {
|
||||||
|
|
||||||
n, err := strconv.ParseFloat(s, 64)
|
n, err := strconv.ParseFloat(s, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = &LexerError{
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
}
|
Data: s,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) Error() error {
|
func (r *Lexer) Error() error {
|
||||||
return r.err
|
return r.fatalError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Lexer) AddError(e error) {
|
func (r *Lexer) AddError(e error) {
|
||||||
if r.err == nil {
|
if r.fatalError == nil {
|
||||||
r.err = e
|
r.fatalError = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Lexer) AddNonFatalError(e error) {
|
||||||
|
r.addNonfatalError(&LexerError{
|
||||||
|
Offset: r.start,
|
||||||
|
Data: string(r.Data[r.start:r.pos]),
|
||||||
|
Reason: e.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Lexer) addNonfatalError(err *LexerError) {
|
||||||
|
if r.UseMultipleErrors {
|
||||||
|
// We don't want to add errors with the same offset.
|
||||||
|
if len(r.multipleErrors) != 0 && r.multipleErrors[len(r.multipleErrors)-1].Offset == err.Offset {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.multipleErrors = append(r.multipleErrors, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.fatalError = err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Lexer) GetNonFatalErrors() []*LexerError {
|
||||||
|
return r.multipleErrors
|
||||||
|
}
|
||||||
|
|
||||||
// Interface fetches an interface{} analogous to the 'encoding/json' package.
|
// Interface fetches an interface{} analogous to the 'encoding/json' package.
|
||||||
func (r *Lexer) Interface() interface{} {
|
func (r *Lexer) Interface() interface{} {
|
||||||
if r.token.kind == tokenUndef && r.Ok() {
|
if r.token.kind == tokenUndef && r.Ok() {
|
||||||
r.fetchToken()
|
r.FetchToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !r.Ok() {
|
if !r.Ok() {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
package jwriter
|
package jwriter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
@ -9,10 +10,22 @@ import (
|
||||||
"github.com/mailru/easyjson/buffer"
|
"github.com/mailru/easyjson/buffer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Flags describe various encoding options. The behavior may be actually implemented in the encoder, but
|
||||||
|
// Flags field in Writer is used to set and pass them around.
|
||||||
|
type Flags int
|
||||||
|
|
||||||
|
const (
|
||||||
|
NilMapAsEmpty Flags = 1 << iota // Encode nil map as '{}' rather than 'null'.
|
||||||
|
NilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'.
|
||||||
|
)
|
||||||
|
|
||||||
// Writer is a JSON writer.
|
// Writer is a JSON writer.
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
Error error
|
Flags Flags
|
||||||
Buffer buffer.Buffer
|
|
||||||
|
Error error
|
||||||
|
Buffer buffer.Buffer
|
||||||
|
NoEscapeHTML bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the size of the data that was written out.
|
// Size returns the size of the data that was written out.
|
||||||
|
@ -25,13 +38,24 @@ func (w *Writer) DumpTo(out io.Writer) (written int, err error) {
|
||||||
return w.Buffer.DumpTo(out)
|
return w.Buffer.DumpTo(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildBytes returns writer data as a single byte slice.
|
// BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice
|
||||||
func (w *Writer) BuildBytes() ([]byte, error) {
|
// as argument that it will try to reuse.
|
||||||
|
func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) {
|
||||||
if w.Error != nil {
|
if w.Error != nil {
|
||||||
return nil, w.Error
|
return nil, w.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.Buffer.BuildBytes(), nil
|
return w.Buffer.BuildBytes(reuse...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadCloser returns an io.ReadCloser that can be used to read the data.
|
||||||
|
// ReadCloser also resets the buffer.
|
||||||
|
func (w *Writer) ReadCloser() (io.ReadCloser, error) {
|
||||||
|
if w.Error != nil {
|
||||||
|
return nil, w.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.Buffer.ReadCloser(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawByte appends raw binary data to the buffer.
|
// RawByte appends raw binary data to the buffer.
|
||||||
|
@ -44,7 +68,7 @@ func (w *Writer) RawString(s string) {
|
||||||
w.Buffer.AppendString(s)
|
w.Buffer.AppendString(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawByte appends raw binary data to the buffer or sets the error if it is given. Useful for
|
// Raw appends raw binary data to the buffer or sets the error if it is given. Useful for
|
||||||
// calling with results of MarshalJSON-like functions.
|
// calling with results of MarshalJSON-like functions.
|
||||||
func (w *Writer) Raw(data []byte, err error) {
|
func (w *Writer) Raw(data []byte, err error) {
|
||||||
switch {
|
switch {
|
||||||
|
@ -59,6 +83,34 @@ func (w *Writer) Raw(data []byte, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RawText encloses raw binary data in quotes and appends in to the buffer.
|
||||||
|
// Useful for calling with results of MarshalText-like functions.
|
||||||
|
func (w *Writer) RawText(data []byte, err error) {
|
||||||
|
switch {
|
||||||
|
case w.Error != nil:
|
||||||
|
return
|
||||||
|
case err != nil:
|
||||||
|
w.Error = err
|
||||||
|
case len(data) > 0:
|
||||||
|
w.String(string(data))
|
||||||
|
default:
|
||||||
|
w.RawString("null")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64Bytes appends data to the buffer after base64 encoding it
|
||||||
|
func (w *Writer) Base64Bytes(data []byte) {
|
||||||
|
if data == nil {
|
||||||
|
w.Buffer.AppendString("null")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Buffer.AppendByte('"')
|
||||||
|
dst := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
|
||||||
|
base64.StdEncoding.Encode(dst, data)
|
||||||
|
w.Buffer.AppendBytes(dst)
|
||||||
|
w.Buffer.AppendByte('"')
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Writer) Uint8(n uint8) {
|
func (w *Writer) Uint8(n uint8) {
|
||||||
w.Buffer.EnsureSpace(3)
|
w.Buffer.EnsureSpace(3)
|
||||||
w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
|
w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
|
||||||
|
@ -200,6 +252,16 @@ func (w *Writer) Bool(v bool) {
|
||||||
|
|
||||||
const chars = "0123456789abcdef"
|
const chars = "0123456789abcdef"
|
||||||
|
|
||||||
|
func isNotEscapedSingleChar(c byte, escapeHTML bool) bool {
|
||||||
|
// Note: might make sense to use a table if there are more chars to escape. With 4 chars
|
||||||
|
// it benchmarks the same.
|
||||||
|
if escapeHTML {
|
||||||
|
return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
|
||||||
|
} else {
|
||||||
|
return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Writer) String(s string) {
|
func (w *Writer) String(s string) {
|
||||||
w.Buffer.AppendByte('"')
|
w.Buffer.AppendByte('"')
|
||||||
|
|
||||||
|
@ -209,39 +271,32 @@ func (w *Writer) String(s string) {
|
||||||
p := 0 // last non-escape symbol
|
p := 0 // last non-escape symbol
|
||||||
|
|
||||||
for i := 0; i < len(s); {
|
for i := 0; i < len(s); {
|
||||||
// single-with character
|
c := s[i]
|
||||||
if c := s[i]; c < utf8.RuneSelf {
|
|
||||||
var escape byte
|
if isNotEscapedSingleChar(c, !w.NoEscapeHTML) {
|
||||||
|
// single-width character, no escaping is required
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
} else if c < utf8.RuneSelf {
|
||||||
|
// single-with character, need to escape
|
||||||
|
w.Buffer.AppendString(s[p:i])
|
||||||
switch c {
|
switch c {
|
||||||
case '\t':
|
case '\t':
|
||||||
escape = 't'
|
w.Buffer.AppendString(`\t`)
|
||||||
case '\r':
|
case '\r':
|
||||||
escape = 'r'
|
w.Buffer.AppendString(`\r`)
|
||||||
case '\n':
|
case '\n':
|
||||||
escape = 'n'
|
w.Buffer.AppendString(`\n`)
|
||||||
case '\\':
|
case '\\':
|
||||||
escape = '\\'
|
w.Buffer.AppendString(`\\`)
|
||||||
case '"':
|
case '"':
|
||||||
escape = '"'
|
w.Buffer.AppendString(`\"`)
|
||||||
case '<', '>':
|
|
||||||
// do nothing
|
|
||||||
default:
|
default:
|
||||||
if c >= 0x20 {
|
|
||||||
// no escaping is required
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if escape != 0 {
|
|
||||||
w.Buffer.AppendString(s[p:i])
|
|
||||||
w.Buffer.AppendByte('\\')
|
|
||||||
w.Buffer.AppendByte(escape)
|
|
||||||
} else {
|
|
||||||
w.Buffer.AppendString(s[p:i])
|
|
||||||
w.Buffer.AppendString(`\u00`)
|
w.Buffer.AppendString(`\u00`)
|
||||||
w.Buffer.AppendByte(chars[c>>4])
|
w.Buffer.AppendByte(chars[c>>4])
|
||||||
w.Buffer.AppendByte(chars[c&0xf])
|
w.Buffer.AppendByte(chars[c&0xf])
|
||||||
}
|
}
|
||||||
|
|
||||||
i++
|
i++
|
||||||
p = i
|
p = i
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Reference in New Issue