diff --git a/cmd/controller/main.go b/cmd/controller/main.go
index 548b246b3..0b22f2eba 100644
--- a/cmd/controller/main.go
+++ b/cmd/controller/main.go
@@ -88,7 +88,7 @@ func main() {
{
secretExports := sharing.NewSecretExportsWarmedUp(
- sharing.NewSecretExports(log.WithName("secretexports")))
+ sharing.NewSecretExports(mgr.GetClient(), log.WithName("secretexports")))
secretExportReconciler := sharing.NewSecretExportReconciler(
mgr.GetClient(), secretExports, log.WithName("secexp"))
diff --git a/config/package-bundle/config/crds.yml b/config/package-bundle/config/crds.yml
index 75be57b63..430c52c38 100644
--- a/config/package-bundle/config/crds.yml
+++ b/config/package-bundle/config/crds.yml
@@ -40,7 +40,7 @@ spec:
items:
type: string
type: array
- toSelectorMatchFields:
+ dangerousToNamespacesSelector:
type: array
items:
properties:
diff --git a/docs/secret-export.md b/docs/secret-export.md
index 4d7c5c194..343811a6a 100644
--- a/docs/secret-export.md
+++ b/docs/secret-export.md
@@ -45,8 +45,8 @@ metadata:
namespace: user1
spec:
toNamespace: user2
- toSelectorMatchFields:
- - key: metadata.annotations.field\\.cattle\\.io/projectId:
+ dangerousToNamespacesSelector:
+ - key: "metadata.annotations['field\\.cattle\\.io/projectId']"
operator: In
value: "cluster1:project1"
@@ -97,7 +97,7 @@ SecretExport CRD allows to "offer" secrets for export.
- `toNamespace` (optional; string) Destination namespace for offer. Use `*` to indicate all namespaces.
- `toNamespaces` (optional; array of strings) List of destination namespaces for offer.
-- `toSelectorMatchFields` (optional; array of selector objects) List of matchers for destination namespaces. If multiple expressions are specified, all those expressions must evaluate to true for the selector to match a namespace. The selector object is composed as follows:
+- `dangerousToNamespacesSelector` (optional; array of selector objects) List of matchers for destination namespaces. If multiple expressions are specified, all those expressions must evaluate to true for the selector to match a namespace. The selector object is composed as follows:
- `key` (required; string) Property to target on the resource for the match. It's support dot notation from [GJSON syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md).
- `operator` (required; enum string) Type of comparison. Must be one of `In`, `NotIn`, `Exists`, `DoesNotExist`.
Operator explanations:
diff --git a/examples/secret-export.yml b/examples/secret-export.yml
index acf8c0363..ba0c476b6 100644
--- a/examples/secret-export.yml
+++ b/examples/secret-export.yml
@@ -105,8 +105,8 @@ metadata:
name: scoped-user-password-multi
namespace: user1
spec:
- toSelectorMatchFields:
- - key: metadata.annotations.field\\.cattle\\.io/projectId
+ dangerousToNamespacesSelector:
+ - key: "metadata.annotations['field\\.cattle\\.io/projectId']"
operator: In
value: "cluster1:project1"
---
diff --git a/go.mod b/go.mod
index c5be2dbd3..563dbf02a 100644
--- a/go.mod
+++ b/go.mod
@@ -18,8 +18,6 @@ require (
sigs.k8s.io/controller-tools v0.11.3
)
-require github.com/tidwall/gjson v1.14.4
-
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
@@ -60,8 +58,6 @@ require (
github.com/prometheus/procfs v0.8.0 // indirect
github.com/spf13/cobra v1.6.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
- github.com/tidwall/match v1.1.1 // indirect
- github.com/tidwall/pretty v1.2.1 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.24.0 // indirect
diff --git a/go.sum b/go.sum
index 8be9f7d4b..bcab4554a 100644
--- a/go.sum
+++ b/go.sum
@@ -306,13 +306,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
-github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
-github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
-github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
-github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
diff --git a/pkg/apis/secretgen2/v1alpha1/secret_export.go b/pkg/apis/secretgen2/v1alpha1/secret_export.go
index 1541cae77..3f958e97a 100644
--- a/pkg/apis/secretgen2/v1alpha1/secret_export.go
+++ b/pkg/apis/secretgen2/v1alpha1/secret_export.go
@@ -52,9 +52,9 @@ const (
// SelectorMatchField is a selector field to match against namespace definition
type SelectorMatchField struct {
- Key string
- Operator SelectorOperator
- Values []string
+ Key string `json:"key,omitempty"`
+ Operator SelectorOperator `json:"operator,omitempty"`
+ Values []string `json:"values,omitempty"`
}
type SecretExportSpec struct {
@@ -63,7 +63,7 @@ type SecretExportSpec struct {
// +optional
ToNamespaces []string `json:"toNamespaces,omitempty"`
// +optional
- ToSelectorMatchFields []SelectorMatchField `json:"toSelectorMatchFields,omitempty"`
+ ToSelectorMatchFields []SelectorMatchField `json:"dangerousToNamespacesSelector,omitempty"`
}
type SecretExportStatus struct {
diff --git a/pkg/sharing/import_secret_test.go b/pkg/sharing/import_secret_test.go
index 59938e4ef..2ee1c16fd 100644
--- a/pkg/sharing/import_secret_test.go
+++ b/pkg/sharing/import_secret_test.go
@@ -69,7 +69,7 @@ func secretImportFor(sourceSecret corev1.Secret) sg2v1alpha1.SecretImport {
func importReconcilers(objects ...runtime.Object) (secretExportReconciler *sharing.SecretExportReconciler, secretImportReconciler *sharing.SecretImportReconciler, k8sClient client.Client) {
k8sClient = fakeClient.NewFakeClient(objects...)
- secretExports := sharing.NewSecretExportsWarmedUp(sharing.NewSecretExports(testLogr))
+ secretExports := sharing.NewSecretExportsWarmedUp(sharing.NewSecretExports(k8sClient, testLogr))
secretExportReconciler = sharing.NewSecretExportReconciler(k8sClient, secretExports, testLogr)
secretExports.WarmUpFunc = secretExportReconciler.WarmUp
secretImportReconciler = sharing.NewSecretImportReconciler(k8sClient, secretExports, testLogr)
diff --git a/pkg/sharing/placeholder_secret_test.go b/pkg/sharing/placeholder_secret_test.go
index 2ad68185d..f817827a4 100644
--- a/pkg/sharing/placeholder_secret_test.go
+++ b/pkg/sharing/placeholder_secret_test.go
@@ -220,7 +220,7 @@ func Test_SecretReconciler_updatesStatus(t *testing.T) {
}
func placeholderReconcilers(objects ...runtime.Object) (secretExportReconciler *sharing.SecretExportReconciler, secretReconciler *sharing.SecretReconciler, k8sClient client.Client) {
k8sClient = fakeClient.NewFakeClient(objects...)
- secretExports := sharing.NewSecretExportsWarmedUp(sharing.NewSecretExports(testLogr))
+ secretExports := sharing.NewSecretExportsWarmedUp(sharing.NewSecretExports(k8sClient, testLogr))
secretExportReconciler = sharing.NewSecretExportReconciler(k8sClient, secretExports, testLogr)
secretExports.WarmUpFunc = secretExportReconciler.WarmUp
secretReconciler = sharing.NewSecretReconciler(k8sClient, secretExports, testLogr)
diff --git a/pkg/sharing/secret_exports.go b/pkg/sharing/secret_exports.go
index 1ce2e4ab1..c107aae1e 100644
--- a/pkg/sharing/secret_exports.go
+++ b/pkg/sharing/secret_exports.go
@@ -4,6 +4,7 @@
package sharing
import (
+ "bytes"
"context"
"encoding/json"
"fmt"
@@ -12,10 +13,11 @@ import (
"sync"
"github.com/go-logr/logr"
- "github.com/tidwall/gjson"
sg2v1alpha1 "github.com/vmware-tanzu/carvel-secretgen-controller/pkg/apis/secretgen2/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
+ "k8s.io/client-go/util/jsonpath"
+ "sigs.k8s.io/controller-runtime/pkg/client"
)
const (
@@ -42,17 +44,23 @@ type SecretExportsProvider interface {
// (SecretExports is used by SecretExportReconciler to export/unexport secrets;
// SecretExports is used by SecretReconciler to determine imported secrets.)
type SecretExports struct {
- log logr.Logger
+ log logr.Logger
+ k8sReader K8sReader
exportedSecretsLock sync.RWMutex
exportedSecrets map[string]exportedSecret
}
+// K8sReader is an interface for reading Kubernetes resources.
+type K8sReader interface {
+ Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error
+}
+
var _ SecretExportsProvider = &SecretExports{}
// NewSecretExports constructs new SecretExports cache.
-func NewSecretExports(log logr.Logger) *SecretExports {
- return &SecretExports{log: log, exportedSecrets: map[string]exportedSecret{}}
+func NewSecretExports(k8sReader K8sReader, log logr.Logger) *SecretExports {
+ return &SecretExports{log: log, k8sReader: k8sReader, exportedSecrets: map[string]exportedSecret{}}
}
// Export adds the in-memory representation (cached)
@@ -90,10 +98,15 @@ type SecretMatcher struct {
Subject string
SecretType corev1.SecretType
- SecretImportReconciler *SecretImportReconciler
- Ctx context.Context
+ Ctx context.Context
}
+// DJO add this instead of selectors := es.export.Spec.ToSelectorMatchFields
+// NamespacesMatcher allows to specify criteria for matching exported secrets based on namespaces fields.
+// type NamespacesMatcher{
+
+// }
+
// MatchedSecretsForImport filters secrets export cache by the given criteria.
// Returned order (last in the array is most specific):
// - secret with highest weight? (default weight=0), or
@@ -111,7 +124,7 @@ func (se *SecretExports) MatchedSecretsForImport(matcher SecretMatcher, nsIsExcl
var matched []exportedSecret
for _, exportedSec := range se.exportedSecrets {
- if exportedSec.Matches(matcher, nsIsExcludedFromWildcard, se.log) {
+ if exportedSec.Matches(matcher, nsIsExcludedFromWildcard, se.log, se.k8sReader) {
matched = append(matched, exportedSec)
}
}
@@ -162,7 +175,7 @@ func (es exportedSecret) Secret() *corev1.Secret {
return es.secret.DeepCopy()
}
-func (es exportedSecret) Matches(matcher SecretMatcher, nsIsExcludedFromWildcard NamespaceWildcardExclusionCheck, log logr.Logger) bool {
+func (es exportedSecret) Matches(matcher SecretMatcher, nsIsExcludedFromWildcard NamespaceWildcardExclusionCheck, log logr.Logger, k8sReader K8sReader) bool {
if matcher.Subject != "" {
// TODO we currently do not match by subject
@@ -188,19 +201,43 @@ func (es exportedSecret) Matches(matcher SecretMatcher, nsIsExcludedFromWildcard
}
selectors := es.export.Spec.ToSelectorMatchFields
- if len(selectors) > 0 {
+
+ isMatched := false
+
+ if es.matchesNamespace(matcher.ToNamespace, nsIsExcludedFromWildcard) {
+ isMatched = true
+ }
+
+ if !isMatched && len(selectors) > 0 {
nsName := matcher.ToNamespace
query := types.NamespacedName{
Name: nsName,
}
namespace := corev1.Namespace{}
- err := matcher.SecretImportReconciler.client.Get(matcher.Ctx, query, &namespace)
+ err := k8sReader.Get(matcher.Ctx, query, &namespace)
+
+ jsonNsString, _ := json.Marshal(namespace)
+ var jsonNsObject interface{}
+ json.Unmarshal(jsonNsString, &jsonNsObject)
+
if err == nil {
- jsonNs, _ := json.Marshal(namespace)
+
for _, s := range selectors {
+
+ jp := jsonpath.New("jsonpath")
+
+ jsonPathKey := "{." + s.Key + "}"
+ err := jp.Parse(jsonPathKey)
+ if err != nil {
+ log.Error(err, fmt.Sprintf("invalid jsonpath: %s", jsonPathKey))
+ return false
+ }
+ var valueBuffer bytes.Buffer
+ err = jp.Execute(&valueBuffer, jsonNsObject)
+ value := valueBuffer.String()
+
switch s.Operator {
case sg2v1alpha1.SelectorOperatorIn:
- value := gjson.GetBytes(jsonNs, s.Key).String()
found := false
for _, svalue := range s.Values {
if svalue == value {
@@ -212,30 +249,26 @@ func (es exportedSecret) Matches(matcher SecretMatcher, nsIsExcludedFromWildcard
return false
}
case sg2v1alpha1.SelectorOperatorNotIn:
- value := gjson.GetBytes(jsonNs, s.Key).String()
for _, svalue := range s.Values {
if svalue == value {
return false
}
}
case sg2v1alpha1.SelectorOperatorExists:
- if !gjson.GetBytes(jsonNs, s.Key).Exists() {
+ if value != "" {
return false
}
case sg2v1alpha1.SelectorOperatorDoesNotExist:
- if gjson.GetBytes(jsonNs, s.Key).Exists() {
+ if value != "" {
return false
}
}
}
- return true
+ isMatched = true
}
}
- if !es.matchesNamespace(matcher.ToNamespace, nsIsExcludedFromWildcard) {
- return false
- }
- return true
+ return isMatched
}
func (es exportedSecret) matchesNamespace(nsToMatch string, nsIsExcludedFromWildcard NamespaceWildcardExclusionCheck) bool {
diff --git a/pkg/sharing/secret_exports_test.go b/pkg/sharing/secret_exports_test.go
index 79cbd39dd..c9273ed10 100644
--- a/pkg/sharing/secret_exports_test.go
+++ b/pkg/sharing/secret_exports_test.go
@@ -13,12 +13,12 @@ import (
"github.com/vmware-tanzu/carvel-secretgen-controller/pkg/sharing"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ fakeClient "sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func TestSecretExports(t *testing.T) {
- t.Run("matching", func(t *testing.T) {
- se := sharing.NewSecretExports(logr.Discard())
+ t.Run("matching", func(t *testing.T) {
// Namespace does not match
secret1 := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: "secret1", Namespace: "ns1"},
@@ -31,7 +31,6 @@ func TestSecretExports(t *testing.T) {
ToNamespace: "wrong-ns",
},
}
- se.Export(export1, secret1)
// Secret type does not match
secret2 := &corev1.Secret{
@@ -45,6 +44,12 @@ func TestSecretExports(t *testing.T) {
ToNamespace: "dst-ns",
},
}
+
+ k8sClient := fakeClient.NewFakeClient(secret1, secret2, export1, export2)
+ se := sharing.NewSecretExports(k8sClient, logr.Discard())
+
+ se.Export(export1, secret1)
+
se.Export(export2, secret2)
nsCheck := func(string) bool { return false }
@@ -177,10 +182,13 @@ func TestSecretExports(t *testing.T) {
})
t.Run("returns secrets in specific order (last secret is most preferred)", func(t *testing.T) {
- se := sharing.NewSecretExports(logr.Discard())
// higher weight, but name comes earlier
- se.Export(&sg2v1alpha1.SecretExport{
+ secret1 := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{Name: "secret1-highest-weight", Namespace: "ns1"},
+ Type: "Opaque",
+ }
+ export1 := &sg2v1alpha1.SecretExport{
ObjectMeta: metav1.ObjectMeta{
Name: "secret1-highest-weight",
Namespace: "ns1",
@@ -189,13 +197,14 @@ func TestSecretExports(t *testing.T) {
},
},
Spec: sg2v1alpha1.SecretExportSpec{ToNamespace: "*"},
- }, &corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{Name: "secret1-highest-weight", Namespace: "ns1"},
- Type: "Opaque",
- })
+ }
// higher weight, but name comes later
- se.Export(&sg2v1alpha1.SecretExport{
+ secret2 := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{Name: "secret1a-highest-weight", Namespace: "ns1"},
+ Type: "Opaque",
+ }
+ export2 := &sg2v1alpha1.SecretExport{
ObjectMeta: metav1.ObjectMeta{
Name: "secret1a-highest-weight",
Namespace: "ns1",
@@ -204,12 +213,13 @@ func TestSecretExports(t *testing.T) {
},
},
Spec: sg2v1alpha1.SecretExportSpec{ToNamespace: "*"},
- }, &corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{Name: "secret1a-highest-weight", Namespace: "ns1"},
- Type: "Opaque",
- })
+ }
- se.Export(&sg2v1alpha1.SecretExport{
+ secret3 := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{Name: "secret2-low-weight", Namespace: "ns1"},
+ Type: "Opaque",
+ }
+ export3 := &sg2v1alpha1.SecretExport{
ObjectMeta: metav1.ObjectMeta{
Name: "secret2-low-weight",
Namespace: "ns1",
@@ -218,58 +228,76 @@ func TestSecretExports(t *testing.T) {
},
},
Spec: sg2v1alpha1.SecretExportSpec{ToNamespace: "*"},
- }, &corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{Name: "secret2-low-weight", Namespace: "ns1"},
- Type: "Opaque",
- })
+ }
// Wildcard ns match
- se.Export(&sg2v1alpha1.SecretExport{
+ secret4 := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{Name: "secret3-diff-ns-wildcard-ns", Namespace: "ns1"},
+ Type: "Opaque",
+ }
+ export4 := &sg2v1alpha1.SecretExport{
ObjectMeta: metav1.ObjectMeta{
Name: "secret3-diff-ns-wildcard-ns",
Namespace: "ns1",
},
Spec: sg2v1alpha1.SecretExportSpec{ToNamespace: "*"},
- }, &corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{Name: "secret3-diff-ns-wildcard-ns", Namespace: "ns1"},
- Type: "Opaque",
- })
+ }
// Specific ns match (even though there is a wildcard as well)
- se.Export(&sg2v1alpha1.SecretExport{
+ secret5 := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{Name: "secret4-diff-ns-specific-ns", Namespace: "ns1"},
+ Type: "Opaque",
+ }
+ export5 := &sg2v1alpha1.SecretExport{
ObjectMeta: metav1.ObjectMeta{
Name: "secret4-diff-ns-specific-ns",
Namespace: "ns1",
},
Spec: sg2v1alpha1.SecretExportSpec{ToNamespace: "dst-ns", ToNamespaces: []string{"*"}},
- }, &corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{Name: "secret4-diff-ns-specific-ns", Namespace: "ns1"},
- Type: "Opaque",
- })
+ }
+
+ k8sClient := fakeClient.NewFakeClient(secret1, secret2, export1, export2)
+ se := sharing.NewSecretExports(k8sClient, logr.Discard())
// Wildcard ns match in same namespace
- se.Export(&sg2v1alpha1.SecretExport{
+ secret6 := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{Name: "secret5-same-ns-wildcard-ns", Namespace: "dst-ns"},
+ Type: "Opaque",
+ }
+ export6 := &sg2v1alpha1.SecretExport{
ObjectMeta: metav1.ObjectMeta{
Name: "secret5-same-ns-wildcard-ns",
Namespace: "dst-ns",
},
Spec: sg2v1alpha1.SecretExportSpec{ToNamespace: "*"},
- }, &corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{Name: "secret5-same-ns-wildcard-ns", Namespace: "dst-ns"},
- Type: "Opaque",
- })
+ }
// Specific ns match (even though there is a wildcard as well)
- se.Export(&sg2v1alpha1.SecretExport{
+ secret7 := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{Name: "secret6-same-ns-specific-ns", Namespace: "dst-ns"},
+ Type: "Opaque",
+ }
+ export7 := &sg2v1alpha1.SecretExport{
ObjectMeta: metav1.ObjectMeta{
Name: "secret6-same-ns-specific-ns",
Namespace: "dst-ns",
},
Spec: sg2v1alpha1.SecretExportSpec{ToNamespace: "dst-ns", ToNamespaces: []string{"*"}},
- }, &corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{Name: "secret6-same-ns-specific-ns", Namespace: "dst-ns"},
- Type: "Opaque",
- })
+ }
+
+ se.Export(export1, secret1)
+
+ se.Export(export2, secret2)
+
+ se.Export(export3, secret3)
+
+ se.Export(export4, secret4)
+
+ se.Export(export5, secret5)
+
+ se.Export(export6, secret6)
+
+ se.Export(export7, secret7)
result := se.MatchedSecretsForImport(sharing.SecretMatcher{
ToNamespace: "dst-ns",
diff --git a/pkg/sharing/secret_import_reconciler.go b/pkg/sharing/secret_import_reconciler.go
index 625f5216a..941d7bafb 100644
--- a/pkg/sharing/secret_import_reconciler.go
+++ b/pkg/sharing/secret_import_reconciler.go
@@ -181,11 +181,10 @@ func (r *SecretImportReconciler) reconcile(
log.Info("Reconciling")
matcher := SecretMatcher{
- FromName: secretImport.Name,
- FromNamespace: secretImport.Spec.FromNamespace,
- ToNamespace: secretImport.Namespace,
- SecretImportReconciler: r,
- Ctx: ctx,
+ FromName: secretImport.Name,
+ FromNamespace: secretImport.Spec.FromNamespace,
+ ToNamespace: secretImport.Namespace,
+ Ctx: ctx,
}
nscheck := makeNamespaceWildcardExclusionCheck(ctx, r.client, log)
diff --git a/test/e2e/secret_exports_test.go b/test/e2e/secret_exports_test.go
index 49bb3289f..bc6d962f5 100644
--- a/test/e2e/secret_exports_test.go
+++ b/test/e2e/secret_exports_test.go
@@ -63,8 +63,8 @@ spec:
toNamespaces:
- sg-test2
- sg-test3
- toSelectorMatchFields:
- - key: "metadata.annotations.field\\.cattle\\.io/projectId"
+ dangerousToNamespacesSelector:
+ - key: "metadata.annotations['field\\.cattle\\.io/projectId']"
operator: In
values:
- "cluster1:project1"
diff --git a/vendor/github.com/tidwall/gjson/LICENSE b/vendor/github.com/tidwall/gjson/LICENSE
deleted file mode 100644
index 58f5819a4..000000000
--- a/vendor/github.com/tidwall/gjson/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2016 Josh Baker
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/tidwall/gjson/README.md b/vendor/github.com/tidwall/gjson/README.md
deleted file mode 100644
index c8db11f14..000000000
--- a/vendor/github.com/tidwall/gjson/README.md
+++ /dev/null
@@ -1,497 +0,0 @@
-
-
-
-
-
-
-
-
-
-get json values quickly
-
-GJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a json document.
-It has features such as [one line retrieval](#get-a-value), [dot notation paths](#path-syntax), [iteration](#iterate-through-an-object-or-array), and [parsing json lines](#json-lines).
-
-Also check out [SJSON](https://github.com/tidwall/sjson) for modifying json, and the [JJ](https://github.com/tidwall/jj) command line tool.
-
-This README is a quick overview of how to use GJSON, for more information check out [GJSON Syntax](SYNTAX.md).
-
-GJSON is also available for [Python](https://github.com/volans-/gjson-py) and [Rust](https://github.com/tidwall/gjson.rs)
-
-Getting Started
-===============
-
-## Installing
-
-To start using GJSON, install Go and run `go get`:
-
-```sh
-$ go get -u github.com/tidwall/gjson
-```
-
-This will retrieve the library.
-
-## Get a value
-Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". When the value is found it's returned immediately.
-
-```go
-package main
-
-import "github.com/tidwall/gjson"
-
-const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
-
-func main() {
- value := gjson.Get(json, "name.last")
- println(value.String())
-}
-```
-
-This will print:
-
-```
-Prichard
-```
-*There's also the [GetMany](#get-multiple-values-at-once) function to get multiple values at once, and [GetBytes](#working-with-bytes) for working with JSON byte slices.*
-
-## Path Syntax
-
-Below is a quick overview of the path syntax, for more complete information please
-check out [GJSON Syntax](SYNTAX.md).
-
-A path is a series of keys separated by a dot.
-A key may contain special wildcard characters '\*' and '?'.
-To access an array value use the index as the key.
-To get the number of elements in an array or to access a child path, use the '#' character.
-The dot and wildcard characters can be escaped with '\\'.
-
-```json
-{
- "name": {"first": "Tom", "last": "Anderson"},
- "age":37,
- "children": ["Sara","Alex","Jack"],
- "fav.movie": "Deer Hunter",
- "friends": [
- {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
- {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
- {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
- ]
-}
-```
-```
-"name.last" >> "Anderson"
-"age" >> 37
-"children" >> ["Sara","Alex","Jack"]
-"children.#" >> 3
-"children.1" >> "Alex"
-"child*.2" >> "Jack"
-"c?ildren.0" >> "Sara"
-"fav\.movie" >> "Deer Hunter"
-"friends.#.first" >> ["Dale","Roger","Jane"]
-"friends.1.last" >> "Craig"
-```
-
-You can also query an array for the first match by using `#(...)`, or find all
-matches with `#(...)#`. Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=`
-comparison operators and the simple pattern matching `%` (like) and `!%`
-(not like) operators.
-
-```
-friends.#(last=="Murphy").first >> "Dale"
-friends.#(last=="Murphy")#.first >> ["Dale","Jane"]
-friends.#(age>45)#.last >> ["Craig","Murphy"]
-friends.#(first%"D*").last >> "Murphy"
-friends.#(first!%"D*").last >> "Craig"
-friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"]
-```
-
-*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was
-changed in v1.3.0 as to avoid confusion with the new
-[multipath](SYNTAX.md#multipaths) syntax. For backwards compatibility,
-`#[...]` will continue to work until the next major release.*
-
-## Result Type
-
-GJSON supports the json types `string`, `number`, `bool`, and `null`.
-Arrays and Objects are returned as their raw json types.
-
-The `Result` type holds one of these:
-
-```
-bool, for JSON booleans
-float64, for JSON numbers
-string, for JSON string literals
-nil, for JSON null
-```
-
-To directly access the value:
-
-```go
-result.Type // can be String, Number, True, False, Null, or JSON
-result.Str // holds the string
-result.Num // holds the float64 number
-result.Raw // holds the raw json
-result.Index // index of raw value in original json, zero means index unknown
-result.Indexes // indexes of all the elements that match on a path containing the '#' query character.
-```
-
-There are a variety of handy functions that work on a result:
-
-```go
-result.Exists() bool
-result.Value() interface{}
-result.Int() int64
-result.Uint() uint64
-result.Float() float64
-result.String() string
-result.Bool() bool
-result.Time() time.Time
-result.Array() []gjson.Result
-result.Map() map[string]gjson.Result
-result.Get(path string) Result
-result.ForEach(iterator func(key, value Result) bool)
-result.Less(token Result, caseSensitive bool) bool
-```
-
-The `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types:
-
-```go
-boolean >> bool
-number >> float64
-string >> string
-null >> nil
-array >> []interface{}
-object >> map[string]interface{}
-```
-
-The `result.Array()` function returns back an array of values.
-If the result represents a non-existent value, then an empty array will be returned.
-If the result is not a JSON array, the return value will be an array containing one result.
-
-### 64-bit integers
-
-The `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits, allowing for large JSON integers.
-
-```go
-result.Int() int64 // -9223372036854775808 to 9223372036854775807
-result.Uint() uint64 // 0 to 18446744073709551615
-```
-
-## Modifiers and path chaining
-
-New in version 1.2 is support for modifier functions and path chaining.
-
-A modifier is a path component that performs custom processing on the
-json.
-
-Multiple paths can be "chained" together using the pipe character.
-This is useful for getting results from a modified query.
-
-For example, using the built-in `@reverse` modifier on the above json document,
-we'll get `children` array and reverse the order:
-
-```
-"children|@reverse" >> ["Jack","Alex","Sara"]
-"children|@reverse|0" >> "Jack"
-```
-
-There are currently the following built-in modifiers:
-
-- `@reverse`: Reverse an array or the members of an object.
-- `@ugly`: Remove all whitespace from a json document.
-- `@pretty`: Make the json document more human readable.
-- `@this`: Returns the current element. It can be used to retrieve the root element.
-- `@valid`: Ensure the json document is valid.
-- `@flatten`: Flattens an array.
-- `@join`: Joins multiple objects into a single object.
-- `@keys`: Returns an array of keys for an object.
-- `@values`: Returns an array of values for an object.
-- `@tostr`: Converts json to a string. Wraps a json string.
-- `@fromstr`: Converts a string from json. Unwraps a json string.
-- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db).
-
-### Modifier arguments
-
-A modifier may accept an optional argument. The argument can be a valid JSON
-document or just characters.
-
-For example, the `@pretty` modifier takes a json object as its argument.
-
-```
-@pretty:{"sortKeys":true}
-```
-
-Which makes the json pretty and orders all of its keys.
-
-```json
-{
- "age":37,
- "children": ["Sara","Alex","Jack"],
- "fav.movie": "Deer Hunter",
- "friends": [
- {"age": 44, "first": "Dale", "last": "Murphy"},
- {"age": 68, "first": "Roger", "last": "Craig"},
- {"age": 47, "first": "Jane", "last": "Murphy"}
- ],
- "name": {"first": "Tom", "last": "Anderson"}
-}
-```
-
-*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`.
-Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.*
-
-### Custom modifiers
-
-You can also add custom modifiers.
-
-For example, here we create a modifier that makes the entire json document upper
-or lower case.
-
-```go
-gjson.AddModifier("case", func(json, arg string) string {
- if arg == "upper" {
- return strings.ToUpper(json)
- }
- if arg == "lower" {
- return strings.ToLower(json)
- }
- return json
-})
-```
-
-```
-"children|@case:upper" >> ["SARA","ALEX","JACK"]
-"children|@case:lower|@reverse" >> ["jack","alex","sara"]
-```
-
-## JSON Lines
-
-There's support for [JSON Lines](http://jsonlines.org/) using the `..` prefix, which treats a multilined document as an array.
-
-For example:
-
-```
-{"name": "Gilbert", "age": 61}
-{"name": "Alexa", "age": 34}
-{"name": "May", "age": 57}
-{"name": "Deloise", "age": 44}
-```
-
-```
-..# >> 4
-..1 >> {"name": "Alexa", "age": 34}
-..3 >> {"name": "Deloise", "age": 44}
-..#.name >> ["Gilbert","Alexa","May","Deloise"]
-..#(name="May").age >> 57
-```
-
-The `ForEachLines` function will iterate through JSON lines.
-
-```go
-gjson.ForEachLine(json, func(line gjson.Result) bool{
- println(line.String())
- return true
-})
-```
-
-## Get nested array values
-
-Suppose you want all the last names from the following json:
-
-```json
-{
- "programmers": [
- {
- "firstName": "Janet",
- "lastName": "McLaughlin",
- }, {
- "firstName": "Elliotte",
- "lastName": "Hunter",
- }, {
- "firstName": "Jason",
- "lastName": "Harold",
- }
- ]
-}
-```
-
-You would use the path "programmers.#.lastName" like such:
-
-```go
-result := gjson.Get(json, "programmers.#.lastName")
-for _, name := range result.Array() {
- println(name.String())
-}
-```
-
-You can also query an object inside an array:
-
-```go
-name := gjson.Get(json, `programmers.#(lastName="Hunter").firstName`)
-println(name.String()) // prints "Elliotte"
-```
-
-## Iterate through an object or array
-
-The `ForEach` function allows for quickly iterating through an object or array.
-The key and value are passed to the iterator function for objects.
-Only the value is passed for arrays.
-Returning `false` from an iterator will stop iteration.
-
-```go
-result := gjson.Get(json, "programmers")
-result.ForEach(func(key, value gjson.Result) bool {
- println(value.String())
- return true // keep iterating
-})
-```
-
-## Simple Parse and Get
-
-There's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result.
-
-For example, all of these will return the same result:
-
-```go
-gjson.Parse(json).Get("name").Get("last")
-gjson.Get(json, "name").Get("last")
-gjson.Get(json, "name.last")
-```
-
-## Check for the existence of a value
-
-Sometimes you just want to know if a value exists.
-
-```go
-value := gjson.Get(json, "name.last")
-if !value.Exists() {
- println("no last name")
-} else {
- println(value.String())
-}
-
-// Or as one step
-if gjson.Get(json, "name.last").Exists() {
- println("has a last name")
-}
-```
-
-## Validate JSON
-
-The `Get*` and `Parse*` functions expects that the json is well-formed. Bad json will not panic, but it may return back unexpected results.
-
-If you are consuming JSON from an unpredictable source then you may want to validate prior to using GJSON.
-
-```go
-if !gjson.Valid(json) {
- return errors.New("invalid json")
-}
-value := gjson.Get(json, "name.last")
-```
-
-## Unmarshal to a map
-
-To unmarshal to a `map[string]interface{}`:
-
-```go
-m, ok := gjson.Parse(json).Value().(map[string]interface{})
-if !ok {
- // not a map
-}
-```
-
-## Working with Bytes
-
-If your JSON is contained in a `[]byte` slice, there's the [GetBytes](https://godoc.org/github.com/tidwall/gjson#GetBytes) function. This is preferred over `Get(string(data), path)`.
-
-```go
-var json []byte = ...
-result := gjson.GetBytes(json, path)
-```
-
-If you are using the `gjson.GetBytes(json, path)` function and you want to avoid converting `result.Raw` to a `[]byte`, then you can use this pattern:
-
-```go
-var json []byte = ...
-result := gjson.GetBytes(json, path)
-var raw []byte
-if result.Index > 0 {
- raw = json[result.Index:result.Index+len(result.Raw)]
-} else {
- raw = []byte(result.Raw)
-}
-```
-
-This is a best-effort no allocation sub slice of the original json. This method utilizes the `result.Index` field, which is the position of the raw data in the original json. It's possible that the value of `result.Index` equals zero, in which case the `result.Raw` is converted to a `[]byte`.
-
-## Get multiple values at once
-
-The `GetMany` function can be used to get multiple values at the same time.
-
-```go
-results := gjson.GetMany(json, "name.first", "name.last", "age")
-```
-
-The return value is a `[]Result`, which will always contain exactly the same number of items as the input paths.
-
-## Performance
-
-Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/),
-[ffjson](https://github.com/pquerna/ffjson),
-[EasyJSON](https://github.com/mailru/easyjson),
-[jsonparser](https://github.com/buger/jsonparser),
-and [json-iterator](https://github.com/json-iterator/go)
-
-```
-BenchmarkGJSONGet-16 11644512 311 ns/op 0 B/op 0 allocs/op
-BenchmarkGJSONUnmarshalMap-16 1122678 3094 ns/op 1920 B/op 26 allocs/op
-BenchmarkJSONUnmarshalMap-16 516681 6810 ns/op 2944 B/op 69 allocs/op
-BenchmarkJSONUnmarshalStruct-16 697053 5400 ns/op 928 B/op 13 allocs/op
-BenchmarkJSONDecoder-16 330450 10217 ns/op 3845 B/op 160 allocs/op
-BenchmarkFFJSONLexer-16 1424979 2585 ns/op 880 B/op 8 allocs/op
-BenchmarkEasyJSONLexer-16 3000000 729 ns/op 501 B/op 5 allocs/op
-BenchmarkJSONParserGet-16 3000000 366 ns/op 21 B/op 0 allocs/op
-BenchmarkJSONIterator-16 3000000 869 ns/op 693 B/op 14 allocs/op
-```
-
-JSON document used:
-
-```json
-{
- "widget": {
- "debug": "on",
- "window": {
- "title": "Sample Konfabulator Widget",
- "name": "main_window",
- "width": 500,
- "height": 500
- },
- "image": {
- "src": "Images/Sun.png",
- "hOffset": 250,
- "vOffset": 250,
- "alignment": "center"
- },
- "text": {
- "data": "Click Here",
- "size": 36,
- "style": "bold",
- "vOffset": 100,
- "alignment": "center",
- "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
- }
- }
-}
-```
-
-Each operation was rotated through one of the following search paths:
-
-```
-widget.window.name
-widget.image.hOffset
-widget.text.onMouseUp
-```
-
-*These benchmarks were run on a MacBook Pro 16" 2.4 GHz Intel Core i9 using Go 1.17 and can be found [here](https://github.com/tidwall/gjson-benchmarks).*
diff --git a/vendor/github.com/tidwall/gjson/SYNTAX.md b/vendor/github.com/tidwall/gjson/SYNTAX.md
deleted file mode 100644
index 7a9b6a2d7..000000000
--- a/vendor/github.com/tidwall/gjson/SYNTAX.md
+++ /dev/null
@@ -1,342 +0,0 @@
-# GJSON Path Syntax
-
-A GJSON Path is a text string syntax that describes a search pattern for quickly retreiving values from a JSON payload.
-
-This document is designed to explain the structure of a GJSON Path through examples.
-
-- [Path structure](#path-structure)
-- [Basic](#basic)
-- [Wildcards](#wildcards)
-- [Escape Character](#escape-character)
-- [Arrays](#arrays)
-- [Queries](#queries)
-- [Dot vs Pipe](#dot-vs-pipe)
-- [Modifiers](#modifiers)
-- [Multipaths](#multipaths)
-- [Literals](#literals)
-
-The definitive implemenation is [github.com/tidwall/gjson](https://github.com/tidwall/gjson).
-Use the [GJSON Playground](https://gjson.dev) to experiment with the syntax online.
-
-## Path structure
-
-A GJSON Path is intended to be easily expressed as a series of components seperated by a `.` character.
-
-Along with `.` character, there are a few more that have special meaning, including `|`, `#`, `@`, `\`, `*`, `!`, and `?`.
-
-## Example
-
-Given this JSON
-
-```json
-{
- "name": {"first": "Tom", "last": "Anderson"},
- "age":37,
- "children": ["Sara","Alex","Jack"],
- "fav.movie": "Deer Hunter",
- "friends": [
- {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
- {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
- {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
- ]
-}
-```
-
-The following GJSON Paths evaluate to the accompanying values.
-
-### Basic
-
-In many cases you'll just want to retreive values by object name or array index.
-
-```go
-name.last "Anderson"
-name.first "Tom"
-age 37
-children ["Sara","Alex","Jack"]
-children.0 "Sara"
-children.1 "Alex"
-friends.1 {"first": "Roger", "last": "Craig", "age": 68}
-friends.1.first "Roger"
-```
-
-### Wildcards
-
-A key may contain the special wildcard characters `*` and `?`.
-The `*` will match on any zero+ characters, and `?` matches on any one character.
-
-```go
-child*.2 "Jack"
-c?ildren.0 "Sara"
-```
-
-### Escape character
-
-Special purpose characters, such as `.`, `*`, and `?` can be escaped with `\`.
-
-```go
-fav\.movie "Deer Hunter"
-```
-
-You'll also need to make sure that the `\` character is correctly escaped when hardcoding a path in your source code.
-
-```go
-// Go
-val := gjson.Get(json, "fav\\.movie") // must escape the slash
-val := gjson.Get(json, `fav\.movie`) // no need to escape the slash
-```
-
-```rust
-// Rust
-let val = gjson::get(json, "fav\\.movie") // must escape the slash
-let val = gjson::get(json, r#"fav\.movie"#) // no need to escape the slash
-```
-
-
-### Arrays
-
-The `#` character allows for digging into JSON Arrays.
-
-To get the length of an array you'll just use the `#` all by itself.
-
-```go
-friends.# 3
-friends.#.age [44,68,47]
-```
-
-### Queries
-
-You can also query an array for the first match by using `#(...)`, or find all matches with `#(...)#`.
-Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` comparison operators,
-and the simple pattern matching `%` (like) and `!%` (not like) operators.
-
-```go
-friends.#(last=="Murphy").first "Dale"
-friends.#(last=="Murphy")#.first ["Dale","Jane"]
-friends.#(age>45)#.last ["Craig","Murphy"]
-friends.#(first%"D*").last "Murphy"
-friends.#(first!%"D*").last "Craig"
-```
-
-To query for a non-object value in an array, you can forgo the string to the right of the operator.
-
-```go
-children.#(!%"*a*") "Alex"
-children.#(%"*a*")# ["Sara","Jack"]
-```
-
-Nested queries are allowed.
-
-```go
-friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"]
-```
-
-*Please note that prior to v1.3.0, queries used the `#[...]` brackets. This was
-changed in v1.3.0 as to avoid confusion with the new [multipath](#multipaths)
-syntax. For backwards compatibility, `#[...]` will continue to work until the
-next major release.*
-
-The `~` (tilde) operator will convert a value to a boolean before comparison.
-
-For example, using the following JSON:
-
-```json
-{
- "vals": [
- { "a": 1, "b": true },
- { "a": 2, "b": true },
- { "a": 3, "b": false },
- { "a": 4, "b": "0" },
- { "a": 5, "b": 0 },
- { "a": 6, "b": "1" },
- { "a": 7, "b": 1 },
- { "a": 8, "b": "true" },
- { "a": 9, "b": false },
- { "a": 10, "b": null },
- { "a": 11 }
- ]
-}
-```
-
-You can now query for all true(ish) or false(ish) values:
-
-```
-vals.#(b==~true)#.a >> [1,2,6,7,8]
-vals.#(b==~false)#.a >> [3,4,5,9,10,11]
-```
-
-The last value which was non-existent is treated as `false`
-
-### Dot vs Pipe
-
-The `.` is standard separator, but it's also possible to use a `|`.
-In most cases they both end up returning the same results.
-The cases where`|` differs from `.` is when it's used after the `#` for [Arrays](#arrays) and [Queries](#queries).
-
-Here are some examples
-
-```go
-friends.0.first "Dale"
-friends|0.first "Dale"
-friends.0|first "Dale"
-friends|0|first "Dale"
-friends|# 3
-friends.# 3
-friends.#(last="Murphy")# [{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}]
-friends.#(last="Murphy")#.first ["Dale","Jane"]
-friends.#(last="Murphy")#|first
-friends.#(last="Murphy")#.0 []
-friends.#(last="Murphy")#|0 {"first": "Dale", "last": "Murphy", "age": 44}
-friends.#(last="Murphy")#.# []
-friends.#(last="Murphy")#|# 2
-```
-
-Let's break down a few of these.
-
-The path `friends.#(last="Murphy")#` all by itself results in
-
-```json
-[{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}]
-```
-
-The `.first` suffix will process the `first` path on each array element *before* returning the results. Which becomes
-
-```json
-["Dale","Jane"]
-```
-
-But the `|first` suffix actually processes the `first` path *after* the previous result.
-Since the previous result is an array, not an object, it's not possible to process
-because `first` does not exist.
-
-Yet, `|0` suffix returns
-
-```json
-{"first": "Dale", "last": "Murphy", "age": 44}
-```
-
-Because `0` is the first index of the previous result.
-
-### Modifiers
-
-A modifier is a path component that performs custom processing on the JSON.
-
-For example, using the built-in `@reverse` modifier on the above JSON payload will reverse the `children` array:
-
-```go
-children.@reverse ["Jack","Alex","Sara"]
-children.@reverse.0 "Jack"
-```
-
-There are currently the following built-in modifiers:
-
-- `@reverse`: Reverse an array or the members of an object.
-- `@ugly`: Remove all whitespace from JSON.
-- `@pretty`: Make the JSON more human readable.
-- `@this`: Returns the current element. It can be used to retrieve the root element.
-- `@valid`: Ensure the json document is valid.
-- `@flatten`: Flattens an array.
-- `@join`: Joins multiple objects into a single object.
-- `@keys`: Returns an array of keys for an object.
-- `@values`: Returns an array of values for an object.
-- `@tostr`: Converts json to a string. Wraps a json string.
-- `@fromstr`: Converts a string from json. Unwraps a json string.
-- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db).
-
-#### Modifier arguments
-
-A modifier may accept an optional argument. The argument can be a valid JSON payload or just characters.
-
-For example, the `@pretty` modifier takes a json object as its argument.
-
-```
-@pretty:{"sortKeys":true}
-```
-
-Which makes the json pretty and orders all of its keys.
-
-```json
-{
- "age":37,
- "children": ["Sara","Alex","Jack"],
- "fav.movie": "Deer Hunter",
- "friends": [
- {"age": 44, "first": "Dale", "last": "Murphy"},
- {"age": 68, "first": "Roger", "last": "Craig"},
- {"age": 47, "first": "Jane", "last": "Murphy"}
- ],
- "name": {"first": "Tom", "last": "Anderson"}
-}
-```
-
-*The full list of `@pretty` options are `sortKeys`, `indent`, `prefix`, and `width`.
-Please see [Pretty Options](https://github.com/tidwall/pretty#customized-output) for more information.*
-
-#### Custom modifiers
-
-You can also add custom modifiers.
-
-For example, here we create a modifier which makes the entire JSON payload upper or lower case.
-
-```go
-gjson.AddModifier("case", func(json, arg string) string {
- if arg == "upper" {
- return strings.ToUpper(json)
- }
- if arg == "lower" {
- return strings.ToLower(json)
- }
- return json
-})
-"children.@case:upper" ["SARA","ALEX","JACK"]
-"children.@case:lower.@reverse" ["jack","alex","sara"]
-```
-
-*Note: Custom modifiers are not yet available in the Rust version*
-
-### Multipaths
-
-Starting with v1.3.0, GJSON added the ability to join multiple paths together
-to form new documents. Wrapping comma-separated paths between `[...]` or
-`{...}` will result in a new array or object, respectively.
-
-For example, using the given multipath:
-
-```
-{name.first,age,"the_murphys":friends.#(last="Murphy")#.first}
-```
-
-Here we selected the first name, age, and the first name for friends with the
-last name "Murphy".
-
-You'll notice that an optional key can be provided, in this case
-"the_murphys", to force assign a key to a value. Otherwise, the name of the
-actual field will be used, in this case "first". If a name cannot be
-determined, then "_" is used.
-
-This results in
-
-```json
-{"first":"Tom","age":37,"the_murphys":["Dale","Jane"]}
-```
-
-### Literals
-
-Starting with v1.12.0, GJSON added support of json literals, which provides a way for constructing static blocks of json. This is can be particularly useful when constructing a new json document using [multipaths](#multipaths).
-
-A json literal begins with the '!' declaration character.
-
-For example, using the given multipath:
-
-```
-{name.first,age,"company":!"Happysoft","employed":!true}
-```
-
-Here we selected the first name and age. Then add two new fields, "company" and "employed".
-
-This results in
-
-```json
-{"first":"Tom","age":37,"company":"Happysoft","employed":true}
-```
-
-*See issue [#249](https://github.com/tidwall/gjson/issues/249) for additional context on JSON Literals.*
diff --git a/vendor/github.com/tidwall/gjson/gjson.go b/vendor/github.com/tidwall/gjson/gjson.go
deleted file mode 100644
index 53cbd2363..000000000
--- a/vendor/github.com/tidwall/gjson/gjson.go
+++ /dev/null
@@ -1,3359 +0,0 @@
-// Package gjson provides searching for json strings.
-package gjson
-
-import (
- "strconv"
- "strings"
- "time"
- "unicode/utf16"
- "unicode/utf8"
- "unsafe"
-
- "github.com/tidwall/match"
- "github.com/tidwall/pretty"
-)
-
-// Type is Result type
-type Type int
-
-const (
- // Null is a null json value
- Null Type = iota
- // False is a json false boolean
- False
- // Number is json number
- Number
- // String is a json string
- String
- // True is a json true boolean
- True
- // JSON is a raw block of JSON
- JSON
-)
-
-// String returns a string representation of the type.
-func (t Type) String() string {
- switch t {
- default:
- return ""
- case Null:
- return "Null"
- case False:
- return "False"
- case Number:
- return "Number"
- case String:
- return "String"
- case True:
- return "True"
- case JSON:
- return "JSON"
- }
-}
-
-// Result represents a json value that is returned from Get().
-type Result struct {
- // Type is the json type
- Type Type
- // Raw is the raw json
- Raw string
- // Str is the json string
- Str string
- // Num is the json number
- Num float64
- // Index of raw value in original json, zero means index unknown
- Index int
- // Indexes of all the elements that match on a path containing the '#'
- // query character.
- Indexes []int
-}
-
-// String returns a string representation of the value.
-func (t Result) String() string {
- switch t.Type {
- default:
- return ""
- case False:
- return "false"
- case Number:
- if len(t.Raw) == 0 {
- // calculated result
- return strconv.FormatFloat(t.Num, 'f', -1, 64)
- }
- var i int
- if t.Raw[0] == '-' {
- i++
- }
- for ; i < len(t.Raw); i++ {
- if t.Raw[i] < '0' || t.Raw[i] > '9' {
- return strconv.FormatFloat(t.Num, 'f', -1, 64)
- }
- }
- return t.Raw
- case String:
- return t.Str
- case JSON:
- return t.Raw
- case True:
- return "true"
- }
-}
-
-// Bool returns an boolean representation.
-func (t Result) Bool() bool {
- switch t.Type {
- default:
- return false
- case True:
- return true
- case String:
- b, _ := strconv.ParseBool(strings.ToLower(t.Str))
- return b
- case Number:
- return t.Num != 0
- }
-}
-
-// Int returns an integer representation.
-func (t Result) Int() int64 {
- switch t.Type {
- default:
- return 0
- case True:
- return 1
- case String:
- n, _ := parseInt(t.Str)
- return n
- case Number:
- // try to directly convert the float64 to int64
- i, ok := safeInt(t.Num)
- if ok {
- return i
- }
- // now try to parse the raw string
- i, ok = parseInt(t.Raw)
- if ok {
- return i
- }
- // fallback to a standard conversion
- return int64(t.Num)
- }
-}
-
-// Uint returns an unsigned integer representation.
-func (t Result) Uint() uint64 {
- switch t.Type {
- default:
- return 0
- case True:
- return 1
- case String:
- n, _ := parseUint(t.Str)
- return n
- case Number:
- // try to directly convert the float64 to uint64
- i, ok := safeInt(t.Num)
- if ok && i >= 0 {
- return uint64(i)
- }
- // now try to parse the raw string
- u, ok := parseUint(t.Raw)
- if ok {
- return u
- }
- // fallback to a standard conversion
- return uint64(t.Num)
- }
-}
-
-// Float returns an float64 representation.
-func (t Result) Float() float64 {
- switch t.Type {
- default:
- return 0
- case True:
- return 1
- case String:
- n, _ := strconv.ParseFloat(t.Str, 64)
- return n
- case Number:
- return t.Num
- }
-}
-
-// Time returns a time.Time representation.
-func (t Result) Time() time.Time {
- res, _ := time.Parse(time.RFC3339, t.String())
- return res
-}
-
-// Array returns back an array of values.
-// If the result represents a null value or is non-existent, then an empty
-// array will be returned.
-// If the result is not a JSON array, the return value will be an
-// array containing one result.
-func (t Result) Array() []Result {
- if t.Type == Null {
- return []Result{}
- }
- if !t.IsArray() {
- return []Result{t}
- }
- r := t.arrayOrMap('[', false)
- return r.a
-}
-
-// IsObject returns true if the result value is a JSON object.
-func (t Result) IsObject() bool {
- return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '{'
-}
-
-// IsArray returns true if the result value is a JSON array.
-func (t Result) IsArray() bool {
- return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '['
-}
-
-// IsBool returns true if the result value is a JSON boolean.
-func (t Result) IsBool() bool {
- return t.Type == True || t.Type == False
-}
-
-// ForEach iterates through values.
-// If the result represents a non-existent value, then no values will be
-// iterated. If the result is an Object, the iterator will pass the key and
-// value of each item. If the result is an Array, the iterator will only pass
-// the value of each item. If the result is not a JSON array or object, the
-// iterator will pass back one value equal to the result.
-func (t Result) ForEach(iterator func(key, value Result) bool) {
- if !t.Exists() {
- return
- }
- if t.Type != JSON {
- iterator(Result{}, t)
- return
- }
- json := t.Raw
- var obj bool
- var i int
- var key, value Result
- for ; i < len(json); i++ {
- if json[i] == '{' {
- i++
- key.Type = String
- obj = true
- break
- } else if json[i] == '[' {
- i++
- key.Type = Number
- key.Num = -1
- break
- }
- if json[i] > ' ' {
- return
- }
- }
- var str string
- var vesc bool
- var ok bool
- var idx int
- for ; i < len(json); i++ {
- if obj {
- if json[i] != '"' {
- continue
- }
- s := i
- i, str, vesc, ok = parseString(json, i+1)
- if !ok {
- return
- }
- if vesc {
- key.Str = unescape(str[1 : len(str)-1])
- } else {
- key.Str = str[1 : len(str)-1]
- }
- key.Raw = str
- key.Index = s + t.Index
- } else {
- key.Num += 1
- }
- for ; i < len(json); i++ {
- if json[i] <= ' ' || json[i] == ',' || json[i] == ':' {
- continue
- }
- break
- }
- s := i
- i, value, ok = parseAny(json, i, true)
- if !ok {
- return
- }
- if t.Indexes != nil {
- if idx < len(t.Indexes) {
- value.Index = t.Indexes[idx]
- }
- } else {
- value.Index = s + t.Index
- }
- if !iterator(key, value) {
- return
- }
- idx++
- }
-}
-
-// Map returns back a map of values. The result should be a JSON object.
-// If the result is not a JSON object, the return value will be an empty map.
-func (t Result) Map() map[string]Result {
- if t.Type != JSON {
- return map[string]Result{}
- }
- r := t.arrayOrMap('{', false)
- return r.o
-}
-
-// Get searches result for the specified path.
-// The result should be a JSON array or object.
-func (t Result) Get(path string) Result {
- r := Get(t.Raw, path)
- if r.Indexes != nil {
- for i := 0; i < len(r.Indexes); i++ {
- r.Indexes[i] += t.Index
- }
- } else {
- r.Index += t.Index
- }
- return r
-}
-
-type arrayOrMapResult struct {
- a []Result
- ai []interface{}
- o map[string]Result
- oi map[string]interface{}
- vc byte
-}
-
-func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) {
- var json = t.Raw
- var i int
- var value Result
- var count int
- var key Result
- if vc == 0 {
- for ; i < len(json); i++ {
- if json[i] == '{' || json[i] == '[' {
- r.vc = json[i]
- i++
- break
- }
- if json[i] > ' ' {
- goto end
- }
- }
- } else {
- for ; i < len(json); i++ {
- if json[i] == vc {
- i++
- break
- }
- if json[i] > ' ' {
- goto end
- }
- }
- r.vc = vc
- }
- if r.vc == '{' {
- if valueize {
- r.oi = make(map[string]interface{})
- } else {
- r.o = make(map[string]Result)
- }
- } else {
- if valueize {
- r.ai = make([]interface{}, 0)
- } else {
- r.a = make([]Result, 0)
- }
- }
- for ; i < len(json); i++ {
- if json[i] <= ' ' {
- continue
- }
- // get next value
- if json[i] == ']' || json[i] == '}' {
- break
- }
- switch json[i] {
- default:
- if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' {
- value.Type = Number
- value.Raw, value.Num = tonum(json[i:])
- value.Str = ""
- } else {
- continue
- }
- case '{', '[':
- value.Type = JSON
- value.Raw = squash(json[i:])
- value.Str, value.Num = "", 0
- case 'n':
- value.Type = Null
- value.Raw = tolit(json[i:])
- value.Str, value.Num = "", 0
- case 't':
- value.Type = True
- value.Raw = tolit(json[i:])
- value.Str, value.Num = "", 0
- case 'f':
- value.Type = False
- value.Raw = tolit(json[i:])
- value.Str, value.Num = "", 0
- case '"':
- value.Type = String
- value.Raw, value.Str = tostr(json[i:])
- value.Num = 0
- }
- value.Index = i + t.Index
-
- i += len(value.Raw) - 1
-
- if r.vc == '{' {
- if count%2 == 0 {
- key = value
- } else {
- if valueize {
- if _, ok := r.oi[key.Str]; !ok {
- r.oi[key.Str] = value.Value()
- }
- } else {
- if _, ok := r.o[key.Str]; !ok {
- r.o[key.Str] = value
- }
- }
- }
- count++
- } else {
- if valueize {
- r.ai = append(r.ai, value.Value())
- } else {
- r.a = append(r.a, value)
- }
- }
- }
-end:
- if t.Indexes != nil {
- if len(t.Indexes) != len(r.a) {
- for i := 0; i < len(r.a); i++ {
- r.a[i].Index = 0
- }
- } else {
- for i := 0; i < len(r.a); i++ {
- r.a[i].Index = t.Indexes[i]
- }
- }
- }
- return
-}
-
-// Parse parses the json and returns a result.
-//
-// This function expects that the json is well-formed, and does not validate.
-// Invalid json will not panic, but it may return back unexpected results.
-// If you are consuming JSON from an unpredictable source then you may want to
-// use the Valid function first.
-func Parse(json string) Result {
- var value Result
- i := 0
- for ; i < len(json); i++ {
- if json[i] == '{' || json[i] == '[' {
- value.Type = JSON
- value.Raw = json[i:] // just take the entire raw
- break
- }
- if json[i] <= ' ' {
- continue
- }
- switch json[i] {
- case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'i', 'I', 'N':
- value.Type = Number
- value.Raw, value.Num = tonum(json[i:])
- case 'n':
- if i+1 < len(json) && json[i+1] != 'u' {
- // nan
- value.Type = Number
- value.Raw, value.Num = tonum(json[i:])
- } else {
- // null
- value.Type = Null
- value.Raw = tolit(json[i:])
- }
- case 't':
- value.Type = True
- value.Raw = tolit(json[i:])
- case 'f':
- value.Type = False
- value.Raw = tolit(json[i:])
- case '"':
- value.Type = String
- value.Raw, value.Str = tostr(json[i:])
- default:
- return Result{}
- }
- break
- }
- if value.Exists() {
- value.Index = i
- }
- return value
-}
-
-// ParseBytes parses the json and returns a result.
-// If working with bytes, this method preferred over Parse(string(data))
-func ParseBytes(json []byte) Result {
- return Parse(string(json))
-}
-
-func squash(json string) string {
- // expects that the lead character is a '[' or '{' or '(' or '"'
- // squash the value, ignoring all nested arrays and objects.
- var i, depth int
- if json[0] != '"' {
- i, depth = 1, 1
- }
- for ; i < len(json); i++ {
- if json[i] >= '"' && json[i] <= '}' {
- switch json[i] {
- case '"':
- i++
- s2 := i
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > s2-1; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- break
- }
- }
- if depth == 0 {
- if i >= len(json) {
- return json
- }
- return json[:i+1]
- }
- case '{', '[', '(':
- depth++
- case '}', ']', ')':
- depth--
- if depth == 0 {
- return json[:i+1]
- }
- }
- }
- }
- return json
-}
-
-func tonum(json string) (raw string, num float64) {
- for i := 1; i < len(json); i++ {
- // less than dash might have valid characters
- if json[i] <= '-' {
- if json[i] <= ' ' || json[i] == ',' {
- // break on whitespace and comma
- raw = json[:i]
- num, _ = strconv.ParseFloat(raw, 64)
- return
- }
- // could be a '+' or '-'. let's assume so.
- } else if json[i] == ']' || json[i] == '}' {
- // break on ']' or '}'
- raw = json[:i]
- num, _ = strconv.ParseFloat(raw, 64)
- return
- }
- }
- raw = json
- num, _ = strconv.ParseFloat(raw, 64)
- return
-}
-
-func tolit(json string) (raw string) {
- for i := 1; i < len(json); i++ {
- if json[i] < 'a' || json[i] > 'z' {
- return json[:i]
- }
- }
- return json
-}
-
-func tostr(json string) (raw string, str string) {
- // expects that the lead character is a '"'
- for i := 1; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- return json[:i+1], json[1:i]
- }
- if json[i] == '\\' {
- i++
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > 0; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- return json[:i+1], unescape(json[1:i])
- }
- }
- var ret string
- if i+1 < len(json) {
- ret = json[:i+1]
- } else {
- ret = json[:i]
- }
- return ret, unescape(json[1:i])
- }
- }
- return json, json[1:]
-}
-
-// Exists returns true if value exists.
-//
-// if gjson.Get(json, "name.last").Exists(){
-// println("value exists")
-// }
-func (t Result) Exists() bool {
- return t.Type != Null || len(t.Raw) != 0
-}
-
-// Value returns one of these types:
-//
-// bool, for JSON booleans
-// float64, for JSON numbers
-// Number, for JSON numbers
-// string, for JSON string literals
-// nil, for JSON null
-// map[string]interface{}, for JSON objects
-// []interface{}, for JSON arrays
-//
-func (t Result) Value() interface{} {
- if t.Type == String {
- return t.Str
- }
- switch t.Type {
- default:
- return nil
- case False:
- return false
- case Number:
- return t.Num
- case JSON:
- r := t.arrayOrMap(0, true)
- if r.vc == '{' {
- return r.oi
- } else if r.vc == '[' {
- return r.ai
- }
- return nil
- case True:
- return true
- }
-}
-
-func parseString(json string, i int) (int, string, bool, bool) {
- var s = i
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- return i + 1, json[s-1 : i+1], false, true
- }
- if json[i] == '\\' {
- i++
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > 0; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- return i + 1, json[s-1 : i+1], true, true
- }
- }
- break
- }
- }
- return i, json[s-1:], false, false
-}
-
-func parseNumber(json string, i int) (int, string) {
- var s = i
- i++
- for ; i < len(json); i++ {
- if json[i] <= ' ' || json[i] == ',' || json[i] == ']' ||
- json[i] == '}' {
- return i, json[s:i]
- }
- }
- return i, json[s:]
-}
-
-func parseLiteral(json string, i int) (int, string) {
- var s = i
- i++
- for ; i < len(json); i++ {
- if json[i] < 'a' || json[i] > 'z' {
- return i, json[s:i]
- }
- }
- return i, json[s:]
-}
-
-type arrayPathResult struct {
- part string
- path string
- pipe string
- piped bool
- more bool
- alogok bool
- arrch bool
- alogkey string
- query struct {
- on bool
- all bool
- path string
- op string
- value string
- }
-}
-
-func parseArrayPath(path string) (r arrayPathResult) {
- for i := 0; i < len(path); i++ {
- if path[i] == '|' {
- r.part = path[:i]
- r.pipe = path[i+1:]
- r.piped = true
- return
- }
- if path[i] == '.' {
- r.part = path[:i]
- if !r.arrch && i < len(path)-1 && isDotPiperChar(path[i+1:]) {
- r.pipe = path[i+1:]
- r.piped = true
- } else {
- r.path = path[i+1:]
- r.more = true
- }
- return
- }
- if path[i] == '#' {
- r.arrch = true
- if i == 0 && len(path) > 1 {
- if path[1] == '.' {
- r.alogok = true
- r.alogkey = path[2:]
- r.path = path[:1]
- } else if path[1] == '[' || path[1] == '(' {
- // query
- r.query.on = true
- qpath, op, value, _, fi, vesc, ok :=
- parseQuery(path[i:])
- if !ok {
- // bad query, end now
- break
- }
- if len(value) >= 2 && value[0] == '"' &&
- value[len(value)-1] == '"' {
- value = value[1 : len(value)-1]
- if vesc {
- value = unescape(value)
- }
- }
- r.query.path = qpath
- r.query.op = op
- r.query.value = value
-
- i = fi - 1
- if i+1 < len(path) && path[i+1] == '#' {
- r.query.all = true
- }
- }
- }
- continue
- }
- }
- r.part = path
- r.path = ""
- return
-}
-
-// splitQuery takes a query and splits it into three parts:
-// path, op, middle, and right.
-// So for this query:
-// #(first_name=="Murphy").last
-// Becomes
-// first_name # path
-// =="Murphy" # middle
-// .last # right
-// Or,
-// #(service_roles.#(=="one")).cap
-// Becomes
-// service_roles.#(=="one") # path
-// # middle
-// .cap # right
-func parseQuery(query string) (
- path, op, value, remain string, i int, vesc, ok bool,
-) {
- if len(query) < 2 || query[0] != '#' ||
- (query[1] != '(' && query[1] != '[') {
- return "", "", "", "", i, false, false
- }
- i = 2
- j := 0 // start of value part
- depth := 1
- for ; i < len(query); i++ {
- if depth == 1 && j == 0 {
- switch query[i] {
- case '!', '=', '<', '>', '%':
- // start of the value part
- j = i
- continue
- }
- }
- if query[i] == '\\' {
- i++
- } else if query[i] == '[' || query[i] == '(' {
- depth++
- } else if query[i] == ']' || query[i] == ')' {
- depth--
- if depth == 0 {
- break
- }
- } else if query[i] == '"' {
- // inside selector string, balance quotes
- i++
- for ; i < len(query); i++ {
- if query[i] == '\\' {
- vesc = true
- i++
- } else if query[i] == '"' {
- break
- }
- }
- }
- }
- if depth > 0 {
- return "", "", "", "", i, false, false
- }
- if j > 0 {
- path = trim(query[2:j])
- value = trim(query[j:i])
- remain = query[i+1:]
- // parse the compare op from the value
- var opsz int
- switch {
- case len(value) == 1:
- opsz = 1
- case value[0] == '!' && value[1] == '=':
- opsz = 2
- case value[0] == '!' && value[1] == '%':
- opsz = 2
- case value[0] == '<' && value[1] == '=':
- opsz = 2
- case value[0] == '>' && value[1] == '=':
- opsz = 2
- case value[0] == '=' && value[1] == '=':
- value = value[1:]
- opsz = 1
- case value[0] == '<':
- opsz = 1
- case value[0] == '>':
- opsz = 1
- case value[0] == '=':
- opsz = 1
- case value[0] == '%':
- opsz = 1
- }
- op = value[:opsz]
- value = trim(value[opsz:])
- } else {
- path = trim(query[2:i])
- remain = query[i+1:]
- }
- return path, op, value, remain, i + 1, vesc, true
-}
-
-func trim(s string) string {
-left:
- if len(s) > 0 && s[0] <= ' ' {
- s = s[1:]
- goto left
- }
-right:
- if len(s) > 0 && s[len(s)-1] <= ' ' {
- s = s[:len(s)-1]
- goto right
- }
- return s
-}
-
-// peek at the next byte and see if it's a '@', '[', or '{'.
-func isDotPiperChar(s string) bool {
- if DisableModifiers {
- return false
- }
- c := s[0]
- if c == '@' {
- // check that the next component is *not* a modifier.
- i := 1
- for ; i < len(s); i++ {
- if s[i] == '.' || s[i] == '|' || s[i] == ':' {
- break
- }
- }
- _, ok := modifiers[s[1:i]]
- return ok
- }
- return c == '[' || c == '{'
-}
-
-type objectPathResult struct {
- part string
- path string
- pipe string
- piped bool
- wild bool
- more bool
-}
-
-func parseObjectPath(path string) (r objectPathResult) {
- for i := 0; i < len(path); i++ {
- if path[i] == '|' {
- r.part = path[:i]
- r.pipe = path[i+1:]
- r.piped = true
- return
- }
- if path[i] == '.' {
- r.part = path[:i]
- if i < len(path)-1 && isDotPiperChar(path[i+1:]) {
- r.pipe = path[i+1:]
- r.piped = true
- } else {
- r.path = path[i+1:]
- r.more = true
- }
- return
- }
- if path[i] == '*' || path[i] == '?' {
- r.wild = true
- continue
- }
- if path[i] == '\\' {
- // go into escape mode. this is a slower path that
- // strips off the escape character from the part.
- epart := []byte(path[:i])
- i++
- if i < len(path) {
- epart = append(epart, path[i])
- i++
- for ; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- if i < len(path) {
- epart = append(epart, path[i])
- }
- continue
- } else if path[i] == '.' {
- r.part = string(epart)
- if i < len(path)-1 && isDotPiperChar(path[i+1:]) {
- r.pipe = path[i+1:]
- r.piped = true
- } else {
- r.path = path[i+1:]
- r.more = true
- }
- return
- } else if path[i] == '|' {
- r.part = string(epart)
- r.pipe = path[i+1:]
- r.piped = true
- return
- } else if path[i] == '*' || path[i] == '?' {
- r.wild = true
- }
- epart = append(epart, path[i])
- }
- }
- // append the last part
- r.part = string(epart)
- return
- }
- }
- r.part = path
- return
-}
-
-func parseSquash(json string, i int) (int, string) {
- // expects that the lead character is a '[' or '{' or '('
- // squash the value, ignoring all nested arrays and objects.
- // the first '[' or '{' or '(' has already been read
- s := i
- i++
- depth := 1
- for ; i < len(json); i++ {
- if json[i] >= '"' && json[i] <= '}' {
- switch json[i] {
- case '"':
- i++
- s2 := i
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > s2-1; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- break
- }
- }
- case '{', '[', '(':
- depth++
- case '}', ']', ')':
- depth--
- if depth == 0 {
- i++
- return i, json[s:i]
- }
- }
- }
- }
- return i, json[s:]
-}
-
-func parseObject(c *parseContext, i int, path string) (int, bool) {
- var pmatch, kesc, vesc, ok, hit bool
- var key, val string
- rp := parseObjectPath(path)
- if !rp.more && rp.piped {
- c.pipe = rp.pipe
- c.piped = true
- }
- for i < len(c.json) {
- for ; i < len(c.json); i++ {
- if c.json[i] == '"' {
- // parse_key_string
- // this is slightly different from getting s string value
- // because we don't need the outer quotes.
- i++
- var s = i
- for ; i < len(c.json); i++ {
- if c.json[i] > '\\' {
- continue
- }
- if c.json[i] == '"' {
- i, key, kesc, ok = i+1, c.json[s:i], false, true
- goto parse_key_string_done
- }
- if c.json[i] == '\\' {
- i++
- for ; i < len(c.json); i++ {
- if c.json[i] > '\\' {
- continue
- }
- if c.json[i] == '"' {
- // look for an escaped slash
- if c.json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > 0; j-- {
- if c.json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- i, key, kesc, ok = i+1, c.json[s:i], true, true
- goto parse_key_string_done
- }
- }
- break
- }
- }
- key, kesc, ok = c.json[s:], false, false
- parse_key_string_done:
- break
- }
- if c.json[i] == '}' {
- return i + 1, false
- }
- }
- if !ok {
- return i, false
- }
- if rp.wild {
- if kesc {
- pmatch = matchLimit(unescape(key), rp.part)
- } else {
- pmatch = matchLimit(key, rp.part)
- }
- } else {
- if kesc {
- pmatch = rp.part == unescape(key)
- } else {
- pmatch = rp.part == key
- }
- }
- hit = pmatch && !rp.more
- for ; i < len(c.json); i++ {
- var num bool
- switch c.json[i] {
- default:
- continue
- case '"':
- i++
- i, val, vesc, ok = parseString(c.json, i)
- if !ok {
- return i, false
- }
- if hit {
- if vesc {
- c.value.Str = unescape(val[1 : len(val)-1])
- } else {
- c.value.Str = val[1 : len(val)-1]
- }
- c.value.Raw = val
- c.value.Type = String
- return i, true
- }
- case '{':
- if pmatch && !hit {
- i, hit = parseObject(c, i+1, rp.path)
- if hit {
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if hit {
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '[':
- if pmatch && !hit {
- i, hit = parseArray(c, i+1, rp.path)
- if hit {
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if hit {
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case 'n':
- if i+1 < len(c.json) && c.json[i+1] != 'u' {
- num = true
- break
- }
- fallthrough
- case 't', 'f':
- vc := c.json[i]
- i, val = parseLiteral(c.json, i)
- if hit {
- c.value.Raw = val
- switch vc {
- case 't':
- c.value.Type = True
- case 'f':
- c.value.Type = False
- }
- return i, true
- }
- case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'i', 'I', 'N':
- num = true
- }
- if num {
- i, val = parseNumber(c.json, i)
- if hit {
- c.value.Raw = val
- c.value.Type = Number
- c.value.Num, _ = strconv.ParseFloat(val, 64)
- return i, true
- }
- }
- break
- }
- }
- return i, false
-}
-
-// matchLimit will limit the complexity of the match operation to avoid ReDos
-// attacks from arbritary inputs.
-// See the github.com/tidwall/match.MatchLimit function for more information.
-func matchLimit(str, pattern string) bool {
- matched, _ := match.MatchLimit(str, pattern, 10000)
- return matched
-}
-
-func queryMatches(rp *arrayPathResult, value Result) bool {
- rpv := rp.query.value
- if len(rpv) > 0 && rpv[0] == '~' {
- // convert to bool
- rpv = rpv[1:]
- if value.Bool() {
- value = Result{Type: True}
- } else {
- value = Result{Type: False}
- }
- }
- if !value.Exists() {
- return false
- }
- if rp.query.op == "" {
- // the query is only looking for existence, such as:
- // friends.#(name)
- // which makes sure that the array "friends" has an element of
- // "name" that exists
- return true
- }
- switch value.Type {
- case String:
- switch rp.query.op {
- case "=":
- return value.Str == rpv
- case "!=":
- return value.Str != rpv
- case "<":
- return value.Str < rpv
- case "<=":
- return value.Str <= rpv
- case ">":
- return value.Str > rpv
- case ">=":
- return value.Str >= rpv
- case "%":
- return matchLimit(value.Str, rpv)
- case "!%":
- return !matchLimit(value.Str, rpv)
- }
- case Number:
- rpvn, _ := strconv.ParseFloat(rpv, 64)
- switch rp.query.op {
- case "=":
- return value.Num == rpvn
- case "!=":
- return value.Num != rpvn
- case "<":
- return value.Num < rpvn
- case "<=":
- return value.Num <= rpvn
- case ">":
- return value.Num > rpvn
- case ">=":
- return value.Num >= rpvn
- }
- case True:
- switch rp.query.op {
- case "=":
- return rpv == "true"
- case "!=":
- return rpv != "true"
- case ">":
- return rpv == "false"
- case ">=":
- return true
- }
- case False:
- switch rp.query.op {
- case "=":
- return rpv == "false"
- case "!=":
- return rpv != "false"
- case "<":
- return rpv == "true"
- case "<=":
- return true
- }
- }
- return false
-}
-func parseArray(c *parseContext, i int, path string) (int, bool) {
- var pmatch, vesc, ok, hit bool
- var val string
- var h int
- var alog []int
- var partidx int
- var multires []byte
- var queryIndexes []int
- rp := parseArrayPath(path)
- if !rp.arrch {
- n, ok := parseUint(rp.part)
- if !ok {
- partidx = -1
- } else {
- partidx = int(n)
- }
- }
- if !rp.more && rp.piped {
- c.pipe = rp.pipe
- c.piped = true
- }
-
- procQuery := func(qval Result) bool {
- if rp.query.all {
- if len(multires) == 0 {
- multires = append(multires, '[')
- }
- }
- var tmp parseContext
- tmp.value = qval
- fillIndex(c.json, &tmp)
- parentIndex := tmp.value.Index
- var res Result
- if qval.Type == JSON {
- res = qval.Get(rp.query.path)
- } else {
- if rp.query.path != "" {
- return false
- }
- res = qval
- }
- if queryMatches(&rp, res) {
- if rp.more {
- left, right, ok := splitPossiblePipe(rp.path)
- if ok {
- rp.path = left
- c.pipe = right
- c.piped = true
- }
- res = qval.Get(rp.path)
- } else {
- res = qval
- }
- if rp.query.all {
- raw := res.Raw
- if len(raw) == 0 {
- raw = res.String()
- }
- if raw != "" {
- if len(multires) > 1 {
- multires = append(multires, ',')
- }
- multires = append(multires, raw...)
- queryIndexes = append(queryIndexes, res.Index+parentIndex)
- }
- } else {
- c.value = res
- return true
- }
- }
- return false
- }
- for i < len(c.json)+1 {
- if !rp.arrch {
- pmatch = partidx == h
- hit = pmatch && !rp.more
- }
- h++
- if rp.alogok {
- alog = append(alog, i)
- }
- for ; ; i++ {
- var ch byte
- if i > len(c.json) {
- break
- } else if i == len(c.json) {
- ch = ']'
- } else {
- ch = c.json[i]
- }
- var num bool
- switch ch {
- default:
- continue
- case '"':
- i++
- i, val, vesc, ok = parseString(c.json, i)
- if !ok {
- return i, false
- }
- if rp.query.on {
- var qval Result
- if vesc {
- qval.Str = unescape(val[1 : len(val)-1])
- } else {
- qval.Str = val[1 : len(val)-1]
- }
- qval.Raw = val
- qval.Type = String
- if procQuery(qval) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- if vesc {
- c.value.Str = unescape(val[1 : len(val)-1])
- } else {
- c.value.Str = val[1 : len(val)-1]
- }
- c.value.Raw = val
- c.value.Type = String
- return i, true
- }
- case '{':
- if pmatch && !hit {
- i, hit = parseObject(c, i+1, rp.path)
- if hit {
- if rp.alogok {
- break
- }
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if rp.query.on {
- if procQuery(Result{Raw: val, Type: JSON}) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '[':
- if pmatch && !hit {
- i, hit = parseArray(c, i+1, rp.path)
- if hit {
- if rp.alogok {
- break
- }
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if rp.query.on {
- if procQuery(Result{Raw: val, Type: JSON}) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case 'n':
- if i+1 < len(c.json) && c.json[i+1] != 'u' {
- num = true
- break
- }
- fallthrough
- case 't', 'f':
- vc := c.json[i]
- i, val = parseLiteral(c.json, i)
- if rp.query.on {
- var qval Result
- qval.Raw = val
- switch vc {
- case 't':
- qval.Type = True
- case 'f':
- qval.Type = False
- }
- if procQuery(qval) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- switch vc {
- case 't':
- c.value.Type = True
- case 'f':
- c.value.Type = False
- }
- return i, true
- }
- case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'i', 'I', 'N':
- num = true
- case ']':
- if rp.arrch && rp.part == "#" {
- if rp.alogok {
- left, right, ok := splitPossiblePipe(rp.alogkey)
- if ok {
- rp.alogkey = left
- c.pipe = right
- c.piped = true
- }
- var indexes = make([]int, 0, 64)
- var jsons = make([]byte, 0, 64)
- jsons = append(jsons, '[')
- for j, k := 0, 0; j < len(alog); j++ {
- idx := alog[j]
- for idx < len(c.json) {
- switch c.json[idx] {
- case ' ', '\t', '\r', '\n':
- idx++
- continue
- }
- break
- }
- if idx < len(c.json) && c.json[idx] != ']' {
- _, res, ok := parseAny(c.json, idx, true)
- if ok {
- res := res.Get(rp.alogkey)
- if res.Exists() {
- if k > 0 {
- jsons = append(jsons, ',')
- }
- raw := res.Raw
- if len(raw) == 0 {
- raw = res.String()
- }
- jsons = append(jsons, []byte(raw)...)
- indexes = append(indexes, res.Index)
- k++
- }
- }
- }
- }
- jsons = append(jsons, ']')
- c.value.Type = JSON
- c.value.Raw = string(jsons)
- c.value.Indexes = indexes
- return i + 1, true
- }
- if rp.alogok {
- break
- }
-
- c.value.Type = Number
- c.value.Num = float64(h - 1)
- c.value.Raw = strconv.Itoa(h - 1)
- c.calcd = true
- return i + 1, true
- }
- if !c.value.Exists() {
- if len(multires) > 0 {
- c.value = Result{
- Raw: string(append(multires, ']')),
- Type: JSON,
- Indexes: queryIndexes,
- }
- } else if rp.query.all {
- c.value = Result{
- Raw: "[]",
- Type: JSON,
- }
- }
- }
- return i + 1, false
- }
- if num {
- i, val = parseNumber(c.json, i)
- if rp.query.on {
- var qval Result
- qval.Raw = val
- qval.Type = Number
- qval.Num, _ = strconv.ParseFloat(val, 64)
- if procQuery(qval) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = Number
- c.value.Num, _ = strconv.ParseFloat(val, 64)
- return i, true
- }
- }
- break
- }
- }
- return i, false
-}
-
-func splitPossiblePipe(path string) (left, right string, ok bool) {
- // take a quick peek for the pipe character. If found we'll split the piped
- // part of the path into the c.pipe field and shorten the rp.
- var possible bool
- for i := 0; i < len(path); i++ {
- if path[i] == '|' {
- possible = true
- break
- }
- }
- if !possible {
- return
- }
-
- if len(path) > 0 && path[0] == '{' {
- squashed := squash(path[1:])
- if len(squashed) < len(path)-1 {
- squashed = path[:len(squashed)+1]
- remain := path[len(squashed):]
- if remain[0] == '|' {
- return squashed, remain[1:], true
- }
- }
- return
- }
-
- // split the left and right side of the path with the pipe character as
- // the delimiter. This is a little tricky because we'll need to basically
- // parse the entire path.
- for i := 0; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- } else if path[i] == '.' {
- if i == len(path)-1 {
- return
- }
- if path[i+1] == '#' {
- i += 2
- if i == len(path) {
- return
- }
- if path[i] == '[' || path[i] == '(' {
- var start, end byte
- if path[i] == '[' {
- start, end = '[', ']'
- } else {
- start, end = '(', ')'
- }
- // inside selector, balance brackets
- i++
- depth := 1
- for ; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- } else if path[i] == start {
- depth++
- } else if path[i] == end {
- depth--
- if depth == 0 {
- break
- }
- } else if path[i] == '"' {
- // inside selector string, balance quotes
- i++
- for ; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- } else if path[i] == '"' {
- break
- }
- }
- }
- }
- }
- }
- } else if path[i] == '|' {
- return path[:i], path[i+1:], true
- }
- }
- return
-}
-
-// ForEachLine iterates through lines of JSON as specified by the JSON Lines
-// format (http://jsonlines.org/).
-// Each line is returned as a GJSON Result.
-func ForEachLine(json string, iterator func(line Result) bool) {
- var res Result
- var i int
- for {
- i, res, _ = parseAny(json, i, true)
- if !res.Exists() {
- break
- }
- if !iterator(res) {
- return
- }
- }
-}
-
-type subSelector struct {
- name string
- path string
-}
-
-// parseSubSelectors returns the subselectors belonging to a '[path1,path2]' or
-// '{"field1":path1,"field2":path2}' type subSelection. It's expected that the
-// first character in path is either '[' or '{', and has already been checked
-// prior to calling this function.
-func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {
- modifier := 0
- depth := 1
- colon := 0
- start := 1
- i := 1
- pushSel := func() {
- var sel subSelector
- if colon == 0 {
- sel.path = path[start:i]
- } else {
- sel.name = path[start:colon]
- sel.path = path[colon+1 : i]
- }
- sels = append(sels, sel)
- colon = 0
- modifier = 0
- start = i + 1
- }
- for ; i < len(path); i++ {
- switch path[i] {
- case '\\':
- i++
- case '@':
- if modifier == 0 && i > 0 && (path[i-1] == '.' || path[i-1] == '|') {
- modifier = i
- }
- case ':':
- if modifier == 0 && colon == 0 && depth == 1 {
- colon = i
- }
- case ',':
- if depth == 1 {
- pushSel()
- }
- case '"':
- i++
- loop:
- for ; i < len(path); i++ {
- switch path[i] {
- case '\\':
- i++
- case '"':
- break loop
- }
- }
- case '[', '(', '{':
- depth++
- case ']', ')', '}':
- depth--
- if depth == 0 {
- pushSel()
- path = path[i+1:]
- return sels, path, true
- }
- }
- }
- return
-}
-
-// nameOfLast returns the name of the last component
-func nameOfLast(path string) string {
- for i := len(path) - 1; i >= 0; i-- {
- if path[i] == '|' || path[i] == '.' {
- if i > 0 {
- if path[i-1] == '\\' {
- continue
- }
- }
- return path[i+1:]
- }
- }
- return path
-}
-
-func isSimpleName(component string) bool {
- for i := 0; i < len(component); i++ {
- if component[i] < ' ' {
- return false
- }
- switch component[i] {
- case '[', ']', '{', '}', '(', ')', '#', '|', '!':
- return false
- }
- }
- return true
-}
-
-var hexchars = [...]byte{
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'a', 'b', 'c', 'd', 'e', 'f',
-}
-
-func appendHex16(dst []byte, x uint16) []byte {
- return append(dst,
- hexchars[x>>12&0xF], hexchars[x>>8&0xF],
- hexchars[x>>4&0xF], hexchars[x>>0&0xF],
- )
-}
-
-// AppendJSONString is a convenience function that converts the provided string
-// to a valid JSON string and appends it to dst.
-func AppendJSONString(dst []byte, s string) []byte {
- dst = append(dst, make([]byte, len(s)+2)...)
- dst = append(dst[:len(dst)-len(s)-2], '"')
- for i := 0; i < len(s); i++ {
- if s[i] < ' ' {
- dst = append(dst, '\\')
- switch s[i] {
- case '\n':
- dst = append(dst, 'n')
- case '\r':
- dst = append(dst, 'r')
- case '\t':
- dst = append(dst, 't')
- default:
- dst = append(dst, 'u')
- dst = appendHex16(dst, uint16(s[i]))
- }
- } else if s[i] == '>' || s[i] == '<' || s[i] == '&' {
- dst = append(dst, '\\', 'u')
- dst = appendHex16(dst, uint16(s[i]))
- } else if s[i] == '\\' {
- dst = append(dst, '\\', '\\')
- } else if s[i] == '"' {
- dst = append(dst, '\\', '"')
- } else if s[i] > 127 {
- // read utf8 character
- r, n := utf8.DecodeRuneInString(s[i:])
- if n == 0 {
- break
- }
- if r == utf8.RuneError && n == 1 {
- dst = append(dst, `\ufffd`...)
- } else if r == '\u2028' || r == '\u2029' {
- dst = append(dst, `\u202`...)
- dst = append(dst, hexchars[r&0xF])
- } else {
- dst = append(dst, s[i:i+n]...)
- }
- i = i + n - 1
- } else {
- dst = append(dst, s[i])
- }
- }
- return append(dst, '"')
-}
-
-type parseContext struct {
- json string
- value Result
- pipe string
- piped bool
- calcd bool
- lines bool
-}
-
-// Get searches json for the specified path.
-// A path is in dot syntax, such as "name.last" or "age".
-// When the value is found it's returned immediately.
-//
-// A path is a series of keys separated by a dot.
-// A key may contain special wildcard characters '*' and '?'.
-// To access an array value use the index as the key.
-// To get the number of elements in an array or to access a child path, use
-// the '#' character.
-// The dot and wildcard character can be escaped with '\'.
-//
-// {
-// "name": {"first": "Tom", "last": "Anderson"},
-// "age":37,
-// "children": ["Sara","Alex","Jack"],
-// "friends": [
-// {"first": "James", "last": "Murphy"},
-// {"first": "Roger", "last": "Craig"}
-// ]
-// }
-// "name.last" >> "Anderson"
-// "age" >> 37
-// "children" >> ["Sara","Alex","Jack"]
-// "children.#" >> 3
-// "children.1" >> "Alex"
-// "child*.2" >> "Jack"
-// "c?ildren.0" >> "Sara"
-// "friends.#.first" >> ["James","Roger"]
-//
-// This function expects that the json is well-formed, and does not validate.
-// Invalid json will not panic, but it may return back unexpected results.
-// If you are consuming JSON from an unpredictable source then you may want to
-// use the Valid function first.
-func Get(json, path string) Result {
- if len(path) > 1 {
- if (path[0] == '@' && !DisableModifiers) || path[0] == '!' {
- // possible modifier
- var ok bool
- var npath string
- var rjson string
- if path[0] == '@' && !DisableModifiers {
- npath, rjson, ok = execModifier(json, path)
- } else if path[0] == '!' {
- npath, rjson, ok = execStatic(json, path)
- }
- if ok {
- path = npath
- if len(path) > 0 && (path[0] == '|' || path[0] == '.') {
- res := Get(rjson, path[1:])
- res.Index = 0
- res.Indexes = nil
- return res
- }
- return Parse(rjson)
- }
- }
- if path[0] == '[' || path[0] == '{' {
- // using a subselector path
- kind := path[0]
- var ok bool
- var subs []subSelector
- subs, path, ok = parseSubSelectors(path)
- if ok {
- if len(path) == 0 || (path[0] == '|' || path[0] == '.') {
- var b []byte
- b = append(b, kind)
- var i int
- for _, sub := range subs {
- res := Get(json, sub.path)
- if res.Exists() {
- if i > 0 {
- b = append(b, ',')
- }
- if kind == '{' {
- if len(sub.name) > 0 {
- if sub.name[0] == '"' && Valid(sub.name) {
- b = append(b, sub.name...)
- } else {
- b = AppendJSONString(b, sub.name)
- }
- } else {
- last := nameOfLast(sub.path)
- if isSimpleName(last) {
- b = AppendJSONString(b, last)
- } else {
- b = AppendJSONString(b, "_")
- }
- }
- b = append(b, ':')
- }
- var raw string
- if len(res.Raw) == 0 {
- raw = res.String()
- if len(raw) == 0 {
- raw = "null"
- }
- } else {
- raw = res.Raw
- }
- b = append(b, raw...)
- i++
- }
- }
- b = append(b, kind+2)
- var res Result
- res.Raw = string(b)
- res.Type = JSON
- if len(path) > 0 {
- res = res.Get(path[1:])
- }
- res.Index = 0
- return res
- }
- }
- }
- }
- var i int
- var c = &parseContext{json: json}
- if len(path) >= 2 && path[0] == '.' && path[1] == '.' {
- c.lines = true
- parseArray(c, 0, path[2:])
- } else {
- for ; i < len(c.json); i++ {
- if c.json[i] == '{' {
- i++
- parseObject(c, i, path)
- break
- }
- if c.json[i] == '[' {
- i++
- parseArray(c, i, path)
- break
- }
- }
- }
- if c.piped {
- res := c.value.Get(c.pipe)
- res.Index = 0
- return res
- }
- fillIndex(json, c)
- return c.value
-}
-
-// GetBytes searches json for the specified path.
-// If working with bytes, this method preferred over Get(string(data), path)
-func GetBytes(json []byte, path string) Result {
- return getBytes(json, path)
-}
-
-// runeit returns the rune from the the \uXXXX
-func runeit(json string) rune {
- n, _ := strconv.ParseUint(json[:4], 16, 64)
- return rune(n)
-}
-
-// unescape unescapes a string
-func unescape(json string) string {
- var str = make([]byte, 0, len(json))
- for i := 0; i < len(json); i++ {
- switch {
- default:
- str = append(str, json[i])
- case json[i] < ' ':
- return string(str)
- case json[i] == '\\':
- i++
- if i >= len(json) {
- return string(str)
- }
- switch json[i] {
- default:
- return string(str)
- case '\\':
- str = append(str, '\\')
- case '/':
- str = append(str, '/')
- case 'b':
- str = append(str, '\b')
- case 'f':
- str = append(str, '\f')
- case 'n':
- str = append(str, '\n')
- case 'r':
- str = append(str, '\r')
- case 't':
- str = append(str, '\t')
- case '"':
- str = append(str, '"')
- case 'u':
- if i+5 > len(json) {
- return string(str)
- }
- r := runeit(json[i+1:])
- i += 5
- if utf16.IsSurrogate(r) {
- // need another code
- if len(json[i:]) >= 6 && json[i] == '\\' &&
- json[i+1] == 'u' {
- // we expect it to be correct so just consume it
- r = utf16.DecodeRune(r, runeit(json[i+2:]))
- i += 6
- }
- }
- // provide enough space to encode the largest utf8 possible
- str = append(str, 0, 0, 0, 0, 0, 0, 0, 0)
- n := utf8.EncodeRune(str[len(str)-8:], r)
- str = str[:len(str)-8+n]
- i-- // backtrack index by one
- }
- }
- }
- return string(str)
-}
-
-// Less return true if a token is less than another token.
-// The caseSensitive paramater is used when the tokens are Strings.
-// The order when comparing two different type is:
-//
-// Null < False < Number < String < True < JSON
-//
-func (t Result) Less(token Result, caseSensitive bool) bool {
- if t.Type < token.Type {
- return true
- }
- if t.Type > token.Type {
- return false
- }
- if t.Type == String {
- if caseSensitive {
- return t.Str < token.Str
- }
- return stringLessInsensitive(t.Str, token.Str)
- }
- if t.Type == Number {
- return t.Num < token.Num
- }
- return t.Raw < token.Raw
-}
-
-func stringLessInsensitive(a, b string) bool {
- for i := 0; i < len(a) && i < len(b); i++ {
- if a[i] >= 'A' && a[i] <= 'Z' {
- if b[i] >= 'A' && b[i] <= 'Z' {
- // both are uppercase, do nothing
- if a[i] < b[i] {
- return true
- } else if a[i] > b[i] {
- return false
- }
- } else {
- // a is uppercase, convert a to lowercase
- if a[i]+32 < b[i] {
- return true
- } else if a[i]+32 > b[i] {
- return false
- }
- }
- } else if b[i] >= 'A' && b[i] <= 'Z' {
- // b is uppercase, convert b to lowercase
- if a[i] < b[i]+32 {
- return true
- } else if a[i] > b[i]+32 {
- return false
- }
- } else {
- // neither are uppercase
- if a[i] < b[i] {
- return true
- } else if a[i] > b[i] {
- return false
- }
- }
- }
- return len(a) < len(b)
-}
-
-// parseAny parses the next value from a json string.
-// A Result is returned when the hit param is set.
-// The return values are (i int, res Result, ok bool)
-func parseAny(json string, i int, hit bool) (int, Result, bool) {
- var res Result
- var val string
- for ; i < len(json); i++ {
- if json[i] == '{' || json[i] == '[' {
- i, val = parseSquash(json, i)
- if hit {
- res.Raw = val
- res.Type = JSON
- }
- var tmp parseContext
- tmp.value = res
- fillIndex(json, &tmp)
- return i, tmp.value, true
- }
- if json[i] <= ' ' {
- continue
- }
- var num bool
- switch json[i] {
- case '"':
- i++
- var vesc bool
- var ok bool
- i, val, vesc, ok = parseString(json, i)
- if !ok {
- return i, res, false
- }
- if hit {
- res.Type = String
- res.Raw = val
- if vesc {
- res.Str = unescape(val[1 : len(val)-1])
- } else {
- res.Str = val[1 : len(val)-1]
- }
- }
- return i, res, true
- case 'n':
- if i+1 < len(json) && json[i+1] != 'u' {
- num = true
- break
- }
- fallthrough
- case 't', 'f':
- vc := json[i]
- i, val = parseLiteral(json, i)
- if hit {
- res.Raw = val
- switch vc {
- case 't':
- res.Type = True
- case 'f':
- res.Type = False
- }
- return i, res, true
- }
- case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'i', 'I', 'N':
- num = true
- }
- if num {
- i, val = parseNumber(json, i)
- if hit {
- res.Raw = val
- res.Type = Number
- res.Num, _ = strconv.ParseFloat(val, 64)
- }
- return i, res, true
- }
-
- }
- return i, res, false
-}
-
-// GetMany searches json for the multiple paths.
-// The return value is a Result array where the number of items
-// will be equal to the number of input paths.
-func GetMany(json string, path ...string) []Result {
- res := make([]Result, len(path))
- for i, path := range path {
- res[i] = Get(json, path)
- }
- return res
-}
-
-// GetManyBytes searches json for the multiple paths.
-// The return value is a Result array where the number of items
-// will be equal to the number of input paths.
-func GetManyBytes(json []byte, path ...string) []Result {
- res := make([]Result, len(path))
- for i, path := range path {
- res[i] = GetBytes(json, path)
- }
- return res
-}
-
-func validpayload(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- i, ok = validany(data, i)
- if !ok {
- return i, false
- }
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- }
- }
- return i, true
- case ' ', '\t', '\n', '\r':
- continue
- }
- }
- return i, false
-}
-func validany(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case '{':
- return validobject(data, i+1)
- case '[':
- return validarray(data, i+1)
- case '"':
- return validstring(data, i+1)
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- return validnumber(data, i+1)
- case 't':
- return validtrue(data, i+1)
- case 'f':
- return validfalse(data, i+1)
- case 'n':
- return validnull(data, i+1)
- }
- }
- return i, false
-}
-func validobject(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case '}':
- return i + 1, true
- case '"':
- key:
- if i, ok = validstring(data, i+1); !ok {
- return i, false
- }
- if i, ok = validcolon(data, i); !ok {
- return i, false
- }
- if i, ok = validany(data, i); !ok {
- return i, false
- }
- if i, ok = validcomma(data, i, '}'); !ok {
- return i, false
- }
- if data[i] == '}' {
- return i + 1, true
- }
- i++
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case '"':
- goto key
- }
- }
- return i, false
- }
- }
- return i, false
-}
-func validcolon(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case ':':
- return i + 1, true
- }
- }
- return i, false
-}
-func validcomma(data []byte, i int, end byte) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case ',':
- return i, true
- case end:
- return i, true
- }
- }
- return i, false
-}
-func validarray(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- for ; i < len(data); i++ {
- if i, ok = validany(data, i); !ok {
- return i, false
- }
- if i, ok = validcomma(data, i, ']'); !ok {
- return i, false
- }
- if data[i] == ']' {
- return i + 1, true
- }
- }
- case ' ', '\t', '\n', '\r':
- continue
- case ']':
- return i + 1, true
- }
- }
- return i, false
-}
-func validstring(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- if data[i] < ' ' {
- return i, false
- } else if data[i] == '\\' {
- i++
- if i == len(data) {
- return i, false
- }
- switch data[i] {
- default:
- return i, false
- case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
- case 'u':
- for j := 0; j < 4; j++ {
- i++
- if i >= len(data) {
- return i, false
- }
- if !((data[i] >= '0' && data[i] <= '9') ||
- (data[i] >= 'a' && data[i] <= 'f') ||
- (data[i] >= 'A' && data[i] <= 'F')) {
- return i, false
- }
- }
- }
- } else if data[i] == '"' {
- return i + 1, true
- }
- }
- return i, false
-}
-func validnumber(data []byte, i int) (outi int, ok bool) {
- i--
- // sign
- if data[i] == '-' {
- i++
- if i == len(data) {
- return i, false
- }
- if data[i] < '0' || data[i] > '9' {
- return i, false
- }
- }
- // int
- if i == len(data) {
- return i, false
- }
- if data[i] == '0' {
- i++
- } else {
- for ; i < len(data); i++ {
- if data[i] >= '0' && data[i] <= '9' {
- continue
- }
- break
- }
- }
- // frac
- if i == len(data) {
- return i, true
- }
- if data[i] == '.' {
- i++
- if i == len(data) {
- return i, false
- }
- if data[i] < '0' || data[i] > '9' {
- return i, false
- }
- i++
- for ; i < len(data); i++ {
- if data[i] >= '0' && data[i] <= '9' {
- continue
- }
- break
- }
- }
- // exp
- if i == len(data) {
- return i, true
- }
- if data[i] == 'e' || data[i] == 'E' {
- i++
- if i == len(data) {
- return i, false
- }
- if data[i] == '+' || data[i] == '-' {
- i++
- }
- if i == len(data) {
- return i, false
- }
- if data[i] < '0' || data[i] > '9' {
- return i, false
- }
- i++
- for ; i < len(data); i++ {
- if data[i] >= '0' && data[i] <= '9' {
- continue
- }
- break
- }
- }
- return i, true
-}
-
-func validtrue(data []byte, i int) (outi int, ok bool) {
- if i+3 <= len(data) && data[i] == 'r' && data[i+1] == 'u' &&
- data[i+2] == 'e' {
- return i + 3, true
- }
- return i, false
-}
-func validfalse(data []byte, i int) (outi int, ok bool) {
- if i+4 <= len(data) && data[i] == 'a' && data[i+1] == 'l' &&
- data[i+2] == 's' && data[i+3] == 'e' {
- return i + 4, true
- }
- return i, false
-}
-func validnull(data []byte, i int) (outi int, ok bool) {
- if i+3 <= len(data) && data[i] == 'u' && data[i+1] == 'l' &&
- data[i+2] == 'l' {
- return i + 3, true
- }
- return i, false
-}
-
-// Valid returns true if the input is valid json.
-//
-// if !gjson.Valid(json) {
-// return errors.New("invalid json")
-// }
-// value := gjson.Get(json, "name.last")
-//
-func Valid(json string) bool {
- _, ok := validpayload(stringBytes(json), 0)
- return ok
-}
-
-// ValidBytes returns true if the input is valid json.
-//
-// if !gjson.Valid(json) {
-// return errors.New("invalid json")
-// }
-// value := gjson.Get(json, "name.last")
-//
-// If working with bytes, this method preferred over ValidBytes(string(data))
-//
-func ValidBytes(json []byte) bool {
- _, ok := validpayload(json, 0)
- return ok
-}
-
-func parseUint(s string) (n uint64, ok bool) {
- var i int
- if i == len(s) {
- return 0, false
- }
- for ; i < len(s); i++ {
- if s[i] >= '0' && s[i] <= '9' {
- n = n*10 + uint64(s[i]-'0')
- } else {
- return 0, false
- }
- }
- return n, true
-}
-
-func parseInt(s string) (n int64, ok bool) {
- var i int
- var sign bool
- if len(s) > 0 && s[0] == '-' {
- sign = true
- i++
- }
- if i == len(s) {
- return 0, false
- }
- for ; i < len(s); i++ {
- if s[i] >= '0' && s[i] <= '9' {
- n = n*10 + int64(s[i]-'0')
- } else {
- return 0, false
- }
- }
- if sign {
- return n * -1, true
- }
- return n, true
-}
-
-// safeInt validates a given JSON number
-// ensures it lies within the minimum and maximum representable JSON numbers
-func safeInt(f float64) (n int64, ok bool) {
- // https://tc39.es/ecma262/#sec-number.min_safe_integer
- // https://tc39.es/ecma262/#sec-number.max_safe_integer
- if f < -9007199254740991 || f > 9007199254740991 {
- return 0, false
- }
- return int64(f), true
-}
-
-// execStatic parses the path to find a static value.
-// The input expects that the path already starts with a '!'
-func execStatic(json, path string) (pathOut, res string, ok bool) {
- name := path[1:]
- if len(name) > 0 {
- switch name[0] {
- case '{', '[', '"', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9':
- _, res = parseSquash(name, 0)
- pathOut = name[len(res):]
- return pathOut, res, true
- }
- }
- for i := 1; i < len(path); i++ {
- if path[i] == '|' {
- pathOut = path[i:]
- name = path[1:i]
- break
- }
- if path[i] == '.' {
- pathOut = path[i:]
- name = path[1:i]
- break
- }
- }
- switch strings.ToLower(name) {
- case "true", "false", "null", "nan", "inf":
- return pathOut, name, true
- }
- return pathOut, res, false
-}
-
-// execModifier parses the path to find a matching modifier function.
-// The input expects that the path already starts with a '@'
-func execModifier(json, path string) (pathOut, res string, ok bool) {
- name := path[1:]
- var hasArgs bool
- for i := 1; i < len(path); i++ {
- if path[i] == ':' {
- pathOut = path[i+1:]
- name = path[1:i]
- hasArgs = len(pathOut) > 0
- break
- }
- if path[i] == '|' {
- pathOut = path[i:]
- name = path[1:i]
- break
- }
- if path[i] == '.' {
- pathOut = path[i:]
- name = path[1:i]
- break
- }
- }
- if fn, ok := modifiers[name]; ok {
- var args string
- if hasArgs {
- var parsedArgs bool
- switch pathOut[0] {
- case '{', '[', '"':
- res := Parse(pathOut)
- if res.Exists() {
- args = squash(pathOut)
- pathOut = pathOut[len(args):]
- parsedArgs = true
- }
- }
- if !parsedArgs {
- idx := strings.IndexByte(pathOut, '|')
- if idx == -1 {
- args = pathOut
- pathOut = ""
- } else {
- args = pathOut[:idx]
- pathOut = pathOut[idx:]
- }
- }
- }
- return pathOut, fn(json, args), true
- }
- return pathOut, res, false
-}
-
-// unwrap removes the '[]' or '{}' characters around json
-func unwrap(json string) string {
- json = trim(json)
- if len(json) >= 2 && (json[0] == '[' || json[0] == '{') {
- json = json[1 : len(json)-1]
- }
- return json
-}
-
-// DisableModifiers will disable the modifier syntax
-var DisableModifiers = false
-
-var modifiers = map[string]func(json, arg string) string{
- "pretty": modPretty,
- "ugly": modUgly,
- "reverse": modReverse,
- "this": modThis,
- "flatten": modFlatten,
- "join": modJoin,
- "valid": modValid,
- "keys": modKeys,
- "values": modValues,
- "tostr": modToStr,
- "fromstr": modFromStr,
- "group": modGroup,
-}
-
-// AddModifier binds a custom modifier command to the GJSON syntax.
-// This operation is not thread safe and should be executed prior to
-// using all other gjson function.
-func AddModifier(name string, fn func(json, arg string) string) {
- modifiers[name] = fn
-}
-
-// ModifierExists returns true when the specified modifier exists.
-func ModifierExists(name string, fn func(json, arg string) string) bool {
- _, ok := modifiers[name]
- return ok
-}
-
-// cleanWS remove any non-whitespace from string
-func cleanWS(s string) string {
- for i := 0; i < len(s); i++ {
- switch s[i] {
- case ' ', '\t', '\n', '\r':
- continue
- default:
- var s2 []byte
- for i := 0; i < len(s); i++ {
- switch s[i] {
- case ' ', '\t', '\n', '\r':
- s2 = append(s2, s[i])
- }
- }
- return string(s2)
- }
- }
- return s
-}
-
-// @pretty modifier makes the json look nice.
-func modPretty(json, arg string) string {
- if len(arg) > 0 {
- opts := *pretty.DefaultOptions
- Parse(arg).ForEach(func(key, value Result) bool {
- switch key.String() {
- case "sortKeys":
- opts.SortKeys = value.Bool()
- case "indent":
- opts.Indent = cleanWS(value.String())
- case "prefix":
- opts.Prefix = cleanWS(value.String())
- case "width":
- opts.Width = int(value.Int())
- }
- return true
- })
- return bytesString(pretty.PrettyOptions(stringBytes(json), &opts))
- }
- return bytesString(pretty.Pretty(stringBytes(json)))
-}
-
-// @this returns the current element. Can be used to retrieve the root element.
-func modThis(json, arg string) string {
- return json
-}
-
-// @ugly modifier removes all whitespace.
-func modUgly(json, arg string) string {
- return bytesString(pretty.Ugly(stringBytes(json)))
-}
-
-// @reverse reverses array elements or root object members.
-func modReverse(json, arg string) string {
- res := Parse(json)
- if res.IsArray() {
- var values []Result
- res.ForEach(func(_, value Result) bool {
- values = append(values, value)
- return true
- })
- out := make([]byte, 0, len(json))
- out = append(out, '[')
- for i, j := len(values)-1, 0; i >= 0; i, j = i-1, j+1 {
- if j > 0 {
- out = append(out, ',')
- }
- out = append(out, values[i].Raw...)
- }
- out = append(out, ']')
- return bytesString(out)
- }
- if res.IsObject() {
- var keyValues []Result
- res.ForEach(func(key, value Result) bool {
- keyValues = append(keyValues, key, value)
- return true
- })
- out := make([]byte, 0, len(json))
- out = append(out, '{')
- for i, j := len(keyValues)-2, 0; i >= 0; i, j = i-2, j+1 {
- if j > 0 {
- out = append(out, ',')
- }
- out = append(out, keyValues[i+0].Raw...)
- out = append(out, ':')
- out = append(out, keyValues[i+1].Raw...)
- }
- out = append(out, '}')
- return bytesString(out)
- }
- return json
-}
-
-// @flatten an array with child arrays.
-// [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,[6,7]]
-// The {"deep":true} arg can be provide for deep flattening.
-// [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,6,7]
-// The original json is returned when the json is not an array.
-func modFlatten(json, arg string) string {
- res := Parse(json)
- if !res.IsArray() {
- return json
- }
- var deep bool
- if arg != "" {
- Parse(arg).ForEach(func(key, value Result) bool {
- if key.String() == "deep" {
- deep = value.Bool()
- }
- return true
- })
- }
- var out []byte
- out = append(out, '[')
- var idx int
- res.ForEach(func(_, value Result) bool {
- var raw string
- if value.IsArray() {
- if deep {
- raw = unwrap(modFlatten(value.Raw, arg))
- } else {
- raw = unwrap(value.Raw)
- }
- } else {
- raw = value.Raw
- }
- raw = strings.TrimSpace(raw)
- if len(raw) > 0 {
- if idx > 0 {
- out = append(out, ',')
- }
- out = append(out, raw...)
- idx++
- }
- return true
- })
- out = append(out, ']')
- return bytesString(out)
-}
-
-// @keys extracts the keys from an object.
-// {"first":"Tom","last":"Smith"} -> ["first","last"]
-func modKeys(json, arg string) string {
- v := Parse(json)
- if !v.Exists() {
- return "[]"
- }
- obj := v.IsObject()
- var out strings.Builder
- out.WriteByte('[')
- var i int
- v.ForEach(func(key, _ Result) bool {
- if i > 0 {
- out.WriteByte(',')
- }
- if obj {
- out.WriteString(key.Raw)
- } else {
- out.WriteString("null")
- }
- i++
- return true
- })
- out.WriteByte(']')
- return out.String()
-}
-
-// @values extracts the values from an object.
-// {"first":"Tom","last":"Smith"} -> ["Tom","Smith"]
-func modValues(json, arg string) string {
- v := Parse(json)
- if !v.Exists() {
- return "[]"
- }
- if v.IsArray() {
- return json
- }
- var out strings.Builder
- out.WriteByte('[')
- var i int
- v.ForEach(func(_, value Result) bool {
- if i > 0 {
- out.WriteByte(',')
- }
- out.WriteString(value.Raw)
- i++
- return true
- })
- out.WriteByte(']')
- return out.String()
-}
-
-// @join multiple objects into a single object.
-// [{"first":"Tom"},{"last":"Smith"}] -> {"first","Tom","last":"Smith"}
-// The arg can be "true" to specify that duplicate keys should be preserved.
-// [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":37,"age":41}
-// Without preserved keys:
-// [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":41}
-// The original json is returned when the json is not an object.
-func modJoin(json, arg string) string {
- res := Parse(json)
- if !res.IsArray() {
- return json
- }
- var preserve bool
- if arg != "" {
- Parse(arg).ForEach(func(key, value Result) bool {
- if key.String() == "preserve" {
- preserve = value.Bool()
- }
- return true
- })
- }
- var out []byte
- out = append(out, '{')
- if preserve {
- // Preserve duplicate keys.
- var idx int
- res.ForEach(func(_, value Result) bool {
- if !value.IsObject() {
- return true
- }
- if idx > 0 {
- out = append(out, ',')
- }
- out = append(out, unwrap(value.Raw)...)
- idx++
- return true
- })
- } else {
- // Deduplicate keys and generate an object with stable ordering.
- var keys []Result
- kvals := make(map[string]Result)
- res.ForEach(func(_, value Result) bool {
- if !value.IsObject() {
- return true
- }
- value.ForEach(func(key, value Result) bool {
- k := key.String()
- if _, ok := kvals[k]; !ok {
- keys = append(keys, key)
- }
- kvals[k] = value
- return true
- })
- return true
- })
- for i := 0; i < len(keys); i++ {
- if i > 0 {
- out = append(out, ',')
- }
- out = append(out, keys[i].Raw...)
- out = append(out, ':')
- out = append(out, kvals[keys[i].String()].Raw...)
- }
- }
- out = append(out, '}')
- return bytesString(out)
-}
-
-// @valid ensures that the json is valid before moving on. An empty string is
-// returned when the json is not valid, otherwise it returns the original json.
-func modValid(json, arg string) string {
- if !Valid(json) {
- return ""
- }
- return json
-}
-
-// @fromstr converts a string to json
-// "{\"id\":1023,\"name\":\"alert\"}" -> {"id":1023,"name":"alert"}
-func modFromStr(json, arg string) string {
- if !Valid(json) {
- return ""
- }
- return Parse(json).String()
-}
-
-// @tostr converts a string to json
-// {"id":1023,"name":"alert"} -> "{\"id\":1023,\"name\":\"alert\"}"
-func modToStr(str, arg string) string {
- return string(AppendJSONString(nil, str))
-}
-
-func modGroup(json, arg string) string {
- res := Parse(json)
- if !res.IsObject() {
- return ""
- }
- var all [][]byte
- res.ForEach(func(key, value Result) bool {
- if !value.IsArray() {
- return true
- }
- var idx int
- value.ForEach(func(_, value Result) bool {
- if idx == len(all) {
- all = append(all, []byte{})
- }
- all[idx] = append(all[idx], ("," + key.Raw + ":" + value.Raw)...)
- idx++
- return true
- })
- return true
- })
- var data []byte
- data = append(data, '[')
- for i, item := range all {
- if i > 0 {
- data = append(data, ',')
- }
- data = append(data, '{')
- data = append(data, item[1:]...)
- data = append(data, '}')
- }
- data = append(data, ']')
- return string(data)
-}
-
-// stringHeader instead of reflect.StringHeader
-type stringHeader struct {
- data unsafe.Pointer
- len int
-}
-
-// sliceHeader instead of reflect.SliceHeader
-type sliceHeader struct {
- data unsafe.Pointer
- len int
- cap int
-}
-
-// getBytes casts the input json bytes to a string and safely returns the
-// results as uniquely allocated data. This operation is intended to minimize
-// copies and allocations for the large json string->[]byte.
-func getBytes(json []byte, path string) Result {
- var result Result
- if json != nil {
- // unsafe cast to string
- result = Get(*(*string)(unsafe.Pointer(&json)), path)
- // safely get the string headers
- rawhi := *(*stringHeader)(unsafe.Pointer(&result.Raw))
- strhi := *(*stringHeader)(unsafe.Pointer(&result.Str))
- // create byte slice headers
- rawh := sliceHeader{data: rawhi.data, len: rawhi.len, cap: rawhi.len}
- strh := sliceHeader{data: strhi.data, len: strhi.len, cap: rawhi.len}
- if strh.data == nil {
- // str is nil
- if rawh.data == nil {
- // raw is nil
- result.Raw = ""
- } else {
- // raw has data, safely copy the slice header to a string
- result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
- }
- result.Str = ""
- } else if rawh.data == nil {
- // raw is nil
- result.Raw = ""
- // str has data, safely copy the slice header to a string
- result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
- } else if uintptr(strh.data) >= uintptr(rawh.data) &&
- uintptr(strh.data)+uintptr(strh.len) <=
- uintptr(rawh.data)+uintptr(rawh.len) {
- // Str is a substring of Raw.
- start := uintptr(strh.data) - uintptr(rawh.data)
- // safely copy the raw slice header
- result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
- // substring the raw
- result.Str = result.Raw[start : start+uintptr(strh.len)]
- } else {
- // safely copy both the raw and str slice headers to strings
- result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
- result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
- }
- }
- return result
-}
-
-// fillIndex finds the position of Raw data and assigns it to the Index field
-// of the resulting value. If the position cannot be found then Index zero is
-// used instead.
-func fillIndex(json string, c *parseContext) {
- if len(c.value.Raw) > 0 && !c.calcd {
- jhdr := *(*stringHeader)(unsafe.Pointer(&json))
- rhdr := *(*stringHeader)(unsafe.Pointer(&(c.value.Raw)))
- c.value.Index = int(uintptr(rhdr.data) - uintptr(jhdr.data))
- if c.value.Index < 0 || c.value.Index >= len(json) {
- c.value.Index = 0
- }
- }
-}
-
-func stringBytes(s string) []byte {
- return *(*[]byte)(unsafe.Pointer(&sliceHeader{
- data: (*stringHeader)(unsafe.Pointer(&s)).data,
- len: len(s),
- cap: len(s),
- }))
-}
-
-func bytesString(b []byte) string {
- return *(*string)(unsafe.Pointer(&b))
-}
-
-func revSquash(json string) string {
- // reverse squash
- // expects that the tail character is a ']' or '}' or ')' or '"'
- // squash the value, ignoring all nested arrays and objects.
- i := len(json) - 1
- var depth int
- if json[i] != '"' {
- depth++
- }
- if json[i] == '}' || json[i] == ']' || json[i] == ')' {
- i--
- }
- for ; i >= 0; i-- {
- switch json[i] {
- case '"':
- i--
- for ; i >= 0; i-- {
- if json[i] == '"' {
- esc := 0
- for i > 0 && json[i-1] == '\\' {
- i--
- esc++
- }
- if esc%2 == 1 {
- continue
- }
- i += esc
- break
- }
- }
- if depth == 0 {
- if i < 0 {
- i = 0
- }
- return json[i:]
- }
- case '}', ']', ')':
- depth++
- case '{', '[', '(':
- depth--
- if depth == 0 {
- return json[i:]
- }
- }
- }
- return json
-}
-
-// Paths returns the original GJSON paths for a Result where the Result came
-// from a simple query path that returns an array, like:
-//
-// gjson.Get(json, "friends.#.first")
-//
-// The returned value will be in the form of a JSON array:
-//
-// ["friends.0.first","friends.1.first","friends.2.first"]
-//
-// The param 'json' must be the original JSON used when calling Get.
-//
-// Returns an empty string if the paths cannot be determined, which can happen
-// when the Result came from a path that contained a multipath, modifier,
-// or a nested query.
-func (t Result) Paths(json string) []string {
- if t.Indexes == nil {
- return nil
- }
- paths := make([]string, 0, len(t.Indexes))
- t.ForEach(func(_, value Result) bool {
- paths = append(paths, value.Path(json))
- return true
- })
- if len(paths) != len(t.Indexes) {
- return nil
- }
- return paths
-}
-
-// Path returns the original GJSON path for a Result where the Result came
-// from a simple path that returns a single value, like:
-//
-// gjson.Get(json, "friends.#(last=Murphy)")
-//
-// The returned value will be in the form of a JSON string:
-//
-// "friends.0"
-//
-// The param 'json' must be the original JSON used when calling Get.
-//
-// Returns an empty string if the paths cannot be determined, which can happen
-// when the Result came from a path that contained a multipath, modifier,
-// or a nested query.
-func (t Result) Path(json string) string {
- var path []byte
- var comps []string // raw components
- i := t.Index - 1
- if t.Index+len(t.Raw) > len(json) {
- // JSON cannot safely contain Result.
- goto fail
- }
- if !strings.HasPrefix(json[t.Index:], t.Raw) {
- // Result is not at the JSON index as exepcted.
- goto fail
- }
- for ; i >= 0; i-- {
- if json[i] <= ' ' {
- continue
- }
- if json[i] == ':' {
- // inside of object, get the key
- for ; i >= 0; i-- {
- if json[i] != '"' {
- continue
- }
- break
- }
- raw := revSquash(json[:i+1])
- i = i - len(raw)
- comps = append(comps, raw)
- // key gotten, now squash the rest
- raw = revSquash(json[:i+1])
- i = i - len(raw)
- i++ // increment the index for next loop step
- } else if json[i] == '{' {
- // Encountered an open object. The original result was probably an
- // object key.
- goto fail
- } else if json[i] == ',' || json[i] == '[' {
- // inside of an array, count the position
- var arrIdx int
- if json[i] == ',' {
- arrIdx++
- i--
- }
- for ; i >= 0; i-- {
- if json[i] == ':' {
- // Encountered an unexpected colon. The original result was
- // probably an object key.
- goto fail
- } else if json[i] == ',' {
- arrIdx++
- } else if json[i] == '[' {
- comps = append(comps, strconv.Itoa(arrIdx))
- break
- } else if json[i] == ']' || json[i] == '}' || json[i] == '"' {
- raw := revSquash(json[:i+1])
- i = i - len(raw) + 1
- }
- }
- }
- }
- if len(comps) == 0 {
- if DisableModifiers {
- goto fail
- }
- return "@this"
- }
- for i := len(comps) - 1; i >= 0; i-- {
- rcomp := Parse(comps[i])
- if !rcomp.Exists() {
- goto fail
- }
- comp := escapeComp(rcomp.String())
- path = append(path, '.')
- path = append(path, comp...)
- }
- if len(path) > 0 {
- path = path[1:]
- }
- return string(path)
-fail:
- return ""
-}
-
-// isSafePathKeyChar returns true if the input character is safe for not
-// needing escaping.
-func isSafePathKeyChar(c byte) bool {
- return c <= ' ' || c > '~' || c == '_' || c == '-' || c == ':' ||
- (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9')
-}
-
-// escapeComp escaped a path compontent, making it safe for generating a
-// path for later use.
-func escapeComp(comp string) string {
- for i := 0; i < len(comp); i++ {
- if !isSafePathKeyChar(comp[i]) {
- ncomp := []byte(comp[:i])
- for ; i < len(comp); i++ {
- if !isSafePathKeyChar(comp[i]) {
- ncomp = append(ncomp, '\\')
- }
- ncomp = append(ncomp, comp[i])
- }
- return string(ncomp)
- }
- }
- return comp
-}
diff --git a/vendor/github.com/tidwall/gjson/logo.png b/vendor/github.com/tidwall/gjson/logo.png
deleted file mode 100644
index 17a8bbe9d..000000000
Binary files a/vendor/github.com/tidwall/gjson/logo.png and /dev/null differ
diff --git a/vendor/github.com/tidwall/match/LICENSE b/vendor/github.com/tidwall/match/LICENSE
deleted file mode 100644
index 58f5819a4..000000000
--- a/vendor/github.com/tidwall/match/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2016 Josh Baker
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/tidwall/match/README.md b/vendor/github.com/tidwall/match/README.md
deleted file mode 100644
index 5fdd4cf63..000000000
--- a/vendor/github.com/tidwall/match/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Match
-
-[![GoDoc](https://godoc.org/github.com/tidwall/match?status.svg)](https://godoc.org/github.com/tidwall/match)
-
-Match is a very simple pattern matcher where '*' matches on any
-number characters and '?' matches on any one character.
-
-## Installing
-
-```
-go get -u github.com/tidwall/match
-```
-
-## Example
-
-```go
-match.Match("hello", "*llo")
-match.Match("jello", "?ello")
-match.Match("hello", "h*o")
-```
-
-
-## Contact
-
-Josh Baker [@tidwall](http://twitter.com/tidwall)
-
-## License
-
-Redcon source code is available under the MIT [License](/LICENSE).
diff --git a/vendor/github.com/tidwall/match/match.go b/vendor/github.com/tidwall/match/match.go
deleted file mode 100644
index 11da28f1b..000000000
--- a/vendor/github.com/tidwall/match/match.go
+++ /dev/null
@@ -1,237 +0,0 @@
-// Package match provides a simple pattern matcher with unicode support.
-package match
-
-import (
- "unicode/utf8"
-)
-
-// Match returns true if str matches pattern. This is a very
-// simple wildcard match where '*' matches on any number characters
-// and '?' matches on any one character.
-//
-// pattern:
-// { term }
-// term:
-// '*' matches any sequence of non-Separator characters
-// '?' matches any single non-Separator character
-// c matches character c (c != '*', '?', '\\')
-// '\\' c matches character c
-//
-func Match(str, pattern string) bool {
- if pattern == "*" {
- return true
- }
- return match(str, pattern, 0, nil, -1) == rMatch
-}
-
-// MatchLimit is the same as Match but will limit the complexity of the match
-// operation. This is to avoid long running matches, specifically to avoid ReDos
-// attacks from arbritary inputs.
-//
-// How it works:
-// The underlying match routine is recursive and may call itself when it
-// encounters a sandwiched wildcard pattern, such as: `user:*:name`.
-// Everytime it calls itself a counter is incremented.
-// The operation is stopped when counter > maxcomp*len(str).
-func MatchLimit(str, pattern string, maxcomp int) (matched, stopped bool) {
- if pattern == "*" {
- return true, false
- }
- counter := 0
- r := match(str, pattern, len(str), &counter, maxcomp)
- if r == rStop {
- return false, true
- }
- return r == rMatch, false
-}
-
-type result int
-
-const (
- rNoMatch result = iota
- rMatch
- rStop
-)
-
-func match(str, pat string, slen int, counter *int, maxcomp int) result {
- // check complexity limit
- if maxcomp > -1 {
- if *counter > slen*maxcomp {
- return rStop
- }
- *counter++
- }
-
- for len(pat) > 0 {
- var wild bool
- pc, ps := rune(pat[0]), 1
- if pc > 0x7f {
- pc, ps = utf8.DecodeRuneInString(pat)
- }
- var sc rune
- var ss int
- if len(str) > 0 {
- sc, ss = rune(str[0]), 1
- if sc > 0x7f {
- sc, ss = utf8.DecodeRuneInString(str)
- }
- }
- switch pc {
- case '?':
- if ss == 0 {
- return rNoMatch
- }
- case '*':
- // Ignore repeating stars.
- for len(pat) > 1 && pat[1] == '*' {
- pat = pat[1:]
- }
-
- // If this star is the last character then it must be a match.
- if len(pat) == 1 {
- return rMatch
- }
-
- // Match and trim any non-wildcard suffix characters.
- var ok bool
- str, pat, ok = matchTrimSuffix(str, pat)
- if !ok {
- return rNoMatch
- }
-
- // Check for single star again.
- if len(pat) == 1 {
- return rMatch
- }
-
- // Perform recursive wildcard search.
- r := match(str, pat[1:], slen, counter, maxcomp)
- if r != rNoMatch {
- return r
- }
- if len(str) == 0 {
- return rNoMatch
- }
- wild = true
- default:
- if ss == 0 {
- return rNoMatch
- }
- if pc == '\\' {
- pat = pat[ps:]
- pc, ps = utf8.DecodeRuneInString(pat)
- if ps == 0 {
- return rNoMatch
- }
- }
- if sc != pc {
- return rNoMatch
- }
- }
- str = str[ss:]
- if !wild {
- pat = pat[ps:]
- }
- }
- if len(str) == 0 {
- return rMatch
- }
- return rNoMatch
-}
-
-// matchTrimSuffix matches and trims any non-wildcard suffix characters.
-// Returns the trimed string and pattern.
-//
-// This is called because the pattern contains extra data after the wildcard
-// star. Here we compare any suffix characters in the pattern to the suffix of
-// the target string. Basically a reverse match that stops when a wildcard
-// character is reached. This is a little trickier than a forward match because
-// we need to evaluate an escaped character in reverse.
-//
-// Any matched characters will be trimmed from both the target
-// string and the pattern.
-func matchTrimSuffix(str, pat string) (string, string, bool) {
- // It's expected that the pattern has at least two bytes and the first byte
- // is a wildcard star '*'
- match := true
- for len(str) > 0 && len(pat) > 1 {
- pc, ps := utf8.DecodeLastRuneInString(pat)
- var esc bool
- for i := 0; ; i++ {
- if pat[len(pat)-ps-i-1] != '\\' {
- if i&1 == 1 {
- esc = true
- ps++
- }
- break
- }
- }
- if pc == '*' && !esc {
- match = true
- break
- }
- sc, ss := utf8.DecodeLastRuneInString(str)
- if !((pc == '?' && !esc) || pc == sc) {
- match = false
- break
- }
- str = str[:len(str)-ss]
- pat = pat[:len(pat)-ps]
- }
- return str, pat, match
-}
-
-var maxRuneBytes = [...]byte{244, 143, 191, 191}
-
-// Allowable parses the pattern and determines the minimum and maximum allowable
-// values that the pattern can represent.
-// When the max cannot be determined, 'true' will be returned
-// for infinite.
-func Allowable(pattern string) (min, max string) {
- if pattern == "" || pattern[0] == '*' {
- return "", ""
- }
-
- minb := make([]byte, 0, len(pattern))
- maxb := make([]byte, 0, len(pattern))
- var wild bool
- for i := 0; i < len(pattern); i++ {
- if pattern[i] == '*' {
- wild = true
- break
- }
- if pattern[i] == '?' {
- minb = append(minb, 0)
- maxb = append(maxb, maxRuneBytes[:]...)
- } else {
- minb = append(minb, pattern[i])
- maxb = append(maxb, pattern[i])
- }
- }
- if wild {
- r, n := utf8.DecodeLastRune(maxb)
- if r != utf8.RuneError {
- if r < utf8.MaxRune {
- r++
- if r > 0x7f {
- b := make([]byte, 4)
- nn := utf8.EncodeRune(b, r)
- maxb = append(maxb[:len(maxb)-n], b[:nn]...)
- } else {
- maxb = append(maxb[:len(maxb)-n], byte(r))
- }
- }
- }
- }
- return string(minb), string(maxb)
-}
-
-// IsPattern returns true if the string is a pattern.
-func IsPattern(str string) bool {
- for i := 0; i < len(str); i++ {
- if str[i] == '*' || str[i] == '?' {
- return true
- }
- }
- return false
-}
diff --git a/vendor/github.com/tidwall/pretty/LICENSE b/vendor/github.com/tidwall/pretty/LICENSE
deleted file mode 100644
index 993b83f23..000000000
--- a/vendor/github.com/tidwall/pretty/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2017 Josh Baker
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/tidwall/pretty/README.md b/vendor/github.com/tidwall/pretty/README.md
deleted file mode 100644
index 76c06a5e5..000000000
--- a/vendor/github.com/tidwall/pretty/README.md
+++ /dev/null
@@ -1,122 +0,0 @@
-# Pretty
-
-[![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/tidwall/pretty)
-
-Pretty is a Go package that provides [fast](#performance) methods for formatting JSON for human readability, or to compact JSON for smaller payloads.
-
-Getting Started
-===============
-
-## Installing
-
-To start using Pretty, install Go and run `go get`:
-
-```sh
-$ go get -u github.com/tidwall/pretty
-```
-
-This will retrieve the library.
-
-## Pretty
-
-Using this example:
-
-```json
-{"name": {"first":"Tom","last":"Anderson"}, "age":37,
-"children": ["Sara","Alex","Jack"],
-"fav.movie": "Deer Hunter", "friends": [
- {"first": "Janet", "last": "Murphy", "age": 44}
- ]}
-```
-
-The following code:
-```go
-result = pretty.Pretty(example)
-```
-
-Will format the json to:
-
-```json
-{
- "name": {
- "first": "Tom",
- "last": "Anderson"
- },
- "age": 37,
- "children": ["Sara", "Alex", "Jack"],
- "fav.movie": "Deer Hunter",
- "friends": [
- {
- "first": "Janet",
- "last": "Murphy",
- "age": 44
- }
- ]
-}
-```
-
-## Color
-
-Color will colorize the json for outputing to the screen.
-
-```go
-result = pretty.Color(json, nil)
-```
-
-Will add color to the result for printing to the terminal.
-The second param is used for a customizing the style, and passing nil will use the default `pretty.TerminalStyle`.
-
-## Ugly
-
-The following code:
-```go
-result = pretty.Ugly(example)
-```
-
-Will format the json to:
-
-```json
-{"name":{"first":"Tom","last":"Anderson"},"age":37,"children":["Sara","Alex","Jack"],"fav.movie":"Deer Hunter","friends":[{"first":"Janet","last":"Murphy","age":44}]}```
-```
-
-## Customized output
-
-There's a `PrettyOptions(json, opts)` function which allows for customizing the output with the following options:
-
-```go
-type Options struct {
- // Width is an max column width for single line arrays
- // Default is 80
- Width int
- // Prefix is a prefix for all lines
- // Default is an empty string
- Prefix string
- // Indent is the nested indentation
- // Default is two spaces
- Indent string
- // SortKeys will sort the keys alphabetically
- // Default is false
- SortKeys bool
-}
-```
-## Performance
-
-Benchmarks of Pretty alongside the builtin `encoding/json` Indent/Compact methods.
-```
-BenchmarkPretty-16 1000000 1034 ns/op 720 B/op 2 allocs/op
-BenchmarkPrettySortKeys-16 586797 1983 ns/op 2848 B/op 14 allocs/op
-BenchmarkUgly-16 4652365 254 ns/op 240 B/op 1 allocs/op
-BenchmarkUglyInPlace-16 6481233 183 ns/op 0 B/op 0 allocs/op
-BenchmarkJSONIndent-16 450654 2687 ns/op 1221 B/op 0 allocs/op
-BenchmarkJSONCompact-16 685111 1699 ns/op 442 B/op 0 allocs/op
-```
-
-*These benchmarks were run on a MacBook Pro 2.4 GHz 8-Core Intel Core i9.*
-
-## Contact
-Josh Baker [@tidwall](http://twitter.com/tidwall)
-
-## License
-
-Pretty source code is available under the MIT [License](/LICENSE).
-
diff --git a/vendor/github.com/tidwall/pretty/pretty.go b/vendor/github.com/tidwall/pretty/pretty.go
deleted file mode 100644
index d705f9cdb..000000000
--- a/vendor/github.com/tidwall/pretty/pretty.go
+++ /dev/null
@@ -1,682 +0,0 @@
-package pretty
-
-import (
- "bytes"
- "encoding/json"
- "sort"
- "strconv"
-)
-
-// Options is Pretty options
-type Options struct {
- // Width is an max column width for single line arrays
- // Default is 80
- Width int
- // Prefix is a prefix for all lines
- // Default is an empty string
- Prefix string
- // Indent is the nested indentation
- // Default is two spaces
- Indent string
- // SortKeys will sort the keys alphabetically
- // Default is false
- SortKeys bool
-}
-
-// DefaultOptions is the default options for pretty formats.
-var DefaultOptions = &Options{Width: 80, Prefix: "", Indent: " ", SortKeys: false}
-
-// Pretty converts the input json into a more human readable format where each
-// element is on it's own line with clear indentation.
-func Pretty(json []byte) []byte { return PrettyOptions(json, nil) }
-
-// PrettyOptions is like Pretty but with customized options.
-func PrettyOptions(json []byte, opts *Options) []byte {
- if opts == nil {
- opts = DefaultOptions
- }
- buf := make([]byte, 0, len(json))
- if len(opts.Prefix) != 0 {
- buf = append(buf, opts.Prefix...)
- }
- buf, _, _, _ = appendPrettyAny(buf, json, 0, true,
- opts.Width, opts.Prefix, opts.Indent, opts.SortKeys,
- 0, 0, -1)
- if len(buf) > 0 {
- buf = append(buf, '\n')
- }
- return buf
-}
-
-// Ugly removes insignificant space characters from the input json byte slice
-// and returns the compacted result.
-func Ugly(json []byte) []byte {
- buf := make([]byte, 0, len(json))
- return ugly(buf, json)
-}
-
-// UglyInPlace removes insignificant space characters from the input json
-// byte slice and returns the compacted result. This method reuses the
-// input json buffer to avoid allocations. Do not use the original bytes
-// slice upon return.
-func UglyInPlace(json []byte) []byte { return ugly(json, json) }
-
-func ugly(dst, src []byte) []byte {
- dst = dst[:0]
- for i := 0; i < len(src); i++ {
- if src[i] > ' ' {
- dst = append(dst, src[i])
- if src[i] == '"' {
- for i = i + 1; i < len(src); i++ {
- dst = append(dst, src[i])
- if src[i] == '"' {
- j := i - 1
- for ; ; j-- {
- if src[j] != '\\' {
- break
- }
- }
- if (j-i)%2 != 0 {
- break
- }
- }
- }
- }
- }
- }
- return dst
-}
-
-func isNaNOrInf(src []byte) bool {
- return src[0] == 'i' || //Inf
- src[0] == 'I' || // inf
- src[0] == '+' || // +Inf
- src[0] == 'N' || // Nan
- (src[0] == 'n' && len(src) > 1 && src[1] != 'u') // nan
-}
-
-func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) {
- for ; i < len(json); i++ {
- if json[i] <= ' ' {
- continue
- }
- if json[i] == '"' {
- return appendPrettyString(buf, json, i, nl)
- }
-
- if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' || isNaNOrInf(json[i:]) {
- return appendPrettyNumber(buf, json, i, nl)
- }
- if json[i] == '{' {
- return appendPrettyObject(buf, json, i, '{', '}', pretty, width, prefix, indent, sortkeys, tabs, nl, max)
- }
- if json[i] == '[' {
- return appendPrettyObject(buf, json, i, '[', ']', pretty, width, prefix, indent, sortkeys, tabs, nl, max)
- }
- switch json[i] {
- case 't':
- return append(buf, 't', 'r', 'u', 'e'), i + 4, nl, true
- case 'f':
- return append(buf, 'f', 'a', 'l', 's', 'e'), i + 5, nl, true
- case 'n':
- return append(buf, 'n', 'u', 'l', 'l'), i + 4, nl, true
- }
- }
- return buf, i, nl, true
-}
-
-type pair struct {
- kstart, kend int
- vstart, vend int
-}
-
-type byKeyVal struct {
- sorted bool
- json []byte
- buf []byte
- pairs []pair
-}
-
-func (arr *byKeyVal) Len() int {
- return len(arr.pairs)
-}
-func (arr *byKeyVal) Less(i, j int) bool {
- if arr.isLess(i, j, byKey) {
- return true
- }
- if arr.isLess(j, i, byKey) {
- return false
- }
- return arr.isLess(i, j, byVal)
-}
-func (arr *byKeyVal) Swap(i, j int) {
- arr.pairs[i], arr.pairs[j] = arr.pairs[j], arr.pairs[i]
- arr.sorted = true
-}
-
-type byKind int
-
-const (
- byKey byKind = 0
- byVal byKind = 1
-)
-
-type jtype int
-
-const (
- jnull jtype = iota
- jfalse
- jnumber
- jstring
- jtrue
- jjson
-)
-
-func getjtype(v []byte) jtype {
- if len(v) == 0 {
- return jnull
- }
- switch v[0] {
- case '"':
- return jstring
- case 'f':
- return jfalse
- case 't':
- return jtrue
- case 'n':
- return jnull
- case '[', '{':
- return jjson
- default:
- return jnumber
- }
-}
-
-func (arr *byKeyVal) isLess(i, j int, kind byKind) bool {
- k1 := arr.json[arr.pairs[i].kstart:arr.pairs[i].kend]
- k2 := arr.json[arr.pairs[j].kstart:arr.pairs[j].kend]
- var v1, v2 []byte
- if kind == byKey {
- v1 = k1
- v2 = k2
- } else {
- v1 = bytes.TrimSpace(arr.buf[arr.pairs[i].vstart:arr.pairs[i].vend])
- v2 = bytes.TrimSpace(arr.buf[arr.pairs[j].vstart:arr.pairs[j].vend])
- if len(v1) >= len(k1)+1 {
- v1 = bytes.TrimSpace(v1[len(k1)+1:])
- }
- if len(v2) >= len(k2)+1 {
- v2 = bytes.TrimSpace(v2[len(k2)+1:])
- }
- }
- t1 := getjtype(v1)
- t2 := getjtype(v2)
- if t1 < t2 {
- return true
- }
- if t1 > t2 {
- return false
- }
- if t1 == jstring {
- s1 := parsestr(v1)
- s2 := parsestr(v2)
- return string(s1) < string(s2)
- }
- if t1 == jnumber {
- n1, _ := strconv.ParseFloat(string(v1), 64)
- n2, _ := strconv.ParseFloat(string(v2), 64)
- return n1 < n2
- }
- return string(v1) < string(v2)
-
-}
-
-func parsestr(s []byte) []byte {
- for i := 1; i < len(s); i++ {
- if s[i] == '\\' {
- var str string
- json.Unmarshal(s, &str)
- return []byte(str)
- }
- if s[i] == '"' {
- return s[1:i]
- }
- }
- return nil
-}
-
-func appendPrettyObject(buf, json []byte, i int, open, close byte, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) {
- var ok bool
- if width > 0 {
- if pretty && open == '[' && max == -1 {
- // here we try to create a single line array
- max := width - (len(buf) - nl)
- if max > 3 {
- s1, s2 := len(buf), i
- buf, i, _, ok = appendPrettyObject(buf, json, i, '[', ']', false, width, prefix, "", sortkeys, 0, 0, max)
- if ok && len(buf)-s1 <= max {
- return buf, i, nl, true
- }
- buf = buf[:s1]
- i = s2
- }
- } else if max != -1 && open == '{' {
- return buf, i, nl, false
- }
- }
- buf = append(buf, open)
- i++
- var pairs []pair
- if open == '{' && sortkeys {
- pairs = make([]pair, 0, 8)
- }
- var n int
- for ; i < len(json); i++ {
- if json[i] <= ' ' {
- continue
- }
- if json[i] == close {
- if pretty {
- if open == '{' && sortkeys {
- buf = sortPairs(json, buf, pairs)
- }
- if n > 0 {
- nl = len(buf)
- if buf[nl-1] == ' ' {
- buf[nl-1] = '\n'
- } else {
- buf = append(buf, '\n')
- }
- }
- if buf[len(buf)-1] != open {
- buf = appendTabs(buf, prefix, indent, tabs)
- }
- }
- buf = append(buf, close)
- return buf, i + 1, nl, open != '{'
- }
- if open == '[' || json[i] == '"' {
- if n > 0 {
- buf = append(buf, ',')
- if width != -1 && open == '[' {
- buf = append(buf, ' ')
- }
- }
- var p pair
- if pretty {
- nl = len(buf)
- if buf[nl-1] == ' ' {
- buf[nl-1] = '\n'
- } else {
- buf = append(buf, '\n')
- }
- if open == '{' && sortkeys {
- p.kstart = i
- p.vstart = len(buf)
- }
- buf = appendTabs(buf, prefix, indent, tabs+1)
- }
- if open == '{' {
- buf, i, nl, _ = appendPrettyString(buf, json, i, nl)
- if sortkeys {
- p.kend = i
- }
- buf = append(buf, ':')
- if pretty {
- buf = append(buf, ' ')
- }
- }
- buf, i, nl, ok = appendPrettyAny(buf, json, i, pretty, width, prefix, indent, sortkeys, tabs+1, nl, max)
- if max != -1 && !ok {
- return buf, i, nl, false
- }
- if pretty && open == '{' && sortkeys {
- p.vend = len(buf)
- if p.kstart > p.kend || p.vstart > p.vend {
- // bad data. disable sorting
- sortkeys = false
- } else {
- pairs = append(pairs, p)
- }
- }
- i--
- n++
- }
- }
- return buf, i, nl, open != '{'
-}
-func sortPairs(json, buf []byte, pairs []pair) []byte {
- if len(pairs) == 0 {
- return buf
- }
- vstart := pairs[0].vstart
- vend := pairs[len(pairs)-1].vend
- arr := byKeyVal{false, json, buf, pairs}
- sort.Stable(&arr)
- if !arr.sorted {
- return buf
- }
- nbuf := make([]byte, 0, vend-vstart)
- for i, p := range pairs {
- nbuf = append(nbuf, buf[p.vstart:p.vend]...)
- if i < len(pairs)-1 {
- nbuf = append(nbuf, ',')
- nbuf = append(nbuf, '\n')
- }
- }
- return append(buf[:vstart], nbuf...)
-}
-
-func appendPrettyString(buf, json []byte, i, nl int) ([]byte, int, int, bool) {
- s := i
- i++
- for ; i < len(json); i++ {
- if json[i] == '"' {
- var sc int
- for j := i - 1; j > s; j-- {
- if json[j] == '\\' {
- sc++
- } else {
- break
- }
- }
- if sc%2 == 1 {
- continue
- }
- i++
- break
- }
- }
- return append(buf, json[s:i]...), i, nl, true
-}
-
-func appendPrettyNumber(buf, json []byte, i, nl int) ([]byte, int, int, bool) {
- s := i
- i++
- for ; i < len(json); i++ {
- if json[i] <= ' ' || json[i] == ',' || json[i] == ':' || json[i] == ']' || json[i] == '}' {
- break
- }
- }
- return append(buf, json[s:i]...), i, nl, true
-}
-
-func appendTabs(buf []byte, prefix, indent string, tabs int) []byte {
- if len(prefix) != 0 {
- buf = append(buf, prefix...)
- }
- if len(indent) == 2 && indent[0] == ' ' && indent[1] == ' ' {
- for i := 0; i < tabs; i++ {
- buf = append(buf, ' ', ' ')
- }
- } else {
- for i := 0; i < tabs; i++ {
- buf = append(buf, indent...)
- }
- }
- return buf
-}
-
-// Style is the color style
-type Style struct {
- Key, String, Number [2]string
- True, False, Null [2]string
- Escape [2]string
- Brackets [2]string
- Append func(dst []byte, c byte) []byte
-}
-
-func hexp(p byte) byte {
- switch {
- case p < 10:
- return p + '0'
- default:
- return (p - 10) + 'a'
- }
-}
-
-// TerminalStyle is for terminals
-var TerminalStyle *Style
-
-func init() {
- TerminalStyle = &Style{
- Key: [2]string{"\x1B[1m\x1B[94m", "\x1B[0m"},
- String: [2]string{"\x1B[32m", "\x1B[0m"},
- Number: [2]string{"\x1B[33m", "\x1B[0m"},
- True: [2]string{"\x1B[36m", "\x1B[0m"},
- False: [2]string{"\x1B[36m", "\x1B[0m"},
- Null: [2]string{"\x1B[2m", "\x1B[0m"},
- Escape: [2]string{"\x1B[35m", "\x1B[0m"},
- Brackets: [2]string{"\x1B[1m", "\x1B[0m"},
- Append: func(dst []byte, c byte) []byte {
- if c < ' ' && (c != '\r' && c != '\n' && c != '\t' && c != '\v') {
- dst = append(dst, "\\u00"...)
- dst = append(dst, hexp((c>>4)&0xF))
- return append(dst, hexp((c)&0xF))
- }
- return append(dst, c)
- },
- }
-}
-
-// Color will colorize the json. The style parma is used for customizing
-// the colors. Passing nil to the style param will use the default
-// TerminalStyle.
-func Color(src []byte, style *Style) []byte {
- if style == nil {
- style = TerminalStyle
- }
- apnd := style.Append
- if apnd == nil {
- apnd = func(dst []byte, c byte) []byte {
- return append(dst, c)
- }
- }
- type stackt struct {
- kind byte
- key bool
- }
- var dst []byte
- var stack []stackt
- for i := 0; i < len(src); i++ {
- if src[i] == '"' {
- key := len(stack) > 0 && stack[len(stack)-1].key
- if key {
- dst = append(dst, style.Key[0]...)
- } else {
- dst = append(dst, style.String[0]...)
- }
- dst = apnd(dst, '"')
- esc := false
- uesc := 0
- for i = i + 1; i < len(src); i++ {
- if src[i] == '\\' {
- if key {
- dst = append(dst, style.Key[1]...)
- } else {
- dst = append(dst, style.String[1]...)
- }
- dst = append(dst, style.Escape[0]...)
- dst = apnd(dst, src[i])
- esc = true
- if i+1 < len(src) && src[i+1] == 'u' {
- uesc = 5
- } else {
- uesc = 1
- }
- } else if esc {
- dst = apnd(dst, src[i])
- if uesc == 1 {
- esc = false
- dst = append(dst, style.Escape[1]...)
- if key {
- dst = append(dst, style.Key[0]...)
- } else {
- dst = append(dst, style.String[0]...)
- }
- } else {
- uesc--
- }
- } else {
- dst = apnd(dst, src[i])
- }
- if src[i] == '"' {
- j := i - 1
- for ; ; j-- {
- if src[j] != '\\' {
- break
- }
- }
- if (j-i)%2 != 0 {
- break
- }
- }
- }
- if esc {
- dst = append(dst, style.Escape[1]...)
- } else if key {
- dst = append(dst, style.Key[1]...)
- } else {
- dst = append(dst, style.String[1]...)
- }
- } else if src[i] == '{' || src[i] == '[' {
- stack = append(stack, stackt{src[i], src[i] == '{'})
- dst = append(dst, style.Brackets[0]...)
- dst = apnd(dst, src[i])
- dst = append(dst, style.Brackets[1]...)
- } else if (src[i] == '}' || src[i] == ']') && len(stack) > 0 {
- stack = stack[:len(stack)-1]
- dst = append(dst, style.Brackets[0]...)
- dst = apnd(dst, src[i])
- dst = append(dst, style.Brackets[1]...)
- } else if (src[i] == ':' || src[i] == ',') && len(stack) > 0 && stack[len(stack)-1].kind == '{' {
- stack[len(stack)-1].key = !stack[len(stack)-1].key
- dst = append(dst, style.Brackets[0]...)
- dst = apnd(dst, src[i])
- dst = append(dst, style.Brackets[1]...)
- } else {
- var kind byte
- if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' || isNaNOrInf(src[i:]) {
- kind = '0'
- dst = append(dst, style.Number[0]...)
- } else if src[i] == 't' {
- kind = 't'
- dst = append(dst, style.True[0]...)
- } else if src[i] == 'f' {
- kind = 'f'
- dst = append(dst, style.False[0]...)
- } else if src[i] == 'n' {
- kind = 'n'
- dst = append(dst, style.Null[0]...)
- } else {
- dst = apnd(dst, src[i])
- }
- if kind != 0 {
- for ; i < len(src); i++ {
- if src[i] <= ' ' || src[i] == ',' || src[i] == ':' || src[i] == ']' || src[i] == '}' {
- i--
- break
- }
- dst = apnd(dst, src[i])
- }
- if kind == '0' {
- dst = append(dst, style.Number[1]...)
- } else if kind == 't' {
- dst = append(dst, style.True[1]...)
- } else if kind == 'f' {
- dst = append(dst, style.False[1]...)
- } else if kind == 'n' {
- dst = append(dst, style.Null[1]...)
- }
- }
- }
- }
- return dst
-}
-
-// Spec strips out comments and trailing commas and convert the input to a
-// valid JSON per the official spec: https://tools.ietf.org/html/rfc8259
-//
-// The resulting JSON will always be the same length as the input and it will
-// include all of the same line breaks at matching offsets. This is to ensure
-// the result can be later processed by a external parser and that that
-// parser will report messages or errors with the correct offsets.
-func Spec(src []byte) []byte {
- return spec(src, nil)
-}
-
-// SpecInPlace is the same as Spec, but this method reuses the input json
-// buffer to avoid allocations. Do not use the original bytes slice upon return.
-func SpecInPlace(src []byte) []byte {
- return spec(src, src)
-}
-
-func spec(src, dst []byte) []byte {
- dst = dst[:0]
- for i := 0; i < len(src); i++ {
- if src[i] == '/' {
- if i < len(src)-1 {
- if src[i+1] == '/' {
- dst = append(dst, ' ', ' ')
- i += 2
- for ; i < len(src); i++ {
- if src[i] == '\n' {
- dst = append(dst, '\n')
- break
- } else if src[i] == '\t' || src[i] == '\r' {
- dst = append(dst, src[i])
- } else {
- dst = append(dst, ' ')
- }
- }
- continue
- }
- if src[i+1] == '*' {
- dst = append(dst, ' ', ' ')
- i += 2
- for ; i < len(src)-1; i++ {
- if src[i] == '*' && src[i+1] == '/' {
- dst = append(dst, ' ', ' ')
- i++
- break
- } else if src[i] == '\n' || src[i] == '\t' ||
- src[i] == '\r' {
- dst = append(dst, src[i])
- } else {
- dst = append(dst, ' ')
- }
- }
- continue
- }
- }
- }
- dst = append(dst, src[i])
- if src[i] == '"' {
- for i = i + 1; i < len(src); i++ {
- dst = append(dst, src[i])
- if src[i] == '"' {
- j := i - 1
- for ; ; j-- {
- if src[j] != '\\' {
- break
- }
- }
- if (j-i)%2 != 0 {
- break
- }
- }
- }
- } else if src[i] == '}' || src[i] == ']' {
- for j := len(dst) - 2; j >= 0; j-- {
- if dst[j] <= ' ' {
- continue
- }
- if dst[j] == ',' {
- dst[j] = ' '
- }
- break
- }
- }
- }
- return dst
-}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 87f3cc788..b7049c76e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -156,15 +156,6 @@ github.com/spf13/pflag
## explicit; go 1.13
github.com/stretchr/testify/assert
github.com/stretchr/testify/require
-# github.com/tidwall/gjson v1.14.4
-## explicit; go 1.12
-github.com/tidwall/gjson
-# github.com/tidwall/match v1.1.1
-## explicit; go 1.15
-github.com/tidwall/match
-# github.com/tidwall/pretty v1.2.1
-## explicit; go 1.16
-github.com/tidwall/pretty
# go.uber.org/atomic v1.10.0
## explicit; go 1.18
go.uber.org/atomic