mirror of
https://github.com/taigrr/yq
synced 2025-01-18 04:53:17 -08:00
Compare commits
19 Commits
path-tree
...
4.0.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e1ce4ca70 | ||
|
|
9bd9468526 | ||
|
|
75044e480c | ||
|
|
36084a60a9 | ||
|
|
9b48cf80e0 | ||
|
|
bb3b08e648 | ||
|
|
dcacad1e7e | ||
|
|
3356061e1e | ||
|
|
2c062bc2a5 | ||
|
|
088ec36acd | ||
|
|
83cb6421df | ||
|
|
a57944d123 | ||
|
|
79867473d5 | ||
|
|
b3efcdc202 | ||
|
|
af2aa9ad91 | ||
|
|
db4762ef7c | ||
|
|
860655b4cd | ||
|
|
d91b25840a | ||
|
|
019acfe456 |
@@ -14,6 +14,5 @@ var noDocSeparators = false
|
||||
var nullInput = false
|
||||
var verbose = false
|
||||
var version = false
|
||||
var shellCompletion = ""
|
||||
|
||||
// var log = logging.MustGetLogger("yq")
|
||||
|
||||
23
cmd/root.go
23
cmd/root.go
@@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -18,20 +17,6 @@ func New() *cobra.Command {
|
||||
cmd.Print(GetVersionDisplay())
|
||||
return nil
|
||||
}
|
||||
if shellCompletion != "" {
|
||||
switch shellCompletion {
|
||||
case "bash", "":
|
||||
return cmd.GenBashCompletion(os.Stdout)
|
||||
case "zsh":
|
||||
return cmd.GenZshCompletion(os.Stdout)
|
||||
case "fish":
|
||||
return cmd.GenFishCompletion(os.Stdout, true)
|
||||
case "powershell":
|
||||
return cmd.GenPowerShellCompletion(os.Stdout)
|
||||
default:
|
||||
return fmt.Errorf("Unknown variant %v", shellCompletion)
|
||||
}
|
||||
}
|
||||
cmd.Println(cmd.UsageString())
|
||||
return nil
|
||||
|
||||
@@ -62,10 +47,12 @@ func New() *cobra.Command {
|
||||
rootCmd.PersistentFlags().IntVarP(&indent, "indent", "I", 2, "sets indent level for output")
|
||||
rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit")
|
||||
|
||||
rootCmd.Flags().StringVarP(&shellCompletion, "shellCompletion", "", "", "[bash/zsh/powershell/fish] prints shell completion script")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&forceColor, "colors", "C", false, "force print with colors")
|
||||
rootCmd.PersistentFlags().BoolVarP(&forceNoColor, "no-colors", "M", false, "force print with no colors")
|
||||
rootCmd.AddCommand(createEvaluateSequenceCommand(), createEvaluateAllCommand())
|
||||
rootCmd.AddCommand(
|
||||
createEvaluateSequenceCommand(),
|
||||
createEvaluateAllCommand(),
|
||||
completionCmd,
|
||||
)
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
61
cmd/shell-completion.go
Normal file
61
cmd/shell-completion.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var completionCmd = &cobra.Command{
|
||||
Use: "shell-completion [bash|zsh|fish|powershell]",
|
||||
Short: "Generate completion script",
|
||||
Long: `To load completions:
|
||||
|
||||
Bash:
|
||||
|
||||
$ source <(yq shell-completion bash)
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
Linux:
|
||||
$ yq shell-completion bash > /etc/bash_completion.d/yq
|
||||
MacOS:
|
||||
$ yq shell-completion bash > /usr/local/etc/bash_completion.d/yq
|
||||
|
||||
Zsh:
|
||||
|
||||
# If shell completion is not already enabled in your environment you will need
|
||||
# to enable it. You can execute the following once:
|
||||
|
||||
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
$ yq shell-completion zsh > "${fpath[1]}/_yq"
|
||||
|
||||
# You will need to start a new shell for this setup to take effect.
|
||||
|
||||
Fish:
|
||||
|
||||
$ yq shell-completion fish | source
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
$ yq shell-completion fish > ~/.config/fish/completions/yq.fish
|
||||
`,
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||
Args: cobra.ExactValidArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var err error = nil
|
||||
switch args[0] {
|
||||
case "bash":
|
||||
err = cmd.Root().GenBashCompletion(os.Stdout)
|
||||
case "zsh":
|
||||
err = cmd.Root().GenZshCompletion(os.Stdout)
|
||||
case "fish":
|
||||
err = cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||
case "powershell":
|
||||
err = cmd.Root().GenPowerShellCompletion(os.Stdout)
|
||||
}
|
||||
return err
|
||||
|
||||
},
|
||||
}
|
||||
13
compare.sh
13
compare.sh
@@ -1,13 +0,0 @@
|
||||
GREEN='\033[0;32m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo "${GREEN}---Old---${NC}"
|
||||
yq $@ > /tmp/yq-old-output
|
||||
cat /tmp/yq-old-output
|
||||
|
||||
echo "${GREEN}---New---${NC}"
|
||||
./yq $@ > /tmp/yq-new-output
|
||||
cat /tmp/yq-new-output
|
||||
|
||||
echo "${GREEN}---Diff---${NC}"
|
||||
colordiff /tmp/yq-old-output /tmp/yq-new-output
|
||||
@@ -1,664 +0,0 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
)
|
||||
|
||||
func resultsToString(results *list.List) []string {
|
||||
var pretty []string = make([]string, 0)
|
||||
for el := results.Front(); el != nil; el = el.Next() {
|
||||
n := el.Value.(*CandidateNode)
|
||||
pretty = append(pretty, NodeToString(n))
|
||||
}
|
||||
return pretty
|
||||
}
|
||||
|
||||
// func TestDataTreeNavigatorDeleteSimple(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// b: apple
|
||||
// c: camel`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("a .- b")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a]
|
||||
// Tag: !!map, Kind: MappingNode, Anchor:
|
||||
// c: camel
|
||||
// `
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorDeleteTwice(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// b: apple
|
||||
// c: camel
|
||||
// d: dingo`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("a .- b OR a .- c")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a]
|
||||
// Tag: !!map, Kind: MappingNode, Anchor:
|
||||
// d: dingo
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorDeleteWithUnion(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// b: apple
|
||||
// c: camel
|
||||
// d: dingo`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("a .- (b OR c)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a]
|
||||
// Tag: !!map, Kind: MappingNode, Anchor:
|
||||
// d: dingo
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorDeleteByIndex(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// - b: apple
|
||||
// - b: sdfsd
|
||||
// - b: apple`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("(a .- (0 or 1))")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a]
|
||||
// Tag: !!seq, Kind: SequenceNode, Anchor:
|
||||
// - b: apple
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorDeleteByFind(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// - b: apple
|
||||
// - b: sdfsd
|
||||
// - b: apple`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("(a .- (* == apple))")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a]
|
||||
// Tag: !!seq, Kind: SequenceNode, Anchor:
|
||||
// - b: sdfsd
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorDeleteArrayByFind(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// - apple
|
||||
// - sdfsd
|
||||
// - apple`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("(a .- (. == apple))")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a]
|
||||
// Tag: !!seq, Kind: SequenceNode, Anchor:
|
||||
// - sdfsd
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorDeleteViaSelf(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `- apple
|
||||
// - sdfsd
|
||||
// - apple`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath(". .- (. == apple)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: []
|
||||
// Tag: !!seq, Kind: SequenceNode, Anchor:
|
||||
// - sdfsd
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorFilterWithSplat(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `f:
|
||||
// a: frog
|
||||
// b: dally
|
||||
// c: log`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath(".f | .[] == \"frog\"")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [f]
|
||||
// Tag: !!int, Kind: ScalarNode, Anchor:
|
||||
// 2
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCountAndCollectWithFilterCmd(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `f:
|
||||
// a: frog
|
||||
// b: dally
|
||||
// c: log`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath(".f | .[] == *og ")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [f]
|
||||
// Tag: !!int, Kind: ScalarNode, Anchor:
|
||||
// 2
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCollectWithFilter(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `f:
|
||||
// a: frog
|
||||
// b: dally
|
||||
// c: log`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("f(collect(. == *og))")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [f]
|
||||
// Tag: , Kind: SequenceNode, Anchor:
|
||||
// - frog
|
||||
// - log
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCountWithFilter2(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `f:
|
||||
// a: frog
|
||||
// b: dally
|
||||
// c: log`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("count(f(. == *og))")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: []
|
||||
// Tag: !!int, Kind: ScalarNode, Anchor:
|
||||
// 2
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCollectWithFilter2(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `f:
|
||||
// a: frog
|
||||
// b: dally
|
||||
// c: log`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("collect(f(. == *og))")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: []
|
||||
// Tag: , Kind: SequenceNode, Anchor:
|
||||
// - frog
|
||||
// - log
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCountMultipleMatchesInside(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `f:
|
||||
// a: [1,2]
|
||||
// b: dally
|
||||
// c: [3,4,5]`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("f | count(a or c)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [f]
|
||||
// Tag: !!int, Kind: ScalarNode, Anchor:
|
||||
// 2
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCollectMultipleMatchesInside(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `f:
|
||||
// a: [1,2]
|
||||
// b: dally
|
||||
// c: [3,4,5]`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("f | collect(a or c)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [f]
|
||||
// Tag: , Kind: SequenceNode, Anchor:
|
||||
// - [1, 2]
|
||||
// - [3, 4, 5]
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCountMultipleMatchesInsideSplat(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `f:
|
||||
// a: [1,2,3]
|
||||
// b: [1,2,3,4]
|
||||
// c: [1,2,3,4,5]`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("f(count( (a or c)*))")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [f]
|
||||
// Tag: !!int, Kind: ScalarNode, Anchor:
|
||||
// 8
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCountMultipleMatchesOutside(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `f:
|
||||
// a: [1,2,3]
|
||||
// b: [1,2,3,4]
|
||||
// c: [1,2,3,4,5]`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("f(a or c)(count(*))")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [f a]
|
||||
// Tag: !!int, Kind: ScalarNode, Anchor:
|
||||
// 3
|
||||
// -- Node --
|
||||
// Document 0, path: [f c]
|
||||
// Tag: !!int, Kind: ScalarNode, Anchor:
|
||||
// 5
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCountOfResults(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `- apple
|
||||
// - sdfsd
|
||||
// - apple`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("count(*)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: []
|
||||
// Tag: !!int, Kind: ScalarNode, Anchor:
|
||||
// 3
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorCountNoMatches(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `- apple
|
||||
// - sdfsd
|
||||
// - apple`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("count(5)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: []
|
||||
// Tag: !!int, Kind: ScalarNode, Anchor:
|
||||
// 0
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorDeleteAndWrite(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// - b: apple
|
||||
// - b: sdfsd
|
||||
// - { b: apple, c: cat }`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("(a .- (0 or 1)) or (a[0].b := frog)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a]
|
||||
// Tag: !!seq, Kind: SequenceNode, Anchor:
|
||||
// - {b: frog, c: cat}
|
||||
|
||||
// -- Node --
|
||||
// Document 0, path: [a 0 b]
|
||||
// Tag: !!str, Kind: ScalarNode, Anchor:
|
||||
// frog
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorDeleteArray(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// - b: apple
|
||||
// - b: sdfsd
|
||||
// - b: apple`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("a .- (b == a*)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a]
|
||||
// Tag: !!seq, Kind: SequenceNode, Anchor:
|
||||
// - b: sdfsd
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorArraySimple(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `- b: apple`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("[0]")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [0]
|
||||
// Tag: !!map, Kind: MappingNode, Anchor:
|
||||
// b: apple
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorSimpleAssignByFind(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// b: apple`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("a(. == apple) := frog")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a b]
|
||||
// Tag: !!str, Kind: ScalarNode, Anchor:
|
||||
// frog
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorOrDeDupes(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// cat: apple
|
||||
// mad: things`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("a.(cat or cat)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a cat]
|
||||
// Tag: !!str, Kind: ScalarNode, Anchor:
|
||||
// apple
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
|
||||
// func TestDataTreeNavigatorAnd(t *testing.T) {
|
||||
|
||||
// nodes := readDoc(t, `a:
|
||||
// cat: apple
|
||||
// pat: apple
|
||||
// cow: apple
|
||||
// mad: things`)
|
||||
|
||||
// path, errPath := treeCreator.ParsePath("a.(*t and c*)")
|
||||
// if errPath != nil {
|
||||
// t.Error(errPath)
|
||||
// }
|
||||
// results, errNav := treeNavigator.GetMatchingNodes(nodes, path)
|
||||
|
||||
// if errNav != nil {
|
||||
// t.Error(errNav)
|
||||
// }
|
||||
|
||||
// expected := `
|
||||
// -- Node --
|
||||
// Document 0, path: [a cat]
|
||||
// Tag: !!str, Kind: ScalarNode, Anchor:
|
||||
// apple
|
||||
// `
|
||||
|
||||
// test.AssertResult(t, expected, resultsToString(results))
|
||||
// }
|
||||
1
pkg/yqlib/doc/.gitignore
vendored
1
pkg/yqlib/doc/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
*.md
|
||||
163
pkg/yqlib/doc/Assign Operator.md
Normal file
163
pkg/yqlib/doc/Assign Operator.md
Normal file
@@ -0,0 +1,163 @@
|
||||
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.
|
||||
## Examples
|
||||
### Update parent to be the child value
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
b:
|
||||
g: foof
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a |= .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
g: foof
|
||||
```
|
||||
|
||||
### Update to be the sibling value
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
b: child
|
||||
b: sibling
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a = .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: sibling
|
||||
b: sibling
|
||||
```
|
||||
|
||||
### Updated multiple paths
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: fieldA
|
||||
b: fieldB
|
||||
c: fieldC
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '(.a, .c) |= "potatoe"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: potatoe
|
||||
b: fieldB
|
||||
c: potatoe
|
||||
```
|
||||
|
||||
### Update string value
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
b: apple
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a.b = "frog"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
b: frog
|
||||
```
|
||||
|
||||
### Update string value via |=
|
||||
Note there is no difference between `=` and `|=` when the RHS is a scalar
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
b: apple
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a.b |= "frog"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
b: frog
|
||||
```
|
||||
|
||||
### Update selected results
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
b: apple
|
||||
c: cactus
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a[] | select(. == "apple") |= "frog"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
b: frog
|
||||
c: cactus
|
||||
```
|
||||
|
||||
### Update array values
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- candy
|
||||
- apple
|
||||
- sandy
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.[] | select(. == "*andy") |= "bogs"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- bogs
|
||||
- apple
|
||||
- bogs
|
||||
```
|
||||
|
||||
### Update empty object
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
'': null
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a.b |= "bogs"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
'': null
|
||||
a:
|
||||
b: bogs
|
||||
```
|
||||
|
||||
### Update empty object and array
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
'': null
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a.b[0] |= "bogs"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
'': null
|
||||
a:
|
||||
b:
|
||||
- bogs
|
||||
```
|
||||
|
||||
41
pkg/yqlib/doc/Collect into Array.md
Normal file
41
pkg/yqlib/doc/Collect into Array.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Collect into Array
|
||||
|
||||
This creates an array using the expression between the square brackets.
|
||||
|
||||
|
||||
## Examples
|
||||
### Collect empty
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '[]'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
```
|
||||
|
||||
### Collect single
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '["cat"]'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- cat
|
||||
```
|
||||
|
||||
### Collect many
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '[.a, .b]' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- cat
|
||||
- dog
|
||||
```
|
||||
|
||||
79
pkg/yqlib/doc/Collect into Object.md
Normal file
79
pkg/yqlib/doc/Collect into Object.md
Normal file
@@ -0,0 +1,79 @@
|
||||
This is used to construct objects (or maps). This can be used against existing yaml, or to create fresh yaml documents.
|
||||
## Examples
|
||||
### Collect empty object
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '{}'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
```
|
||||
|
||||
### Wrap (prefix) existing object
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
name: Mike
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '{"wrap": .}' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
wrap:
|
||||
name: Mike
|
||||
```
|
||||
|
||||
### Using splat to create multiple objects
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
name: Mike
|
||||
pets:
|
||||
- cat
|
||||
- dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '{.name: .pets[]}' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
Mike: cat
|
||||
Mike: dog
|
||||
```
|
||||
|
||||
### Working with multiple documents
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
name: Mike
|
||||
pets:
|
||||
- cat
|
||||
- dog
|
||||
---
|
||||
name: Rosey
|
||||
pets:
|
||||
- monkey
|
||||
- sheep
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '{.name: .pets[]}' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
Mike: cat
|
||||
Mike: dog
|
||||
Rosey: monkey
|
||||
Rosey: sheep
|
||||
```
|
||||
|
||||
### Creating yaml from scratch
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '{"wrap": "frog"}'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
wrap: frog
|
||||
```
|
||||
|
||||
120
pkg/yqlib/doc/Comments Operator.md
Normal file
120
pkg/yqlib/doc/Comments Operator.md
Normal file
@@ -0,0 +1,120 @@
|
||||
Use these comment operators to set or retrieve comments.
|
||||
## Examples
|
||||
### Set line comment
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a lineComment="single"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: cat # single
|
||||
```
|
||||
|
||||
### Set head comment
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '. headComment="single"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
# single
|
||||
|
||||
a: cat
|
||||
```
|
||||
|
||||
### Set foot comment, using an expression
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '. footComment=.a' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: cat
|
||||
|
||||
# cat
|
||||
```
|
||||
|
||||
### Remove comment
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat # comment
|
||||
b: dog # leave this
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a lineComment=""' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: cat
|
||||
b: dog # leave this
|
||||
```
|
||||
|
||||
### Remove all comments
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat # comment
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. comments=""' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: cat
|
||||
```
|
||||
|
||||
### Get line comment
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat # meow
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a | lineComment' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
meow
|
||||
```
|
||||
|
||||
### Get head comment
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat # meow
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '. | headComment' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
|
||||
```
|
||||
|
||||
### Get foot comment
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat # meow
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '. | footComment' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
|
||||
```
|
||||
|
||||
66
pkg/yqlib/doc/Delete Operator.md
Normal file
66
pkg/yqlib/doc/Delete Operator.md
Normal file
@@ -0,0 +1,66 @@
|
||||
Deletes matching entries in maps or arrays.
|
||||
## Examples
|
||||
### Delete entry in map
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'del(.b)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: cat
|
||||
```
|
||||
|
||||
### Delete entry in array
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'del(.[1])' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- 1
|
||||
- 3
|
||||
```
|
||||
|
||||
### Delete no matches
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'del(.c)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: cat
|
||||
b: dog
|
||||
```
|
||||
|
||||
### Delete matching entries
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: dog
|
||||
c: bat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'del( .[] | select(. == "*at") )' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
b: dog
|
||||
```
|
||||
|
||||
55
pkg/yqlib/doc/Document Index Operator.md
Normal file
55
pkg/yqlib/doc/Document Index Operator.md
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
## Examples
|
||||
### Retrieve a document index
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
---
|
||||
a: frog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a | documentIndex' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
0
|
||||
---
|
||||
1
|
||||
```
|
||||
|
||||
### Filter by document index
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
---
|
||||
a: frog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'select(. | documentIndex == 1)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: frog
|
||||
```
|
||||
|
||||
### Print Document Index with matches
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
---
|
||||
a: frog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a | ({"match": ., "doc": (. | documentIndex)})' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
match: cat
|
||||
doc: 0
|
||||
match: frog
|
||||
doc: 1
|
||||
```
|
||||
|
||||
62
pkg/yqlib/doc/Equals Operator.md
Normal file
62
pkg/yqlib/doc/Equals Operator.md
Normal file
@@ -0,0 +1,62 @@
|
||||
## Equals Operator
|
||||
|
||||
This is a boolean operator that will return ```true``` if the LHS is equal to the RHS and ``false`` otherwise.
|
||||
|
||||
```
|
||||
.a == .b
|
||||
```
|
||||
|
||||
It is most often used with the select operator to find particular nodes:
|
||||
|
||||
```
|
||||
select(.a == .b)
|
||||
```
|
||||
|
||||
|
||||
## Examples
|
||||
### Match string
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- cat
|
||||
- goat
|
||||
- dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.[] | (. == "*at")' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
true
|
||||
true
|
||||
false
|
||||
```
|
||||
|
||||
### Match number
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.[] | (. == 4)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
false
|
||||
true
|
||||
false
|
||||
```
|
||||
|
||||
### Match nulls
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input 'null == ~'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
true
|
||||
```
|
||||
|
||||
99
pkg/yqlib/doc/Explode Operator.md
Normal file
99
pkg/yqlib/doc/Explode Operator.md
Normal file
@@ -0,0 +1,99 @@
|
||||
Explodes (or dereferences) aliases and anchors.
|
||||
## Examples
|
||||
### Explode alias and anchor
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
f:
|
||||
a: &a cat
|
||||
b: *a
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'explode(.f)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
f:
|
||||
a: cat
|
||||
b: cat
|
||||
```
|
||||
|
||||
### Explode with no aliases or anchors
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: mike
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'explode(.a)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: mike
|
||||
```
|
||||
|
||||
### Explode with alias keys
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
f:
|
||||
a: &a cat
|
||||
*a: b
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'explode(.f)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
f:
|
||||
a: cat
|
||||
cat: b
|
||||
```
|
||||
|
||||
### Explode with merge anchors
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
!!merge <<:
|
||||
- *foo
|
||||
- *bar
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval 'explode(.)' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
foo:
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar:
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: bar_b
|
||||
a: foo_a
|
||||
thing: bar_thing
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foo_c
|
||||
a: foo_a
|
||||
thing: foobar_thing
|
||||
```
|
||||
|
||||
121
pkg/yqlib/doc/Mulitply Operator.md
Normal file
121
pkg/yqlib/doc/Mulitply Operator.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Mulitply Operator
|
||||
## Examples
|
||||
### Merge objects together
|
||||
sample.yml:
|
||||
```yaml
|
||||
{a: {also: me}, b: {also: {g: wizz}}}
|
||||
```
|
||||
Expression
|
||||
```bash
|
||||
yq '. * {"a":.b}' < sample.yml
|
||||
```
|
||||
Result
|
||||
```yaml
|
||||
{a: {also: {g: wizz}}, b: {also: {g: wizz}}}
|
||||
```
|
||||
### Merge keeps style of LHS
|
||||
sample.yml:
|
||||
```yaml
|
||||
a: {things: great}
|
||||
b:
|
||||
also: "me"
|
||||
|
||||
```
|
||||
Expression
|
||||
```bash
|
||||
yq '. * {"a":.b}' < sample.yml
|
||||
```
|
||||
Result
|
||||
```yaml
|
||||
a: {things: great, also: "me"}
|
||||
b:
|
||||
also: "me"
|
||||
```
|
||||
### Merge arrays
|
||||
sample.yml:
|
||||
```yaml
|
||||
{a: [1,2,3], b: [3,4,5]}
|
||||
```
|
||||
Expression
|
||||
```bash
|
||||
yq '. * {"a":.b}' < sample.yml
|
||||
```
|
||||
Result
|
||||
```yaml
|
||||
{a: [3, 4, 5], b: [3, 4, 5]}
|
||||
```
|
||||
### Merge to prefix an element
|
||||
sample.yml:
|
||||
```yaml
|
||||
{a: cat, b: dog}
|
||||
```
|
||||
Expression
|
||||
```bash
|
||||
yq '. * {"a": {"c": .a}}' < sample.yml
|
||||
```
|
||||
Result
|
||||
```yaml
|
||||
{a: {c: cat}, b: dog}
|
||||
```
|
||||
### Merge with simple aliases
|
||||
sample.yml:
|
||||
```yaml
|
||||
{a: &cat {c: frog}, b: {f: *cat}, c: {g: thongs}}
|
||||
```
|
||||
Expression
|
||||
```bash
|
||||
yq '.c * .b' < sample.yml
|
||||
```
|
||||
Result
|
||||
```yaml
|
||||
{g: thongs, f: *cat}
|
||||
```
|
||||
### Merge does not copy anchor names
|
||||
sample.yml:
|
||||
```yaml
|
||||
{a: {c: &cat frog}, b: {f: *cat}, c: {g: thongs}}
|
||||
```
|
||||
Expression
|
||||
```bash
|
||||
yq '.c * .a' < sample.yml
|
||||
```
|
||||
Result
|
||||
```yaml
|
||||
{g: thongs, c: frog}
|
||||
```
|
||||
### Merge with merge anchors
|
||||
sample.yml:
|
||||
```yaml
|
||||
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
<<: [*foo,*bar]
|
||||
c: foobarList_c
|
||||
|
||||
foobar:
|
||||
c: foobar_c
|
||||
<<: *foo
|
||||
thing: foobar_thing
|
||||
|
||||
```
|
||||
Expression
|
||||
```bash
|
||||
yq '.foobar * .foobarList' < sample.yml
|
||||
```
|
||||
Result
|
||||
```yaml
|
||||
c: foobarList_c
|
||||
<<: [*foo, *bar]
|
||||
thing: foobar_thing
|
||||
b: foobarList_b
|
||||
```
|
||||
196
pkg/yqlib/doc/Multiply Operator.md
Normal file
196
pkg/yqlib/doc/Multiply Operator.md
Normal file
@@ -0,0 +1,196 @@
|
||||
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.
|
||||
|
||||
Upcoming versions of `yq` will add support for other types of multiplication (numbers, strings).
|
||||
|
||||
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
||||
## Examples
|
||||
### Merge objects together, returning merged result only
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
field: me
|
||||
fieldA: cat
|
||||
b:
|
||||
field:
|
||||
g: wizz
|
||||
fieldB: dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a * .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
field:
|
||||
g: wizz
|
||||
fieldA: cat
|
||||
fieldB: dog
|
||||
```
|
||||
|
||||
### Merge objects together, returning parent object
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
field: me
|
||||
fieldA: cat
|
||||
b:
|
||||
field:
|
||||
g: wizz
|
||||
fieldB: dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '. * {"a":.b}' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
field:
|
||||
g: wizz
|
||||
fieldA: cat
|
||||
fieldB: dog
|
||||
b:
|
||||
field:
|
||||
g: wizz
|
||||
fieldB: dog
|
||||
```
|
||||
|
||||
### Merge keeps style of LHS
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: {things: great}
|
||||
b:
|
||||
also: "me"
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '. * {"a":.b}' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: {things: great, also: "me"}
|
||||
b:
|
||||
also: "me"
|
||||
```
|
||||
|
||||
### Merge arrays
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
b:
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '. * {"a":.b}' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
b:
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
```
|
||||
|
||||
### Merge to prefix an element
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '. * {"a": {"c": .a}}' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
c: cat
|
||||
b: dog
|
||||
```
|
||||
|
||||
### Merge with simple aliases
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: &cat
|
||||
c: frog
|
||||
b:
|
||||
f: *cat
|
||||
c:
|
||||
g: thongs
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.c * .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
g: thongs
|
||||
f: *cat
|
||||
```
|
||||
|
||||
### Merge does not copy anchor names
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
c: &cat frog
|
||||
b:
|
||||
f: *cat
|
||||
c:
|
||||
g: thongs
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.c * .a' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
g: thongs
|
||||
c: frog
|
||||
```
|
||||
|
||||
### Merge with merge anchors
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
!!merge <<:
|
||||
- *foo
|
||||
- *bar
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.foobar * .foobarList' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
c: foobarList_c
|
||||
<<:
|
||||
- *foo
|
||||
- *bar
|
||||
thing: foobar_thing
|
||||
b: foobarList_b
|
||||
```
|
||||
|
||||
72
pkg/yqlib/doc/Not Operator.md
Normal file
72
pkg/yqlib/doc/Not Operator.md
Normal file
@@ -0,0 +1,72 @@
|
||||
This is a boolean operator and will return `true` when given a `false` value (including null), and `false` otherwise.
|
||||
## Examples
|
||||
### Not true is false
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input 'true | not'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
false
|
||||
```
|
||||
|
||||
### Not false is true
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input 'false | not'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
true
|
||||
```
|
||||
|
||||
### String values considered to be true
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '"cat" | not'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
false
|
||||
```
|
||||
|
||||
### Empty string value considered to be true
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '"" | not'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
false
|
||||
```
|
||||
|
||||
### Numbers are considered to be true
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '1 | not'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
false
|
||||
```
|
||||
|
||||
### Zero is considered to be true
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '0 | not'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
false
|
||||
```
|
||||
|
||||
### Null is considered to be false
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '~ | not'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
true
|
||||
```
|
||||
|
||||
125
pkg/yqlib/doc/Recursive Descent Operator.md
Normal file
125
pkg/yqlib/doc/Recursive Descent Operator.md
Normal file
@@ -0,0 +1,125 @@
|
||||
This operator recursively matches all children nodes given of a particular element, including that node itself. This is most often used to apply a filter recursively against all matches, for instance to set the `style` of all nodes in a yaml doc:
|
||||
|
||||
```bash
|
||||
yq eval '.. style= "flow"' file.yaml
|
||||
```
|
||||
## Examples
|
||||
### Map
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
b: apple
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '..' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
b: apple
|
||||
b: apple
|
||||
apple
|
||||
```
|
||||
|
||||
### Array
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '..' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
1
|
||||
2
|
||||
3
|
||||
```
|
||||
|
||||
### Array of maps
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- a: cat
|
||||
- 2
|
||||
- true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '..' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- a: cat
|
||||
- 2
|
||||
- true
|
||||
a: cat
|
||||
cat
|
||||
2
|
||||
true
|
||||
```
|
||||
|
||||
### Aliases are not traversed
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: &cat
|
||||
c: frog
|
||||
b: *cat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '..' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: &cat
|
||||
c: frog
|
||||
b: *cat
|
||||
&cat
|
||||
c: frog
|
||||
frog
|
||||
*cat
|
||||
```
|
||||
|
||||
### Merge docs are not traversed
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
!!merge <<:
|
||||
- *foo
|
||||
- *bar
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.foobar | ..' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
foobar_c
|
||||
*foo
|
||||
foobar_thing
|
||||
```
|
||||
|
||||
39
pkg/yqlib/doc/Select Operator.md
Normal file
39
pkg/yqlib/doc/Select Operator.md
Normal file
@@ -0,0 +1,39 @@
|
||||
Select is used to filter arrays and maps by a boolean expression.
|
||||
## Examples
|
||||
### Select elements from array
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- cat
|
||||
- goat
|
||||
- dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.[] | select(. == "*at")' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
cat
|
||||
goat
|
||||
```
|
||||
|
||||
### Select and update matching values in map
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
things: cat
|
||||
bob: goat
|
||||
horse: dog
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '(.a[] | select(. == "*at")) |= "rabbit"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
things: rabbit
|
||||
bob: rabbit
|
||||
horse: dog
|
||||
```
|
||||
|
||||
165
pkg/yqlib/doc/Style Operator.md
Normal file
165
pkg/yqlib/doc/Style Operator.md
Normal file
@@ -0,0 +1,165 @@
|
||||
The style operator can be used to get or set the style of nodes (e.g. string style, yaml style)
|
||||
## Examples
|
||||
### Set tagged style
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. style="tagged"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
!!map
|
||||
a: !!str cat
|
||||
b: !!int 5
|
||||
c: !!float 3.2
|
||||
e: !!bool true
|
||||
```
|
||||
|
||||
### Set double quote style
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. style="double"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: "cat"
|
||||
b: "5"
|
||||
c: "3.2"
|
||||
e: "true"
|
||||
```
|
||||
|
||||
### Set single quote style
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. style="single"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: 'cat'
|
||||
b: '5'
|
||||
c: '3.2'
|
||||
e: 'true'
|
||||
```
|
||||
|
||||
### Set literal quote style
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. style="literal"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: |-
|
||||
cat
|
||||
b: |-
|
||||
5
|
||||
c: |-
|
||||
3.2
|
||||
e: |-
|
||||
true
|
||||
```
|
||||
|
||||
### Set folded quote style
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. style="folded"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: >-
|
||||
cat
|
||||
b: >-
|
||||
5
|
||||
c: >-
|
||||
3.2
|
||||
e: >-
|
||||
true
|
||||
```
|
||||
|
||||
### Set flow quote style
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. style="flow"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
{a: cat, b: 5, c: 3.2, e: true}
|
||||
```
|
||||
|
||||
### Set empty (default) quote style
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. style=""' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
|
||||
### Read style
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. | style' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
45
pkg/yqlib/doc/Tag Operator.md
Normal file
45
pkg/yqlib/doc/Tag Operator.md
Normal file
@@ -0,0 +1,45 @@
|
||||
The tag operator can be used to get or set the tag of ndoes (e.g. `!!str`, `!!int`, `!!bool`).
|
||||
## Examples
|
||||
### Get tag
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
f: []
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.. | tag' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
!!map
|
||||
!!str
|
||||
!!int
|
||||
!!float
|
||||
!!bool
|
||||
!!seq
|
||||
```
|
||||
|
||||
### Convert numbers to strings
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: cat
|
||||
b: 5
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '(.. | select(tag == "!!int")) tag= "!!str"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: cat
|
||||
b: "5"
|
||||
c: 3.2
|
||||
e: true
|
||||
```
|
||||
|
||||
353
pkg/yqlib/doc/Traverse Operator.md
Normal file
353
pkg/yqlib/doc/Traverse Operator.md
Normal file
@@ -0,0 +1,353 @@
|
||||
This is the simples (and perhaps most used) operator, it is used to navigate deeply into yaml structurse.
|
||||
## Examples
|
||||
### Simple map navigation
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
b: apple
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
b: apple
|
||||
```
|
||||
|
||||
### Splat
|
||||
Often used to pipe children into other operators
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- b: apple
|
||||
- c: banana
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.[]' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
b: apple
|
||||
c: banana
|
||||
```
|
||||
|
||||
### Children don't exist
|
||||
Nodes are added dynamically while traversing
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
c: banana
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a.b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
null
|
||||
```
|
||||
|
||||
### Wildcard matching
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
cat: apple
|
||||
mad: things
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a."*a*"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
apple
|
||||
things
|
||||
```
|
||||
|
||||
### Aliases
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: &cat
|
||||
c: frog
|
||||
b: *cat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
*cat
|
||||
```
|
||||
|
||||
### Traversing aliases with splat
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: &cat
|
||||
c: frog
|
||||
b: *cat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.b.[]' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
frog
|
||||
```
|
||||
|
||||
### Traversing aliases explicitly
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: &cat
|
||||
c: frog
|
||||
b: *cat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.b.c' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
frog
|
||||
```
|
||||
|
||||
### Traversing arrays by index
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '[0]' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
1
|
||||
```
|
||||
|
||||
### Maps with numeric keys
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
2: cat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '[2]' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
cat
|
||||
```
|
||||
|
||||
### Maps with non existing numeric keys
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: b
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '[0]' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
null
|
||||
```
|
||||
|
||||
### Traversing merge anchors
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
!!merge <<:
|
||||
- *foo
|
||||
- *bar
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.foobar.a' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
foo_a
|
||||
```
|
||||
|
||||
### Traversing merge anchors with override
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
!!merge <<:
|
||||
- *foo
|
||||
- *bar
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.foobar.c' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
foo_c
|
||||
```
|
||||
|
||||
### Traversing merge anchors with local override
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
!!merge <<:
|
||||
- *foo
|
||||
- *bar
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.foobar.thing' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
foobar_thing
|
||||
```
|
||||
|
||||
### Splatting merge anchors
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
!!merge <<:
|
||||
- *foo
|
||||
- *bar
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.foobar.[]' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
foo_c
|
||||
foo_a
|
||||
foobar_thing
|
||||
```
|
||||
|
||||
### Traversing merge anchor lists
|
||||
Note that the later merge anchors override previous
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
!!merge <<:
|
||||
- *foo
|
||||
- *bar
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.foobarList.thing' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
bar_thing
|
||||
```
|
||||
|
||||
### Splatting merge anchor lists
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
foo: &foo
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar: &bar
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: foobarList_b
|
||||
!!merge <<:
|
||||
- *foo
|
||||
- *bar
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foobar_c
|
||||
!!merge <<: *foo
|
||||
thing: foobar_thing
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.foobarList.[]' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
bar_b
|
||||
foo_a
|
||||
bar_thing
|
||||
foobarList_c
|
||||
```
|
||||
|
||||
31
pkg/yqlib/doc/Union Operator.md
Normal file
31
pkg/yqlib/doc/Union Operator.md
Normal file
@@ -0,0 +1,31 @@
|
||||
This operator is used to combine different results together.
|
||||
## Examples
|
||||
### Combine scalars
|
||||
Running
|
||||
```bash
|
||||
yq eval --null-input '1, true, "cat"'
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
1
|
||||
true
|
||||
cat
|
||||
```
|
||||
|
||||
### Combine selected paths
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: fieldA
|
||||
b: fieldB
|
||||
c: fieldC
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a, .c' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
fieldA
|
||||
fieldC
|
||||
```
|
||||
|
||||
7
pkg/yqlib/doc/headers/Assign Operator.md
Normal file
7
pkg/yqlib/doc/headers/Assign Operator.md
Normal file
@@ -0,0 +1,7 @@
|
||||
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.
|
||||
4
pkg/yqlib/doc/headers/Collect into Array.md
Normal file
4
pkg/yqlib/doc/headers/Collect into Array.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Collect into Array
|
||||
|
||||
This creates an array using the expression between the square brackets.
|
||||
|
||||
1
pkg/yqlib/doc/headers/Collect into Object.md
Normal file
1
pkg/yqlib/doc/headers/Collect into Object.md
Normal file
@@ -0,0 +1 @@
|
||||
This is used to construct objects (or maps). This can be used against existing yaml, or to create fresh yaml documents.
|
||||
1
pkg/yqlib/doc/headers/Comments Operator.md
Normal file
1
pkg/yqlib/doc/headers/Comments Operator.md
Normal file
@@ -0,0 +1 @@
|
||||
Use these comment operators to set or retrieve comments.
|
||||
1
pkg/yqlib/doc/headers/Delete Operator.md
Normal file
1
pkg/yqlib/doc/headers/Delete Operator.md
Normal file
@@ -0,0 +1 @@
|
||||
Deletes matching entries in maps or arrays.
|
||||
14
pkg/yqlib/doc/headers/Equals Operator.md
Normal file
14
pkg/yqlib/doc/headers/Equals Operator.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## Equals Operator
|
||||
|
||||
This is a boolean operator that will return ```true``` if the LHS is equal to the RHS and ``false`` otherwise.
|
||||
|
||||
```
|
||||
.a == .b
|
||||
```
|
||||
|
||||
It is most often used with the select operator to find particular nodes:
|
||||
|
||||
```
|
||||
select(.a == .b)
|
||||
```
|
||||
|
||||
1
pkg/yqlib/doc/headers/Explode Operator.md
Normal file
1
pkg/yqlib/doc/headers/Explode Operator.md
Normal file
@@ -0,0 +1 @@
|
||||
Explodes (or dereferences) aliases and anchors.
|
||||
5
pkg/yqlib/doc/headers/Multiply Operator.md
Normal file
5
pkg/yqlib/doc/headers/Multiply Operator.md
Normal file
@@ -0,0 +1,5 @@
|
||||
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.
|
||||
|
||||
Upcoming versions of `yq` will add support for other types of multiplication (numbers, strings).
|
||||
|
||||
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
||||
1
pkg/yqlib/doc/headers/Not Operator.md
Normal file
1
pkg/yqlib/doc/headers/Not Operator.md
Normal file
@@ -0,0 +1 @@
|
||||
This is a boolean operator and will return `true` when given a `false` value (including null), and `false` otherwise.
|
||||
5
pkg/yqlib/doc/headers/Recursive Descent Operator.md
Normal file
5
pkg/yqlib/doc/headers/Recursive Descent Operator.md
Normal file
@@ -0,0 +1,5 @@
|
||||
This operator recursively matches all children nodes given of a particular element, including that node itself. This is most often used to apply a filter recursively against all matches, for instance to set the `style` of all nodes in a yaml doc:
|
||||
|
||||
```bash
|
||||
yq eval '.. style= "flow"' file.yaml
|
||||
```
|
||||
1
pkg/yqlib/doc/headers/Select Operator.md
Normal file
1
pkg/yqlib/doc/headers/Select Operator.md
Normal file
@@ -0,0 +1 @@
|
||||
Select is used to filter arrays and maps by a boolean expression.
|
||||
1
pkg/yqlib/doc/headers/Style Operator.md
Normal file
1
pkg/yqlib/doc/headers/Style Operator.md
Normal file
@@ -0,0 +1 @@
|
||||
The style operator can be used to get or set the style of nodes (e.g. string style, yaml style)
|
||||
1
pkg/yqlib/doc/headers/Tag Operator.md
Normal file
1
pkg/yqlib/doc/headers/Tag Operator.md
Normal file
@@ -0,0 +1 @@
|
||||
The tag operator can be used to get or set the tag of ndoes (e.g. `!!str`, `!!int`, `!!bool`).
|
||||
1
pkg/yqlib/doc/headers/Traverse Operator.md
Normal file
1
pkg/yqlib/doc/headers/Traverse Operator.md
Normal file
@@ -0,0 +1 @@
|
||||
This is the simples (and perhaps most used) operator, it is used to navigate deeply into yaml structurse.
|
||||
1
pkg/yqlib/doc/headers/Union Operator.md
Normal file
1
pkg/yqlib/doc/headers/Union Operator.md
Normal file
@@ -0,0 +1 @@
|
||||
This operator is used to combine different results together.
|
||||
47
pkg/yqlib/encoder_test.go
Normal file
47
pkg/yqlib/encoder_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mikefarah/yq/v4/test"
|
||||
)
|
||||
|
||||
var sampleYaml = `zabbix: winner
|
||||
apple: great
|
||||
banana:
|
||||
- {cobra: kai, angus: bob}
|
||||
`
|
||||
|
||||
var expectedJson = `{
|
||||
"zabbix": "winner",
|
||||
"apple": "great",
|
||||
"banana": [
|
||||
{
|
||||
"cobra": "kai",
|
||||
"angus": "bob"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
func TestJsonEncoderPreservesObjectOrder(t *testing.T) {
|
||||
var output bytes.Buffer
|
||||
writer := bufio.NewWriter(&output)
|
||||
|
||||
var jsonEncoder = NewJsonEncoder(writer, 2)
|
||||
inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
node := inputs.Front().Value.(*CandidateNode).Node
|
||||
err = jsonEncoder.Encode(node)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
writer.Flush()
|
||||
test.AssertResult(t, expectedJson, output.String())
|
||||
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
"gopkg.in/yaml.v3"
|
||||
logging "gopkg.in/op/go-logging.v1"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type OperationType struct {
|
||||
@@ -17,16 +17,12 @@ type OperationType struct {
|
||||
}
|
||||
|
||||
// operators TODO:
|
||||
// - generator doc from operator tests
|
||||
// - slurp - stdin, read in sequence, vs read all
|
||||
// - write in place
|
||||
// - get path operator (like doc index)
|
||||
// - get file index op (like doc index)
|
||||
// - get file name op (like doc index)
|
||||
// - write in place
|
||||
// - mergeAppend (merges and appends arrays)
|
||||
// - mergeEmpty (sets only if the document is empty, do I do that now?)
|
||||
// - updateTag - not recursive
|
||||
// - get tag (tag)
|
||||
// - compare ??
|
||||
// - validate ??
|
||||
// - exists
|
||||
@@ -37,8 +33,10 @@ var And = &OperationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: AndOp
|
||||
var Union = &OperationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: UnionOperator}
|
||||
|
||||
var Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignUpdateOperator}
|
||||
|
||||
var AssignAttributes = &OperationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: AssignAttributesOperator}
|
||||
var AssignStyle = &OperationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: AssignStyleOperator}
|
||||
var AssignTag = &OperationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: AssignTagOperator}
|
||||
var AssignComment = &OperationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: AssignCommentsOperator}
|
||||
|
||||
var Multiply = &OperationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 40, Handler: MultiplyOperator}
|
||||
@@ -50,6 +48,7 @@ var Pipe = &OperationType{Type: "PIPE", NumArgs: 2, Precedence: 45, Handler: Pip
|
||||
var Length = &OperationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: LengthOperator}
|
||||
var Collect = &OperationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: CollectOperator}
|
||||
var GetStyle = &OperationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: GetStyleOperator}
|
||||
var GetTag = &OperationType{Type: "GET_TAG", NumArgs: 0, Precedence: 50, Handler: GetTagOperator}
|
||||
var GetComment = &OperationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50, Handler: GetCommentsOperator}
|
||||
var GetDocumentIndex = &OperationType{Type: "GET_DOCUMENT_INDEX", NumArgs: 0, Precedence: 50, Handler: GetDocumentIndexOperator}
|
||||
|
||||
@@ -66,11 +65,8 @@ var Empty = &OperationType{Type: "EMPTY", NumArgs: 50, Handler: EmptyOperator}
|
||||
|
||||
var RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator}
|
||||
|
||||
// not sure yet
|
||||
|
||||
var Select = &OperationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: SelectOperator}
|
||||
|
||||
var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 2, Precedence: 40, Handler: DeleteChildOperator}
|
||||
var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: DeleteChildOperator}
|
||||
|
||||
// var Exists = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35}
|
||||
// filters matches if they have the existing path
|
||||
|
||||
@@ -2,15 +2,28 @@ package yqlib
|
||||
|
||||
import "container/list"
|
||||
|
||||
type AssignOpPreferences struct {
|
||||
UpdateAssign bool
|
||||
}
|
||||
|
||||
func AssignUpdateOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
|
||||
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
preferences := pathNode.Operation.Preferences.(*AssignOpPreferences)
|
||||
|
||||
var rhs *list.List
|
||||
if !preferences.UpdateAssign {
|
||||
rhs, err = d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
|
||||
}
|
||||
|
||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
||||
candidate := el.Value.(*CandidateNode)
|
||||
|
||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
|
||||
if preferences.UpdateAssign {
|
||||
rhs, err = d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
120
pkg/yqlib/operator_assign_test.go
Normal file
120
pkg/yqlib/operator_assign_test.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var assignOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Update parent to be the child value",
|
||||
document: `{a: {b: {g: foof}}}`,
|
||||
expression: `.a |= .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {g: foof}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Update to be the sibling value",
|
||||
document: `{a: {b: child}, b: sibling}`,
|
||||
expression: `.a = .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: sibling, b: sibling}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Updated multiple paths",
|
||||
document: `{a: fieldA, b: fieldB, c: fieldC}`,
|
||||
expression: `(.a, .c) |= "potatoe"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: potatoe, b: fieldB, c: potatoe}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Update string value",
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a.b = "frog"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: frog}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Update string value via |=",
|
||||
subdescription: "Note there is no difference between `=` and `|=` when the RHS is a scalar",
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a.b |= "frog"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: frog}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a.b | (. |= "frog")`,
|
||||
expected: []string{
|
||||
"D0, P[a b], (!!str)::frog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a.b |= 5`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: 5}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a.b |= 3.142`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: 3.142}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Update selected results",
|
||||
document: `{a: {b: apple, c: cactus}}`,
|
||||
expression: `.a[] | select(. == "apple") |= "frog"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: frog, c: cactus}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Update array values",
|
||||
document: `[candy, apple, sandy]`,
|
||||
expression: `.[] | select(. == "*andy") |= "bogs"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::[bogs, apple, bogs]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Update empty object",
|
||||
document: `{}`,
|
||||
expression: `.a.b |= "bogs"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: bogs}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Update empty object and array",
|
||||
document: `{}`,
|
||||
expression: `.a.b[0] |= "bogs"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: [bogs]}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{}`,
|
||||
expression: `.a.b[1].c |= "bogs"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: [null, {c: bogs}]}}\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestAssignOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range assignOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Assign Operator", assignOperatorScenarios)
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var assignOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a.b |= "frog"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: frog}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a.b | (. |= "frog")`,
|
||||
expected: []string{
|
||||
"D0, P[a b], (!!str)::frog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a.b |= 5`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: 5}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a.b |= 3.142`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: 3.142}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: {b: {g: foof}}}`,
|
||||
expression: `.a |= .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {g: foof}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: {b: apple, c: cactus}}`,
|
||||
expression: `.a[] | select(. == "apple") |= "frog"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: frog, c: cactus}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `[candy, apple, sandy]`,
|
||||
expression: `.[] | select(. == "*andy") |= "bogs"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::[bogs, apple, bogs]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{}`,
|
||||
expression: `.a.b |= "bogs"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: bogs}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{}`,
|
||||
expression: `.a.b[0] |= "bogs"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: [bogs]}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{}`,
|
||||
expression: `.a.b[1].c |= "bogs"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: {b: [null, {c: bogs}]}}\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestAssignOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range assignOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
}
|
||||
@@ -6,18 +6,21 @@ import (
|
||||
|
||||
var collectObjectOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: ``,
|
||||
expression: `{}`,
|
||||
expected: []string{},
|
||||
description: `Collect empty object`,
|
||||
document: ``,
|
||||
expression: `{}`,
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
document: "{name: Mike}\n",
|
||||
expression: `{"wrap": .}`,
|
||||
description: `Wrap (prefix) existing object`,
|
||||
document: "{name: Mike}\n",
|
||||
expression: `{"wrap": .}`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::wrap: {name: Mike}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: "{name: Mike}\n---\n{name: Bob}",
|
||||
expression: `{"wrap": .}`,
|
||||
expected: []string{
|
||||
@@ -26,6 +29,7 @@ var collectObjectOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{name: Mike, age: 32}`,
|
||||
expression: `{.name: .age}`,
|
||||
expected: []string{
|
||||
@@ -33,16 +37,19 @@ var collectObjectOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{name: Mike, pets: [cat, dog]}`,
|
||||
expression: `{.name: .pets[]}`,
|
||||
description: `Using splat to create multiple objects`,
|
||||
document: `{name: Mike, pets: [cat, dog]}`,
|
||||
expression: `{.name: .pets[]}`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::Mike: cat\n",
|
||||
"D0, P[], (!!map)::Mike: dog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: "{name: Mike, pets: [cat, dog]}\n---\n{name: Rosey, pets: [monkey, sheep]}",
|
||||
expression: `{.name: .pets[]}`,
|
||||
description: `Working with multiple documents`,
|
||||
dontFormatInputForDoc: false,
|
||||
document: "{name: Mike, pets: [cat, dog]}\n---\n{name: Rosey, pets: [monkey, sheep]}",
|
||||
expression: `{.name: .pets[]}`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::Mike: cat\n",
|
||||
"D0, P[], (!!map)::Mike: dog\n",
|
||||
@@ -51,6 +58,7 @@ var collectObjectOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{name: Mike, pets: [cat, dog], food: [hotdog, burger]}`,
|
||||
expression: `{.name: .pets[], "f":.food[]}`,
|
||||
expected: []string{
|
||||
@@ -61,6 +69,7 @@ var collectObjectOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{name: Mike, pets: {cows: [apl, bba]}}`,
|
||||
expression: `{"a":.name, "b":.pets}`,
|
||||
expected: []string{
|
||||
@@ -70,13 +79,15 @@ b: {cows: [apl, bba]}
|
||||
},
|
||||
},
|
||||
{
|
||||
document: ``,
|
||||
expression: `{"wrap": "frog"}`,
|
||||
description: "Creating yaml from scratch",
|
||||
document: ``,
|
||||
expression: `{"wrap": "frog"}`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::wrap: frog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{name: Mike}`,
|
||||
expression: `{"wrap": .}`,
|
||||
expected: []string{
|
||||
@@ -84,6 +95,7 @@ b: {cows: [apl, bba]}
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{name: Mike}`,
|
||||
expression: `{"wrap": {"further": .}} | (.. style= "flow")`,
|
||||
expected: []string{
|
||||
|
||||
@@ -6,43 +6,54 @@ import (
|
||||
|
||||
var collectOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: ``,
|
||||
expression: `[]`,
|
||||
expected: []string{},
|
||||
description: "Collect empty",
|
||||
document: ``,
|
||||
expression: `[]`,
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
document: ``,
|
||||
expression: `["cat"]`,
|
||||
description: "Collect single",
|
||||
document: ``,
|
||||
expression: `["cat"]`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::- cat\n",
|
||||
},
|
||||
}, {
|
||||
document: ``,
|
||||
skipDoc: true,
|
||||
expression: `[true]`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::- true\n",
|
||||
},
|
||||
}, {
|
||||
document: ``,
|
||||
expression: `["cat", "dog"]`,
|
||||
},
|
||||
{
|
||||
description: "Collect many",
|
||||
document: `{a: cat, b: dog}`,
|
||||
expression: `[.a, .b]`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::- cat\n- dog\n",
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
document: ``,
|
||||
skipDoc: true,
|
||||
expression: `1 | collect`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::- 1\n",
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
document: `[1,2,3]`,
|
||||
skipDoc: true,
|
||||
expression: `[.[]]`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::- 1\n- 2\n- 3\n",
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
document: `a: {b: [1,2,3]}`,
|
||||
expression: `[.a.b[]]`,
|
||||
skipDoc: true,
|
||||
expected: []string{
|
||||
"D0, P[a b], (!!seq)::- 1\n- 2\n- 3\n",
|
||||
},
|
||||
|
||||
@@ -3,19 +3,15 @@ package yqlib
|
||||
import (
|
||||
"container/list"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
|
||||
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// for each lhs, splat the node,
|
||||
// the intersect it against the rhs expression
|
||||
// recreate the contents using only the intersection result.
|
||||
|
||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
||||
candidate := el.Value.(*CandidateNode)
|
||||
elMap := list.New()
|
||||
elMap.PushBack(candidate)
|
||||
@@ -25,20 +21,22 @@ func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNod
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if candidate.Node.Kind == yaml.SequenceNode {
|
||||
realNode := UnwrapDoc(candidate.Node)
|
||||
|
||||
if realNode.Kind == yaml.SequenceNode {
|
||||
deleteFromArray(candidate, nodesToDelete)
|
||||
} else if candidate.Node.Kind == yaml.MappingNode {
|
||||
} else if realNode.Kind == yaml.MappingNode {
|
||||
deleteFromMap(candidate, nodesToDelete)
|
||||
} else {
|
||||
log.Debug("Cannot delete from node that's not a map or array %v", NodeToString(candidate))
|
||||
}
|
||||
}
|
||||
return lhs, nil
|
||||
return matchingNodes, nil
|
||||
}
|
||||
|
||||
func deleteFromMap(candidate *CandidateNode, nodesToDelete *list.List) {
|
||||
log.Debug("deleteFromMap")
|
||||
node := candidate.Node
|
||||
node := UnwrapDoc(candidate.Node)
|
||||
contents := node.Content
|
||||
newContents := make([]*yaml.Node, 0)
|
||||
|
||||
@@ -51,8 +49,13 @@ func deleteFromMap(candidate *CandidateNode, nodesToDelete *list.List) {
|
||||
Document: candidate.Document,
|
||||
Path: append(candidate.Path, key.Value),
|
||||
}
|
||||
// _, shouldDelete := nodesToDelete.Get(childCandidate.GetKey())
|
||||
shouldDelete := true
|
||||
|
||||
shouldDelete := false
|
||||
for el := nodesToDelete.Front(); el != nil && !shouldDelete; el = el.Next() {
|
||||
if el.Value.(*CandidateNode).GetKey() == childCandidate.GetKey() {
|
||||
shouldDelete = true
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("shouldDelete %v ? %v", childCandidate.GetKey(), shouldDelete)
|
||||
|
||||
@@ -65,21 +68,26 @@ func deleteFromMap(candidate *CandidateNode, nodesToDelete *list.List) {
|
||||
|
||||
func deleteFromArray(candidate *CandidateNode, nodesToDelete *list.List) {
|
||||
log.Debug("deleteFromArray")
|
||||
node := candidate.Node
|
||||
node := UnwrapDoc(candidate.Node)
|
||||
contents := node.Content
|
||||
newContents := make([]*yaml.Node, 0)
|
||||
|
||||
for index := 0; index < len(contents); index = index + 1 {
|
||||
value := contents[index]
|
||||
|
||||
// childCandidate := &CandidateNode{
|
||||
// Node: value,
|
||||
// Document: candidate.Document,
|
||||
// Path: append(candidate.Path, index),
|
||||
// }
|
||||
childCandidate := &CandidateNode{
|
||||
Node: value,
|
||||
Document: candidate.Document,
|
||||
Path: append(candidate.Path, index),
|
||||
}
|
||||
|
||||
shouldDelete := false
|
||||
for el := nodesToDelete.Front(); el != nil && !shouldDelete; el = el.Next() {
|
||||
if el.Value.(*CandidateNode).GetKey() == childCandidate.GetKey() {
|
||||
shouldDelete = true
|
||||
}
|
||||
}
|
||||
|
||||
// _, shouldDelete := nodesToDelete.Get(childCandidate.GetKey())
|
||||
shouldDelete := true
|
||||
if !shouldDelete {
|
||||
newContents = append(newContents, value)
|
||||
}
|
||||
|
||||
47
pkg/yqlib/operator_delete_test.go
Normal file
47
pkg/yqlib/operator_delete_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var deleteOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Delete entry in map",
|
||||
document: `{a: cat, b: dog}`,
|
||||
expression: `del(.b)`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: cat}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Delete entry in array",
|
||||
document: `[1,2,3]`,
|
||||
expression: `del(.[1])`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::[1, 3]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Delete no matches",
|
||||
document: `{a: cat, b: dog}`,
|
||||
expression: `del(.c)`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: cat, b: dog}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Delete matching entries",
|
||||
document: `{a: cat, b: dog, c: bat}`,
|
||||
expression: `del( .[] | select(. == "*at") )`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{b: dog}\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestDeleteOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range deleteOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Delete Operator", deleteOperatorScenarios)
|
||||
}
|
||||
@@ -6,22 +6,25 @@ import (
|
||||
|
||||
var equalsOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: `[cat,goat,dog]`,
|
||||
expression: `.[] | (. == "*at")`,
|
||||
description: "Match string",
|
||||
document: `[cat,goat,dog]`,
|
||||
expression: `.[] | (. == "*at")`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!bool)::true\n",
|
||||
"D0, P[1], (!!bool)::true\n",
|
||||
"D0, P[2], (!!bool)::false\n",
|
||||
},
|
||||
}, {
|
||||
document: `[3, 4, 5]`,
|
||||
expression: `.[] | (. == 4)`,
|
||||
description: "Match number",
|
||||
document: `[3, 4, 5]`,
|
||||
expression: `.[] | (. == 4)`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!bool)::false\n",
|
||||
"D0, P[1], (!!bool)::true\n",
|
||||
"D0, P[2], (!!bool)::false\n",
|
||||
},
|
||||
}, {
|
||||
skipDoc: true,
|
||||
document: `a: { cat: {b: apple, c: whatever}, pat: {b: banana} }`,
|
||||
expression: `.a | (.[].b == "apple")`,
|
||||
expected: []string{
|
||||
@@ -30,6 +33,7 @@ var equalsOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: ``,
|
||||
expression: `null == null`,
|
||||
expected: []string{
|
||||
@@ -37,8 +41,9 @@ var equalsOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: ``,
|
||||
expression: `null == ~`,
|
||||
description: "Match nulls",
|
||||
document: ``,
|
||||
expression: `null == ~`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::true\n",
|
||||
},
|
||||
|
||||
@@ -6,27 +6,54 @@ import (
|
||||
|
||||
var explodeTest = []expressionScenario{
|
||||
{
|
||||
document: `{a: mike}`,
|
||||
expression: `explode(.a)`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: mike}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{f : {a: &a cat, b: *a}}`,
|
||||
expression: `explode(.f)`,
|
||||
description: "Explode alias and anchor",
|
||||
document: `{f : {a: &a cat, b: *a}}`,
|
||||
expression: `explode(.f)`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{f: {a: cat, b: cat}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{f : {a: &a cat, *a: b}}`,
|
||||
expression: `explode(.f)`,
|
||||
description: "Explode with no aliases or anchors",
|
||||
document: `a: mike`,
|
||||
expression: `explode(.a)`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::a: mike\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Explode with alias keys",
|
||||
document: `{f : {a: &a cat, *a: b}}`,
|
||||
expression: `explode(.f)`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{f: {a: cat, cat: b}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Explode with merge anchors",
|
||||
document: mergeDocSample,
|
||||
expression: `explode(.)`,
|
||||
expected: []string{`D0, P[], (doc)::foo:
|
||||
a: foo_a
|
||||
thing: foo_thing
|
||||
c: foo_c
|
||||
bar:
|
||||
b: bar_b
|
||||
thing: bar_thing
|
||||
c: bar_c
|
||||
foobarList:
|
||||
b: bar_b
|
||||
a: foo_a
|
||||
thing: bar_thing
|
||||
c: foobarList_c
|
||||
foobar:
|
||||
c: foo_c
|
||||
a: foo_a
|
||||
thing: foobar_thing
|
||||
`},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foo* | explode(.) | (. style="flow")`,
|
||||
expected: []string{
|
||||
@@ -36,6 +63,7 @@ var explodeTest = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foo* | explode(explode(.)) | (. style="flow")`,
|
||||
expected: []string{
|
||||
@@ -45,6 +73,7 @@ var explodeTest = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{f : {a: &a cat, b: &b {f: *a}, *a: *b}}`,
|
||||
expression: `explode(.f)`,
|
||||
expected: []string{
|
||||
@@ -57,4 +86,5 @@ func TestExplodeOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range explodeTest {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Explode Operator", explodeTest)
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *Candid
|
||||
assignmentOp := &Operation{OperationType: AssignAttributes}
|
||||
if rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode {
|
||||
assignmentOp.OperationType = Assign
|
||||
assignmentOp.Preferences = &AssignOpPreferences{false}
|
||||
}
|
||||
rhsOp := &Operation{OperationType: ValueOp, CandidateNode: rhs}
|
||||
|
||||
|
||||
@@ -22,11 +22,19 @@ var multiplyOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Merge objects together",
|
||||
document: `{a: {also: me}, b: {also: {g: wizz}}}`,
|
||||
description: "Merge objects together, returning merged result only",
|
||||
document: `{a: {field: me, fieldA: cat}, b: {field: {g: wizz}, fieldB: dog}}`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
"D0, P[a], (!!map)::{field: {g: wizz}, fieldA: cat, fieldB: dog}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Merge objects together, returning parent object",
|
||||
document: `{a: {field: me, fieldA: cat}, b: {field: {g: wizz}, fieldB: dog}}`,
|
||||
expression: `. * {"a":.b}`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n",
|
||||
"D0, P[], (!!map)::{a: {field: {g: wizz}, fieldA: cat, fieldB: dog}, b: {field: {g: wizz}, fieldB: dog}}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -62,7 +70,8 @@ var multiplyOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Merge keeps style of LHS",
|
||||
description: "Merge keeps style of LHS",
|
||||
dontFormatInputForDoc: true,
|
||||
document: `a: {things: great}
|
||||
b:
|
||||
also: "me"
|
||||
@@ -121,5 +130,5 @@ func TestMultiplyOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range multiplyOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Mulitply Operator", multiplyOperatorScenarios)
|
||||
documentScenarios(t, "Multiply Operator", multiplyOperatorScenarios)
|
||||
}
|
||||
|
||||
@@ -5,52 +5,61 @@ import (
|
||||
)
|
||||
|
||||
var notOperatorScenarios = []expressionScenario{
|
||||
// {
|
||||
// document: `cat`,
|
||||
// expression: `. | not`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!bool)::false\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// document: `1`,
|
||||
// expression: `. | not`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!bool)::false\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// document: `0`,
|
||||
// expression: `. | not`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!bool)::false\n",
|
||||
// },
|
||||
// },
|
||||
{
|
||||
document: `~`,
|
||||
expression: `. | not`,
|
||||
description: "Not true is false",
|
||||
expression: `true | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Not false is true",
|
||||
expression: `false | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::true\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "String values considered to be true",
|
||||
expression: `"cat" | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Empty string value considered to be true",
|
||||
expression: `"" | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Numbers are considered to be true",
|
||||
expression: `1 | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Zero is considered to be true",
|
||||
expression: `0 | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
description: "Null is considered to be false",
|
||||
expression: `~ | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::true\n",
|
||||
},
|
||||
},
|
||||
// {
|
||||
// document: `false`,
|
||||
// expression: `. | not`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!bool)::true\n",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// document: `true`,
|
||||
// expression: `. | not`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!bool)::false\n",
|
||||
// },
|
||||
// },
|
||||
}
|
||||
|
||||
func TestNotOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range notOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Not Operator", notOperatorScenarios)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `cat`,
|
||||
expression: `..`,
|
||||
expected: []string{
|
||||
@@ -13,6 +14,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: frog}`,
|
||||
expression: `..`,
|
||||
expected: []string{
|
||||
@@ -21,8 +23,9 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `..`,
|
||||
description: "Map",
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `..`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {b: apple}}\n",
|
||||
"D0, P[a], (!!map)::{b: apple}\n",
|
||||
@@ -30,8 +33,9 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `[1,2,3]`,
|
||||
expression: `..`,
|
||||
description: "Array",
|
||||
document: `[1,2,3]`,
|
||||
expression: `..`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::[1, 2, 3]\n",
|
||||
"D0, P[0], (!!int)::1\n",
|
||||
@@ -40,8 +44,9 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `[{a: cat},2,true]`,
|
||||
expression: `..`,
|
||||
description: "Array of maps",
|
||||
document: `[{a: cat},2,true]`,
|
||||
expression: `..`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::[{a: cat}, 2, true]\n",
|
||||
"D0, P[0], (!!map)::{a: cat}\n",
|
||||
@@ -51,8 +56,9 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
expression: `..`,
|
||||
description: "Aliases are not traversed",
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
expression: `..`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: &cat {c: frog}, b: *cat}\n",
|
||||
"D0, P[a], (!!map)::&cat {c: frog}\n",
|
||||
@@ -61,8 +67,9 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar | ..`,
|
||||
description: "Merge docs are not traversed",
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar | ..`,
|
||||
expected: []string{
|
||||
"D0, P[foobar], (!!map)::c: foobar_c\n!!merge <<: *foo\nthing: foobar_thing\n",
|
||||
"D0, P[foobar c], (!!str)::foobar_c\n",
|
||||
@@ -71,6 +78,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList | ..`,
|
||||
expected: []string{
|
||||
@@ -88,4 +96,5 @@ func TestRecursiveDescentOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range recursiveDescentOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Recursive Descent Operator", recursiveDescentOperatorScenarios)
|
||||
}
|
||||
|
||||
@@ -6,36 +6,47 @@ import (
|
||||
|
||||
var selectOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: `[cat,goat,dog]`,
|
||||
expression: `.[] | select(. == "*at")`,
|
||||
description: "Select elements from array",
|
||||
document: `[cat,goat,dog]`,
|
||||
expression: `.[] | select(. == "*at")`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!str)::cat\n",
|
||||
"D0, P[1], (!!str)::goat\n",
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `[hot, fot, dog]`,
|
||||
expression: `.[] | select(. == "*at")`,
|
||||
expected: []string{},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `a: [cat,goat,dog]`,
|
||||
expression: `.a[] | select(. == "*at")`,
|
||||
expected: []string{
|
||||
"D0, P[a 0], (!!str)::cat\n",
|
||||
"D0, P[a 1], (!!str)::goat\n"},
|
||||
}, {
|
||||
document: `a: { things: cat, bob: goat, horse: dog }`,
|
||||
expression: `.a[] | select(. == "*at")`,
|
||||
},
|
||||
{
|
||||
description: "Select and update matching values in map",
|
||||
document: `a: { things: cat, bob: goat, horse: dog }`,
|
||||
expression: `(.a[] | select(. == "*at")) |= "rabbit"`,
|
||||
expected: []string{
|
||||
"D0, P[a things], (!!str)::cat\n",
|
||||
"D0, P[a bob], (!!str)::goat\n"},
|
||||
}, {
|
||||
"D0, P[], (doc)::a: {things: rabbit, bob: rabbit, horse: dog}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `a: { things: {include: true}, notMe: {include: false}, andMe: {include: fold} }`,
|
||||
expression: `.a[] | select(.include)`,
|
||||
expected: []string{
|
||||
"D0, P[a things], (!!map)::{include: true}\n",
|
||||
"D0, P[a andMe], (!!map)::{include: fold}\n",
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `[cat,~,dog]`,
|
||||
expression: `.[] | select(. == ~)`,
|
||||
expected: []string{
|
||||
@@ -48,4 +59,5 @@ func TestSelectOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range selectOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Select Operator", selectOperatorScenarios)
|
||||
}
|
||||
|
||||
114
pkg/yqlib/operator_style_test.go
Normal file
114
pkg/yqlib/operator_style_test.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var styleOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Set tagged style",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `.. style="tagged"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::!!map\na: !!str cat\nb: !!int 5\nc: !!float 3.2\ne: !!bool true\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Set double quote style",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `.. style="double"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::a: \"cat\"\nb: \"5\"\nc: \"3.2\"\ne: \"true\"\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Set single quote style",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `.. style="single"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::a: 'cat'\nb: '5'\nc: '3.2'\ne: 'true'\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Set literal quote style",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `.. style="literal"`,
|
||||
expected: []string{
|
||||
`D0, P[], (!!map)::a: |-
|
||||
cat
|
||||
b: |-
|
||||
5
|
||||
c: |-
|
||||
3.2
|
||||
e: |-
|
||||
true
|
||||
`,
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Set folded quote style",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `.. style="folded"`,
|
||||
expected: []string{
|
||||
`D0, P[], (!!map)::a: >-
|
||||
cat
|
||||
b: >-
|
||||
5
|
||||
c: >-
|
||||
3.2
|
||||
e: >-
|
||||
true
|
||||
`,
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Set flow quote style",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `.. style="flow"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: cat, b: 5, c: 3.2, e: true}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Set empty (default) quote style",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `.. style=""`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::a: cat\nb: 5\nc: 3.2\ne: true\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: cat, b: double}`,
|
||||
expression: `.a style=.b`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: \"cat\", b: double}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Read style",
|
||||
document: `{a: "cat", b: 'thing'}`,
|
||||
expression: `.. | style`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::flow\n",
|
||||
"D0, P[a], (!!str)::double\n",
|
||||
"D0, P[b], (!!str)::single\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `a: cat`,
|
||||
expression: `.. | style`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::\"\"\n",
|
||||
"D0, P[a], (!!str)::\"\"\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestStyleOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range styleOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Style Operator", styleOperatorScenarios)
|
||||
}
|
||||
51
pkg/yqlib/operator_tag.go
Normal file
51
pkg/yqlib/operator_tag.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func AssignTagOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
|
||||
|
||||
log.Debugf("AssignTagOperator: %v")
|
||||
|
||||
rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag := ""
|
||||
|
||||
if rhs.Front() != nil {
|
||||
tag = rhs.Front().Value.(*CandidateNode).Node.Value
|
||||
}
|
||||
|
||||
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
||||
candidate := el.Value.(*CandidateNode)
|
||||
log.Debugf("Setting tag of : %v", candidate.GetKey())
|
||||
candidate.Node.Tag = tag
|
||||
}
|
||||
|
||||
return matchingNodes, nil
|
||||
}
|
||||
|
||||
func GetTagOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
|
||||
log.Debugf("GetTagOperator")
|
||||
|
||||
var results = list.New()
|
||||
|
||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
||||
candidate := el.Value.(*CandidateNode)
|
||||
node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Node.Tag, Tag: "!!str"}
|
||||
lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path}
|
||||
results.PushBack(lengthCand)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
36
pkg/yqlib/operator_tag_test.go
Normal file
36
pkg/yqlib/operator_tag_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var tagOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Get tag",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true, f: []}`,
|
||||
expression: `.. | tag`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::'!!map'\n",
|
||||
"D0, P[a], (!!str)::'!!str'\n",
|
||||
"D0, P[b], (!!str)::'!!int'\n",
|
||||
"D0, P[c], (!!str)::'!!float'\n",
|
||||
"D0, P[e], (!!str)::'!!bool'\n",
|
||||
"D0, P[f], (!!str)::'!!seq'\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Convert numbers to strings",
|
||||
document: `{a: cat, b: 5, c: 3.2, e: true}`,
|
||||
expression: `(.. | select(tag == "!!int")) tag= "!!str"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: cat, b: \"5\", c: 3.2, e: true}\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestTagOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range tagOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Tag Operator", tagOperatorScenarios)
|
||||
}
|
||||
@@ -28,28 +28,34 @@ foobar:
|
||||
|
||||
var traversePathOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a`,
|
||||
description: "Simple map navigation",
|
||||
document: `{a: {b: apple}}`,
|
||||
expression: `.a`,
|
||||
expected: []string{
|
||||
"D0, P[a], (!!map)::{b: apple}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `[{b: apple}, {c: banana}]`,
|
||||
expression: `.[]`,
|
||||
description: "Splat",
|
||||
subdescription: "Often used to pipe children into other operators",
|
||||
document: `[{b: apple}, {c: banana}]`,
|
||||
expression: `.[]`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!map)::{b: apple}\n",
|
||||
"D0, P[1], (!!map)::{c: banana}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{}`,
|
||||
expression: `.a.b`,
|
||||
description: "Children don't exist",
|
||||
subdescription: "Nodes are added dynamically while traversing",
|
||||
document: `{c: banana}`,
|
||||
expression: `.a.b`,
|
||||
expected: []string{
|
||||
"D0, P[a b], (!!null)::null\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{}`,
|
||||
expression: `.[1].a`,
|
||||
expected: []string{
|
||||
@@ -57,6 +63,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{}`,
|
||||
expression: `.a.[1]`,
|
||||
expected: []string{
|
||||
@@ -64,14 +71,16 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: {cat: apple, mad: things}}`,
|
||||
expression: `.a."*a*"`,
|
||||
description: "Wildcard matching",
|
||||
document: `{a: {cat: apple, mad: things}}`,
|
||||
expression: `.a."*a*"`,
|
||||
expected: []string{
|
||||
"D0, P[a cat], (!!str)::apple\n",
|
||||
"D0, P[a mad], (!!str)::things\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: {cat: {b: 3}, mad: {b: 4}, fad: {c: t}}}`,
|
||||
expression: `.a."*a*".b`,
|
||||
expected: []string{
|
||||
@@ -81,6 +90,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: {cat: apple, mad: things}}`,
|
||||
expression: `.a | (.cat, .mad)`,
|
||||
expected: []string{
|
||||
@@ -89,6 +99,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: {cat: apple, mad: things}}`,
|
||||
expression: `.a | (.cat, .mad, .fad)`,
|
||||
expected: []string{
|
||||
@@ -98,6 +109,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `{a: {cat: apple, mad: things}}`,
|
||||
expression: `.a | (.cat, .mad, .fad) | select( (. == null) | not)`,
|
||||
expected: []string{
|
||||
@@ -106,40 +118,53 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
expression: `.b`,
|
||||
description: "Aliases",
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
expression: `.b`,
|
||||
expected: []string{
|
||||
"D0, P[b], (alias)::*cat\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
expression: `.b.[]`,
|
||||
description: "Traversing aliases with splat",
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
expression: `.b.[]`,
|
||||
expected: []string{
|
||||
"D0, P[b c], (!!str)::frog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
expression: `.b.c`,
|
||||
description: "Traversing aliases explicitly",
|
||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||
expression: `.b.c`,
|
||||
expected: []string{
|
||||
"D0, P[b c], (!!str)::frog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: `[1,2,3]`,
|
||||
expression: `.b`,
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
document: `[1,2,3]`,
|
||||
expression: `[0]`,
|
||||
description: "Traversing arrays by index",
|
||||
document: `[1,2,3]`,
|
||||
expression: `[0]`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!int)::1\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: `Maps can have numbers as keys, so this default to a non-exisiting key behaviour.`,
|
||||
description: "Maps with numeric keys",
|
||||
document: `{2: cat}`,
|
||||
expression: `[2]`,
|
||||
expected: []string{
|
||||
"D0, P[2], (!!str)::cat\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Maps with non existing numeric keys",
|
||||
document: `{a: b}`,
|
||||
expression: `[0]`,
|
||||
expected: []string{
|
||||
@@ -147,6 +172,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar`,
|
||||
expected: []string{
|
||||
@@ -154,29 +180,33 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar.a`,
|
||||
description: "Traversing merge anchors",
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar.a`,
|
||||
expected: []string{
|
||||
"D0, P[foobar a], (!!str)::foo_a\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar.c`,
|
||||
description: "Traversing merge anchors with override",
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar.c`,
|
||||
expected: []string{
|
||||
"D0, P[foobar c], (!!str)::foo_c\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar.thing`,
|
||||
description: "Traversing merge anchors with local override",
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar.thing`,
|
||||
expected: []string{
|
||||
"D0, P[foobar thing], (!!str)::foobar_thing\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar.[]`,
|
||||
description: "Splatting merge anchors",
|
||||
document: mergeDocSample,
|
||||
expression: `.foobar.[]`,
|
||||
expected: []string{
|
||||
"D0, P[foobar c], (!!str)::foo_c\n",
|
||||
"D0, P[foobar a], (!!str)::foo_a\n",
|
||||
@@ -184,6 +214,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList`,
|
||||
expected: []string{
|
||||
@@ -191,6 +222,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList.a`,
|
||||
expected: []string{
|
||||
@@ -198,13 +230,16 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList.thing`,
|
||||
description: "Traversing merge anchor lists",
|
||||
subdescription: "Note that the later merge anchors override previous",
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList.thing`,
|
||||
expected: []string{
|
||||
"D0, P[foobarList thing], (!!str)::bar_thing\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList.c`,
|
||||
expected: []string{
|
||||
@@ -212,6 +247,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList.b`,
|
||||
expected: []string{
|
||||
@@ -219,8 +255,9 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
},
|
||||
},
|
||||
{
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList.[]`,
|
||||
description: "Splatting merge anchor lists",
|
||||
document: mergeDocSample,
|
||||
expression: `.foobarList.[]`,
|
||||
expected: []string{
|
||||
"D0, P[foobarList b], (!!str)::bar_b\n",
|
||||
"D0, P[foobarList a], (!!str)::foo_a\n",
|
||||
@@ -234,4 +271,5 @@ func TestTraversePathOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range traversePathOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Traverse Operator", traversePathOperatorScenarios)
|
||||
}
|
||||
|
||||
@@ -6,20 +6,21 @@ import (
|
||||
|
||||
var unionOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: `{}`,
|
||||
expression: `"cat", "dog"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::cat\n",
|
||||
"D0, P[], (!!str)::dog\n",
|
||||
},
|
||||
}, {
|
||||
document: `{a: frog}`,
|
||||
expression: `1, true, "cat", .a`,
|
||||
description: "Combine scalars",
|
||||
expression: `1, true, "cat"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!int)::1\n",
|
||||
"D0, P[], (!!bool)::true\n",
|
||||
"D0, P[], (!!str)::cat\n",
|
||||
"D0, P[a], (!!str)::frog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Combine selected paths",
|
||||
document: `{a: fieldA, b: fieldB, c: fieldC}`,
|
||||
expression: `.a, .c`,
|
||||
expected: []string{
|
||||
"D0, P[a], (!!str)::fieldA\n",
|
||||
"D0, P[c], (!!str)::fieldC\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -28,4 +29,5 @@ func TestUnionOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range unionOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Union Operator", unionOperatorScenarios)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -13,11 +14,13 @@ import (
|
||||
)
|
||||
|
||||
type expressionScenario struct {
|
||||
description string
|
||||
document string
|
||||
expression string
|
||||
expected []string
|
||||
skipDoc bool
|
||||
description string
|
||||
subdescription string
|
||||
document string
|
||||
expression string
|
||||
expected []string
|
||||
skipDoc bool
|
||||
dontFormatInputForDoc bool // dont format input doc for documentation generation
|
||||
}
|
||||
|
||||
func testScenario(t *testing.T, s *expressionScenario) {
|
||||
@@ -48,6 +51,15 @@ func testScenario(t *testing.T, s *expressionScenario) {
|
||||
test.AssertResultComplexWithContext(t, s.expected, resultsToString(results), fmt.Sprintf("exp: %v\ndoc: %v", s.expression, s.document))
|
||||
}
|
||||
|
||||
func resultsToString(results *list.List) []string {
|
||||
var pretty []string = make([]string, 0)
|
||||
for el := results.Front(); el != nil; el = el.Next() {
|
||||
n := el.Value.(*CandidateNode)
|
||||
pretty = append(pretty, NodeToString(n))
|
||||
}
|
||||
return pretty
|
||||
}
|
||||
|
||||
func writeOrPanic(w *bufio.Writer, text string) {
|
||||
_, err := w.WriteString(text)
|
||||
if err != nil {
|
||||
@@ -55,16 +67,54 @@ func writeOrPanic(w *bufio.Writer, text string) {
|
||||
}
|
||||
}
|
||||
|
||||
func copyFromHeader(title string, out *os.File) error {
|
||||
source := fmt.Sprintf("doc/headers/%v.md", title)
|
||||
_, err := os.Stat(source)
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
in, err := os.Open(source) // nolint gosec
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer safelyCloseFile(in)
|
||||
_, err = io.Copy(out, in)
|
||||
return err
|
||||
}
|
||||
|
||||
func formatYaml(yaml string) string {
|
||||
var output bytes.Buffer
|
||||
printer := NewPrinter(bufio.NewWriter(&output), false, true, false, 2, true)
|
||||
|
||||
node, err := treeCreator.ParsePath(".. style= \"\"")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = EvaluateStream("sample.yaml", strings.NewReader(yaml), node, printer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return output.String()
|
||||
}
|
||||
|
||||
func documentScenarios(t *testing.T, title string, scenarios []expressionScenario) {
|
||||
f, err := os.Create(fmt.Sprintf("doc/%v.md", title))
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = copyFromHeader(title, f)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
w := bufio.NewWriter(f)
|
||||
writeOrPanic(w, fmt.Sprintf("# %v\n", title))
|
||||
writeOrPanic(w, "## Examples\n")
|
||||
|
||||
writeOrPanic(w, "\n## Examples\n")
|
||||
|
||||
for index, s := range scenarios {
|
||||
if !s.skipDoc {
|
||||
@@ -74,16 +124,33 @@ func documentScenarios(t *testing.T, title string, scenarios []expressionScenari
|
||||
} else {
|
||||
writeOrPanic(w, fmt.Sprintf("### Example %v\n", index))
|
||||
}
|
||||
if s.document != "" {
|
||||
writeOrPanic(w, "sample.yml:\n")
|
||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v\n```\n", s.document))
|
||||
if s.subdescription != "" {
|
||||
writeOrPanic(w, s.subdescription)
|
||||
writeOrPanic(w, "\n\n")
|
||||
}
|
||||
if s.expression != "" {
|
||||
writeOrPanic(w, "Expression\n")
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq '%v' < sample.yml\n```\n", s.expression))
|
||||
formattedDoc := ""
|
||||
if s.document != "" {
|
||||
if s.dontFormatInputForDoc {
|
||||
formattedDoc = s.document
|
||||
} else {
|
||||
formattedDoc = formatYaml(s.document)
|
||||
}
|
||||
//TODO: pretty here
|
||||
writeOrPanic(w, "Given a sample.yml file of:\n")
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n", formattedDoc))
|
||||
writeOrPanic(w, "then\n")
|
||||
if s.expression != "" {
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq eval '%v' sample.yml\n```\n", s.expression))
|
||||
} else {
|
||||
writeOrPanic(w, "```bash\nyq eval sample.yml\n```\n")
|
||||
}
|
||||
} else {
|
||||
writeOrPanic(w, "Running\n")
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq eval --null-input '%v'\n```\n", s.expression))
|
||||
}
|
||||
|
||||
writeOrPanic(w, "Result\n")
|
||||
writeOrPanic(w, "will output\n")
|
||||
|
||||
var output bytes.Buffer
|
||||
var err error
|
||||
@@ -94,7 +161,7 @@ func documentScenarios(t *testing.T, title string, scenarios []expressionScenari
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = EvaluateStream("sample.yaml", strings.NewReader(s.document), node, printer)
|
||||
err = EvaluateStream("sample.yaml", strings.NewReader(formattedDoc), node, printer)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -105,7 +172,7 @@ func documentScenarios(t *testing.T, title string, scenarios []expressionScenari
|
||||
}
|
||||
}
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n", output.String()))
|
||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", output.String()))
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var styleOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: `{a: cat}`,
|
||||
expression: `.a style="single"`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: 'cat'}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Set style using a path",
|
||||
document: `{a: cat, b: double}`,
|
||||
expression: `.a style=.b`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: \"cat\", b: double}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: "cat", b: 'dog'}`,
|
||||
expression: `.. style=""`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::a: cat\nb: dog\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: "cat", b: 'thing'}`,
|
||||
expression: `.. | style`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::flow\n",
|
||||
"D0, P[a], (!!str)::double\n",
|
||||
"D0, P[b], (!!str)::single\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `a: cat`,
|
||||
expression: `.. | style`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::\"\"\n",
|
||||
"D0, P[a], (!!str)::\"\"\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestStyleOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range styleOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Style Operator", styleOperatorScenarios)
|
||||
}
|
||||
@@ -99,6 +99,27 @@ var pathTests = []struct {
|
||||
append(make([]interface{}, 0), "a", "PIPE", "b", "ASSIGN_STYLE", "folded (string)"),
|
||||
append(make([]interface{}, 0), "a", "b", "PIPE", "folded (string)", "ASSIGN_STYLE"),
|
||||
},
|
||||
{
|
||||
`tag == "str"`,
|
||||
append(make([]interface{}, 0), "GET_TAG", "EQUALS", "str (string)"),
|
||||
append(make([]interface{}, 0), "GET_TAG", "str (string)", "EQUALS"),
|
||||
},
|
||||
{
|
||||
`. tag= "str"`,
|
||||
append(make([]interface{}, 0), "SELF", "ASSIGN_TAG", "str (string)"),
|
||||
append(make([]interface{}, 0), "SELF", "str (string)", "ASSIGN_TAG"),
|
||||
},
|
||||
{
|
||||
`lineComment == "str"`,
|
||||
append(make([]interface{}, 0), "GET_COMMENT", "EQUALS", "str (string)"),
|
||||
append(make([]interface{}, 0), "GET_COMMENT", "str (string)", "EQUALS"),
|
||||
},
|
||||
{
|
||||
`. lineComment= "str"`,
|
||||
append(make([]interface{}, 0), "SELF", "ASSIGN_COMMENT", "str (string)"),
|
||||
append(make([]interface{}, 0), "SELF", "str (string)", "ASSIGN_COMMENT"),
|
||||
},
|
||||
|
||||
// {
|
||||
// `.a.b tag="!!str"`,
|
||||
// append(make([]interface{}, 0), "EXPLODE", "(", "a", "PIPE", "b", ")"),
|
||||
|
||||
@@ -24,10 +24,11 @@ const (
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
TokenType TokenType
|
||||
Operation *Operation
|
||||
TokenType TokenType
|
||||
Operation *Operation
|
||||
AssignOperation *Operation // e.g. tag (GetTag) op becomes AssignTag if '=' follows it
|
||||
CheckForPostTraverse bool // e.g. [1]cat should really be [1].cat
|
||||
|
||||
CheckForPostTraverse bool // e.g. [1]cat should really be [1].cat
|
||||
}
|
||||
|
||||
func (t *Token) toString() string {
|
||||
@@ -83,14 +84,22 @@ func documentToken() lex.Action {
|
||||
}
|
||||
|
||||
func opToken(op *OperationType) lex.Action {
|
||||
return opTokenWithPrefs(op, nil)
|
||||
return opTokenWithPrefs(op, nil, nil)
|
||||
}
|
||||
|
||||
func opTokenWithPrefs(op *OperationType, preferences interface{}) lex.Action {
|
||||
func opAssignableToken(opType *OperationType, assignOpType *OperationType) lex.Action {
|
||||
return opTokenWithPrefs(opType, assignOpType, nil)
|
||||
}
|
||||
|
||||
func opTokenWithPrefs(op *OperationType, assignOpType *OperationType, preferences interface{}) lex.Action {
|
||||
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
||||
value := string(m.Bytes)
|
||||
op := &Operation{OperationType: op, Value: op.Type, StringValue: value, Preferences: preferences}
|
||||
return &Token{TokenType: OperationToken, Operation: op}, nil
|
||||
var assign *Operation
|
||||
if assignOpType != nil {
|
||||
assign = &Operation{OperationType: assignOpType, Value: assignOpType.Type, StringValue: value, Preferences: preferences}
|
||||
}
|
||||
return &Token{TokenType: OperationToken, Operation: op, AssignOperation: assign}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,27 +200,26 @@ func initLexer() (*lex.Lexer, error) {
|
||||
|
||||
lexer.Add([]byte(`documentIndex`), opToken(GetDocumentIndex))
|
||||
|
||||
lexer.Add([]byte(`style\s*=`), opToken(AssignStyle))
|
||||
lexer.Add([]byte(`style`), opToken(GetStyle))
|
||||
lexer.Add([]byte(`style`), opAssignableToken(GetStyle, AssignStyle))
|
||||
|
||||
lexer.Add([]byte(`lineComment\s*=`), opTokenWithPrefs(AssignComment, &CommentOpPreferences{LineComment: true}))
|
||||
lexer.Add([]byte(`lineComment`), opTokenWithPrefs(GetComment, &CommentOpPreferences{LineComment: true}))
|
||||
lexer.Add([]byte(`tag`), opAssignableToken(GetTag, AssignTag))
|
||||
|
||||
lexer.Add([]byte(`headComment\s*=`), opTokenWithPrefs(AssignComment, &CommentOpPreferences{HeadComment: true}))
|
||||
lexer.Add([]byte(`headComment`), opTokenWithPrefs(GetComment, &CommentOpPreferences{HeadComment: true}))
|
||||
lexer.Add([]byte(`lineComment`), opTokenWithPrefs(GetComment, AssignComment, &CommentOpPreferences{LineComment: true}))
|
||||
|
||||
lexer.Add([]byte(`footComment\s*=`), opTokenWithPrefs(AssignComment, &CommentOpPreferences{FootComment: true}))
|
||||
lexer.Add([]byte(`footComment`), opTokenWithPrefs(GetComment, &CommentOpPreferences{FootComment: true}))
|
||||
lexer.Add([]byte(`headComment`), opTokenWithPrefs(GetComment, AssignComment, &CommentOpPreferences{HeadComment: true}))
|
||||
|
||||
lexer.Add([]byte(`comments\s*=`), opTokenWithPrefs(AssignComment, &CommentOpPreferences{LineComment: true, HeadComment: true, FootComment: true}))
|
||||
lexer.Add([]byte(`footComment`), opTokenWithPrefs(GetComment, AssignComment, &CommentOpPreferences{FootComment: true}))
|
||||
|
||||
lexer.Add([]byte(`comments\s*=`), opTokenWithPrefs(AssignComment, nil, &CommentOpPreferences{LineComment: true, HeadComment: true, FootComment: true}))
|
||||
|
||||
lexer.Add([]byte(`collect`), opToken(Collect))
|
||||
|
||||
lexer.Add([]byte(`\s*==\s*`), opToken(Equals))
|
||||
lexer.Add([]byte(`\s*=\s*`), opTokenWithPrefs(Assign, nil, &AssignOpPreferences{false}))
|
||||
|
||||
lexer.Add([]byte(`\s*.-\s*`), opToken(DeleteChild))
|
||||
lexer.Add([]byte(`del`), opToken(DeleteChild))
|
||||
|
||||
lexer.Add([]byte(`\s*\|=\s*`), opToken(Assign))
|
||||
lexer.Add([]byte(`\s*\|=\s*`), opTokenWithPrefs(Assign, nil, &AssignOpPreferences{true}))
|
||||
|
||||
lexer.Add([]byte(`\[-?[0-9]+\]`), arrayIndextoken(false))
|
||||
lexer.Add([]byte(`\.\[-?[0-9]+\]`), arrayIndextoken(true))
|
||||
@@ -286,15 +294,28 @@ func (p *pathTokeniser) Tokenise(path string) ([]*Token, error) {
|
||||
}
|
||||
var postProcessedTokens = make([]*Token, 0)
|
||||
|
||||
skipNextToken := false
|
||||
|
||||
for index, token := range tokens {
|
||||
if skipNextToken {
|
||||
skipNextToken = false
|
||||
} else {
|
||||
|
||||
postProcessedTokens = append(postProcessedTokens, token)
|
||||
if index != len(tokens)-1 && token.AssignOperation != nil &&
|
||||
tokens[index+1].TokenType == OperationToken &&
|
||||
tokens[index+1].Operation.OperationType == Assign {
|
||||
token.Operation = token.AssignOperation
|
||||
skipNextToken = true
|
||||
}
|
||||
|
||||
if index != len(tokens)-1 && token.CheckForPostTraverse &&
|
||||
tokens[index+1].TokenType == OperationToken &&
|
||||
tokens[index+1].Operation.OperationType == TraversePath {
|
||||
op := &Operation{OperationType: Pipe, Value: "PIPE"}
|
||||
postProcessedTokens = append(postProcessedTokens, &Token{TokenType: OperationToken, Operation: op})
|
||||
postProcessedTokens = append(postProcessedTokens, token)
|
||||
|
||||
if index != len(tokens)-1 && token.CheckForPostTraverse &&
|
||||
tokens[index+1].TokenType == OperationToken &&
|
||||
tokens[index+1].Operation.OperationType == TraversePath {
|
||||
op := &Operation{OperationType: Pipe, Value: "PIPE"}
|
||||
postProcessedTokens = append(postProcessedTokens, &Token{TokenType: OperationToken, Operation: op})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ type resultsPrinter struct {
|
||||
printDocSeparators bool
|
||||
writer io.Writer
|
||||
firstTimePrinting bool
|
||||
previousDocIndex uint
|
||||
}
|
||||
|
||||
func NewPrinter(writer io.Writer, outputToJSON bool, unwrapScalar bool, colorsEnabled bool, indent int, printDocSeparators bool) Printer {
|
||||
@@ -53,6 +54,7 @@ func (p *resultsPrinter) writeString(writer io.Writer, txt string) error {
|
||||
}
|
||||
|
||||
func (p *resultsPrinter) PrintResults(matchingNodes *list.List) error {
|
||||
log.Debug("PrintResults for %v matches", matchingNodes.Len())
|
||||
var err error
|
||||
if p.outputToJSON {
|
||||
explodeOp := Operation{OperationType: Explode}
|
||||
@@ -70,13 +72,16 @@ func (p *resultsPrinter) PrintResults(matchingNodes *list.List) error {
|
||||
log.Debug("no matching results, nothing to print")
|
||||
return nil
|
||||
}
|
||||
|
||||
previousDocIndex := matchingNodes.Front().Value.(*CandidateNode).Document
|
||||
if p.firstTimePrinting {
|
||||
p.previousDocIndex = matchingNodes.Front().Value.(*CandidateNode).Document
|
||||
p.firstTimePrinting = false
|
||||
}
|
||||
|
||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
||||
mappedDoc := el.Value.(*CandidateNode)
|
||||
|
||||
if (!p.firstTimePrinting || (previousDocIndex != mappedDoc.Document)) && p.printDocSeparators {
|
||||
log.Debug("-- print sep logic: p.firstTimePrinting: %v, previousDocIndex: %v, mappedDoc.Document: %v, printDocSeparators: %v", p.firstTimePrinting, p.previousDocIndex, mappedDoc.Document, p.printDocSeparators)
|
||||
if (p.previousDocIndex != mappedDoc.Document) && p.printDocSeparators {
|
||||
log.Debug("-- writing doc sep")
|
||||
if err := p.writeString(bufferedWriter, "---\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -87,9 +92,8 @@ func (p *resultsPrinter) PrintResults(matchingNodes *list.List) error {
|
||||
return err
|
||||
}
|
||||
|
||||
previousDocIndex = mappedDoc.Document
|
||||
p.previousDocIndex = mappedDoc.Document
|
||||
}
|
||||
p.firstTimePrinting = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
99
pkg/yqlib/printer_test.go
Normal file
99
pkg/yqlib/printer_test.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mikefarah/yq/v4/test"
|
||||
)
|
||||
|
||||
var multiDocSample = `a: banana
|
||||
---
|
||||
a: apple
|
||||
---
|
||||
a: coconut
|
||||
`
|
||||
|
||||
func TestPrinterMultipleDocsInSequence(t *testing.T) {
|
||||
var output bytes.Buffer
|
||||
var writer = bufio.NewWriter(&output)
|
||||
printer := NewPrinter(writer, false, true, false, 2, true)
|
||||
|
||||
inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
el := inputs.Front()
|
||||
sample1 := nodeToMap(el.Value.(*CandidateNode))
|
||||
|
||||
el = el.Next()
|
||||
sample2 := nodeToMap(el.Value.(*CandidateNode))
|
||||
|
||||
el = el.Next()
|
||||
sample3 := nodeToMap(el.Value.(*CandidateNode))
|
||||
|
||||
err = printer.PrintResults(sample1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = printer.PrintResults(sample2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = printer.PrintResults(sample3)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
writer.Flush()
|
||||
test.AssertResult(t, multiDocSample, output.String())
|
||||
|
||||
}
|
||||
|
||||
func TestPrinterMultipleDocsInSinglePrint(t *testing.T) {
|
||||
var output bytes.Buffer
|
||||
var writer = bufio.NewWriter(&output)
|
||||
printer := NewPrinter(writer, false, true, false, 2, true)
|
||||
|
||||
inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = printer.PrintResults(inputs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
writer.Flush()
|
||||
test.AssertResult(t, multiDocSample, output.String())
|
||||
}
|
||||
|
||||
func TestPrinterMultipleDocsJson(t *testing.T) {
|
||||
var output bytes.Buffer
|
||||
var writer = bufio.NewWriter(&output)
|
||||
printer := NewPrinter(writer, true, true, false, 0, false)
|
||||
|
||||
inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = printer.PrintResults(inputs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
expected := `{"a":"banana"}
|
||||
{"a":"apple"}
|
||||
{"a":"coconut"}
|
||||
`
|
||||
|
||||
writer.Flush()
|
||||
test.AssertResult(t, expected, output.String())
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func EvaluateFileStreamsSequence(expression string, filenames []string, printer
|
||||
// }
|
||||
// }
|
||||
|
||||
// // thanks https://stackoverflow.com/questions/21060945/simple-way-to-copy-a-file-in-golang
|
||||
// thanks https://stackoverflow.com/questions/21060945/simple-way-to-copy-a-file-in-golang
|
||||
// func copyFileContents(src, dst string) (err error) {
|
||||
// in, err := os.Open(src) // nolint gosec
|
||||
// if err != nil {
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
- make local xcompile (builds binaries for all platforms)
|
||||
|
||||
- git release
|
||||
./scripts/publish.sh
|
||||
./scripts/release.sh
|
||||
./scripts/upload.sh
|
||||
|
||||
- snapcraft
|
||||
- will auto create a candidate, test it works then promote
|
||||
|
||||
21
scripts/release.sh
Executable file
21
scripts/release.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
GITHUB_TOKEN="${GITHUB_TOKEN:?missing required input \'GITHUB_TOKEN\'}"
|
||||
|
||||
CURRENT="$(git describe --tags --abbrev=0)"
|
||||
PREVIOUS="$(git describe --tags --abbrev=0 --always "${CURRENT}"^)"
|
||||
OWNER="mikefarah"
|
||||
REPO="yq"
|
||||
|
||||
release() {
|
||||
github-release release \
|
||||
--user "$OWNER" \
|
||||
--draft \
|
||||
--repo "$REPO" \
|
||||
--tag "$CURRENT"
|
||||
}
|
||||
|
||||
|
||||
|
||||
release
|
||||
|
||||
@@ -7,14 +7,6 @@ PREVIOUS="$(git describe --tags --abbrev=0 --always "${CURRENT}"^)"
|
||||
OWNER="mikefarah"
|
||||
REPO="yq"
|
||||
|
||||
release() {
|
||||
github-release release \
|
||||
--user "$OWNER" \
|
||||
--draft \
|
||||
--repo "$REPO" \
|
||||
--tag "$CURRENT"
|
||||
}
|
||||
|
||||
upload() {
|
||||
mkdir -p ./build-done
|
||||
while IFS= read -r -d $'\0'; do
|
||||
@@ -32,5 +24,5 @@ upload() {
|
||||
done < <(find ./build -mindepth 1 -maxdepth 1 -print0)
|
||||
}
|
||||
|
||||
# release
|
||||
|
||||
upload
|
||||
@@ -1,5 +1,5 @@
|
||||
name: yq
|
||||
version: '3.4.0'
|
||||
version: '4.0.0-alpha1'
|
||||
summary: A lightweight and portable command-line YAML processor
|
||||
description: |
|
||||
The aim of the project is to be the jq or sed of yaml files.
|
||||
|
||||
Reference in New Issue
Block a user