mirror of
https://github.com/taigrr/yq
synced 2025-01-18 04:53:17 -08:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a366cacad3 | ||
|
|
7c8d3b9e70 | ||
|
|
ca10642e23 | ||
|
|
73518f3915 | ||
|
|
348ddb7a59 | ||
|
|
f46fe384bd | ||
|
|
071ec3c08c | ||
|
|
c2ed3a3e6d | ||
|
|
cc4c69bc89 | ||
|
|
917fd0eb6a | ||
|
|
b2056a2056 | ||
|
|
ba59201217 | ||
|
|
cfddee9101 | ||
|
|
1941bb66a5 | ||
|
|
29af9e4c63 | ||
|
|
af3a9ae846 | ||
|
|
5fb88627ca | ||
|
|
4fc3793fcb | ||
|
|
d612897e89 | ||
|
|
806f906041 | ||
|
|
e8b2f6e383 | ||
|
|
e419b5a39d | ||
|
|
a5b6e08282 | ||
|
|
2417c0ee8f | ||
|
|
d3b2e37b66 | ||
|
|
54723c1a36 | ||
|
|
0318c80d33 | ||
|
|
8980846632 | ||
|
|
0fb62d3ae1 | ||
|
|
85398727ad | ||
|
|
0bf3d781f6 | ||
|
|
5c15936bf3 | ||
|
|
b0735d8152 | ||
|
|
f17cbfd007 | ||
|
|
7b7ab70286 | ||
|
|
43165fa340 | ||
|
|
feda9f044d | ||
|
|
85629af59c | ||
|
|
5fc26e9453 | ||
|
|
07a6fa4df5 | ||
|
|
ec29ee7c14 | ||
|
|
24538c0cc7 | ||
|
|
61398bfd3a | ||
|
|
f7d95021c1 | ||
|
|
6bb8b1fb77 | ||
|
|
5e2c19cc86 | ||
|
|
30c269a66c | ||
|
|
70a1c60d7b | ||
|
|
e4d48bbc0d | ||
|
|
369ff94ad0 | ||
|
|
1c7fb14631 | ||
|
|
f4de5c4300 | ||
|
|
a72c14f06b | ||
|
|
5ed52aca66 | ||
|
|
5a5ac0dfef | ||
|
|
5a55869745 | ||
|
|
15e18bb98b | ||
|
|
644063646e | ||
|
|
aabed1a237 | ||
|
|
c12764dba8 | ||
|
|
1d5ecb244d | ||
|
|
7798a141cf | ||
|
|
b7583a538d | ||
|
|
529e88b630 | ||
|
|
658006eb93 | ||
|
|
2743a7e058 | ||
|
|
f95862fba3 | ||
|
|
3f58c4bc38 | ||
|
|
a7975df7cd | ||
|
|
3d5a5e0360 | ||
|
|
155755ae2f | ||
|
|
601c13531c | ||
|
|
69d00c89df | ||
|
|
2faff7b05f | ||
|
|
165949041d | ||
|
|
dbd7ab0f13 | ||
|
|
6d512ad718 | ||
|
|
4fef4a7ab1 | ||
|
|
dcb17b51a9 | ||
|
|
385417556d |
@@ -1 +1 @@
|
|||||||
bin/*
|
bin
|
||||||
|
|||||||
@@ -6,5 +6,26 @@ RUN set -e -x \
|
|||||||
&& /opt/devtools.sh
|
&& /opt/devtools.sh
|
||||||
ENV PATH=/go/bin:$PATH
|
ENV PATH=/go/bin:$PATH
|
||||||
|
|
||||||
|
# install mkdocs
|
||||||
|
RUN set -ex \
|
||||||
|
&& buildDeps=' \
|
||||||
|
build-essential \
|
||||||
|
python3-dev \
|
||||||
|
' \
|
||||||
|
&& apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
$buildDeps \
|
||||||
|
python3 \
|
||||||
|
python3-setuptools \
|
||||||
|
python3-wheel \
|
||||||
|
python3-pip \
|
||||||
|
&& pip3 install --upgrade \
|
||||||
|
pip \
|
||||||
|
'Markdown>=2.6.9' \
|
||||||
|
'mkdocs>=0.16.3' \
|
||||||
|
'mkdocs-material>=1.10.1' \
|
||||||
|
'markdown-include>=0.5.1' \
|
||||||
|
&& apt-get purge -y --auto-remove $buildDeps \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV CGO_ENABLED 0
|
ENV CGO_ENABLED 0
|
||||||
ENV GOPATH /go:/yq
|
ENV GOPATH /go:/yq
|
||||||
|
|||||||
10
Makefile
10
Makefile
@@ -17,7 +17,6 @@ help:
|
|||||||
@echo ' make vendor Install dependencies to vendor directory.'
|
@echo ' make vendor Install dependencies to vendor directory.'
|
||||||
@echo ' make format Run code formatter.'
|
@echo ' make format Run code formatter.'
|
||||||
@echo ' make check Run static code analysis (lint).'
|
@echo ' make check Run static code analysis (lint).'
|
||||||
@echo ' make secure Run gosec.'
|
|
||||||
@echo ' make test Run tests on project.'
|
@echo ' make test Run tests on project.'
|
||||||
@echo ' make cover Run tests and capture code coverage metrics on project.'
|
@echo ' make cover Run tests and capture code coverage metrics on project.'
|
||||||
@echo ' make clean Clean the directory tree of produced artifacts.'
|
@echo ' make clean Clean the directory tree of produced artifacts.'
|
||||||
@@ -85,10 +84,6 @@ format: vendor
|
|||||||
check: format
|
check: format
|
||||||
${DOCKRUN} bash ./scripts/check.sh
|
${DOCKRUN} bash ./scripts/check.sh
|
||||||
|
|
||||||
.PHONY: secure
|
|
||||||
secure:
|
|
||||||
${DOCKRUN} bash ./scripts/secure.sh
|
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: check
|
test: check
|
||||||
${DOCKRUN} bash ./scripts/test.sh
|
${DOCKRUN} bash ./scripts/test.sh
|
||||||
@@ -101,6 +96,11 @@ cover: check
|
|||||||
@find cover -type d -exec chmod 755 {} \; || :
|
@find cover -type d -exec chmod 755 {} \; || :
|
||||||
@find cover -type f -exec chmod 644 {} \; || :
|
@find cover -type f -exec chmod 644 {} \; || :
|
||||||
|
|
||||||
|
.PHONY: build-docs
|
||||||
|
build-docs: prepare mkdocs.yml mkdocs/*
|
||||||
|
${DOCKRUN} mkdocs build
|
||||||
|
@find docs -type d -exec chmod 755 {} \; || :
|
||||||
|
@find docs -type f -exec chmod 644 {} \; || :
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release: xcompile
|
release: xcompile
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -119,8 +119,6 @@ See [webi](https://webinstall.dev/)
|
|||||||
Supported by @adithyasunil26 (https://github.com/webinstall/webi-installers/tree/master/yq)
|
Supported by @adithyasunil26 (https://github.com/webinstall/webi-installers/tree/master/yq)
|
||||||
|
|
||||||
### Windows:
|
### Windows:
|
||||||
[](https://chocolatey.org/packages/yq)
|
|
||||||
[](https://chocolatey.org/packages/yq)
|
|
||||||
```
|
```
|
||||||
choco install yq
|
choco install yq
|
||||||
```
|
```
|
||||||
@@ -153,21 +151,19 @@ sudo apt install yq -y
|
|||||||
Supported by @rmescandon (https://launchpad.net/~rmescandon/+archive/ubuntu/yq)
|
Supported by @rmescandon (https://launchpad.net/~rmescandon/+archive/ubuntu/yq)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- [Detailed documentation with many examples](https://mikefarah.gitbook.io/yq/)
|
|
||||||
- Written in portable go, so you can download a lovely dependency free binary
|
- Written in portable go, so you can download a lovely dependency free binary
|
||||||
- Uses similar syntax as `jq` but works with YAML and JSON files
|
- Uses similar syntax as `jq` but works with YAML and JSON files
|
||||||
- Fully supports multi document yaml files
|
- Fully supports multi document yaml files
|
||||||
- Colorized yaml output
|
- Colorized yaml output
|
||||||
- [Deeply traverse yaml](https://mikefarah.gitbook.io/yq/operators/traverse-read)
|
- [Deeply traverse yaml](https://mikefarah.gitbook.io/yq/v/v4.x/traverse)
|
||||||
- [Sort yaml by keys](https://mikefarah.gitbook.io/yq/operators/sort-keys)
|
- [Sort yaml by keys](https://mikefarah.gitbook.io/yq/v/v4.x/sort-keys)
|
||||||
- Manipulate yaml [comments](https://mikefarah.gitbook.io/yq/operators/comment-operators), [styling](https://mikefarah.gitbook.io/yq/operators/style), [tags](https://mikefarah.gitbook.io/yq/operators/tag) and [anchors and aliases](https://mikefarah.gitbook.io/yq/operators/anchor-and-alias-operators).
|
- Manipulate yaml [comments](https://mikefarah.gitbook.io/yq/comment-operators), [styling](https://mikefarah.gitbook.io/yq/style), [tags](https://mikefarah.gitbook.io/yq/tag) and [anchors and aliases](https://mikefarah.gitbook.io/yq/anchor-and-alias-operators).
|
||||||
- [Update yaml inplace](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate#flags)
|
- [Update yaml inplace](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate#flags)
|
||||||
- [Complex expressions to select and update](https://mikefarah.gitbook.io/yq/operators/select#select-and-update-matching-values-in-map)
|
- [Complex expressions to select and update](https://mikefarah.gitbook.io/yq/v/v4.x/select#select-and-update-matching-values-in-map)
|
||||||
- Keeps yaml formatting and comments when updating (though there are issues with whitespace)
|
- Keeps yaml formatting and comments when updating (though there are issues with whitespace)
|
||||||
- [Convert to/from json to yaml](https://mikefarah.gitbook.io/yq/v/v4.x/usage/convert)
|
- [Convert to/from json to yaml](https://mikefarah.gitbook.io/yq/v/v4.x/usage/convert)
|
||||||
- [Pipe data in by using '-'](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate)
|
- [Pipe data in by using '-'](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate)
|
||||||
- [General shell completion scripts (bash/zsh/fish/powershell)](https://mikefarah.gitbook.io/yq/v/v4.x/commands/shell-completion)
|
- [General shell completion scripts (bash/zsh/fish/powershell)](https://mikefarah.gitbook.io/yq/v/v4.x/commands/shell-completion)
|
||||||
- [Reduce](https://mikefarah.gitbook.io/yq/operators/reduce) to merge multiple files or sum an array or other fancy things.
|
|
||||||
|
|
||||||
## [Usage](https://mikefarah.gitbook.io/yq/)
|
## [Usage](https://mikefarah.gitbook.io/yq/)
|
||||||
|
|
||||||
|
|||||||
@@ -42,21 +42,14 @@ func evaluateAll(cmd *cobra.Command, args []string) error {
|
|||||||
colorsEnabled = true
|
colorsEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
firstFileIndex := -1
|
if writeInplace && len(args) < 2 {
|
||||||
if !nullInput && len(args) == 1 {
|
|
||||||
firstFileIndex = 0
|
|
||||||
} else if len(args) > 1 {
|
|
||||||
firstFileIndex = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if writeInplace && (firstFileIndex == -1) {
|
|
||||||
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
|
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if writeInplace {
|
if writeInplace {
|
||||||
// only use colors if its forced
|
// only use colors if its forced
|
||||||
colorsEnabled = forceColor
|
colorsEnabled = forceColor
|
||||||
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[firstFileIndex])
|
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[1])
|
||||||
out, err = writeInPlaceHandler.CreateTempFile()
|
out, err = writeInPlaceHandler.CreateTempFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -62,21 +62,14 @@ func evaluateSequence(cmd *cobra.Command, args []string) error {
|
|||||||
colorsEnabled = true
|
colorsEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
firstFileIndex := -1
|
if writeInplace && len(args) < 2 {
|
||||||
if !nullInput && len(args) == 1 {
|
|
||||||
firstFileIndex = 0
|
|
||||||
} else if len(args) > 1 {
|
|
||||||
firstFileIndex = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if writeInplace && (firstFileIndex == -1) {
|
|
||||||
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
|
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if writeInplace {
|
if writeInplace {
|
||||||
// only use colors if its forced
|
// only use colors if its forced
|
||||||
colorsEnabled = forceColor
|
colorsEnabled = forceColor
|
||||||
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[firstFileIndex])
|
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[1])
|
||||||
out, err = writeInPlaceHandler.CreateTempFile()
|
out, err = writeInPlaceHandler.CreateTempFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -109,7 +102,7 @@ func evaluateSequence(cmd *cobra.Command, args []string) error {
|
|||||||
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer)
|
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = streamEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer)
|
err = streamEvaluator.EvaluateFiles(args[0], args[1:], printer)
|
||||||
}
|
}
|
||||||
completedSuccessfully = err == nil
|
completedSuccessfully = err == nil
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ var (
|
|||||||
GitDescribe string
|
GitDescribe string
|
||||||
|
|
||||||
// Version is main version number that is being run at the moment.
|
// Version is main version number that is being run at the moment.
|
||||||
Version = "4.7.0"
|
Version = "4.4.1"
|
||||||
|
|
||||||
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
|
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
|
||||||
// then it means that it is a final release. Otherwise, this is a pre-release
|
// then it means that it is a final release. Otherwise, this is a pre-release
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM mikefarah/yq:4.7.0
|
FROM mikefarah/yq:4.4.1
|
||||||
|
|
||||||
COPY entrypoint.sh /entrypoint.sh
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
|||||||
10
go.mod
10
go.mod
@@ -1,14 +1,14 @@
|
|||||||
module github.com/mikefarah/yq/v4
|
module github.com/mikefarah/yq/v4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/elliotchance/orderedmap v1.4.0
|
github.com/elliotchance/orderedmap v1.3.0
|
||||||
github.com/fatih/color v1.10.0
|
github.com/fatih/color v1.10.0
|
||||||
github.com/goccy/go-yaml v1.8.9
|
github.com/goccy/go-yaml v1.8.4
|
||||||
github.com/jinzhu/copier v0.2.8
|
github.com/jinzhu/copier v0.1.0
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.1
|
||||||
github.com/timtadh/data-structures v0.5.3 // indirect
|
github.com/timtadh/data-structures v0.5.3 // indirect
|
||||||
github.com/timtadh/lexmachine v0.2.2
|
github.com/timtadh/lexmachine v0.2.2
|
||||||
golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e // indirect
|
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect
|
||||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
|
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
)
|
)
|
||||||
|
|||||||
33
go.sum
33
go.sum
@@ -36,8 +36,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A=
|
github.com/elliotchance/orderedmap v1.3.0 h1:k6m77/d0zCXTjsk12nX40TkEBkSICq8T4s6R6bpCqU0=
|
||||||
github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
|
github.com/elliotchance/orderedmap v1.3.0/go.mod h1:8hdSl6jmveQw8ScByd3AaNHNk51RhbTazdqtTty+NFw=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
@@ -52,8 +52,8 @@ github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTM
|
|||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/goccy/go-yaml v1.8.9 h1:4AEXg2qx+/w29jXnXpMY6mTckmYu1TMoHteKuMf0HFg=
|
github.com/goccy/go-yaml v1.8.4 h1:AOEdR7aQgbgwHznGe3BLkDQVujxCPUpHOZZcQcp8Y3M=
|
||||||
github.com/goccy/go-yaml v1.8.9/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
github.com/goccy/go-yaml v1.8.4/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
@@ -101,8 +101,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
|||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jinzhu/copier v0.2.8 h1:N8MbL5niMwE3P4dOwurJixz5rMkKfujmMRFmAanSzWE=
|
github.com/jinzhu/copier v0.1.0 h1:Vh8xALtH3rrKGB/XIRe5d0yCTHPZFauWPLvdpDAbi88=
|
||||||
github.com/jinzhu/copier v0.2.8/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro=
|
github.com/jinzhu/copier v0.1.0/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
@@ -168,9 +168,10 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
|
|||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
|
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
@@ -179,9 +180,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/timtadh/data-structures v0.5.3 h1:F2tEjoG9qWIyUjbvXVgJqEOGJPMIiYn7U5W5mE+i/vQ=
|
github.com/timtadh/data-structures v0.5.3 h1:F2tEjoG9qWIyUjbvXVgJqEOGJPMIiYn7U5W5mE+i/vQ=
|
||||||
github.com/timtadh/data-structures v0.5.3/go.mod h1:9R4XODhJ8JdWFEI8P/HJKqxuJctfBQw6fDibMQny2oU=
|
github.com/timtadh/data-structures v0.5.3/go.mod h1:9R4XODhJ8JdWFEI8P/HJKqxuJctfBQw6fDibMQny2oU=
|
||||||
@@ -255,9 +255,10 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e h1:XNp2Flc/1eWQGk5BLzqTAN7fQIwIbfyVTuVxXxZh73M=
|
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 h1:/dSxr6gT0FNI1MO5WLJo8mTmItROeOKTkDn+7OwWBos=
|
||||||
golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
@@ -267,6 +268,7 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
@@ -279,6 +281,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
|
|||||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=
|
||||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
@@ -305,6 +308,7 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
|
|||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -315,10 +319,11 @@ gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx
|
|||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package yqlib
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
@@ -16,15 +18,10 @@ type CandidateNode struct {
|
|||||||
// when performing op against all nodes given, this will treat all the nodes as one
|
// when performing op against all nodes given, this will treat all the nodes as one
|
||||||
// (e.g. top level cross document merge). This property does not propegate to child nodes.
|
// (e.g. top level cross document merge). This property does not propegate to child nodes.
|
||||||
EvaluateTogether bool
|
EvaluateTogether bool
|
||||||
IsMapKey bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *CandidateNode) GetKey() string {
|
func (n *CandidateNode) GetKey() string {
|
||||||
keyPrefix := ""
|
return fmt.Sprintf("%v - %v", n.Document, n.Path)
|
||||||
if n.IsMapKey {
|
|
||||||
keyPrefix = "key-"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%v%v - %v", keyPrefix, n.Document, n.Path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *CandidateNode) CreateChild(path interface{}, node *yaml.Node) *CandidateNode {
|
func (n *CandidateNode) CreateChild(path interface{}, node *yaml.Node) *CandidateNode {
|
||||||
@@ -66,10 +63,11 @@ func (n *CandidateNode) UpdateFrom(other *CandidateNode) {
|
|||||||
n.UpdateAttributesFrom(other)
|
n.UpdateAttributesFrom(other)
|
||||||
n.Node.Content = other.Node.Content
|
n.Node.Content = other.Node.Content
|
||||||
n.Node.Value = other.Node.Value
|
n.Node.Value = other.Node.Value
|
||||||
|
n.Node.Alias = other.Node.Alias
|
||||||
|
n.Node.Anchor = other.Node.Anchor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) {
|
func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) {
|
||||||
log.Debug("UpdateAttributesFrom: n: %v other: %v", n.GetKey(), other.GetKey())
|
|
||||||
if n.Node.Kind != other.Node.Kind {
|
if n.Node.Kind != other.Node.Kind {
|
||||||
// clear out the contents when switching to a different type
|
// clear out the contents when switching to a different type
|
||||||
// e.g. map to array
|
// e.g. map to array
|
||||||
@@ -78,22 +76,56 @@ func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) {
|
|||||||
}
|
}
|
||||||
n.Node.Kind = other.Node.Kind
|
n.Node.Kind = other.Node.Kind
|
||||||
n.Node.Tag = other.Node.Tag
|
n.Node.Tag = other.Node.Tag
|
||||||
n.Node.Alias = other.Node.Alias
|
|
||||||
n.Node.Anchor = other.Node.Anchor
|
|
||||||
|
|
||||||
// merge will pickup the style of the new thing
|
// merge will pickup the style of the new thing
|
||||||
// when autocreating nodes
|
// when autocreating nodes
|
||||||
if n.Node.Style == 0 {
|
if n.Node.Style == 0 {
|
||||||
n.Node.Style = other.Node.Style
|
n.Node.Style = other.Node.Style
|
||||||
}
|
}
|
||||||
|
n.Node.FootComment = n.Node.FootComment + other.Node.FootComment
|
||||||
if other.Node.FootComment != "" {
|
n.Node.HeadComment = n.Node.HeadComment + other.Node.HeadComment
|
||||||
n.Node.FootComment = other.Node.FootComment
|
n.Node.LineComment = n.Node.LineComment + other.Node.LineComment
|
||||||
}
|
}
|
||||||
if other.Node.HeadComment != "" {
|
|
||||||
n.Node.HeadComment = other.Node.HeadComment
|
func (n *CandidateNode) PathStackToString() string {
|
||||||
}
|
return mergePathStackToString(n.Path)
|
||||||
if other.Node.LineComment != "" {
|
}
|
||||||
n.Node.LineComment = other.Node.LineComment
|
|
||||||
}
|
func mergePathStackToString(pathStack []interface{}) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
for index, path := range pathStack {
|
||||||
|
switch path.(type) {
|
||||||
|
case int, int64:
|
||||||
|
// if arrayMergeStrategy == AppendArrayMergeStrategy {
|
||||||
|
// sb.WriteString("[+]")
|
||||||
|
// } else {
|
||||||
|
sb.WriteString(fmt.Sprintf("[%v]", path))
|
||||||
|
// }
|
||||||
|
|
||||||
|
default:
|
||||||
|
s := fmt.Sprintf("%v", path)
|
||||||
|
var _, errParsingInt = strconv.ParseInt(s, 10, 64) // nolint
|
||||||
|
|
||||||
|
hasSpecial := strings.Contains(s, ".") || strings.Contains(s, "[") || strings.Contains(s, "]") || strings.Contains(s, "\"")
|
||||||
|
hasDoubleQuotes := strings.Contains(s, "\"")
|
||||||
|
wrappingCharacterStart := "\""
|
||||||
|
wrappingCharacterEnd := "\""
|
||||||
|
if hasDoubleQuotes {
|
||||||
|
wrappingCharacterStart = "("
|
||||||
|
wrappingCharacterEnd = ")"
|
||||||
|
}
|
||||||
|
if hasSpecial || errParsingInt == nil {
|
||||||
|
sb.WriteString(wrappingCharacterStart)
|
||||||
|
}
|
||||||
|
sb.WriteString(s)
|
||||||
|
if hasSpecial || errParsingInt == nil {
|
||||||
|
sb.WriteString(wrappingCharacterEnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < len(pathStack)-1 {
|
||||||
|
sb.WriteString(".")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,20 +18,6 @@ func (n *Context) SingleChildContext(candidate *CandidateNode) Context {
|
|||||||
return n.ChildContext(list)
|
return n.ChildContext(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Context) GetVariable(name string) *list.List {
|
|
||||||
if n.Variables == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return n.Variables[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Context) SetVariable(name string, value *list.List) {
|
|
||||||
if n.Variables == nil {
|
|
||||||
n.Variables = make(map[string]*list.List)
|
|
||||||
}
|
|
||||||
n.Variables[name] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Context) ChildContext(results *list.List) Context {
|
func (n *Context) ChildContext(results *list.List) Context {
|
||||||
clone := Context{}
|
clone := Context{}
|
||||||
err := copier.Copy(&clone, n)
|
err := copier.Copy(&clone, n)
|
||||||
@@ -42,13 +28,3 @@ func (n *Context) ChildContext(results *list.List) Context {
|
|||||||
clone.MatchingNodes = results
|
clone.MatchingNodes = results
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Context) Clone() Context {
|
|
||||||
clone := Context{}
|
|
||||||
err := copier.Copy(&clone, n)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error cloning context :(")
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return clone
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ will output
|
|||||||
- hello
|
- hello
|
||||||
```
|
```
|
||||||
|
|
||||||
## Append to array
|
## Update array (append)
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
a:
|
a:
|
||||||
@@ -127,36 +127,6 @@ b:
|
|||||||
- 4
|
- 4
|
||||||
```
|
```
|
||||||
|
|
||||||
## Relative append
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a:
|
|
||||||
a1:
|
|
||||||
b:
|
|
||||||
- cat
|
|
||||||
a2:
|
|
||||||
b:
|
|
||||||
- dog
|
|
||||||
a3: {}
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.a[].b += ["mouse"]' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a:
|
|
||||||
a1:
|
|
||||||
b:
|
|
||||||
- cat
|
|
||||||
- mouse
|
|
||||||
a2:
|
|
||||||
b:
|
|
||||||
- dog
|
|
||||||
- mouse
|
|
||||||
a3: {b: [mouse]}
|
|
||||||
```
|
|
||||||
|
|
||||||
## String concatenation
|
## String concatenation
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -173,6 +143,22 @@ a: catmeow
|
|||||||
b: meow
|
b: meow
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Relative string concatenation
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: cat
|
||||||
|
b: meow
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a += .b' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a: catmeow
|
||||||
|
b: meow
|
||||||
|
```
|
||||||
|
|
||||||
## Number addition - float
|
## Number addition - float
|
||||||
If the lhs or rhs are floats then the expression will be calculated with floats.
|
If the lhs or rhs are floats then the expression will be calculated with floats.
|
||||||
|
|
||||||
@@ -209,20 +195,18 @@ a: 7
|
|||||||
b: 4
|
b: 4
|
||||||
```
|
```
|
||||||
|
|
||||||
## Increment numbers
|
## Increment number
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
a: 3
|
a: 3
|
||||||
b: 5
|
|
||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
yq eval '.[] += 1' sample.yml
|
yq eval '.a += 1' sample.yml
|
||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
a: 4
|
a: 4
|
||||||
b: 6
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Add to null
|
## Add to null
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Use the `alias` and `anchor` operators to read and write yaml aliases and anchors. The `explode` operator normalises a yaml file (dereference (or expands) aliases and remove anchor names).
|
Use the `alias` and `anchor` operators to read and write yaml aliases and anchors. The `explode` operator normalises a yaml file (dereference aliases and remove anchor names).
|
||||||
|
|
||||||
`yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag.
|
`yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
|
This operator is used to update node values. It can be used in either the:
|
||||||
|
|
||||||
|
### plain form: `=`
|
||||||
|
Which will assign the LHS node values to the RHS node values. The RHS expression is run against the matching nodes in the pipeline.
|
||||||
|
|
||||||
|
### relative form: `|=`
|
||||||
|
This will do a similar thing to the plain form, however, the RHS expression is run against _the LHS nodes_. This is useful for updating values based on old values, e.g. increment.
|
||||||
## Create yaml file
|
## Create yaml file
|
||||||
Running
|
Running
|
||||||
```bash
|
```bash
|
||||||
@@ -1,15 +1,4 @@
|
|||||||
Use these comment operators to set or retrieve comments.
|
Use these comment operators to set or retrieve comments.
|
||||||
|
|
||||||
Like the `=` and `|=` assign operators, the same syntax applies when updating comments:
|
|
||||||
|
|
||||||
|
|
||||||
### plain form: `=`
|
|
||||||
This will assign the LHS nodes comments to the expression on the RHS. The RHS is run against the matching nodes in the pipeline
|
|
||||||
|
|
||||||
### relative form: `|=`
|
|
||||||
Similar to the plain form, however the RHS evaluates against each matching LHS node! This is useful if you want to set the comments as a relative expression of the node, for instance its value or path.
|
|
||||||
|
|
||||||
|
|
||||||
## Set line comment
|
## Set line comment
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -29,24 +29,6 @@ true
|
|||||||
false
|
false
|
||||||
```
|
```
|
||||||
|
|
||||||
## Don't match string
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- cat
|
|
||||||
- goat
|
|
||||||
- dog
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] | (. != "*at")' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
false
|
|
||||||
false
|
|
||||||
true
|
|
||||||
```
|
|
||||||
|
|
||||||
## Match number
|
## Match number
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -65,24 +47,6 @@ true
|
|||||||
false
|
false
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dont match number
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- 3
|
|
||||||
- 4
|
|
||||||
- 5
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] | (. != 4)' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
true
|
|
||||||
false
|
|
||||||
true
|
|
||||||
```
|
|
||||||
|
|
||||||
## Match nulls
|
## Match nulls
|
||||||
Running
|
Running
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -35,26 +35,6 @@ will output
|
|||||||
0
|
0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Get file indices of multiple documents
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: cat
|
|
||||||
```
|
|
||||||
And another sample another.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: cat
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval-all 'fileIndex' sample.yml another.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
0
|
|
||||||
---
|
|
||||||
1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Get file index alias
|
## Get file index alias
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -19,29 +19,6 @@ true
|
|||||||
false
|
false
|
||||||
```
|
```
|
||||||
|
|
||||||
## Select, checking for existence of deep paths
|
|
||||||
Simply pipe in parent expressions into `has`
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- a:
|
|
||||||
b:
|
|
||||||
c: cat
|
|
||||||
- a:
|
|
||||||
b:
|
|
||||||
d: dog
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] | select(.a.b | has("c"))' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a:
|
|
||||||
b:
|
|
||||||
c: cat
|
|
||||||
```
|
|
||||||
|
|
||||||
## Has array index
|
## Has array index
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -1,34 +1,19 @@
|
|||||||
Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently numbers, arrays and objects are supported.
|
Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently only objects are supported, which have the effect of merging the RHS into the LHS.
|
||||||
|
|
||||||
## Objects and arrays - merging
|
To concatenate arrays when merging objects, use the *+ form (see examples below). This will recursively merge objects, appending arrays when it encounters them.
|
||||||
Objects are merged deeply matching on matching keys. By default, array values override and are not deeply merged.
|
|
||||||
|
|
||||||
|
To merge only existing fields, use the *? form. Note that this can be used with the concatenate arrays too *+?.
|
||||||
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
||||||
|
|
||||||
### Merge Flags
|
Multiplication of strings and numbers are not yet supported.
|
||||||
You can control how objects are merged by using one or more of the following flags. Multiple flags can be used together, e.g. `.a *+? .b`. See examples below
|
|
||||||
|
|
||||||
- `+` to append arrays
|
## Merging files
|
||||||
- `?` to only merge existing fields
|
|
||||||
- `d` to deeply merge arrays
|
|
||||||
|
|
||||||
### Merging files
|
|
||||||
Note the use of `eval-all` to ensure all documents are loaded into memory.
|
Note the use of `eval-all` to ensure all documents are loaded into memory.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' file1.yaml file2.yaml
|
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' file1.yaml file2.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Multiply integers
|
|
||||||
Running
|
|
||||||
```bash
|
|
||||||
yq eval --null-input '3 * 4'
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
12
|
|
||||||
```
|
|
||||||
|
|
||||||
## Merge objects together, returning merged result only
|
## Merge objects together, returning merged result only
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -86,6 +71,7 @@ Given a sample.yml file of:
|
|||||||
a: {things: great}
|
a: {things: great}
|
||||||
b:
|
b:
|
||||||
also: "me"
|
also: "me"
|
||||||
|
|
||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
@@ -205,32 +191,6 @@ thing:
|
|||||||
- 4
|
- 4
|
||||||
```
|
```
|
||||||
|
|
||||||
## Merge, deeply merging arrays
|
|
||||||
Merging arrays deeply means arrays are merge like objects, with indexes as their key. In this case, we merge the first item in the array, and do nothing with the second.
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a:
|
|
||||||
- name: fred
|
|
||||||
age: 12
|
|
||||||
- name: bob
|
|
||||||
age: 32
|
|
||||||
b:
|
|
||||||
- name: fred
|
|
||||||
age: 34
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.a *d .b' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
- name: fred
|
|
||||||
age: 34
|
|
||||||
- name: bob
|
|
||||||
age: 32
|
|
||||||
```
|
|
||||||
|
|
||||||
## Merge to prefix an element
|
## Merge to prefix an element
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
Reduce is a powerful way to process a collection of data into a new form.
|
|
||||||
|
|
||||||
```
|
|
||||||
<exp> as $<name> ireduce (<init>; <block>)
|
|
||||||
```
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```
|
|
||||||
.[] as $item ireduce (0; . + $item)
|
|
||||||
```
|
|
||||||
|
|
||||||
On the LHS we are configuring the collection of items that will be reduced `<exp>` as well as what each element will be called `$<name>`. Note that the array has been splatted into its individual elements.
|
|
||||||
|
|
||||||
On the RHS there is `<init>`, the starting value of the accumulator and `<block>`, the expression that will update the accumulator for each element in the collection. Note that within the block expression, `.` will evaluate to the current value of the accumulator.
|
|
||||||
|
|
||||||
## yq vs jq syntax
|
|
||||||
Reduce syntax in `yq` is a little different from `jq` - as `yq` (currently) isn't as sophisticated as `jq` and its only supports infix notation (e.g. a + b, where the operator is in the middle of the two parameters) - where as `jq` uses a mix of infix notation with _prefix_ notation (e.g. `reduce a b` is like writing `+ a b`).
|
|
||||||
|
|
||||||
To that end, the reduce operator is called `ireduce` for backwards compatability if a `jq` like prefix version of `reduce` is ever added.
|
|
||||||
|
|
||||||
|
|
||||||
## Sum numbers
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- 10
|
|
||||||
- 2
|
|
||||||
- 5
|
|
||||||
- 3
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] as $item ireduce (0; . + $item)' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
20
|
|
||||||
```
|
|
||||||
|
|
||||||
## Merge all yaml files together
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: cat
|
|
||||||
```
|
|
||||||
And another sample another.yml file of:
|
|
||||||
```yaml
|
|
||||||
b: dog
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval-all '. as $item ireduce ({}; . * $item )' sample.yml another.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a: cat
|
|
||||||
b: dog
|
|
||||||
```
|
|
||||||
|
|
||||||
## Convert an array to an object
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- name: Cathy
|
|
||||||
has: apples
|
|
||||||
- name: Bob
|
|
||||||
has: bananas
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] as $item ireduce ({}; .[$item | .name] = ($item | .has) )' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
Cathy: apples
|
|
||||||
Bob: bananas
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -18,40 +18,6 @@ will output
|
|||||||
cat; meow; 1; ; true
|
cat; meow; 1; ; true
|
||||||
```
|
```
|
||||||
|
|
||||||
## Substitute / Replace string
|
|
||||||
This uses golang regex, described [here](https://github.com/google/re2/wiki/Syntax)
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: dogs are great
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.a |= sub("dogs", "cats")' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a: cats are great
|
|
||||||
```
|
|
||||||
|
|
||||||
## Substitute / Replace string with regex
|
|
||||||
This uses golang regex, described [here](https://github.com/google/re2/wiki/Syntax)
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: cat
|
|
||||||
b: heat
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] |= sub("([a])", "${1}r")' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a: cart
|
|
||||||
b: heart
|
|
||||||
```
|
|
||||||
|
|
||||||
## Split strings
|
## Split strings
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
|
|
||||||
## Number subtraction - float
|
|
||||||
If the lhs or rhs are floats then the expression will be calculated with floats.
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: 3
|
|
||||||
b: 4.5
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.a = .a - .b' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a: -1.5
|
|
||||||
b: 4.5
|
|
||||||
```
|
|
||||||
|
|
||||||
## Number subtraction - float
|
|
||||||
If the lhs or rhs are floats then the expression will be calculated with floats.
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: 3
|
|
||||||
b: 4.5
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.a = .a - .b' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a: -1.5
|
|
||||||
b: 4.5
|
|
||||||
```
|
|
||||||
|
|
||||||
## Number subtraction - int
|
|
||||||
If both the lhs and rhs are ints then the expression will be calculated with ints.
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: 3
|
|
||||||
b: 4
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.a = .a - .b' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a: -1
|
|
||||||
b: 4
|
|
||||||
```
|
|
||||||
|
|
||||||
## Decrement numbers
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: 3
|
|
||||||
b: 5
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] -= 1' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a: 2
|
|
||||||
b: 4
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ c: banana
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Special characters
|
## Special characters
|
||||||
Use quotes with brackets around path elements with special characters
|
Use quotes around path elements with special characters
|
||||||
|
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -41,23 +41,7 @@ Given a sample.yml file of:
|
|||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
yq eval '.["{}"]' sample.yml
|
yq eval '."{}"' sample.yml
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
frog
|
|
||||||
```
|
|
||||||
|
|
||||||
## Keys with spaces
|
|
||||||
Use quotes with brackets around path elements with special characters
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
"red rabbit": frog
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.["red rabbit"]' sample.yml
|
|
||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
For more complex scenarios, variables can be used to hold values of expression to be used in other expressions.
|
|
||||||
|
|
||||||
## Single value variable
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a: cat
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.a as $foo | $foo' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
cat
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multi value variable
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- cat
|
|
||||||
- dog
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] as $foo | $foo' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
cat
|
|
||||||
dog
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using variables as a lookup
|
|
||||||
Example taken from [jq](https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...)
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
"posts":
|
|
||||||
- "title": Frist psot
|
|
||||||
"author": anon
|
|
||||||
- "title": A well-written article
|
|
||||||
"author": person1
|
|
||||||
"realnames":
|
|
||||||
"anon": Anonymous Coward
|
|
||||||
"person1": Person McPherson
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.realnames as $names | .posts[] | {"title":.title, "author": $names[.author]}' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
title: Frist psot
|
|
||||||
author: Anonymous Coward
|
|
||||||
title: A well-written article
|
|
||||||
author: Person McPherson
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Use the `alias` and `anchor` operators to read and write yaml aliases and anchors. The `explode` operator normalises a yaml file (dereference (or expands) aliases and remove anchor names).
|
Use the `alias` and `anchor` operators to read and write yaml aliases and anchors. The `explode` operator normalises a yaml file (dereference aliases and remove anchor names).
|
||||||
|
|
||||||
`yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag.
|
`yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag.
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1 @@
|
|||||||
Use these comment operators to set or retrieve comments.
|
Use these comment operators to set or retrieve comments.
|
||||||
|
|
||||||
Like the `=` and `|=` assign operators, the same syntax applies when updating comments:
|
|
||||||
|
|
||||||
|
|
||||||
### plain form: `=`
|
|
||||||
This will assign the LHS nodes comments to the expression on the RHS. The RHS is run against the matching nodes in the pipeline
|
|
||||||
|
|
||||||
### relative form: `|=`
|
|
||||||
Similar to the plain form, however the RHS evaluates against each matching LHS node! This is useful if you want to set the comments as a relative expression of the node, for instance its value or path.
|
|
||||||
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently numbers, arrays and objects are supported.
|
|
||||||
|
|
||||||
## Objects and arrays - merging
|
|
||||||
Objects are merged deeply matching on matching keys. By default, array values override and are not deeply merged.
|
|
||||||
|
|
||||||
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
|
||||||
|
|
||||||
### Merge Flags
|
|
||||||
You can control how objects are merged by using one or more of the following flags. Multiple flags can be used together, e.g. `.a *+? .b`. See examples below
|
|
||||||
|
|
||||||
- `+` to append arrays
|
|
||||||
- `?` to only merge existing fields
|
|
||||||
- `d` to deeply merge arrays
|
|
||||||
|
|
||||||
### Merging files
|
|
||||||
Note the use of `eval-all` to ensure all documents are loaded into memory.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' file1.yaml file2.yaml
|
|
||||||
```
|
|
||||||
15
pkg/yqlib/doc/headers/Multiply.md
Normal file
15
pkg/yqlib/doc/headers/Multiply.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently only objects are supported, which have the effect of merging the RHS into the LHS.
|
||||||
|
|
||||||
|
To concatenate arrays when merging objects, use the *+ form (see examples below). This will recursively merge objects, appending arrays when it encounters them.
|
||||||
|
|
||||||
|
To merge only existing fields, use the *? form. Note that this can be used with the concatenate arrays too *+?.
|
||||||
|
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
||||||
|
|
||||||
|
Multiplication of strings and numbers are not yet supported.
|
||||||
|
|
||||||
|
## Merging files
|
||||||
|
Note the use of `eval-all` to ensure all documents are loaded into memory.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' file1.yaml file2.yaml
|
||||||
|
```
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
Reduce is a powerful way to process a collection of data into a new form.
|
|
||||||
|
|
||||||
```
|
|
||||||
<exp> as $<name> ireduce (<init>; <block>)
|
|
||||||
```
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```
|
|
||||||
.[] as $item ireduce (0; . + $item)
|
|
||||||
```
|
|
||||||
|
|
||||||
On the LHS we are configuring the collection of items that will be reduced `<exp>` as well as what each element will be called `$<name>`. Note that the array has been splatted into its individual elements.
|
|
||||||
|
|
||||||
On the RHS there is `<init>`, the starting value of the accumulator and `<block>`, the expression that will update the accumulator for each element in the collection. Note that within the block expression, `.` will evaluate to the current value of the accumulator.
|
|
||||||
|
|
||||||
## yq vs jq syntax
|
|
||||||
Reduce syntax in `yq` is a little different from `jq` - as `yq` (currently) isn't as sophisticated as `jq` and its only supports infix notation (e.g. a + b, where the operator is in the middle of the two parameters) - where as `jq` uses a mix of infix notation with _prefix_ notation (e.g. `reduce a b` is like writing `+ a b`).
|
|
||||||
|
|
||||||
To that end, the reduce operator is called `ireduce` for backwards compatability if a `jq` like prefix version of `reduce` is ever added.
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
For more complex scenarios, variables can be used to hold values of expression to be used in other expressions.
|
|
||||||
@@ -5,6 +5,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var myPathTokeniser = newExpressionTokeniser()
|
||||||
|
var myPathPostfixer = newExpressionPostFixer()
|
||||||
|
|
||||||
type ExpressionNode struct {
|
type ExpressionNode struct {
|
||||||
Operation *Operation
|
Operation *Operation
|
||||||
Lhs *ExpressionNode
|
Lhs *ExpressionNode
|
||||||
@@ -16,21 +19,19 @@ type ExpressionParser interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type expressionParserImpl struct {
|
type expressionParserImpl struct {
|
||||||
pathTokeniser expressionTokeniser
|
|
||||||
pathPostFixer expressionPostFixer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExpressionParser() ExpressionParser {
|
func NewExpressionParser() ExpressionParser {
|
||||||
return &expressionParserImpl{newExpressionTokeniser(), newExpressionPostFixer()}
|
return &expressionParserImpl{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *expressionParserImpl) ParseExpression(expression string) (*ExpressionNode, error) {
|
func (p *expressionParserImpl) ParseExpression(expression string) (*ExpressionNode, error) {
|
||||||
tokens, err := p.pathTokeniser.Tokenise(expression)
|
tokens, err := myPathTokeniser.Tokenise(expression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var Operations []*Operation
|
var Operations []*Operation
|
||||||
Operations, err = p.pathPostFixer.ConvertToPostfix(tokens)
|
Operations, err = myPathPostfixer.ConvertToPostfix(tokens)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -69,7 +70,7 @@ func (p *expressionParserImpl) createExpressionTree(postFixPath []*Operation) (*
|
|||||||
stack = append(stack, &newNode)
|
stack = append(stack, &newNode)
|
||||||
}
|
}
|
||||||
if len(stack) != 1 {
|
if len(stack) != 1 {
|
||||||
return nil, fmt.Errorf("Bad expression, please check expression syntax")
|
return nil, fmt.Errorf("expected end of expression but found '%v', please check expression syntax", strings.TrimSpace(stack[1].Operation.StringValue))
|
||||||
}
|
}
|
||||||
return stack[0], nil
|
return stack[0], nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,5 +38,5 @@ func TestPathTreeOneArgForOneArgOp(t *testing.T) {
|
|||||||
|
|
||||||
func TestPathTreeExtraArgs(t *testing.T) {
|
func TestPathTreeExtraArgs(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression("sortKeys(.) explode(.)")
|
_, err := NewExpressionParser().ParseExpression("sortKeys(.) explode(.)")
|
||||||
test.AssertResultComplex(t, "Bad expression, please check expression syntax", err.Error())
|
test.AssertResultComplex(t, "expected end of expression but found 'explode', please check expression syntax", err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,22 +20,20 @@ func newExpressionPostFixer() expressionPostFixer {
|
|||||||
func popOpToResult(opStack []*token, result []*Operation) ([]*token, []*Operation) {
|
func popOpToResult(opStack []*token, result []*Operation) ([]*token, []*Operation) {
|
||||||
var newOp *token
|
var newOp *token
|
||||||
opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
|
opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
|
||||||
log.Debugf("popped %v from opstack to results", newOp.toString(true))
|
|
||||||
return opStack, append(result, newOp.Operation)
|
return opStack, append(result, newOp.Operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Operation, error) {
|
func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Operation, error) {
|
||||||
var result []*Operation
|
var result []*Operation
|
||||||
// surround the whole thing with brackets
|
// surround the whole thing with quotes
|
||||||
var opStack = []*token{{TokenType: openBracket}}
|
var opStack = []*token{&token{TokenType: openBracket}}
|
||||||
var tokens = append(infixTokens, &token{TokenType: closeBracket})
|
var tokens = append(infixTokens, &token{TokenType: closeBracket})
|
||||||
|
|
||||||
for _, currentToken := range tokens {
|
for _, currentToken := range tokens {
|
||||||
log.Debugf("postfix processing currentToken %v", currentToken.toString(true))
|
log.Debugf("postfix processing currentToken %v, %v", currentToken.toString(), currentToken.Operation)
|
||||||
switch currentToken.TokenType {
|
switch currentToken.TokenType {
|
||||||
case openBracket, openCollect, openCollectObject:
|
case openBracket, openCollect, openCollectObject:
|
||||||
opStack = append(opStack, currentToken)
|
opStack = append(opStack, currentToken)
|
||||||
log.Debugf("put %v onto the opstack", currentToken.toString(true))
|
|
||||||
case closeCollect, closeCollectObject:
|
case closeCollect, closeCollectObject:
|
||||||
var opener tokenType = openCollect
|
var opener tokenType = openCollect
|
||||||
var collectOperator *operationType = collectOpType
|
var collectOperator *operationType = collectOpType
|
||||||
@@ -43,23 +41,23 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
|
|||||||
opener = openCollectObject
|
opener = openCollectObject
|
||||||
collectOperator = collectObjectOpType
|
collectOperator = collectObjectOpType
|
||||||
}
|
}
|
||||||
|
itemsInMiddle := false
|
||||||
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener {
|
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener {
|
||||||
opStack, result = popOpToResult(opStack, result)
|
opStack, result = popOpToResult(opStack, result)
|
||||||
|
itemsInMiddle = true
|
||||||
|
}
|
||||||
|
if !itemsInMiddle {
|
||||||
|
// must be an empty collection, add the empty object as a LHS parameter
|
||||||
|
result = append(result, &Operation{OperationType: emptyOpType})
|
||||||
}
|
}
|
||||||
if len(opStack) == 0 {
|
if len(opStack) == 0 {
|
||||||
return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket")
|
return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket")
|
||||||
}
|
}
|
||||||
// now we should have [ as the last element on the opStack, get rid of it
|
// now we should have [] as the last element on the opStack, get rid of it
|
||||||
opStack = opStack[0 : len(opStack)-1]
|
opStack = opStack[0 : len(opStack)-1]
|
||||||
log.Debugf("deleteing open bracket from opstack")
|
|
||||||
|
|
||||||
//and append a collect to the opStack
|
//and append a collect to the opStack
|
||||||
result = append(result, &Operation{OperationType: collectOperator})
|
opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: shortPipeOpType}})
|
||||||
log.Debugf("put collect onto the result")
|
opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: collectOperator}})
|
||||||
result = append(result, &Operation{OperationType: shortPipeOpType})
|
|
||||||
log.Debugf("put shortpipe onto the result")
|
|
||||||
|
|
||||||
case closeBracket:
|
case closeBracket:
|
||||||
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket {
|
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket {
|
||||||
opStack, result = popOpToResult(opStack, result)
|
opStack, result = popOpToResult(opStack, result)
|
||||||
@@ -75,12 +73,11 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
|
|||||||
// pop off higher precedent operators onto the result
|
// pop off higher precedent operators onto the result
|
||||||
for len(opStack) > 0 &&
|
for len(opStack) > 0 &&
|
||||||
opStack[len(opStack)-1].TokenType == operationToken &&
|
opStack[len(opStack)-1].TokenType == operationToken &&
|
||||||
opStack[len(opStack)-1].Operation.OperationType.Precedence > currentPrecedence {
|
opStack[len(opStack)-1].Operation.OperationType.Precedence >= currentPrecedence {
|
||||||
opStack, result = popOpToResult(opStack, result)
|
opStack, result = popOpToResult(opStack, result)
|
||||||
}
|
}
|
||||||
// add this operator to the opStack
|
// add this operator to the opStack
|
||||||
opStack = append(opStack, currentToken)
|
opStack = append(opStack, currentToken)
|
||||||
log.Debugf("put %v onto the opstack", currentToken.toString(true))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,85 +12,40 @@ var pathTests = []struct {
|
|||||||
expectedTokens []interface{}
|
expectedTokens []interface{}
|
||||||
expectedPostFix []interface{}
|
expectedPostFix []interface{}
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
`[]|join(".")`,
|
|
||||||
append(make([]interface{}, 0), "[", "EMPTY", "]", "PIPE", "JOIN", "(", ". (string)", ")"),
|
|
||||||
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE", ". (string)", "JOIN", "PIPE"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`{"cool": .b or .c}`,
|
|
||||||
append(make([]interface{}, 0), "{", "cool (string)", "CREATE_MAP", "b", "OR", "c", "}"),
|
|
||||||
append(make([]interface{}, 0), "cool (string)", "b", "c", "OR", "CREATE_MAP", "COLLECT_OBJECT", "SHORT_PIPE"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`{"cool": []|join(".")}`,
|
|
||||||
append(make([]interface{}, 0), "{", "cool (string)", "CREATE_MAP", "[", "EMPTY", "]", "PIPE", "JOIN", "(", ". (string)", ")", "}"),
|
|
||||||
append(make([]interface{}, 0), "cool (string)", "EMPTY", "COLLECT", "SHORT_PIPE", ". (string)", "JOIN", "PIPE", "CREATE_MAP", "COLLECT_OBJECT", "SHORT_PIPE"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`.a as $item ireduce (0; . + $item)`, // note - add code to shuffle reduce to this position for postfix
|
|
||||||
append(make([]interface{}, 0), "a", "ASSIGN_VARIABLE", "GET_VARIABLE", "REDUCE", "(", "0 (int64)", "BLOCK", "SELF", "ADD", "GET_VARIABLE", ")"),
|
|
||||||
append(make([]interface{}, 0), "a", "GET_VARIABLE", "ASSIGN_VARIABLE", "0 (int64)", "SELF", "GET_VARIABLE", "ADD", "BLOCK", "REDUCE"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`.a | .b | .c`,
|
|
||||||
append(make([]interface{}, 0), "a", "PIPE", "b", "PIPE", "c"),
|
|
||||||
append(make([]interface{}, 0), "a", "b", "c", "PIPE", "PIPE"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
`[]`,
|
`[]`,
|
||||||
append(make([]interface{}, 0), "[", "EMPTY", "]"),
|
append(make([]interface{}, 0), "[", "]"),
|
||||||
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
`{}`,
|
|
||||||
append(make([]interface{}, 0), "{", "EMPTY", "}"),
|
|
||||||
append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`[{}]`,
|
|
||||||
append(make([]interface{}, 0), "[", "{", "EMPTY", "}", "]"),
|
|
||||||
append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE", "COLLECT", "SHORT_PIPE"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`.realnames as $names | $names["anon"]`,
|
|
||||||
append(make([]interface{}, 0), "realnames", "ASSIGN_VARIABLE", "GET_VARIABLE", "PIPE", "GET_VARIABLE", "TRAVERSE_ARRAY", "[", "anon (string)", "]"),
|
|
||||||
append(make([]interface{}, 0), "realnames", "GET_VARIABLE", "ASSIGN_VARIABLE", "GET_VARIABLE", "anon (string)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "PIPE"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`.b[.a]`,
|
|
||||||
append(make([]interface{}, 0), "b", "TRAVERSE_ARRAY", "[", "a", "]"),
|
|
||||||
append(make([]interface{}, 0), "b", "a", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
`.[]`,
|
`.[]`,
|
||||||
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
append(make([]interface{}, 0), "TRAVERSE_ARRAY", "[", "]"),
|
||||||
append(make([]interface{}, 0), "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a[]`,
|
`.a[]`,
|
||||||
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]"),
|
||||||
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a.[]`,
|
`.a.[]`,
|
||||||
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]"),
|
||||||
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a[0]`,
|
`.a[0]`,
|
||||||
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
||||||
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a.[0]`,
|
`.a.[0]`,
|
||||||
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
||||||
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a[].c`,
|
`.a[].c`,
|
||||||
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "SHORT_PIPE", "c"),
|
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]", "SHORT_PIPE", "c"),
|
||||||
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "c", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "c", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`[3]`,
|
`[3]`,
|
||||||
@@ -114,18 +69,18 @@ var pathTests = []struct {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a | .[].b == "apple"`,
|
`.a | .[].b == "apple"`,
|
||||||
append(make([]interface{}, 0), "a", "PIPE", "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "SHORT_PIPE", "b", "EQUALS", "apple (string)"),
|
append(make([]interface{}, 0), "a", "PIPE", "TRAVERSE_ARRAY", "[", "]", "SHORT_PIPE", "b", "EQUALS", "apple (string)"),
|
||||||
append(make([]interface{}, 0), "a", "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "apple (string)", "EQUALS", "PIPE"),
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "apple (string)", "EQUALS", "PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`(.a | .[].b) == "apple"`,
|
`(.a | .[].b) == "apple"`,
|
||||||
append(make([]interface{}, 0), "(", "a", "PIPE", "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "SHORT_PIPE", "b", ")", "EQUALS", "apple (string)"),
|
append(make([]interface{}, 0), "(", "a", "PIPE", "TRAVERSE_ARRAY", "[", "]", "SHORT_PIPE", "b", ")", "EQUALS", "apple (string)"),
|
||||||
append(make([]interface{}, 0), "a", "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "PIPE", "apple (string)", "EQUALS"),
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "PIPE", "apple (string)", "EQUALS"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.[] | select(. == "*at")`,
|
`.[] | select(. == "*at")`,
|
||||||
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at (string)", ")"),
|
append(make([]interface{}, 0), "TRAVERSE_ARRAY", "[", "]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at (string)", ")"),
|
||||||
append(make([]interface{}, 0), "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"),
|
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`[true]`,
|
`[true]`,
|
||||||
@@ -158,9 +113,9 @@ var pathTests = []struct {
|
|||||||
append(make([]interface{}, 0), "a", "mike (string)", "CREATE_MAP", "COLLECT_OBJECT", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "mike (string)", "CREATE_MAP", "COLLECT_OBJECT", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`{.a: .c, .b.[]: .f.g[]}`,
|
`{.a: .c, .b.[]: .f.g.[]}`,
|
||||||
append(make([]interface{}, 0), "{", "a", "CREATE_MAP", "c", "UNION", "b", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "CREATE_MAP", "f", "SHORT_PIPE", "g", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "}"),
|
append(make([]interface{}, 0), "{", "a", "CREATE_MAP", "c", "UNION", "b", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]", "CREATE_MAP", "f", "SHORT_PIPE", "g", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]", "}"),
|
||||||
append(make([]interface{}, 0), "a", "c", "CREATE_MAP", "b", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "f", "g", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "CREATE_MAP", "UNION", "COLLECT_OBJECT", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "c", "CREATE_MAP", "b", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "f", "g", "SHORT_PIPE", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "CREATE_MAP", "UNION", "COLLECT_OBJECT", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`explode(.a.b)`,
|
`explode(.a.b)`,
|
||||||
@@ -212,6 +167,11 @@ var pathTests = []struct {
|
|||||||
append(make([]interface{}, 0), "foo*", "PIPE", "(", "SELF", "ASSIGN_STYLE", "flow (string)", ")"),
|
append(make([]interface{}, 0), "foo*", "PIPE", "(", "SELF", "ASSIGN_STYLE", "flow (string)", ")"),
|
||||||
append(make([]interface{}, 0), "foo*", "SELF", "flow (string)", "ASSIGN_STYLE", "PIPE"),
|
append(make([]interface{}, 0), "foo*", "SELF", "flow (string)", "ASSIGN_STYLE", "PIPE"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`{}`,
|
||||||
|
append(make([]interface{}, 0), "{", "}"),
|
||||||
|
append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokeniser = newExpressionTokeniser()
|
var tokeniser = newExpressionTokeniser()
|
||||||
@@ -225,7 +185,7 @@ func TestPathParsing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var tokenValues []interface{}
|
var tokenValues []interface{}
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
tokenValues = append(tokenValues, token.toString(false))
|
tokenValues = append(tokenValues, token.toString())
|
||||||
}
|
}
|
||||||
test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path))
|
test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path))
|
||||||
|
|
||||||
|
|||||||
@@ -34,11 +34,9 @@ type token struct {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *token) toString(detail bool) string {
|
func (t *token) toString() string {
|
||||||
if t.TokenType == operationToken {
|
if t.TokenType == operationToken {
|
||||||
if detail {
|
log.Debug("toString, its an op")
|
||||||
return fmt.Sprintf("%v (%v)", t.Operation.toString(), t.Operation.OperationType.Precedence)
|
|
||||||
}
|
|
||||||
return t.Operation.toString()
|
return t.Operation.toString()
|
||||||
} else if t.TokenType == openBracket {
|
} else if t.TokenType == openBracket {
|
||||||
return "("
|
return "("
|
||||||
@@ -100,9 +98,6 @@ func multiplyWithPrefs() lex.Action {
|
|||||||
if strings.Contains(options, "?") {
|
if strings.Contains(options, "?") {
|
||||||
prefs.TraversePrefs = traversePreferences{DontAutoCreate: true}
|
prefs.TraversePrefs = traversePreferences{DontAutoCreate: true}
|
||||||
}
|
}
|
||||||
if strings.Contains(options, "d") {
|
|
||||||
prefs.DeepMergeArrays = true
|
|
||||||
}
|
|
||||||
op := &Operation{OperationType: multiplyOpType, Value: multiplyOpType.Type, StringValue: options, Preferences: prefs}
|
op := &Operation{OperationType: multiplyOpType, Value: multiplyOpType.Type, StringValue: options, Preferences: prefs}
|
||||||
return &token{TokenType: operationToken, Operation: op}, nil
|
return &token{TokenType: operationToken, Operation: op}, nil
|
||||||
}
|
}
|
||||||
@@ -185,19 +180,6 @@ func stringValue(wrapped bool) lex.Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVariableOpToken() lex.Action {
|
|
||||||
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
|
||||||
value := string(m.Bytes)
|
|
||||||
|
|
||||||
value = value[1:]
|
|
||||||
|
|
||||||
getVarOperation := createValueOperation(value, value)
|
|
||||||
getVarOperation.OperationType = getVariableOpType
|
|
||||||
|
|
||||||
return &token{TokenType: operationToken, Operation: getVarOperation, CheckForPostTraverse: true}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func envOp(strenv bool) lex.Action {
|
func envOp(strenv bool) lex.Action {
|
||||||
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
||||||
value := string(m.Bytes)
|
value := string(m.Bytes)
|
||||||
@@ -255,8 +237,6 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`or`), opToken(orOpType))
|
lexer.Add([]byte(`or`), opToken(orOpType))
|
||||||
lexer.Add([]byte(`and`), opToken(andOpType))
|
lexer.Add([]byte(`and`), opToken(andOpType))
|
||||||
lexer.Add([]byte(`not`), opToken(notOpType))
|
lexer.Add([]byte(`not`), opToken(notOpType))
|
||||||
lexer.Add([]byte(`ireduce`), opToken(reduceOpType))
|
|
||||||
lexer.Add([]byte(`;`), opToken(blockOpType))
|
|
||||||
lexer.Add([]byte(`\/\/`), opToken(alternativeOpType))
|
lexer.Add([]byte(`\/\/`), opToken(alternativeOpType))
|
||||||
|
|
||||||
lexer.Add([]byte(`documentIndex`), opToken(getDocumentIndexOpType))
|
lexer.Add([]byte(`documentIndex`), opToken(getDocumentIndexOpType))
|
||||||
@@ -264,8 +244,6 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`splitDoc`), opToken(splitDocumentOpType))
|
lexer.Add([]byte(`splitDoc`), opToken(splitDocumentOpType))
|
||||||
|
|
||||||
lexer.Add([]byte(`join`), opToken(joinStringOpType))
|
lexer.Add([]byte(`join`), opToken(joinStringOpType))
|
||||||
lexer.Add([]byte(`sub`), opToken(subStringOpType))
|
|
||||||
|
|
||||||
lexer.Add([]byte(`split`), opToken(splitStringOpType))
|
lexer.Add([]byte(`split`), opToken(splitStringOpType))
|
||||||
lexer.Add([]byte(`keys`), opToken(keysOpType))
|
lexer.Add([]byte(`keys`), opToken(keysOpType))
|
||||||
|
|
||||||
@@ -291,7 +269,6 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`collect`), opToken(collectOpType))
|
lexer.Add([]byte(`collect`), opToken(collectOpType))
|
||||||
|
|
||||||
lexer.Add([]byte(`\s*==\s*`), opToken(equalsOpType))
|
lexer.Add([]byte(`\s*==\s*`), opToken(equalsOpType))
|
||||||
lexer.Add([]byte(`\s*!=\s*`), opToken(notEqualsOpType))
|
|
||||||
lexer.Add([]byte(`\s*=\s*`), assignOpToken(false))
|
lexer.Add([]byte(`\s*=\s*`), assignOpToken(false))
|
||||||
|
|
||||||
lexer.Add([]byte(`del`), opToken(deleteChildOpType))
|
lexer.Add([]byte(`del`), opToken(deleteChildOpType))
|
||||||
@@ -324,15 +301,11 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`\]`), literalToken(closeCollect, true))
|
lexer.Add([]byte(`\]`), literalToken(closeCollect, true))
|
||||||
lexer.Add([]byte(`\{`), literalToken(openCollectObject, false))
|
lexer.Add([]byte(`\{`), literalToken(openCollectObject, false))
|
||||||
lexer.Add([]byte(`\}`), literalToken(closeCollectObject, true))
|
lexer.Add([]byte(`\}`), literalToken(closeCollectObject, true))
|
||||||
lexer.Add([]byte(`\*[\+|\?d]*`), multiplyWithPrefs())
|
lexer.Add([]byte(`\*[\+|\?]*`), multiplyWithPrefs())
|
||||||
lexer.Add([]byte(`\+`), opToken(addOpType))
|
lexer.Add([]byte(`\+`), opToken(addOpType))
|
||||||
lexer.Add([]byte(`\+=`), opToken(addAssignOpType))
|
lexer.Add([]byte(`\+=`), opToken(addAssignOpType))
|
||||||
lexer.Add([]byte(`\-`), opToken(subtractOpType))
|
|
||||||
lexer.Add([]byte(`\-=`), opToken(subtractAssignOpType))
|
|
||||||
lexer.Add([]byte(`\$[a-zA-Z_-0-9]+`), getVariableOpToken())
|
|
||||||
lexer.Add([]byte(`as`), opToken(assignVariableOpType))
|
|
||||||
|
|
||||||
err := lexer.CompileNFA()
|
err := lexer.Compile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -366,7 +339,7 @@ func (p *expressionTokeniserImpl) Tokenise(expression string) ([]*token, error)
|
|||||||
|
|
||||||
if tok != nil {
|
if tok != nil {
|
||||||
currentToken := tok.(*token)
|
currentToken := tok.(*token)
|
||||||
log.Debugf("Tokenising %v", currentToken.toString(true))
|
log.Debugf("Tokenising %v", currentToken.toString())
|
||||||
tokens = append(tokens, currentToken)
|
tokens = append(tokens, currentToken)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -396,12 +369,6 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
|
|||||||
//need to put a traverse array then a collect currentToken
|
//need to put a traverse array then a collect currentToken
|
||||||
// do this by adding traverse then converting currentToken to collect
|
// do this by adding traverse then converting currentToken to collect
|
||||||
|
|
||||||
if index == 0 || tokens[index-1].TokenType != operationToken ||
|
|
||||||
tokens[index-1].Operation.OperationType != traversePathOpType {
|
|
||||||
op := &Operation{OperationType: selfReferenceOpType, StringValue: "SELF"}
|
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
|
||||||
}
|
|
||||||
|
|
||||||
op := &Operation{OperationType: traverseArrayOpType, StringValue: "TRAVERSE_ARRAY"}
|
op := &Operation{OperationType: traverseArrayOpType, StringValue: "TRAVERSE_ARRAY"}
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
|
|
||||||
@@ -419,14 +386,6 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
|
|||||||
|
|
||||||
postProcessedTokens = append(postProcessedTokens, currentToken)
|
postProcessedTokens = append(postProcessedTokens, currentToken)
|
||||||
|
|
||||||
if index != len(tokens)-1 &&
|
|
||||||
((currentToken.TokenType == openCollect && tokens[index+1].TokenType == closeCollect) ||
|
|
||||||
(currentToken.TokenType == openCollectObject && tokens[index+1].TokenType == closeCollectObject)) {
|
|
||||||
|
|
||||||
op := &Operation{OperationType: emptyOpType, StringValue: "EMPTY"}
|
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
|
||||||
}
|
|
||||||
|
|
||||||
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
||||||
tokens[index+1].TokenType == operationToken &&
|
tokens[index+1].TokenType == operationToken &&
|
||||||
tokens[index+1].Operation.OperationType == traversePathOpType {
|
tokens[index+1].Operation.OperationType == traversePathOpType {
|
||||||
@@ -436,8 +395,18 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
|
|||||||
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
||||||
tokens[index+1].TokenType == openCollect {
|
tokens[index+1].TokenType == openCollect {
|
||||||
|
|
||||||
op := &Operation{OperationType: traverseArrayOpType}
|
op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"}
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
|
|
||||||
|
op = &Operation{OperationType: traverseArrayOpType}
|
||||||
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
|
}
|
||||||
|
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
||||||
|
tokens[index+1].TokenType == traverseArrayCollect {
|
||||||
|
|
||||||
|
op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"}
|
||||||
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
|
|
||||||
}
|
}
|
||||||
return postProcessedTokens, skipNextToken
|
return postProcessedTokens, skipNextToken
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ type operationType struct {
|
|||||||
|
|
||||||
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
|
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
|
||||||
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
|
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
|
||||||
var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator}
|
|
||||||
|
|
||||||
var blockOpType = &operationType{Type: "BLOCK", Precedence: 10, NumArgs: 2, Handler: emptyOperator}
|
|
||||||
|
|
||||||
var unionOpType = &operationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: unionOperator}
|
var unionOpType = &operationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: unionOperator}
|
||||||
|
|
||||||
@@ -35,11 +32,9 @@ var pipeOpType = &operationType{Type: "PIPE", NumArgs: 2, Precedence: 30, Handle
|
|||||||
|
|
||||||
var assignOpType = &operationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: assignUpdateOperator}
|
var assignOpType = &operationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: assignUpdateOperator}
|
||||||
var addAssignOpType = &operationType{Type: "ADD_ASSIGN", NumArgs: 2, Precedence: 40, Handler: addAssignOperator}
|
var addAssignOpType = &operationType{Type: "ADD_ASSIGN", NumArgs: 2, Precedence: 40, Handler: addAssignOperator}
|
||||||
var subtractAssignOpType = &operationType{Type: "SUBTRACT_ASSIGN", NumArgs: 2, Precedence: 40, Handler: subtractAssignOperator}
|
|
||||||
|
|
||||||
var assignAttributesOpType = &operationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: assignAttributesOperator}
|
var assignAttributesOpType = &operationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: assignAttributesOperator}
|
||||||
var assignStyleOpType = &operationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: assignStyleOperator}
|
var assignStyleOpType = &operationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: assignStyleOperator}
|
||||||
var assignVariableOpType = &operationType{Type: "ASSIGN_VARIABLE", NumArgs: 2, Precedence: 40, Handler: assignVariableOperator}
|
|
||||||
var assignTagOpType = &operationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: assignTagOperator}
|
var assignTagOpType = &operationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: assignTagOperator}
|
||||||
var assignCommentOpType = &operationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: assignCommentsOperator}
|
var assignCommentOpType = &operationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: assignCommentsOperator}
|
||||||
var assignAnchorOpType = &operationType{Type: "ASSIGN_ANCHOR", NumArgs: 2, Precedence: 40, Handler: assignAnchorOperator}
|
var assignAnchorOpType = &operationType{Type: "ASSIGN_ANCHOR", NumArgs: 2, Precedence: 40, Handler: assignAnchorOperator}
|
||||||
@@ -47,21 +42,16 @@ var assignAliasOpType = &operationType{Type: "ASSIGN_ALIAS", NumArgs: 2, Precede
|
|||||||
|
|
||||||
var multiplyOpType = &operationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 42, Handler: multiplyOperator}
|
var multiplyOpType = &operationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 42, Handler: multiplyOperator}
|
||||||
var addOpType = &operationType{Type: "ADD", NumArgs: 2, Precedence: 42, Handler: addOperator}
|
var addOpType = &operationType{Type: "ADD", NumArgs: 2, Precedence: 42, Handler: addOperator}
|
||||||
var subtractOpType = &operationType{Type: "SUBTRACT", NumArgs: 2, Precedence: 42, Handler: subtractOperator}
|
|
||||||
var alternativeOpType = &operationType{Type: "ALTERNATIVE", NumArgs: 2, Precedence: 42, Handler: alternativeOperator}
|
var alternativeOpType = &operationType{Type: "ALTERNATIVE", NumArgs: 2, Precedence: 42, Handler: alternativeOperator}
|
||||||
|
|
||||||
var equalsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: equalsOperator}
|
var equalsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: equalsOperator}
|
||||||
var notEqualsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: notEqualsOperator}
|
var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 40, Handler: createMapOperator}
|
||||||
|
|
||||||
//createmap needs to be above union, as we use union to build the components of the objects
|
|
||||||
var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 15, Handler: createMapOperator}
|
|
||||||
|
|
||||||
var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence: 45, Handler: pipeOperator}
|
var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence: 45, Handler: pipeOperator}
|
||||||
|
|
||||||
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
||||||
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: collectOperator}
|
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: collectOperator}
|
||||||
var splitDocumentOpType = &operationType{Type: "SPLIT_DOC", NumArgs: 0, Precedence: 50, Handler: splitDocumentOperator}
|
var splitDocumentOpType = &operationType{Type: "SPLIT_DOC", NumArgs: 0, Precedence: 50, Handler: splitDocumentOperator}
|
||||||
var getVariableOpType = &operationType{Type: "GET_VARIABLE", NumArgs: 0, Precedence: 55, Handler: getVariableOperator}
|
|
||||||
var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator}
|
var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator}
|
||||||
var getTagOpType = &operationType{Type: "GET_TAG", NumArgs: 0, Precedence: 50, Handler: getTagOperator}
|
var getTagOpType = &operationType{Type: "GET_TAG", NumArgs: 0, Precedence: 50, Handler: getTagOperator}
|
||||||
var getCommentOpType = &operationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50, Handler: getCommentsOperator}
|
var getCommentOpType = &operationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50, Handler: getCommentsOperator}
|
||||||
@@ -75,20 +65,19 @@ var getPathOpType = &operationType{Type: "GET_PATH", NumArgs: 0, Precedence: 50,
|
|||||||
var explodeOpType = &operationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: explodeOperator}
|
var explodeOpType = &operationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: explodeOperator}
|
||||||
var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}
|
var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}
|
||||||
var joinStringOpType = &operationType{Type: "JOIN", NumArgs: 1, Precedence: 50, Handler: joinStringOperator}
|
var joinStringOpType = &operationType{Type: "JOIN", NumArgs: 1, Precedence: 50, Handler: joinStringOperator}
|
||||||
var subStringOpType = &operationType{Type: "SUBSTR", NumArgs: 1, Precedence: 50, Handler: substituteStringOperator}
|
|
||||||
var splitStringOpType = &operationType{Type: "SPLIT", NumArgs: 1, Precedence: 50, Handler: splitStringOperator}
|
var splitStringOpType = &operationType{Type: "SPLIT", NumArgs: 1, Precedence: 50, Handler: splitStringOperator}
|
||||||
|
|
||||||
var keysOpType = &operationType{Type: "KEYS", NumArgs: 0, Precedence: 50, Handler: keysOperator}
|
var keysOpType = &operationType{Type: "KEYS", NumArgs: 0, Precedence: 50, Handler: keysOperator}
|
||||||
|
|
||||||
var collectObjectOpType = &operationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: collectObjectOperator}
|
var collectObjectOpType = &operationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: collectObjectOperator}
|
||||||
var traversePathOpType = &operationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 55, Handler: traversePathOperator}
|
var traversePathOpType = &operationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 50, Handler: traversePathOperator}
|
||||||
var traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 2, Precedence: 50, Handler: traverseArrayOperator}
|
var traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 1, Precedence: 50, Handler: traverseArrayOperator}
|
||||||
|
|
||||||
var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 55, Handler: selfOperator}
|
var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 50, Handler: selfOperator}
|
||||||
var valueOpType = &operationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: valueOperator}
|
var valueOpType = &operationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: valueOperator}
|
||||||
var envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler: envOperator}
|
var envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler: envOperator}
|
||||||
var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator}
|
var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator}
|
||||||
var emptyOpType = &operationType{Type: "EMPTY", Precedence: 50, Handler: emptyOperator}
|
var emptyOpType = &operationType{Type: "EMPTY", NumArgs: 50, Handler: emptyOperator}
|
||||||
|
|
||||||
var recursiveDescentOpType = &operationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: recursiveDescentOperator}
|
var recursiveDescentOpType = &operationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: recursiveDescentOperator}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ func createAddOp(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode {
|
|||||||
|
|
||||||
func addAssignOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func addAssignOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
assignmentOp := &Operation{OperationType: assignOpType}
|
assignmentOp := &Operation{OperationType: assignOpType}
|
||||||
assignmentOp.UpdateAssign = true
|
assignmentOp.UpdateAssign = false
|
||||||
selfExpression := &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
|
||||||
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: expressionNode.Lhs, Rhs: createAddOp(selfExpression, expressionNode.Rhs)}
|
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: expressionNode.Lhs, Rhs: createAddOp(expressionNode.Lhs, expressionNode.Rhs)}
|
||||||
return d.GetMatchingNodes(context, assignmentOpNode)
|
return d.GetMatchingNodes(context, assignmentOpNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ func toNodes(candidate *CandidateNode) []*yaml.Node {
|
|||||||
func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("Add operator")
|
log.Debugf("Add operator")
|
||||||
|
|
||||||
return crossFunction(d, context, expressionNode, add, false)
|
return crossFunction(d, context, expressionNode, add)
|
||||||
}
|
}
|
||||||
|
|
||||||
func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
|
|||||||
@@ -63,21 +63,13 @@ var addOperatorScenarios = []expressionScenario{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Append to array",
|
description: "Update array (append)",
|
||||||
document: `{a: [1,2], b: [3,4]}`,
|
document: `{a: [1,2], b: [3,4]}`,
|
||||||
expression: `.a = .a + .b`,
|
expression: `.a = .a + .b`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[], (doc)::{a: [1, 2, 3, 4], b: [3, 4]}\n",
|
"D0, P[], (doc)::{a: [1, 2, 3, 4], b: [3, 4]}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "Relative append",
|
|
||||||
document: `a: { a1: {b: [cat]}, a2: {b: [dog]}, a3: {} }`,
|
|
||||||
expression: `.a[].b += ["mouse"]`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: {a1: {b: [cat, mouse]}, a2: {b: [dog, mouse]}, a3: {b: [mouse]}}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "String concatenation",
|
description: "String concatenation",
|
||||||
document: `{a: cat, b: meow}`,
|
document: `{a: cat, b: meow}`,
|
||||||
@@ -86,6 +78,14 @@ var addOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::{a: catmeow, b: meow}\n",
|
"D0, P[], (doc)::{a: catmeow, b: meow}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Relative string concatenation",
|
||||||
|
document: `{a: cat, b: meow}`,
|
||||||
|
expression: `.a += .b`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::{a: catmeow, b: meow}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Number addition - float",
|
description: "Number addition - float",
|
||||||
subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",
|
subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",
|
||||||
@@ -105,11 +105,11 @@ var addOperatorScenarios = []expressionScenario{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Increment numbers",
|
description: "Increment number",
|
||||||
document: `{a: 3, b: 5}`,
|
document: `{a: 3}`,
|
||||||
expression: `.[] += 1`,
|
expression: `.a += 1`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[], (doc)::{a: 4, b: 6}\n",
|
"D0, P[], (doc)::{a: 4}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
|
// corssFunction no matches
|
||||||
|
// can boolean use crossfunction
|
||||||
|
|
||||||
func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- alternative")
|
log.Debugf("-- alternative")
|
||||||
return crossFunction(d, context, expressionNode, alternativeFunc, true)
|
return crossFunction(d, context, expressionNode, alternativeFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
if lhs == nil {
|
|
||||||
return rhs, nil
|
|
||||||
}
|
|
||||||
lhs.Node = unwrapDoc(lhs.Node)
|
lhs.Node = unwrapDoc(lhs.Node)
|
||||||
rhs.Node = unwrapDoc(rhs.Node)
|
rhs.Node = unwrapDoc(rhs.Node)
|
||||||
log.Debugf("Alternative LHS: %v", lhs.Node.Tag)
|
log.Debugf("Alternative LHS: %v", lhs.Node.Tag)
|
||||||
|
|||||||
@@ -13,14 +13,6 @@ var alternativeOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[a], (!!str)::bridge\n",
|
"D0, P[a], (!!str)::bridge\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
expression: `select(tag == "seq") // "cat"`,
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: frog`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!str)::cat\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "LHS is not defined",
|
description: "LHS is not defined",
|
||||||
expression: `.a // "hello"`,
|
expression: `.a // "hello"`,
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ func assignUpdateOperator(d *dataTreeNavigator, context Context, expressionNode
|
|||||||
|
|
||||||
// does not update content or values
|
// does not update content or values
|
||||||
func assignAttributesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func assignAttributesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debug("getting lhs matching nodes for update")
|
|
||||||
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
|
|||||||
@@ -143,5 +143,5 @@ func TestAssignOperatorScenarios(t *testing.T) {
|
|||||||
for _, tt := range assignOperatorScenarios {
|
for _, tt := range assignOperatorScenarios {
|
||||||
testScenario(t, &tt)
|
testScenario(t, &tt)
|
||||||
}
|
}
|
||||||
documentScenarios(t, "Assign (Update)", assignOperatorScenarios)
|
documentScenarios(t, "Assign", assignOperatorScenarios)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func orOperator(d *dataTreeNavigator, context Context, expressionNode *Expressio
|
|||||||
return crossFunction(d, context, expressionNode, performBoolOp(
|
return crossFunction(d, context, expressionNode, performBoolOp(
|
||||||
func(b1 bool, b2 bool) bool {
|
func(b1 bool, b2 bool) bool {
|
||||||
return b1 || b2
|
return b1 || b2
|
||||||
}), false)
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
@@ -57,7 +57,7 @@ func andOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
|
|||||||
return crossFunction(d, context, expressionNode, performBoolOp(
|
return crossFunction(d, context, expressionNode, performBoolOp(
|
||||||
func(b1 bool, b2 bool) bool {
|
func(b1 bool, b2 bool) bool {
|
||||||
return b1 && b2
|
return b1 && b2
|
||||||
}), false)
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func notOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func notOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func sequenceFor(d *dataTreeNavigator, context Context, matchingNode *CandidateN
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &CandidateNode{Node: &node, Document: document, Path: path}, nil
|
return &CandidateNode{Node: &node, Document: document, Path: path}, nil
|
||||||
}, false)
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -7,32 +7,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
contextToUse := context.Clone()
|
|
||||||
contextToUse.DontAutoCreate = true
|
nodesToDelete, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
nodesToDelete, err := d.GetMatchingNodes(contextToUse, expressionNode.Rhs)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
//need to iterate backwards to ensure correct indices when deleting multiple
|
|
||||||
for el := nodesToDelete.MatchingNodes.Back(); el != nil; el = el.Prev() {
|
for el := nodesToDelete.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
if len(candidate.Path) > 0 {
|
deleteImmediateChildOp := &Operation{
|
||||||
deleteImmediateChildOp := &Operation{
|
OperationType: deleteImmediateChildOpType,
|
||||||
OperationType: deleteImmediateChildOpType,
|
Value: candidate.Path[len(candidate.Path)-1],
|
||||||
Value: candidate.Path[len(candidate.Path)-1],
|
}
|
||||||
}
|
|
||||||
|
|
||||||
deleteImmediateChildOpNode := &ExpressionNode{
|
deleteImmediateChildOpNode := &ExpressionNode{
|
||||||
Operation: deleteImmediateChildOp,
|
Operation: deleteImmediateChildOp,
|
||||||
Rhs: createTraversalTree(candidate.Path[0:len(candidate.Path)-1], traversePreferences{}, false),
|
Rhs: createTraversalTree(candidate.Path[0:len(candidate.Path)-1], traversePreferences{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := d.GetMatchingNodes(contextToUse, deleteImmediateChildOpNode)
|
_, err := d.GetMatchingNodes(context, deleteImmediateChildOpNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return context, nil
|
return context, nil
|
||||||
|
|||||||
@@ -37,38 +37,6 @@ var deleteOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::[1, 3]\n",
|
"D0, P[], (doc)::[1, 3]\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: [1,2,3]`,
|
|
||||||
expression: `del(.a[])`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: []\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: [10,x,10, 10, x, 10]`,
|
|
||||||
expression: `del(.a[] | select(. == 10))`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: [x, x]\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: {thing1: yep, thing2: cool, thing3: hi, b: {thing1: cool, great: huh}}`,
|
|
||||||
expression: `del(..)`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!map)::{}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: {thing1: yep, thing2: cool, thing3: hi, b: {thing1: cool, great: huh}}`,
|
|
||||||
expression: `del(.. | select(tag == "!!map") | (.b.thing1,.thing2))`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!map)::a: {thing1: yep, thing3: hi, b: {great: huh}}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Delete nested entry in array",
|
description: "Delete nested entry in array",
|
||||||
document: `[{a: cat, b: dog}]`,
|
document: `[{a: cat, b: dog}]`,
|
||||||
|
|||||||
@@ -1,33 +1,21 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import "gopkg.in/yaml.v3"
|
|
||||||
|
|
||||||
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- equalsOperation")
|
log.Debugf("-- equalsOperation")
|
||||||
return crossFunction(d, context, expressionNode, isEquals(false), false)
|
return crossFunction(d, context, expressionNode, isEquals)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func isEquals(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
value := false
|
||||||
value := false
|
|
||||||
|
|
||||||
lhsNode := unwrapDoc(lhs.Node)
|
lhsNode := unwrapDoc(lhs.Node)
|
||||||
rhsNode := unwrapDoc(rhs.Node)
|
rhsNode := unwrapDoc(rhs.Node)
|
||||||
|
|
||||||
if lhsNode.Tag == "!!null" {
|
if lhsNode.Tag == "!!null" {
|
||||||
value = (rhsNode.Tag == "!!null")
|
value = (rhsNode.Tag == "!!null")
|
||||||
} else if lhsNode.Kind == yaml.ScalarNode && rhsNode.Kind == yaml.ScalarNode {
|
} else {
|
||||||
value = matchKey(lhsNode.Value, rhsNode.Value)
|
value = matchKey(lhsNode.Value, rhsNode.Value)
|
||||||
}
|
|
||||||
log.Debugf("%v == %v ? %v", NodeToString(lhs), NodeToString(rhs), value)
|
|
||||||
if flip {
|
|
||||||
value = !value
|
|
||||||
}
|
|
||||||
return createBooleanCandidate(lhs, value), nil
|
|
||||||
}
|
}
|
||||||
}
|
log.Debugf("%v == %v ? %v", NodeToString(lhs), NodeToString(rhs), value)
|
||||||
|
return createBooleanCandidate(lhs, value), nil
|
||||||
func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
log.Debugf("-- equalsOperation")
|
|
||||||
return crossFunction(d, context, expressionNode, isEquals(true), false)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,6 @@ var equalsOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!bool)::false\n",
|
"D0, P[], (!!bool)::false\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{a: { b: {things: \"\"}, f: [1], g: [] }}",
|
|
||||||
expression: ".. | select(. == \"\")",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[a b things], (!!str)::\"\"\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Match string",
|
description: "Match string",
|
||||||
document: `[cat,goat,dog]`,
|
document: `[cat,goat,dog]`,
|
||||||
@@ -31,18 +23,7 @@ var equalsOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[1], (!!bool)::true\n",
|
"D0, P[1], (!!bool)::true\n",
|
||||||
"D0, P[2], (!!bool)::false\n",
|
"D0, P[2], (!!bool)::false\n",
|
||||||
},
|
},
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
description: "Don't match string",
|
|
||||||
document: `[cat,goat,dog]`,
|
|
||||||
expression: `.[] | (. != "*at")`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[0], (!!bool)::false\n",
|
|
||||||
"D0, P[1], (!!bool)::false\n",
|
|
||||||
"D0, P[2], (!!bool)::true\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Match number",
|
description: "Match number",
|
||||||
document: `[3, 4, 5]`,
|
document: `[3, 4, 5]`,
|
||||||
expression: `.[] | (. == 4)`,
|
expression: `.[] | (. == 4)`,
|
||||||
@@ -51,18 +32,7 @@ var equalsOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[1], (!!bool)::true\n",
|
"D0, P[1], (!!bool)::true\n",
|
||||||
"D0, P[2], (!!bool)::false\n",
|
"D0, P[2], (!!bool)::false\n",
|
||||||
},
|
},
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
description: "Dont match number",
|
|
||||||
document: `[3, 4, 5]`,
|
|
||||||
expression: `.[] | (. != 4)`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[0], (!!bool)::true\n",
|
|
||||||
"D0, P[1], (!!bool)::false\n",
|
|
||||||
"D0, P[2], (!!bool)::true\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `a: { cat: {b: apple, c: whatever}, pat: {b: banana} }`,
|
document: `a: { cat: {b: apple, c: whatever}, pat: {b: banana} }`,
|
||||||
expression: `.a | (.[].b == "apple")`,
|
expression: `.a | (.[].b == "apple")`,
|
||||||
|
|||||||
@@ -21,16 +21,6 @@ var fileOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!int)::0\n",
|
"D0, P[], (!!int)::0\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "Get file indices of multiple documents",
|
|
||||||
document: `{a: cat}`,
|
|
||||||
document2: `{a: cat}`,
|
|
||||||
expression: `fileIndex`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!int)::0\n",
|
|
||||||
"D0, P[], (!!int)::1\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Get file index alias",
|
description: "Get file index alias",
|
||||||
document: `{a: cat}`,
|
document: `{a: cat}`,
|
||||||
|
|||||||
@@ -28,15 +28,6 @@ var hasOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[3], (!!bool)::false\n",
|
"D0, P[3], (!!bool)::false\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "Select, checking for existence of deep paths",
|
|
||||||
subdescription: "Simply pipe in parent expressions into `has`",
|
|
||||||
document: "- {a: {b: {c: cat}}}\n- {a: {b: {d: dog}}}",
|
|
||||||
expression: `.[] | select(.a.b | has("c"))`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[0], (!!map)::{a: {b: {c: cat}}}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
dontFormatInputForDoc: true,
|
dontFormatInputForDoc: true,
|
||||||
description: "Has array index",
|
description: "Has array index",
|
||||||
|
|||||||
@@ -10,14 +10,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type multiplyPreferences struct {
|
type multiplyPreferences struct {
|
||||||
AppendArrays bool
|
AppendArrays bool
|
||||||
DeepMergeArrays bool
|
TraversePrefs traversePreferences
|
||||||
TraversePrefs traversePreferences
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func multiplyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func multiplyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- MultiplyOperator")
|
log.Debugf("-- MultiplyOperator")
|
||||||
return crossFunction(d, context, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences)), false)
|
return crossFunction(d, context, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
@@ -31,7 +30,6 @@ func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, contex
|
|||||||
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
|
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
|
||||||
|
|
||||||
var newBlank = lhs.CreateChild(nil, &yaml.Node{})
|
var newBlank = lhs.CreateChild(nil, &yaml.Node{})
|
||||||
|
|
||||||
var newThing, err = mergeObjects(d, context, newBlank, lhs, multiplyPreferences{})
|
var newThing, err = mergeObjects(d, context, newBlank, lhs, multiplyPreferences{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -39,31 +37,11 @@ func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, contex
|
|||||||
return mergeObjects(d, context, newThing, rhs, preferences)
|
return mergeObjects(d, context, newThing, rhs, preferences)
|
||||||
} else if lhs.Node.Tag == "!!int" && rhs.Node.Tag == "!!int" {
|
} else if lhs.Node.Tag == "!!int" && rhs.Node.Tag == "!!int" {
|
||||||
return multiplyIntegers(lhs, rhs)
|
return multiplyIntegers(lhs, rhs)
|
||||||
} else if (lhs.Node.Tag == "!!int" || lhs.Node.Tag == "!!float") && (rhs.Node.Tag == "!!int" || rhs.Node.Tag == "!!float") {
|
|
||||||
return multiplyFloats(lhs, rhs)
|
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Cannot multiply %v with %v", lhs.Node.Tag, rhs.Node.Tag)
|
return nil, fmt.Errorf("Cannot multiply %v with %v", lhs.Node.Tag, rhs.Node.Tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func multiplyFloats(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
|
||||||
target := lhs.CreateChild(nil, &yaml.Node{})
|
|
||||||
target.Node.Kind = yaml.ScalarNode
|
|
||||||
target.Node.Style = lhs.Node.Style
|
|
||||||
target.Node.Tag = "!!float"
|
|
||||||
|
|
||||||
lhsNum, err := strconv.ParseFloat(lhs.Node.Value, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rhsNum, err := strconv.ParseFloat(rhs.Node.Value, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
target.Node.Value = fmt.Sprintf("%v", lhsNum*rhsNum)
|
|
||||||
return target, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
target := lhs.CreateChild(nil, &yaml.Node{})
|
target := lhs.CreateChild(nil, &yaml.Node{})
|
||||||
target.Node.Kind = yaml.ScalarNode
|
target.Node.Kind = yaml.ScalarNode
|
||||||
@@ -88,7 +66,7 @@ func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs
|
|||||||
|
|
||||||
// shouldn't recurse arrays if appending
|
// shouldn't recurse arrays if appending
|
||||||
prefs := recursiveDescentPreferences{RecurseArray: !shouldAppendArrays,
|
prefs := recursiveDescentPreferences{RecurseArray: !shouldAppendArrays,
|
||||||
TraversePreferences: traversePreferences{DontFollowAlias: true, IncludeMapKeys: true}}
|
TraversePreferences: traversePreferences{DontFollowAlias: true}}
|
||||||
err := recursiveDecent(d, results, context.SingleChildContext(rhs), prefs)
|
err := recursiveDecent(d, results, context.SingleChildContext(rhs), prefs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -100,12 +78,7 @@ func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs
|
|||||||
}
|
}
|
||||||
|
|
||||||
for el := results.Front(); el != nil; el = el.Next() {
|
for el := results.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
err := applyAssignment(d, context, pathIndexToStartFrom, lhs, el.Value.(*CandidateNode), preferences)
|
||||||
if candidate.Node.Tag == "!!merge" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err := applyAssignment(d, context, pathIndexToStartFrom, lhs, candidate, preferences)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -115,21 +88,20 @@ func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs
|
|||||||
|
|
||||||
func applyAssignment(d *dataTreeNavigator, context Context, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error {
|
func applyAssignment(d *dataTreeNavigator, context Context, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error {
|
||||||
shouldAppendArrays := preferences.AppendArrays
|
shouldAppendArrays := preferences.AppendArrays
|
||||||
log.Debugf("merge - applyAssignment lhs %v, rhs: %v", lhs.GetKey(), rhs.GetKey())
|
log.Debugf("merge - applyAssignment lhs %v, rhs: %v", NodeToString(lhs), NodeToString(rhs))
|
||||||
|
|
||||||
lhsPath := rhs.Path[pathIndexToStartFrom:]
|
lhsPath := rhs.Path[pathIndexToStartFrom:]
|
||||||
|
|
||||||
assignmentOp := &Operation{OperationType: assignAttributesOpType}
|
assignmentOp := &Operation{OperationType: assignAttributesOpType}
|
||||||
if shouldAppendArrays && rhs.Node.Kind == yaml.SequenceNode {
|
if rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode {
|
||||||
assignmentOp.OperationType = addAssignOpType
|
|
||||||
} else if !preferences.DeepMergeArrays && rhs.Node.Kind == yaml.SequenceNode ||
|
|
||||||
(rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode) {
|
|
||||||
assignmentOp.OperationType = assignOpType
|
assignmentOp.OperationType = assignOpType
|
||||||
assignmentOp.UpdateAssign = false
|
assignmentOp.UpdateAssign = false
|
||||||
|
} else if shouldAppendArrays && rhs.Node.Kind == yaml.SequenceNode {
|
||||||
|
assignmentOp.OperationType = addAssignOpType
|
||||||
}
|
}
|
||||||
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhs}
|
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhs}
|
||||||
|
|
||||||
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath, preferences.TraversePrefs, rhs.IsMapKey), Rhs: &ExpressionNode{Operation: rhsOp}}
|
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath, preferences.TraversePrefs), Rhs: &ExpressionNode{Operation: rhsOp}}
|
||||||
|
|
||||||
_, err := d.GetMatchingNodes(context.SingleChildContext(lhs), assignmentOpNode)
|
_, err := d.GetMatchingNodes(context.SingleChildContext(lhs), assignmentOpNode)
|
||||||
|
|
||||||
|
|||||||
@@ -4,57 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var doc1 = `list:
|
|
||||||
# Hi this is a comment.
|
|
||||||
# Hello this is another comment.
|
|
||||||
- "abc"`
|
|
||||||
|
|
||||||
var doc2 = `list2:
|
|
||||||
# This is yet another comment.
|
|
||||||
# Indeed this is yet another comment.
|
|
||||||
- "123"`
|
|
||||||
|
|
||||||
var docExpected = `D0, P[], (!!map)::list:
|
|
||||||
# Hi this is a comment.
|
|
||||||
# Hello this is another comment.
|
|
||||||
- "abc"
|
|
||||||
list2:
|
|
||||||
# This is yet another comment.
|
|
||||||
# Indeed this is yet another comment.
|
|
||||||
- "123"
|
|
||||||
`
|
|
||||||
|
|
||||||
var multiplyOperatorScenarios = []expressionScenario{
|
var multiplyOperatorScenarios = []expressionScenario{
|
||||||
{
|
|
||||||
description: "Multiply integers",
|
|
||||||
expression: `3 * 4`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!int)::12\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: doc1,
|
|
||||||
document2: doc2,
|
|
||||||
expression: `select(fi == 0) * select(fi == 1)`,
|
|
||||||
expected: []string{
|
|
||||||
docExpected,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
expression: `3 * 4.5`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!float)::13.5\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
expression: `4.5 * 3`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!float)::13.5\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: {also: [1]}, b: {also: me}}`,
|
document: `{a: {also: [1]}, b: {also: me}}`,
|
||||||
@@ -63,30 +13,6 @@ var multiplyOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
|
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "# b\nb:\n # a\n a: cat",
|
|
||||||
expression: "{} * .",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!map)::# b\nb:\n # a\n a: cat\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "# b\nb:\n # a\n a: cat",
|
|
||||||
expression: ". * {}",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!map)::# b\nb:\n # a\n a: cat\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{a: &a { b: &b { c: &c cat } } }`,
|
|
||||||
expression: `{} * .`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!map)::{a: &a {b: &b {c: &c cat}}}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: 2, b: 5}`,
|
document: `{a: 2, b: 5}`,
|
||||||
@@ -166,7 +92,7 @@ var multiplyOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: {things: great}, b: {also: me}}`,
|
document: `{a: {things: great}, b: {also: me}}`,
|
||||||
expression: `. * {"a": .b}`,
|
expression: `. * {"a":.b}`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n",
|
"D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n",
|
||||||
},
|
},
|
||||||
@@ -174,10 +100,16 @@ var multiplyOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
description: "Merge keeps style of LHS",
|
description: "Merge keeps style of LHS",
|
||||||
dontFormatInputForDoc: true,
|
dontFormatInputForDoc: true,
|
||||||
document: "a: {things: great}\nb:\n also: \"me\"",
|
document: `a: {things: great}
|
||||||
expression: `. * {"a":.b}`,
|
b:
|
||||||
|
also: "me"
|
||||||
|
`,
|
||||||
|
expression: `. * {"a":.b}`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[], (!!map)::a: {things: great, also: \"me\"}\nb:\n also: \"me\"\n",
|
`D0, P[], (!!map)::a: {things: great, also: "me"}
|
||||||
|
b:
|
||||||
|
also: "me"
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -207,7 +139,7 @@ var multiplyOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [{thing: one}], b: [{missing: two, thing: two}]}`,
|
document: `{a: [{thing: one}], b: [{missing: two, thing: two}]}`,
|
||||||
expression: `.a *?d .b`,
|
expression: `.a *? .b`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a], (!!seq)::[{thing: two}]\n",
|
"D0, P[a], (!!seq)::[{thing: two}]\n",
|
||||||
},
|
},
|
||||||
@@ -236,15 +168,6 @@ var multiplyOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[a], (!!map)::{thing: [1, 2, 3, 4]}\n",
|
"D0, P[a], (!!map)::{thing: [1, 2, 3, 4]}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "Merge, deeply merging arrays",
|
|
||||||
subdescription: "Merging arrays deeply means arrays are merge like objects, with indexes as their key. In this case, we merge the first item in the array, and do nothing with the second.",
|
|
||||||
document: `{a: [{name: fred, age: 12}, {name: bob, age: 32}], b: [{name: fred, age: 34}]}`,
|
|
||||||
expression: `.a *d .b`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[a], (!!seq)::[{name: fred, age: 34}, {name: bob, age: 32}]\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Merge to prefix an element",
|
description: "Merge to prefix an element",
|
||||||
document: `{a: cat, b: dog}`,
|
document: `{a: cat, b: dog}`,
|
||||||
@@ -283,5 +206,5 @@ func TestMultiplyOperatorScenarios(t *testing.T) {
|
|||||||
for _, tt := range multiplyOperatorScenarios {
|
for _, tt := range multiplyOperatorScenarios {
|
||||||
testScenario(t, &tt)
|
testScenario(t, &tt)
|
||||||
}
|
}
|
||||||
documentScenarios(t, "Multiply (Merge)", multiplyOperatorScenarios)
|
documentScenarios(t, "Multiply", multiplyOperatorScenarios)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
package yqlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func reduceOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
log.Debugf("-- reduceOp")
|
|
||||||
//.a as $var reduce (0; . + $var)
|
|
||||||
//lhs is the assignment operator
|
|
||||||
//rhs is the reduce block
|
|
||||||
// '.' refers to the current accumulator, initialised to 0
|
|
||||||
// $var references a single element from the .a
|
|
||||||
|
|
||||||
//ensure lhs is actually an assignment
|
|
||||||
//and rhs is a block (empty)
|
|
||||||
if expressionNode.Lhs.Operation.OperationType != assignVariableOpType {
|
|
||||||
return Context{}, fmt.Errorf("reduce must be given a variables assignment, got %v instead", expressionNode.Lhs.Operation.OperationType.Type)
|
|
||||||
} else if expressionNode.Rhs.Operation.OperationType != blockOpType {
|
|
||||||
return Context{}, fmt.Errorf("reduce must be given a block, got %v instead", expressionNode.Rhs.Operation.OperationType.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
arrayExpNode := expressionNode.Lhs.Lhs
|
|
||||||
array, err := d.GetMatchingNodes(context, arrayExpNode)
|
|
||||||
|
|
||||||
log.Debugf("array of %v things", array.MatchingNodes.Len())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
variableName := expressionNode.Lhs.Rhs.Operation.StringValue
|
|
||||||
|
|
||||||
initExp := expressionNode.Rhs.Lhs
|
|
||||||
|
|
||||||
accum, err := d.GetMatchingNodes(context, initExp)
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("with variable %v", variableName)
|
|
||||||
|
|
||||||
blockExp := expressionNode.Rhs.Rhs
|
|
||||||
for el := array.MatchingNodes.Front(); el != nil; el = el.Next() {
|
|
||||||
candidate := el.Value.(*CandidateNode)
|
|
||||||
log.Debugf("REDUCING WITH %v", NodeToString(candidate))
|
|
||||||
l := list.New()
|
|
||||||
l.PushBack(candidate)
|
|
||||||
accum.SetVariable(variableName, l)
|
|
||||||
|
|
||||||
accum, err = d.GetMatchingNodes(accum, blockExp)
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return accum, nil
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package yqlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var reduceOperatorScenarios = []expressionScenario{
|
|
||||||
{
|
|
||||||
description: "Sum numbers",
|
|
||||||
document: `[10,2, 5, 3]`,
|
|
||||||
expression: `.[] as $item ireduce (0; . + $item)`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!int)::20\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Merge all yaml files together",
|
|
||||||
document: `a: cat`,
|
|
||||||
document2: `b: dog`,
|
|
||||||
expression: `. as $item ireduce ({}; . * $item )`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!map)::a: cat\nb: dog\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Convert an array to an object",
|
|
||||||
document: `[{name: Cathy, has: apples},{name: Bob, has: bananas}]`,
|
|
||||||
expression: `.[] as $item ireduce ({}; .[$item | .name] = ($item | .has) )`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!map)::Cathy: apples\nBob: bananas\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReduceOperatorScenarios(t *testing.T) {
|
|
||||||
for _, tt := range reduceOperatorScenarios {
|
|
||||||
testScenario(t, &tt)
|
|
||||||
}
|
|
||||||
documentScenarios(t, "Reduce", reduceOperatorScenarios)
|
|
||||||
}
|
|
||||||
@@ -3,77 +3,11 @@ package yqlib
|
|||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSubstituteParameters(d *dataTreeNavigator, block *ExpressionNode, context Context) (string, string, error) {
|
|
||||||
regEx := ""
|
|
||||||
replacementText := ""
|
|
||||||
|
|
||||||
regExNodes, err := d.GetMatchingNodes(context, block.Lhs)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
if regExNodes.MatchingNodes.Front() != nil {
|
|
||||||
regEx = regExNodes.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("regEx %v", regEx)
|
|
||||||
|
|
||||||
replacementNodes, err := d.GetMatchingNodes(context, block.Rhs)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
if replacementNodes.MatchingNodes.Front() != nil {
|
|
||||||
replacementText = replacementNodes.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
return regEx, replacementText, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func substitute(original string, regex *regexp.Regexp, replacement string) *yaml.Node {
|
|
||||||
replacedString := regex.ReplaceAllString(original, replacement)
|
|
||||||
return &yaml.Node{Kind: yaml.ScalarNode, Value: replacedString, Tag: "!!str"}
|
|
||||||
}
|
|
||||||
|
|
||||||
func substituteStringOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
//rhs block operator
|
|
||||||
//lhs of block = regex
|
|
||||||
//rhs of block = replacement expression
|
|
||||||
block := expressionNode.Rhs
|
|
||||||
|
|
||||||
regExStr, replacementText, err := getSubstituteParameters(d, block, context)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
regEx, err := regexp.Compile(regExStr)
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var results = list.New()
|
|
||||||
|
|
||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
|
||||||
candidate := el.Value.(*CandidateNode)
|
|
||||||
node := unwrapDoc(candidate.Node)
|
|
||||||
if node.Tag != "!!str" {
|
|
||||||
return Context{}, fmt.Errorf("cannot sustitute with %v, can only substitute strings", node.Tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
targetNode := substitute(node.Value, regEx, replacementText)
|
|
||||||
result := candidate.CreateChild(nil, targetNode)
|
|
||||||
results.PushBack(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.ChildContext(results), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func joinStringOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func joinStringOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- joinStringOperator")
|
log.Debugf("-- joinStringOperator")
|
||||||
joinStr := ""
|
joinStr := ""
|
||||||
@@ -92,7 +26,7 @@ func joinStringOperator(d *dataTreeNavigator, context Context, expressionNode *E
|
|||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := unwrapDoc(candidate.Node)
|
node := unwrapDoc(candidate.Node)
|
||||||
if node.Kind != yaml.SequenceNode {
|
if node.Kind != yaml.SequenceNode {
|
||||||
return Context{}, fmt.Errorf("cannot join with %v, can only join arrays of scalars", node.Tag)
|
return Context{}, fmt.Errorf("Cannot join with %v, can only join arrays of scalars", node.Tag)
|
||||||
}
|
}
|
||||||
targetNode := join(node.Content, joinStr)
|
targetNode := join(node.Content, joinStr)
|
||||||
result := candidate.CreateChild(nil, targetNode)
|
result := candidate.CreateChild(nil, targetNode)
|
||||||
|
|||||||
@@ -13,24 +13,6 @@ var stringsOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!str)::cat; meow; 1; ; true\n",
|
"D0, P[], (!!str)::cat; meow; 1; ; true\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "Substitute / Replace string",
|
|
||||||
subdescription: "This uses golang regex, described [here](https://github.com/google/re2/wiki/Syntax)",
|
|
||||||
document: `a: dogs are great`,
|
|
||||||
expression: `.a |= sub("dogs", "cats")`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: cats are great\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Substitute / Replace string with regex",
|
|
||||||
subdescription: "This uses golang regex, described [here](https://github.com/google/re2/wiki/Syntax)",
|
|
||||||
document: "a: cat\nb: heat",
|
|
||||||
expression: `.[] |= sub("([a])", "${1}r")`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: cart\nb: heart\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Split strings",
|
description: "Split strings",
|
||||||
document: `"cat; meow; 1; ; true"`,
|
document: `"cat; meow; 1; ; true"`,
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
package yqlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func createSubtractOp(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode {
|
|
||||||
return &ExpressionNode{Operation: &Operation{OperationType: subtractOpType},
|
|
||||||
Lhs: lhs,
|
|
||||||
Rhs: rhs}
|
|
||||||
}
|
|
||||||
|
|
||||||
func subtractAssignOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
assignmentOp := &Operation{OperationType: assignOpType}
|
|
||||||
assignmentOp.UpdateAssign = true
|
|
||||||
selfExpression := &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
|
||||||
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: expressionNode.Lhs, Rhs: createSubtractOp(selfExpression, expressionNode.Rhs)}
|
|
||||||
return d.GetMatchingNodes(context, assignmentOpNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func subtractOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
log.Debugf("Subtract operator")
|
|
||||||
|
|
||||||
return crossFunction(d, context, expressionNode, subtract, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func subtract(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
|
||||||
lhs.Node = unwrapDoc(lhs.Node)
|
|
||||||
rhs.Node = unwrapDoc(rhs.Node)
|
|
||||||
|
|
||||||
lhsNode := lhs.Node
|
|
||||||
|
|
||||||
if lhsNode.Tag == "!!null" {
|
|
||||||
return lhs.CreateChild(nil, rhs.Node), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
target := lhs.CreateChild(nil, &yaml.Node{})
|
|
||||||
|
|
||||||
switch lhsNode.Kind {
|
|
||||||
case yaml.MappingNode:
|
|
||||||
return nil, fmt.Errorf("Maps not yet supported for subtraction")
|
|
||||||
case yaml.SequenceNode:
|
|
||||||
return nil, fmt.Errorf("Sequences not yet supported for subtraction")
|
|
||||||
// target.Node.Kind = yaml.SequenceNode
|
|
||||||
// target.Node.Style = lhsNode.Style
|
|
||||||
// target.Node.Tag = "!!seq"
|
|
||||||
// target.Node.Content = append(lhsNode.Content, toNodes(rhs)...)
|
|
||||||
case yaml.ScalarNode:
|
|
||||||
if rhs.Node.Kind != yaml.ScalarNode {
|
|
||||||
return nil, fmt.Errorf("%v (%v) cannot be added to a %v", rhs.Node.Tag, rhs.Path, lhsNode.Tag)
|
|
||||||
}
|
|
||||||
target.Node.Kind = yaml.ScalarNode
|
|
||||||
target.Node.Style = lhsNode.Style
|
|
||||||
return subtractScalars(target, lhsNode, rhs.Node)
|
|
||||||
}
|
|
||||||
|
|
||||||
return target, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func subtractScalars(target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) (*CandidateNode, error) {
|
|
||||||
|
|
||||||
if lhs.Tag == "!!str" {
|
|
||||||
return nil, fmt.Errorf("strings cannot be subtracted")
|
|
||||||
} else if lhs.Tag == "!!int" && rhs.Tag == "!!int" {
|
|
||||||
lhsNum, err := strconv.Atoi(lhs.Value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rhsNum, err := strconv.Atoi(rhs.Value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result := lhsNum - rhsNum
|
|
||||||
target.Node.Tag = "!!int"
|
|
||||||
target.Node.Value = fmt.Sprintf("%v", result)
|
|
||||||
} else if (lhs.Tag == "!!int" || lhs.Tag == "!!float") && (rhs.Tag == "!!int" || rhs.Tag == "!!float") {
|
|
||||||
lhsNum, err := strconv.ParseFloat(lhs.Value, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rhsNum, err := strconv.ParseFloat(rhs.Value, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result := lhsNum - rhsNum
|
|
||||||
target.Node.Tag = "!!float"
|
|
||||||
target.Node.Value = fmt.Sprintf("%v", result)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("%v cannot be added to %v", lhs.Tag, rhs.Tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
return target, nil
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package yqlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var subtractOperatorScenarios = []expressionScenario{
|
|
||||||
{
|
|
||||||
description: "Number subtraction - float",
|
|
||||||
subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",
|
|
||||||
document: `{a: 3, b: 4.5}`,
|
|
||||||
expression: `.a = .a - .b`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{a: -1.5, b: 4.5}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Number subtraction - float",
|
|
||||||
subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",
|
|
||||||
document: `{a: 3, b: 4.5}`,
|
|
||||||
expression: `.a = .a - .b`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{a: -1.5, b: 4.5}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Number subtraction - int",
|
|
||||||
subdescription: "If both the lhs and rhs are ints then the expression will be calculated with ints.",
|
|
||||||
document: `{a: 3, b: 4}`,
|
|
||||||
expression: `.a = .a - .b`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{a: -1, b: 4}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Decrement numbers",
|
|
||||||
document: `{a: 3, b: 5}`,
|
|
||||||
expression: `.[] -= 1`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{a: 2, b: 4}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubtractOperatorScenarios(t *testing.T) {
|
|
||||||
for _, tt := range subtractOperatorScenarios {
|
|
||||||
testScenario(t, &tt)
|
|
||||||
}
|
|
||||||
documentScenarios(t, "Subtract", subtractOperatorScenarios)
|
|
||||||
}
|
|
||||||
@@ -10,10 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type traversePreferences struct {
|
type traversePreferences struct {
|
||||||
DontFollowAlias bool
|
DontFollowAlias bool
|
||||||
IncludeMapKeys bool
|
IncludeMapKeys bool
|
||||||
DontAutoCreate bool // by default, we automatically create entries on the fly.
|
DontAutoCreate bool // by default, we automatically create entries on the fly.
|
||||||
DontIncludeMapValues bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func splat(d *dataTreeNavigator, context Context, prefs traversePreferences) (Context, error) {
|
func splat(d *dataTreeNavigator, context Context, prefs traversePreferences) (Context, error) {
|
||||||
@@ -21,7 +20,7 @@ func splat(d *dataTreeNavigator, context Context, prefs traversePreferences) (Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
func traversePathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func traversePathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- traversePathOperator")
|
log.Debugf("-- Traversing")
|
||||||
var matches = list.New()
|
var matches = list.New()
|
||||||
|
|
||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
@@ -76,16 +75,6 @@ func traverse(d *dataTreeNavigator, context Context, matchingNode *CandidateNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
//lhs may update the variable context, we should pass that into the RHS
|
|
||||||
// BUT we still return the original context back (see jq)
|
|
||||||
// https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...
|
|
||||||
|
|
||||||
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// rhs is a collect expression that will yield indexes to retreive of the arrays
|
// rhs is a collect expression that will yield indexes to retreive of the arrays
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
@@ -94,13 +83,7 @@ func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Content
|
var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Content
|
||||||
|
return traverseNodesWithArrayIndices(context, indicesToTraverse, traversePreferences{})
|
||||||
//now we traverse the result of the lhs against the indices we found
|
|
||||||
result, err := traverseNodesWithArrayIndices(lhs, indicesToTraverse, traversePreferences{})
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
return context.ChildContext(result.MatchingNodes), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func traverseNodesWithArrayIndices(context Context, indicesToTraverse []*yaml.Node, prefs traversePreferences) (Context, error) {
|
func traverseNodesWithArrayIndices(context Context, indicesToTraverse []*yaml.Node, prefs traversePreferences) (Context, error) {
|
||||||
@@ -215,21 +198,10 @@ func traverseMap(context Context, matchingNode *CandidateNode, key string, prefs
|
|||||||
if !prefs.DontAutoCreate && !context.DontAutoCreate && newMatches.Len() == 0 {
|
if !prefs.DontAutoCreate && !context.DontAutoCreate && newMatches.Len() == 0 {
|
||||||
//no matches, create one automagically
|
//no matches, create one automagically
|
||||||
valueNode := &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"}
|
valueNode := &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"}
|
||||||
keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: key}
|
|
||||||
node := matchingNode.Node
|
node := matchingNode.Node
|
||||||
node.Content = append(node.Content, keyNode, valueNode)
|
node.Content = append(node.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: key}, valueNode)
|
||||||
|
candidateNode := matchingNode.CreateChild(key, valueNode)
|
||||||
if prefs.IncludeMapKeys {
|
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
||||||
log.Debug("including key")
|
|
||||||
candidateNode := matchingNode.CreateChild(key, keyNode)
|
|
||||||
candidateNode.IsMapKey = true
|
|
||||||
newMatches.Set(fmt.Sprintf("keyOf-%v", candidateNode.GetKey()), candidateNode)
|
|
||||||
}
|
|
||||||
if !prefs.DontIncludeMapValues {
|
|
||||||
log.Debug("including value")
|
|
||||||
candidateNode := matchingNode.CreateChild(key, valueNode)
|
|
||||||
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results := list.New()
|
results := list.New()
|
||||||
@@ -265,16 +237,11 @@ func doTraverseMap(newMatches *orderedmap.OrderedMap, candidate *CandidateNode,
|
|||||||
} else if splat || keyMatches(key, wantedKey) {
|
} else if splat || keyMatches(key, wantedKey) {
|
||||||
log.Debug("MATCHED")
|
log.Debug("MATCHED")
|
||||||
if prefs.IncludeMapKeys {
|
if prefs.IncludeMapKeys {
|
||||||
log.Debug("including key")
|
|
||||||
candidateNode := candidate.CreateChild(key.Value, key)
|
candidateNode := candidate.CreateChild(key.Value, key)
|
||||||
candidateNode.IsMapKey = true
|
|
||||||
newMatches.Set(fmt.Sprintf("keyOf-%v", candidateNode.GetKey()), candidateNode)
|
newMatches.Set(fmt.Sprintf("keyOf-%v", candidateNode.GetKey()), candidateNode)
|
||||||
}
|
}
|
||||||
if !prefs.DontIncludeMapValues {
|
candidateNode := candidate.CreateChild(key.Value, value)
|
||||||
log.Debug("including value")
|
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
||||||
candidateNode := candidate.CreateChild(key.Value, value)
|
|
||||||
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,30 +47,13 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Special characters",
|
description: "Special characters",
|
||||||
subdescription: "Use quotes with brackets around path elements with special characters",
|
subdescription: "Use quotes around path elements with special characters",
|
||||||
document: `{"{}": frog}`,
|
document: `{"{}": frog}`,
|
||||||
expression: `.["{}"]`,
|
expression: `."{}"`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[{}], (!!str)::frog\n",
|
"D0, P[{}], (!!str)::frog\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "Keys with spaces",
|
|
||||||
subdescription: "Use quotes with brackets around path elements with special characters",
|
|
||||||
document: `{"red rabbit": frog}`,
|
|
||||||
expression: `.["red rabbit"]`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[red rabbit], (!!str)::frog\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{"flying fox": frog}`,
|
|
||||||
expression: `.["flying fox"]`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[flying fox], (!!str)::frog\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Dynamic keys",
|
description: "Dynamic keys",
|
||||||
subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`,
|
subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`,
|
||||||
@@ -80,14 +63,6 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[apple], (!!str)::crispy yum\n",
|
"D0, P[apple], (!!str)::crispy yum\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{b: apple, fruit: {apple: yum, banana: smooth}}`,
|
|
||||||
expression: `.fruit[.b]`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[fruit apple], (!!str)::yum\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Children don't exist",
|
description: "Children don't exist",
|
||||||
subdescription: "Nodes are added dynamically while traversing",
|
subdescription: "Nodes are added dynamically while traversing",
|
||||||
@@ -108,7 +83,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{}`,
|
document: `{}`,
|
||||||
expression: `.a[1]`,
|
expression: `.a.[1]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a 1], (!!null)::null\n",
|
"D0, P[a 1], (!!null)::null\n",
|
||||||
},
|
},
|
||||||
@@ -179,7 +154,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||||
expression: `.b[]`,
|
expression: `.b.[]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[b c], (!!str)::frog\n",
|
"D0, P[b c], (!!str)::frog\n",
|
||||||
},
|
},
|
||||||
@@ -261,7 +236,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: mergeDocSample,
|
document: mergeDocSample,
|
||||||
expression: `.foobar[]`,
|
expression: `.foobar.[]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[foobar c], (!!str)::foo_c\n",
|
"D0, P[foobar c], (!!str)::foo_c\n",
|
||||||
"D0, P[foobar a], (!!str)::foo_a\n",
|
"D0, P[foobar a], (!!str)::foo_a\n",
|
||||||
@@ -323,7 +298,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: mergeDocSample,
|
document: mergeDocSample,
|
||||||
expression: `.foobarList[]`,
|
expression: `.foobarList.[]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[foobarList b], (!!str)::bar_b\n",
|
"D0, P[foobarList b], (!!str)::bar_b\n",
|
||||||
"D0, P[foobarList a], (!!str)::foo_a\n",
|
"D0, P[foobarList a], (!!str)::foo_a\n",
|
||||||
@@ -369,7 +344,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [a,b,c]}`,
|
document: `{a: [a,b,c]}`,
|
||||||
expression: `.a[0, 2]`,
|
expression: `.a.[0, 2]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a 0], (!!str)::a\n",
|
"D0, P[a 0], (!!str)::a\n",
|
||||||
"D0, P[a 2], (!!str)::c\n",
|
"D0, P[a 2], (!!str)::c\n",
|
||||||
@@ -386,7 +361,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [a,b,c]}`,
|
document: `{a: [a,b,c]}`,
|
||||||
expression: `.a[-1]`,
|
expression: `.a.[-1]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a -1], (!!str)::c\n",
|
"D0, P[a -1], (!!str)::c\n",
|
||||||
},
|
},
|
||||||
@@ -402,7 +377,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [a,b,c]}`,
|
document: `{a: [a,b,c]}`,
|
||||||
expression: `.a[-2]`,
|
expression: `.a.[-2]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a -2], (!!str)::b\n",
|
"D0, P[a -2], (!!str)::b\n",
|
||||||
},
|
},
|
||||||
@@ -420,7 +395,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [a,b,c]}`,
|
document: `{a: [a,b,c]}`,
|
||||||
expression: `.a[]`,
|
expression: `.a.[]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a 0], (!!str)::a\n",
|
"D0, P[a 0], (!!str)::a\n",
|
||||||
"D0, P[a 1], (!!str)::b\n",
|
"D0, P[a 1], (!!str)::b\n",
|
||||||
@@ -443,5 +418,5 @@ func TestTraversePathOperatorScenarios(t *testing.T) {
|
|||||||
for _, tt := range traversePathOperatorScenarios {
|
for _, tt := range traversePathOperatorScenarios {
|
||||||
testScenario(t, &tt)
|
testScenario(t, &tt)
|
||||||
}
|
}
|
||||||
documentScenarios(t, "Traverse (Read)", traversePathOperatorScenarios)
|
documentScenarios(t, "Traverse", traversePathOperatorScenarios)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
package yqlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getVariableOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
variableName := expressionNode.Operation.StringValue
|
|
||||||
log.Debug("getVariableOperator %v", variableName)
|
|
||||||
result := context.GetVariable(variableName)
|
|
||||||
if result == nil {
|
|
||||||
result = list.New()
|
|
||||||
}
|
|
||||||
return context.ChildContext(result), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func assignVariableOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, nil
|
|
||||||
}
|
|
||||||
if expressionNode.Rhs.Operation.OperationType.Type != "GET_VARIABLE" {
|
|
||||||
return Context{}, fmt.Errorf("RHS of 'as' operator must be a variable name e.g. $foo")
|
|
||||||
}
|
|
||||||
variableName := expressionNode.Rhs.Operation.StringValue
|
|
||||||
context.SetVariable(variableName, lhs.MatchingNodes)
|
|
||||||
return context, nil
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package yqlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var variableOperatorScenarios = []expressionScenario{
|
|
||||||
{
|
|
||||||
description: "Single value variable",
|
|
||||||
document: `a: cat`,
|
|
||||||
expression: `.a as $foo | $foo`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[a], (!!str)::cat\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Multi value variable",
|
|
||||||
document: `[cat, dog]`,
|
|
||||||
expression: `.[] as $foo | $foo`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[0], (!!str)::cat\n",
|
|
||||||
"D0, P[1], (!!str)::dog\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Using variables as a lookup",
|
|
||||||
subdescription: "Example taken from [jq](https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...)",
|
|
||||||
document: `{"posts": [{"title": "Frist psot", "author": "anon"},
|
|
||||||
{"title": "A well-written article", "author": "person1"}],
|
|
||||||
"realnames": {"anon": "Anonymous Coward",
|
|
||||||
"person1": "Person McPherson"}}`,
|
|
||||||
expression: `.realnames as $names | .posts[] | {"title":.title, "author": $names[.author]}`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!map)::title: \"Frist psot\"\nauthor: \"Anonymous Coward\"\n",
|
|
||||||
"D0, P[], (!!map)::title: \"A well-written article\"\nauthor: \"Person McPherson\"\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVariableOperatorScenarios(t *testing.T) {
|
|
||||||
for _, tt := range variableOperatorScenarios {
|
|
||||||
testScenario(t, &tt)
|
|
||||||
}
|
|
||||||
documentScenarios(t, "Variable Operators", variableOperatorScenarios)
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jinzhu/copier"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,20 +23,7 @@ func emptyOperator(d *dataTreeNavigator, context Context, expressionNode *Expres
|
|||||||
|
|
||||||
type crossFunctionCalculation func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
|
type crossFunctionCalculation func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
|
||||||
|
|
||||||
func resultsForRhs(d *dataTreeNavigator, context Context, lhsCandidate *CandidateNode, rhs Context, calculation crossFunctionCalculation, results *list.List) error {
|
func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (Context, error) {
|
||||||
for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
|
||||||
log.Debugf("Applying calc")
|
|
||||||
rhsCandidate := rightEl.Value.(*CandidateNode)
|
|
||||||
resultCandidate, err := calculation(d, context, lhsCandidate, rhsCandidate)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
results.PushBack(resultCandidate)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation, calcWhenEmpty bool) (Context, error) {
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -51,33 +37,24 @@ func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *Expressi
|
|||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if calcWhenEmpty && lhs.MatchingNodes.Len() == 0 {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
if rhs.MatchingNodes.Len() == 0 {
|
lhsCandidate := el.Value.(*CandidateNode)
|
||||||
resultCandidate, err := calculation(d, context, nil, nil)
|
|
||||||
|
for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
||||||
|
log.Debugf("Applying calc")
|
||||||
|
rhsCandidate := rightEl.Value.(*CandidateNode)
|
||||||
|
resultCandidate, err := calculation(d, context, lhsCandidate, rhsCandidate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
results.PushBack(resultCandidate)
|
results.PushBack(resultCandidate)
|
||||||
}
|
}
|
||||||
err := resultsForRhs(d, context, nil, rhs, calculation, results)
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
|
||||||
lhsCandidate := el.Value.(*CandidateNode)
|
|
||||||
|
|
||||||
err := resultsForRhs(d, context, lhsCandidate, rhs, calculation, results)
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return context.ChildContext(results), nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func crossFunction(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation, calcWhenEmpty bool) (Context, error) {
|
func crossFunction(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (Context, error) {
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
var evaluateAllTogether = true
|
var evaluateAllTogether = true
|
||||||
@@ -88,11 +65,11 @@ func crossFunction(d *dataTreeNavigator, context Context, expressionNode *Expres
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if evaluateAllTogether {
|
if evaluateAllTogether {
|
||||||
return doCrossFunc(d, context, expressionNode, calculation, calcWhenEmpty)
|
return doCrossFunc(d, context, expressionNode, calculation)
|
||||||
}
|
}
|
||||||
|
|
||||||
for matchEl := context.MatchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
|
for matchEl := context.MatchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
|
||||||
innerResults, err := doCrossFunc(d, context.SingleChildContext(matchEl.Value.(*CandidateNode)), expressionNode, calculation, calcWhenEmpty)
|
innerResults, err := doCrossFunc(d, context.SingleChildContext(matchEl.Value.(*CandidateNode)), expressionNode, calculation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
@@ -111,25 +88,16 @@ func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode {
|
|||||||
return owner.CreateChild(nil, node)
|
return owner.CreateChild(nil, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTraversalTree(path []interface{}, traversePrefs traversePreferences, targetKey bool) *ExpressionNode {
|
func createTraversalTree(path []interface{}, traversePrefs traversePreferences) *ExpressionNode {
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
return &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
return &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
||||||
} else if len(path) == 1 {
|
} else if len(path) == 1 {
|
||||||
lastPrefs := traversePrefs
|
return &ExpressionNode{Operation: &Operation{OperationType: traversePathOpType, Preferences: traversePrefs, Value: path[0], StringValue: fmt.Sprintf("%v", path[0])}}
|
||||||
if targetKey {
|
|
||||||
err := copier.Copy(&lastPrefs, traversePrefs)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
lastPrefs.IncludeMapKeys = true
|
|
||||||
lastPrefs.DontIncludeMapValues = true
|
|
||||||
}
|
|
||||||
return &ExpressionNode{Operation: &Operation{OperationType: traversePathOpType, Preferences: lastPrefs, Value: path[0], StringValue: fmt.Sprintf("%v", path[0])}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExpressionNode{
|
return &ExpressionNode{
|
||||||
Operation: &Operation{OperationType: shortPipeOpType},
|
Operation: &Operation{OperationType: shortPipeOpType},
|
||||||
Lhs: createTraversalTree(path[0:1], traversePrefs, false),
|
Lhs: createTraversalTree(path[0:1], traversePrefs),
|
||||||
Rhs: createTraversalTree(path[1:], traversePrefs, targetKey),
|
Rhs: createTraversalTree(path[1:], traversePrefs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func NewPrinter(writer io.Writer, outputToJSON bool, unwrapScalar bool, colorsEn
|
|||||||
unwrapScalar: unwrapScalar,
|
unwrapScalar: unwrapScalar,
|
||||||
colorsEnabled: colorsEnabled,
|
colorsEnabled: colorsEnabled,
|
||||||
indent: indent,
|
indent: indent,
|
||||||
printDocSeparators: !outputToJSON && printDocSeparators,
|
printDocSeparators: printDocSeparators,
|
||||||
firstTimePrinting: true,
|
firstTimePrinting: true,
|
||||||
treeNavigator: NewDataTreeNavigator(),
|
treeNavigator: NewDataTreeNavigator(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,9 +130,7 @@ func TestPrinterMultipleDocsInSinglePrint(t *testing.T) {
|
|||||||
func TestPrinterMultipleDocsJson(t *testing.T) {
|
func TestPrinterMultipleDocsJson(t *testing.T) {
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
var writer = bufio.NewWriter(&output)
|
var writer = bufio.NewWriter(&output)
|
||||||
// note printDocSeparators is true, it should still not print document separators
|
printer := NewPrinter(writer, true, true, false, 0, false)
|
||||||
// when outputing JSON.
|
|
||||||
printer := NewPrinter(writer, true, true, false, 0, true)
|
|
||||||
|
|
||||||
inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0)
|
inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,14 +2,11 @@
|
|||||||
- increment version in snapcraft.yaml
|
- increment version in snapcraft.yaml
|
||||||
- increment version in github-action/Dockerfile
|
- increment version in github-action/Dockerfile
|
||||||
- make sure local build passes
|
- make sure local build passes
|
||||||
- run gosec (manual because docker platforms)
|
|
||||||
- commit version update changes
|
|
||||||
- tag git with same version number
|
- tag git with same version number
|
||||||
- commit vX tag - this will trigger github actions
|
- commit vX tag - this will trigger github actions
|
||||||
- use github actions to publish docker and make github release
|
- use github actions to publish docker and make github release
|
||||||
- check github updated yq action in marketplace
|
- check github updated yq action in marketplace
|
||||||
|
|
||||||
|
|
||||||
- snapcraft
|
- snapcraft
|
||||||
- will auto create a candidate, test it works then promote
|
- will auto create a candidate, test it works then promote
|
||||||
|
|
||||||
|
|||||||
@@ -10,3 +10,12 @@ else
|
|||||||
./bin/golangci-lint run --timeout=5m
|
./bin/golangci-lint run --timeout=5m
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ./bin/golangci-lint \
|
||||||
|
# --tests \
|
||||||
|
# --vendor \
|
||||||
|
# --disable=aligncheck \
|
||||||
|
# --disable=gotype \
|
||||||
|
# --disable=goconst \
|
||||||
|
# --disable=gocyclo \
|
||||||
|
# --deadline=300s \
|
||||||
|
# ./...
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -ex
|
set -ex
|
||||||
go get golang.org/x/tools/cmd/goimports
|
go get golang.org/x/tools/cmd/goimports
|
||||||
wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.37.1
|
wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.24.0
|
||||||
# wget -O- -nv https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s v2.6.1
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
if command -v gosec &> /dev/null
|
|
||||||
then
|
|
||||||
gosec ${PWD}
|
|
||||||
else
|
|
||||||
./bin/gosec ${PWD}
|
|
||||||
fi
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
name: yq
|
name: yq
|
||||||
version: '4.7.0'
|
version: '4.4.1'
|
||||||
summary: A lightweight and portable command-line YAML processor
|
summary: A lightweight and portable command-line YAML processor
|
||||||
description: |
|
description: |
|
||||||
The aim of the project is to be the jq or sed of yaml files.
|
The aim of the project is to be the jq or sed of yaml files.
|
||||||
|
|||||||
BIN
yq_linux_amd64.tar.gz
Normal file
BIN
yq_linux_amd64.tar.gz
Normal file
Binary file not shown.
Reference in New Issue
Block a user