diff --git a/Dockerfile b/Dockerfile index 1858eea9..bbd6b914 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,3 +21,5 @@ ARG VERSION=none LABEL version=${VERSION} WORKDIR /workdir + +ENTRYPOINT ["/usr/bin/yq"] diff --git a/README.md b/README.md index 87e97d78..90e4caa5 100644 --- a/README.md +++ b/README.md @@ -26,16 +26,16 @@ snap install yq `yq` installs with [_strict confinement_](https://docs.snapcraft.io/snap-confinement/6233) in snap, this means it doesn't have direct access to root files. To read root files you can: ``` -sudo cat /etc/myfile | yq r - a.path +sudo cat /etc/myfile | yq e '.a.path' - ``` And to write to a root file you can either use [sponge](https://linux.die.net/man/1/sponge): ``` -sudo cat /etc/myfile | yq w - a.path value | sudo sponge /etc/myfile +sudo cat /etc/myfile | yq e '.a.path = "value"' - | sudo sponge /etc/myfile ``` or write to a temporary file: ``` -sudo cat /etc/myfile | yq w - a.path value | sudo tee /etc/myfile.tmp +sudo cat /etc/myfile | yq e '.a.path = "value"' | sudo tee /etc/myfile.tmp sudo mv /etc/myfile.tmp /etc/myfile rm /etc/myfile.tmp ``` @@ -49,15 +49,14 @@ wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY} -O / chmod +x /usr/bin/yq ``` -For instance, VERSION=3.4.1 and BINARY=yq_linux_amd64 - +For instance, VERSION=4.0.0 and BINARY=yq_linux_amd64 ### Run with Docker #### Oneshot use: ```bash -docker run --rm -v "${PWD}":/workdir mikefarah/yq yq [flags] FILE... +docker run --rm -v "${PWD}":/workdir mikefarah/yq [flags] [expression ]FILE... ``` #### Run commands interactively: @@ -70,13 +69,13 @@ It can be useful to have a bash function to avoid typing the whole docker comman ```bash yq() { - docker run --rm -i -v "${PWD}":/workdir mikefarah/yq yq "$@" + docker run --rm -i -v "${PWD}":/workdir mikefarah/yq "$@" } ``` ### Go Get: ``` -GO111MODULE=on go get github.com/mikefarah/yq/v3 +GO111MODULE=on go get github.com/mikefarah/yq/v4 ``` ## Community Supported Installation methods @@ -117,22 +116,18 @@ Supported by @rmescandon (https://launchpad.net/~rmescandon/+archive/ubuntu/yq) ## Features - Written in portable go, so you can download a lovely dependency free binary -- [Colorize the output](https://mikefarah.gitbook.io/yq/usage/output-format#colorize-output) -- [Deep read a yaml file with a given path expression](https://mikefarah.gitbook.io/yq/commands/read#basic) -- [List matching paths of a given path expression](https://mikefarah.gitbook.io/yq/commands/read#path-only) -- [Return the lengths of arrays/object/scalars](https://mikefarah.gitbook.io/yq/commands/read#printing-length-of-the-results) -- Update a yaml file given a [path expression](https://mikefarah.gitbook.io/yq/commands/write-update#basic) or [script file](https://mikefarah.gitbook.io/yq/commands/write-update#basic) -- Update creates any missing entries in the path on the fly -- Deeply [compare](https://mikefarah.gitbook.io/yq/commands/compare) yaml files -- Keeps yaml formatting and comments when updating -- [Validate a yaml file](https://mikefarah.gitbook.io/yq/commands/validate) -- Create a yaml file given a [deep path and value](https://mikefarah.gitbook.io/yq/commands/create#creating-a-simple-yaml-file) or a [script file](https://mikefarah.gitbook.io/yq/commands/create#creating-using-a-create-script) -- [Prefix a path to a yaml file](https://mikefarah.gitbook.io/yq/commands/prefix) -- [Convert to/from json to yaml](https://mikefarah.gitbook.io/yq/usage/convert) -- [Pipe data in by using '-'](https://mikefarah.gitbook.io/yq/commands/read#from-stdin) -- [Merge](https://mikefarah.gitbook.io/yq/commands/merge) multiple yaml files with various options for [overriding](https://mikefarah.gitbook.io/yq/commands/merge#overwrite-values) and [appending](https://mikefarah.gitbook.io/yq/commands/merge#append-values-with-arrays) -- Supports multiple documents in a single yaml file for [reading](https://mikefarah.gitbook.io/yq/commands/read#multiple-documents), [writing](https://mikefarah.gitbook.io/yq/commands/write-update#multiple-documents) and [merging](https://mikefarah.gitbook.io/yq/commands/merge#multiple-documents) -- General shell completion scripts (bash/zsh/fish/powershell) (https://mikefarah.gitbook.io/yq/commands/shell-completion) +- Uses similar syntax as `jq` but works with YAML and JSON files +- Fully supports multi document yaml files +- Colorized yaml output +- [Deeply traverse yaml](https://mikefarah.gitbook.io/yq/v/v4.x/traverse) +- [Sort yaml by keys](https://mikefarah.gitbook.io/yq/v/v4.x/sort-keys) +- Manipulate yaml [comments](https://app.gitbook.com/@mikefarah/s/yq/v/v4.x/comment-operators), [styling](https://app.gitbook.com/@mikefarah/s/yq/v/v4.x/style), [tags](https://app.gitbook.com/@mikefarah/s/yq/v/v4.x/tag) and [anchors](https://app.gitbook.com/@mikefarah/s/yq/v/v4.x/explode). +- [Update yaml inplace](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate#flags) +- [Complex expressions to select and update](https://mikefarah.gitbook.io/yq/v/v4.x/select#select-and-update-matching-values-in-map) +- Keeps yaml formatting and comments when updating (though there are issues with whitespace) +- [Convert to/from json to yaml](https://mikefarah.gitbook.io/yq/v/v4.x/usage/convert) +- [Pipe data in by using '-'](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate) +- General shell completion scripts (bash/zsh/fish/powershell) (https://mikefarah.gitbook.io/yq/v/v4.x/commands/shell-completion) ## [Usage](https://mikefarah.gitbook.io/yq/) @@ -144,31 +139,35 @@ Usage: yq [command] Available Commands: - compare yq x [--prettyPrint/-P] dataA.yaml dataB.yaml 'b.e(name==fr*).value' - delete yq d [--inplace/-i] [--doc/-d index] sample.yaml 'b.e(name==fred)' + eval Apply expression to each document in each yaml file given in sequence + eval-all Loads _all_ yaml documents of _all_ yaml files and runs expression once help Help about any command - merge yq m [--inplace/-i] [--doc/-d index] [--overwrite/-x] [--append/-a] sample.yaml sample2.yaml - new yq n [--script/-s script_file] a.b.c newValue - prefix yq p [--inplace/-i] [--doc/-d index] sample.yaml a.b.c - read yq r [--printMode/-p pv] sample.yaml 'b.e(name==fr*).value' - shell-completion Generates shell completion scripts - validate yq v sample.yaml - write yq w [--inplace/-i] [--script/-s script_file] [--doc/-d index] sample.yaml 'b.e(name==fr*).value' newValue + shell-completion Generate completion script Flags: - -C, --colors print with colors + -C, --colors force print with colors + -e, --exit-status set exit status if there are no matches or null or false is returned -h, --help help for yq -I, --indent int sets indent level for output (default 2) - -P, --prettyPrint pretty print - -j, --tojson output as json. By default it prints a json document in one line, use the prettyPrint flag to print a formatted doc. + -i, --inplace update the yaml file inplace of first yaml file given. + -M, --no-colors force print with no colors + -N, --no-doc Don't print document separators (---) + -n, --null-input Don't read input, simply evaluate the expression given. Useful for creating yaml docs from scratch. + -j, --tojson output as json. Set indent to 0 to print json in one line. -v, --verbose verbose mode -V, --version Print version information and quit Use "yq [command] --help" for more information about a command. ``` -## Upgrade from V2 -If you've been using v2 and want/need to upgrade, checkout the [upgrade guide](https://mikefarah.gitbook.io/yq/upgrading-from-v2). +Simple Example: + +```bash +yq e '.a.b | length' f1.yml f2.yml +``` + +## Upgrade from V3 +If you've been using v3 and want/need to upgrade, checkout the [upgrade guide](https://mikefarah.gitbook.io/yq/v/v4.x/upgrading-from-v3). ## V4 is in development! If you're keen - check out the alpha release [here](https://github.com/mikefarah/yq/releases/), or in docker `mikefarah/yq:4-beta1` and the docs (also in beta) [here](https://mikefarah.gitbook.io/yq/v/v4.x-alpha/). @@ -179,4 +178,3 @@ For now - new features will be held off from V3 in anticipation of the V4 build. ## Known Issues / Missing Features - `yq` attempts to preserve comment positions and whitespace as much as possible, but it does not handle all scenarios (see https://github.com/go-yaml/yaml/tree/v3 for details) -- You cannot (yet) select multiple paths/keys from the yaml to be printed out (https://github.com/mikefarah/yq/issues/287) (although you can in v4!) diff --git a/cmd/commands_test.go b/cmd/commands_test.go index a0f0a741..f6d79256 100644 --- a/cmd/commands_test.go +++ b/cmd/commands_test.go @@ -1,83 +1,83 @@ package cmd -import ( - "strings" - "testing" +// import ( +// "strings" +// "testing" - "github.com/mikefarah/yq/v3/test" - "github.com/spf13/cobra" -) +// "github.com/mikefarah/yq/v3/test" +// "github.com/spf13/cobra" +// ) -func getRootCommand() *cobra.Command { - return New() -} +// func getRootCommand() *cobra.Command { +// return New() +// } -func TestRootCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "") - if result.Error != nil { - t.Error(result.Error) - } +// func TestRootCmd(t *testing.T) { +// cmd := getRootCommand() +// result := test.RunCmd(cmd, "") +// if result.Error != nil { +// t.Error(result.Error) +// } - if !strings.Contains(result.Output, "Usage:") { - t.Error("Expected usage message to be printed out, but the usage message was not found.") - } -} +// if !strings.Contains(result.Output, "Usage:") { +// t.Error("Expected usage message to be printed out, but the usage message was not found.") +// } +// } -func TestRootCmd_Help(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "--help") - if result.Error != nil { - t.Error(result.Error) - } +// func TestRootCmd_Help(t *testing.T) { +// cmd := getRootCommand() +// result := test.RunCmd(cmd, "--help") +// if result.Error != nil { +// t.Error(result.Error) +// } - if !strings.Contains(result.Output, "yq is a lightweight and portable command-line YAML processor. It aims to be the jq or sed of yaml files.") { - t.Error("Expected usage message to be printed out, but the usage message was not found.") - } -} +// if !strings.Contains(result.Output, "yq is a lightweight and portable command-line YAML processor. It aims to be the jq or sed of yaml files.") { +// t.Error("Expected usage message to be printed out, but the usage message was not found.") +// } +// } -func TestRootCmd_VerboseLong(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "--verbose") - if result.Error != nil { - t.Error(result.Error) - } +// func TestRootCmd_VerboseLong(t *testing.T) { +// cmd := getRootCommand() +// result := test.RunCmd(cmd, "--verbose") +// if result.Error != nil { +// t.Error(result.Error) +// } - if !verbose { - t.Error("Expected verbose to be true") - } -} +// if !verbose { +// t.Error("Expected verbose to be true") +// } +// } -func TestRootCmd_VerboseShort(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "-v") - if result.Error != nil { - t.Error(result.Error) - } +// func TestRootCmd_VerboseShort(t *testing.T) { +// cmd := getRootCommand() +// result := test.RunCmd(cmd, "-v") +// if result.Error != nil { +// t.Error(result.Error) +// } - if !verbose { - t.Error("Expected verbose to be true") - } -} +// if !verbose { +// t.Error("Expected verbose to be true") +// } +// } -func TestRootCmd_VersionShort(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "-V") - if result.Error != nil { - t.Error(result.Error) - } - if !strings.Contains(result.Output, "yq version") { - t.Error("expected version message to be printed out, but the message was not found.") - } -} +// func TestRootCmd_VersionShort(t *testing.T) { +// cmd := getRootCommand() +// result := test.RunCmd(cmd, "-V") +// if result.Error != nil { +// t.Error(result.Error) +// } +// if !strings.Contains(result.Output, "yq version") { +// t.Error("expected version message to be printed out, but the message was not found.") +// } +// } -func TestRootCmd_VersionLong(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "--version") - if result.Error != nil { - t.Error(result.Error) - } - if !strings.Contains(result.Output, "yq version") { - t.Error("expected version message to be printed out, but the message was not found.") - } -} +// func TestRootCmd_VersionLong(t *testing.T) { +// cmd := getRootCommand() +// result := test.RunCmd(cmd, "--version") +// if result.Error != nil { +// t.Error(result.Error) +// } +// if !strings.Contains(result.Output, "yq version") { +// t.Error("expected version message to be printed out, but the message was not found.") +// } +// } diff --git a/cmd/compare.go b/cmd/compare.go deleted file mode 100644 index f6b3188f..00000000 --- a/cmd/compare.go +++ /dev/null @@ -1,89 +0,0 @@ -package cmd - -import ( - "bufio" - "bytes" - "os" - "strings" - - "github.com/kylelemons/godebug/diff" - "github.com/mikefarah/yq/v3/pkg/yqlib" - errors "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -// turn off for unit tests :( -var forceOsExit = true - -func createCompareCmd() *cobra.Command { - var cmdCompare = &cobra.Command{ - Use: "compare [yaml_file_a] [yaml_file_b]", - Aliases: []string{"x"}, - Short: "yq x [--prettyPrint/-P] dataA.yaml dataB.yaml 'b.e(name==fr*).value'", - Example: ` -yq x - data2.yml # reads from stdin -yq x -pp dataA.yaml dataB.yaml '**' # compare paths -yq x -d1 dataA.yaml dataB.yaml 'a.b.c' -`, - Long: "Deeply compares two yaml files, prints the difference. Use with prettyPrint flag to ignore formatting differences.", - RunE: compareDocuments, - } - cmdCompare.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") - cmdCompare.PersistentFlags().StringVarP(&printMode, "printMode", "p", "v", "print mode (v (values, default), p (paths), pv (path and value pairs)") - cmdCompare.PersistentFlags().StringVarP(&defaultValue, "defaultValue", "D", "", "default value printed when there are no results") - cmdCompare.PersistentFlags().BoolVarP(&stripComments, "stripComments", "", false, "strip comments out before comparing") - cmdCompare.PersistentFlags().BoolVarP(&explodeAnchors, "explodeAnchors", "X", false, "explode anchors") - return cmdCompare -} - -func compareDocuments(cmd *cobra.Command, args []string) error { - var path = "" - - if len(args) < 2 { - return errors.New("Must provide at 2 yaml files") - } else if len(args) > 2 { - path = args[2] - } - - var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() - if errorParsingDocIndex != nil { - return errorParsingDocIndex - } - - var matchingNodesA []*yqlib.NodeContext - var matchingNodesB []*yqlib.NodeContext - var errorDoingThings error - - matchingNodesA, errorDoingThings = readYamlFile(args[0], path, updateAll, docIndexInt) - - if errorDoingThings != nil { - return errorDoingThings - } - - matchingNodesB, errorDoingThings = readYamlFile(args[1], path, updateAll, docIndexInt) - if errorDoingThings != nil { - return errorDoingThings - } - - var dataBufferA bytes.Buffer - var dataBufferB bytes.Buffer - errorDoingThings = printResults(matchingNodesA, bufio.NewWriter(&dataBufferA)) - if errorDoingThings != nil { - return errorDoingThings - } - errorDoingThings = printResults(matchingNodesB, bufio.NewWriter(&dataBufferB)) - if errorDoingThings != nil { - return errorDoingThings - } - - diffString := diff.Diff(strings.TrimSuffix(dataBufferA.String(), "\n"), strings.TrimSuffix(dataBufferB.String(), "\n")) - - if len(diffString) > 1 { - cmd.Print(diffString) - cmd.Print("\n") - if forceOsExit { - os.Exit(1) - } - } - return nil -} diff --git a/cmd/compare_test.go b/cmd/compare_test.go deleted file mode 100644 index 903b1521..00000000 --- a/cmd/compare_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package cmd - -import ( - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -func TestCompareSameCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "compare ../examples/data1.yaml ../examples/data1.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestCompareIgnoreCommentsCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "compare --stripComments ../examples/data1.yaml ../examples/data1-no-comments.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestCompareDontIgnoreCommentsCmd(t *testing.T) { - forceOsExit = false - cmd := getRootCommand() - result := test.RunCmd(cmd, "compare ../examples/data1.yaml ../examples/data1-no-comments.yaml") - - expectedOutput := `-a: simple # just the best -+a: simple - b: [1, 2] - c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestCompareExplodeAnchorsCommentsCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "compare --explodeAnchors ../examples/simple-anchor.yaml ../examples/simple-anchor-exploded.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestCompareDontExplodeAnchorsCmd(t *testing.T) { - forceOsExit = false - cmd := getRootCommand() - result := test.RunCmd(cmd, "compare ../examples/simple-anchor.yaml ../examples/simple-anchor-exploded.yaml") - - expectedOutput := `-foo: &foo -+foo: - a: 1 - foobar: -- !!merge <<: *foo -+ a: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestCompareDifferentCmd(t *testing.T) { - forceOsExit = false - cmd := getRootCommand() - result := test.RunCmd(cmd, "compare ../examples/data1.yaml ../examples/data3.yaml") - - expectedOutput := `-a: simple # just the best --b: [1, 2] -+a: "simple" # just the best -+b: [1, 3] - c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestComparePrettyCmd(t *testing.T) { - forceOsExit = false - cmd := getRootCommand() - result := test.RunCmd(cmd, "compare -P ../examples/data1.yaml ../examples/data3.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := ` a: simple # just the best - b: - - 1 -- - 2 -+ - 3 - c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestComparePathsCmd(t *testing.T) { - forceOsExit = false - cmd := getRootCommand() - result := test.RunCmd(cmd, "compare -P -ppv ../examples/data1.yaml ../examples/data3.yaml **") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := ` a: simple # just the best - b.[0]: 1 --b.[1]: 2 -+b.[1]: 3 - c.test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} diff --git a/cmd/constant.go b/cmd/constant.go index a1f63119..fb8f94d4 100644 --- a/cmd/constant.go +++ b/cmd/constant.go @@ -1,36 +1,18 @@ package cmd -import ( - "github.com/mikefarah/yq/v3/pkg/yqlib" - logging "gopkg.in/op/go-logging.v1" -) - -var customTag = "" -var printMode = "v" -var printLength = false var unwrapScalar = true -var customStyle = "" -var anchorName = "" -var makeAlias = false -var stripComments = false -var collectIntoArray = false + var writeInplace = false -var writeScript = "" -var sourceYamlFile = "" var outputToJSON = false + var exitStatus = false -var prettyPrint = false -var explodeAnchors = false +var forceColor = false +var forceNoColor = false var colorsEnabled = false -var defaultValue = "" var indent = 2 -var overwriteFlag = false -var autoCreateFlag = true -var arrayMergeStrategyFlag = "update" -var commentsMergeStrategyFlag = "setWhenBlank" +var noDocSeparators = false +var nullInput = false var verbose = false var version = false -var docIndex = "0" -var log = logging.MustGetLogger("yq") -var lib = yqlib.NewYqLib() -var valueParser = yqlib.NewValueParser() + +var completedSuccessfully = false diff --git a/cmd/delete.go b/cmd/delete.go deleted file mode 100644 index b1153109..00000000 --- a/cmd/delete.go +++ /dev/null @@ -1,41 +0,0 @@ -package cmd - -import ( - "github.com/mikefarah/yq/v3/pkg/yqlib" - errors "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -func createDeleteCmd() *cobra.Command { - var cmdDelete = &cobra.Command{ - Use: "delete [yaml_file] [path_expression]", - Aliases: []string{"d"}, - Short: "yq d [--inplace/-i] [--doc/-d index] sample.yaml 'b.e(name==fred)'", - Example: ` -yq delete things.yaml 'a.b.c' -yq delete things.yaml 'a.*.c' -yq delete things.yaml 'a.(child.subchild==co*).c' -yq delete things.yaml 'a.**' -yq delete --inplace things.yaml 'a.b.c' -yq delete --inplace -- things.yaml '--key-starting-with-dash' # need to use '--' to stop processing arguments as flags -yq d -i things.yaml 'a.b.c' - `, - Long: `Deletes the nodes matching the given path expression from the YAML file. -Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. -`, - RunE: deleteProperty, - } - cmdDelete.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") - cmdDelete.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") - return cmdDelete -} - -func deleteProperty(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("Must provide ") - } - var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 1) - updateCommands[0] = yqlib.UpdateCommand{Command: "delete", Path: args[1]} - - return updateDoc(args[0], updateCommands, cmd.OutOrStdout()) -} diff --git a/cmd/delete_test.go b/cmd/delete_test.go deleted file mode 100644 index ae5c6179..00000000 --- a/cmd/delete_test.go +++ /dev/null @@ -1,246 +0,0 @@ -package cmd - -import ( - "fmt" - "strings" - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -func TestDeleteYamlCmd(t *testing.T) { - content := `a: 2 -b: - c: things - d: something else -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.c", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: 2 -b: - d: something else -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestDeleteDeepDoesNotExistCmd(t *testing.T) { - content := `a: 2` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.c", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: 2 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestDeleteSplatYaml(t *testing.T) { - content := `a: other -b: [3, 4] -c: - toast: leave - test: 1 - tell: 1 - tasty.taco: cool -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete %s c.te*", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: other -b: [3, 4] -c: - toast: leave - tasty.taco: cool -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestDeleteSplatArrayYaml(t *testing.T) { - content := `a: 2 -b: - hi: - - thing: item1 - name: fred - - thing: item2 - name: sam -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.hi[*].thing", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: 2 -b: - hi: - - name: fred - - name: sam -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestDeleteDeepSplatArrayYaml(t *testing.T) { - content := `thing: 123 -b: - hi: - - thing: item1 - name: fred -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete %s **.thing", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `b: - hi: - - name: fred -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestDeleteSplatPrefixYaml(t *testing.T) { - content := `a: 2 -b: - hi: - c: things - d: something else - there: - c: more things - d: more something else - there2: - c: more things also - d: more something else also -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.there*.c", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: 2 -b: - hi: - c: things - d: something else - there: - d: more something else - there2: - d: more something else also -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestDeleteYamlArrayCmd(t *testing.T) { - content := `- 1 -- 2 -- 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete %s [1]", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `- 1 -- 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestDeleteYamlArrayExpressionCmd(t *testing.T) { - content := `- name: fred -- name: cat -- name: thing -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete %s (name==cat)", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `- name: fred -- name: thing -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestDeleteYamlMulti(t *testing.T) { - content := `apples: great ---- -- 1 -- 2 -- 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete -d 1 %s [1]", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `apples: great ---- -- 1 -- 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestDeleteYamlMultiAllCmd(t *testing.T) { - content := `b: - c: 3 -apples: great ---- -apples: great -something: else -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("delete %s -d * apples", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 3 ---- -something: else` - test.AssertResult(t, expectedOutput, strings.Trim(result.Output, "\n ")) -} diff --git a/cmd/evaluate_all_command.go b/cmd/evaluate_all_command.go new file mode 100644 index 00000000..fc7b4ec2 --- /dev/null +++ b/cmd/evaluate_all_command.go @@ -0,0 +1,90 @@ +package cmd + +import ( + "errors" + "fmt" + "os" + + "github.com/mikefarah/yq/v4/pkg/yqlib" + "github.com/spf13/cobra" +) + +func createEvaluateAllCommand() *cobra.Command { + var cmdEvalAll = &cobra.Command{ + Use: "eval-all [expression] [yaml_file1]...", + Aliases: []string{"ea"}, + Short: "Loads _all_ yaml documents of _all_ yaml files and runs expression once", + Example: ` +# merges f2.yml into f1.yml (inplace) +yq eval-all --inplace 'select(fileIndex == 0) * select(fileIndex == 1)' f1.yml f2.yml +`, + Long: "Evaluate All:\nUseful when you need to run an expression across several yaml documents or files. Consumes more memory than eval", + RunE: evaluateAll, + } + return cmdEvalAll +} +func evaluateAll(cmd *cobra.Command, args []string) error { + cmd.SilenceUsage = true + // 0 args, read std in + // 1 arg, null input, process expression + // 1 arg, read file in sequence + // 2+ args, [0] = expression, file the rest + + var err error + stat, _ := os.Stdin.Stat() + pipingStdIn := (stat.Mode() & os.ModeCharDevice) == 0 + + out := cmd.OutOrStdout() + + fileInfo, _ := os.Stdout.Stat() + + if forceColor || (!forceNoColor && (fileInfo.Mode()&os.ModeCharDevice) != 0) { + colorsEnabled = true + } + + if writeInplace && len(args) < 2 { + return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file") + } + + if writeInplace { + // only use colors if its forced + colorsEnabled = forceColor + writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[1]) + out, err = writeInPlaceHandler.CreateTempFile() + if err != nil { + return err + } + // need to indirectly call the function so that completedSuccessfully is + // passed when we finish execution as opposed to now + defer func() { writeInPlaceHandler.FinishWriteInPlace(completedSuccessfully) }() + } + + printer := yqlib.NewPrinter(out, outputToJSON, unwrapScalar, colorsEnabled, indent, !noDocSeparators) + + allAtOnceEvaluator := yqlib.NewAllAtOnceEvaluator() + switch len(args) { + case 0: + if pipingStdIn { + err = allAtOnceEvaluator.EvaluateFiles("", []string{"-"}, printer) + } else { + cmd.Println(cmd.UsageString()) + return nil + } + case 1: + if nullInput { + err = yqlib.NewStreamEvaluator().EvaluateNew(args[0], printer) + } else { + err = allAtOnceEvaluator.EvaluateFiles("", []string{args[0]}, printer) + } + default: + err = allAtOnceEvaluator.EvaluateFiles(args[0], args[1:], printer) + } + + completedSuccessfully = err == nil + + if err == nil && exitStatus && !printer.PrintedAnything() { + return errors.New("no matches found") + } + + return err +} diff --git a/cmd/evalute_sequence_command.go b/cmd/evalute_sequence_command.go new file mode 100644 index 00000000..c9eabeb8 --- /dev/null +++ b/cmd/evalute_sequence_command.go @@ -0,0 +1,100 @@ +package cmd + +import ( + "errors" + "fmt" + "os" + + "github.com/mikefarah/yq/v4/pkg/yqlib" + "github.com/spf13/cobra" +) + +func createEvaluateSequenceCommand() *cobra.Command { + var cmdEvalSequence = &cobra.Command{ + Use: "eval [expression] [yaml_file1]...", + Aliases: []string{"e"}, + Short: "Apply expression to each document in each yaml file given in sequence", + Example: ` +# runs the expression against each file, in series +yq e '.a.b | length' f1.yml f2.yml + +# prints out the file +yq e sample.yaml + +# prints a new yaml document +yq e -n '.a.b.c = "cat"' + + +# updates file.yaml directly +yq e '.a.b = "cool"' -i file.yaml +`, + Long: "Evaluate Sequence:\nIterate over each yaml document, apply the expression and print the results, in sequence.", + RunE: evaluateSequence, + } + return cmdEvalSequence +} +func evaluateSequence(cmd *cobra.Command, args []string) error { + cmd.SilenceUsage = true + // 0 args, read std in + // 1 arg, null input, process expression + // 1 arg, read file in sequence + // 2+ args, [0] = expression, file the rest + + var err error + stat, _ := os.Stdin.Stat() + pipingStdIn := (stat.Mode() & os.ModeCharDevice) == 0 + + out := cmd.OutOrStdout() + + fileInfo, _ := os.Stdout.Stat() + + if forceColor || (!forceNoColor && (fileInfo.Mode()&os.ModeCharDevice) != 0) { + colorsEnabled = true + } + + if writeInplace && len(args) < 2 { + return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file") + } + + if writeInplace { + // only use colors if its forced + colorsEnabled = forceColor + writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[1]) + out, err = writeInPlaceHandler.CreateTempFile() + if err != nil { + return err + } + // need to indirectly call the function so that completedSuccessfully is + // passed when we finish execution as opposed to now + defer func() { writeInPlaceHandler.FinishWriteInPlace(completedSuccessfully) }() + } + + printer := yqlib.NewPrinter(out, outputToJSON, unwrapScalar, colorsEnabled, indent, !noDocSeparators) + + streamEvaluator := yqlib.NewStreamEvaluator() + + switch len(args) { + case 0: + if pipingStdIn { + err = streamEvaluator.EvaluateFiles("", []string{"-"}, printer) + } else { + cmd.Println(cmd.UsageString()) + return nil + } + case 1: + if nullInput { + err = streamEvaluator.EvaluateNew(args[0], printer) + } else { + err = streamEvaluator.EvaluateFiles("", []string{args[0]}, printer) + } + default: + err = streamEvaluator.EvaluateFiles(args[0], args[1:], printer) + } + completedSuccessfully = err == nil + + if err == nil && exitStatus && !printer.PrintedAnything() { + return errors.New("no matches found") + } + + return err +} diff --git a/cmd/merge.go b/cmd/merge.go deleted file mode 100644 index ef4ca927..00000000 --- a/cmd/merge.go +++ /dev/null @@ -1,124 +0,0 @@ -package cmd - -import ( - "github.com/mikefarah/yq/v3/pkg/yqlib" - errors "github.com/pkg/errors" - "github.com/spf13/cobra" - yaml "gopkg.in/yaml.v3" -) - -func createMergeCmd() *cobra.Command { - var cmdMerge = &cobra.Command{ - Use: "merge [initial_yaml_file] [additional_yaml_file]...", - Aliases: []string{"m"}, - Short: "yq m [--inplace/-i] [--doc/-d index] [--overwrite/-x] [--arrayMerge/-a strategy] sample.yaml sample2.yaml", - Example: ` -yq merge things.yaml other.yaml -yq merge --inplace things.yaml other.yaml -yq m -i things.yaml other.yaml -yq m --overwrite things.yaml other.yaml -yq m -i -x things.yaml other.yaml -yq m -i -a=append things.yaml other.yaml -yq m -i --autocreate=false things.yaml other.yaml - `, - Long: `Updates the yaml file by adding/updating the path(s) and value(s) from additional yaml file(s). -Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. - -If overwrite flag is set then existing values will be overwritten using the values from each additional yaml file. -If append flag is set then existing arrays will be merged with the arrays from each additional yaml file. -`, - RunE: mergeProperties, - } - cmdMerge.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") - cmdMerge.PersistentFlags().BoolVarP(&overwriteFlag, "overwrite", "x", false, "update the yaml file by overwriting existing values") - cmdMerge.PersistentFlags().BoolVarP(&autoCreateFlag, "autocreate", "c", true, "automatically create any missing entries") - cmdMerge.PersistentFlags().StringVarP(&arrayMergeStrategyFlag, "arrays", "a", "update", `array merge strategy (update/append/overwrite) -update: recursively update arrays by their index -append: concatenate arrays together -overwrite: replace arrays -`) - cmdMerge.PersistentFlags().StringVarP(&commentsMergeStrategyFlag, "comments", "", "setWhenBlank", `comments merge strategy (setWhenBlank/ignore/append/overwrite) -setWhenBlank: set comment if the original document has no comment at that node -ignore: leave comments as-is in the original -append: append comments together -overwrite: overwrite comments completely -`) - cmdMerge.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") - return cmdMerge -} - -/* -* We don't deeply traverse arrays when appending a merge, instead we want to -* append the entire array element. - */ -func createReadFunctionForMerge(arrayMergeStrategy yqlib.ArrayMergeStrategy) func(*yaml.Node) ([]*yqlib.NodeContext, error) { - return func(dataBucket *yaml.Node) ([]*yqlib.NodeContext, error) { - return lib.GetForMerge(dataBucket, "**", arrayMergeStrategy) - } -} - -func mergeProperties(cmd *cobra.Command, args []string) error { - var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0) - - if len(args) < 1 { - return errors.New("Must provide at least 1 yaml file") - } - var arrayMergeStrategy yqlib.ArrayMergeStrategy - - switch arrayMergeStrategyFlag { - case "update": - arrayMergeStrategy = yqlib.UpdateArrayMergeStrategy - case "append": - arrayMergeStrategy = yqlib.AppendArrayMergeStrategy - case "overwrite": - arrayMergeStrategy = yqlib.OverwriteArrayMergeStrategy - default: - return errors.New("Array merge strategy must be one of: update/append/overwrite") - } - - var commentsMergeStrategy yqlib.CommentsMergeStrategy - - switch commentsMergeStrategyFlag { - case "setWhenBlank": - commentsMergeStrategy = yqlib.SetWhenBlankCommentsMergeStrategy - case "ignore": - commentsMergeStrategy = yqlib.IgnoreCommentsMergeStrategy - case "append": - commentsMergeStrategy = yqlib.AppendCommentsMergeStrategy - case "overwrite": - commentsMergeStrategy = yqlib.OverwriteCommentsMergeStrategy - default: - return errors.New("Comments merge strategy must be one of: setWhenBlank/ignore/append/overwrite") - } - - if len(args) > 1 { - // first generate update commands from the file - var filesToMerge = args[1:] - - for _, fileToMerge := range filesToMerge { - matchingNodes, errorProcessingFile := doReadYamlFile(fileToMerge, createReadFunctionForMerge(arrayMergeStrategy), false, 0) - if errorProcessingFile != nil { - return errorProcessingFile - } - log.Debugf("finished reading for merge!") - for _, matchingNode := range matchingNodes { - log.Debugf("matched node %v", lib.PathStackToString(matchingNode.PathStack)) - yqlib.DebugNode(matchingNode.Node) - } - for _, matchingNode := range matchingNodes { - mergePath := lib.MergePathStackToString(matchingNode.PathStack, arrayMergeStrategy) - updateCommands = append(updateCommands, yqlib.UpdateCommand{ - Command: "merge", - Path: mergePath, - Value: matchingNode.Node, - Overwrite: overwriteFlag, - CommentsMergeStrategy: commentsMergeStrategy, - // dont update the content for nodes midway, only leaf nodes - DontUpdateNodeContent: matchingNode.IsMiddleNode && (arrayMergeStrategy != yqlib.OverwriteArrayMergeStrategy || matchingNode.Node.Kind != yaml.SequenceNode), - }) - } - } - } - - return updateDoc(args[0], updateCommands, cmd.OutOrStdout()) -} diff --git a/cmd/merge_test.go b/cmd/merge_test.go deleted file mode 100644 index a3d5f79c..00000000 --- a/cmd/merge_test.go +++ /dev/null @@ -1,551 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "runtime" - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -func TestMergeCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge ../examples/data1.yaml ../examples/data2.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: simple # just the best -b: [1, 2] -c: - test: 1 - toast: leave - tell: 1 - tasty.taco: cool -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeOneFileCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge ../examples/data1.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: simple # just the best -b: [1, 2] -c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeNoAutoCreateCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge -c=false ../examples/data1.yaml ../examples/data2.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: simple # just the best -b: [1, 2] -c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeOverwriteCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge -c=false --overwrite ../examples/data1.yaml ../examples/data2.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: other # just the best -b: [3, 4] -c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeOverwriteDeepExampleCmd(t *testing.T) { - content := `c: - test: 1 - thing: whatever -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - mergeContent := `c: - test: 5 -` - mergeFilename := test.WriteTempYamlFile(mergeContent) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge --autocreate=false --overwrite %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `c: - test: 5 - thing: whatever -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeAppendCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge --autocreate=false --arrays=append ../examples/data1.yaml ../examples/data2.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: simple # just the best -b: [1, 2, 3, 4] -c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeAppendArraysCmd(t *testing.T) { - content := `people: - - name: Barry - age: 21` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - mergeContent := `people: - - name: Roger - age: 44` - mergeFilename := test.WriteTempYamlFile(mergeContent) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge --arrays=append -d* %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `people: - - name: Barry - age: 21 - - name: Roger - age: 44 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeAliasArraysCmd(t *testing.T) { - content := ` -vars: - variable1: &var1 cat - -usage: - value1: *var1 - valueAnother: *var1 - valuePlain: thing -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - mergeContent := ` -vars: - variable2: &var2 puppy - -usage: - value2: *var2 - valueAnother: *var2 - valuePlain: *var2 -` - - mergeFilename := test.WriteTempYamlFile(mergeContent) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge -x %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `vars: - variable1: &var1 cat - variable2: &var2 puppy -usage: - value1: *var1 - valueAnother: *var2 - valuePlain: *var2 - value2: *var2 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeOverwriteAndAppendCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge --autocreate=false --arrays=append --overwrite ../examples/data1.yaml ../examples/data2.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: other # just the best -b: [1, 2, 3, 4] -c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -var commentContentA = ` -a: valueA1 # commentA1 -b: valueB1 -` - -var commentContentB = ` -a: valueA2 # commentA2 -b: valueB2 # commentB2 -c: valueC2 # commentC2 -` - -func TestMergeCommentsSetWhenBlankCmd(t *testing.T) { - filename := test.WriteTempYamlFile(commentContentA) - defer test.RemoveTempYamlFile(filename) - - mergeFilename := test.WriteTempYamlFile(commentContentB) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge --comments=setWhenBlank %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: valueA1 # commentA1 -b: valueB1 # commentB2 -c: valueC2 # commentC2 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeCommentsIgnoreCmd(t *testing.T) { - filename := test.WriteTempYamlFile(commentContentA) - defer test.RemoveTempYamlFile(filename) - - mergeFilename := test.WriteTempYamlFile(commentContentB) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge --comments=ignore %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: valueA1 # commentA1 -b: valueB1 -c: valueC2 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeCommentsAppendCmd(t *testing.T) { - filename := test.WriteTempYamlFile(commentContentA) - defer test.RemoveTempYamlFile(filename) - - mergeFilename := test.WriteTempYamlFile(commentContentB) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge --comments=append %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: valueA1 # commentA1 # commentA2 -b: valueB1 # commentB2 -c: valueC2 # commentC2 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeCommentsOverwriteCmd(t *testing.T) { - filename := test.WriteTempYamlFile(commentContentA) - defer test.RemoveTempYamlFile(filename) - - mergeFilename := test.WriteTempYamlFile(commentContentB) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge --comments=overwrite %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: valueA1 # commentA2 -b: valueB1 # commentB2 -c: valueC2 # commentC2 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeOverwriteArraysTooCmd(t *testing.T) { - content := `a: simple # just the best -b: [1, 2] -c: - test: 1 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - mergeContent := `a: things -b: [6]` - mergeFilename := test.WriteTempYamlFile(mergeContent) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge --autocreate=false --arrays=overwrite --overwrite %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `a: things # just the best -b: [6] -c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeRootArraysCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge --arrays=append ../examples/sample_array.yaml ../examples/sample_array_2.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `- 1 -- 2 -- 3 -- 4 -- 5 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeOverwriteArraysCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge --arrays=overwrite ../examples/sample_array.yaml ../examples/sample_array_2.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `- 4 -- 5 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeUpdateArraysCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge -x --arrays=update ../examples/sample_array.yaml ../examples/sample_array_2.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `- 4 -- 5 -- 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeCmd_Multi(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge -d1 ../examples/multiple_docs_small.yaml ../examples/data1.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: Easy! as one two three ---- -another: - document: here -a: simple # just the best -b: [1, 2] -c: - test: 1 ---- -- 1 -- 2 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeYamlMultiAllCmd(t *testing.T) { - content := `b: - c: 3 -apples: green ---- -something: else` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - mergeContent := `apples: red -something: good` - mergeFilename := test.WriteTempYamlFile(mergeContent) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge -d* %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 3 -apples: green -something: good ---- -something: else -apples: red -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeSpecialCharacterKeysCmd(t *testing.T) { - content := `` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - mergeContent := `key[bracket]: value -key.bracket: value -key"value": value -key'value': value -` - mergeFilename := test.WriteTempYamlFile(mergeContent) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, mergeContent, result.Output) -} - -func TestMergeYamlMultiAllOverwriteCmd(t *testing.T) { - content := `b: - c: 3 -apples: green ---- -something: else` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - mergeContent := `apples: red -something: good` - mergeFilename := test.WriteTempYamlFile(mergeContent) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge --overwrite -d* %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 3 -apples: red -something: good ---- -something: good -apples: red -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeYamlNullMapCmd(t *testing.T) { - content := `b:` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - mergeContent := `b: - thing: a frog -` - mergeFilename := test.WriteTempYamlFile(mergeContent) - defer test.RemoveTempYamlFile(mergeFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge %s %s", filename, mergeFilename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, mergeContent, result.Output) -} - -func TestMergeCmd_Error(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge") - if result.Error == nil { - t.Error("Expected command to fail due to missing arg") - } - expectedOutput := `Must provide at least 1 yaml file` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestMergeCmd_ErrorUnreadableFile(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge ../examples/data1.yaml fake-unknown") - if result.Error == nil { - t.Error("Expected command to fail due to unknown file") - } - var expectedOutput string - if runtime.GOOS == "windows" { - expectedOutput = `open fake-unknown: The system cannot find the file specified.` - } else { - expectedOutput = `open fake-unknown: no such file or directory` - } - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestMergeCmd_Inplace(t *testing.T) { - filename := test.WriteTempYamlFile(test.ReadTempYamlFile("../examples/data1.yaml")) - err := os.Chmod(filename, os.FileMode(int(0666))) - if err != nil { - t.Error(err) - } - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("merge -i %s ../examples/data2.yaml", filename)) - if result.Error != nil { - t.Error(result.Error) - } - info, _ := os.Stat(filename) - gotOutput := test.ReadTempYamlFile(filename) - expectedOutput := `a: simple # just the best -b: [1, 2] -c: - test: 1 - toast: leave - tell: 1 - tasty.taco: cool -` - test.AssertResult(t, expectedOutput, gotOutput) - test.AssertResult(t, os.FileMode(int(0666)), info.Mode()) -} - -func TestMergeAllowEmptyTargetCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge ../examples/empty.yaml ../examples/data1.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: simple # just the best -b: [1, 2] -c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestMergeAllowEmptyMergeCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "merge ../examples/data1.yaml ../examples/empty.yaml") - expectedOutput := `a: simple # just the best -b: [1, 2] -c: - test: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} diff --git a/cmd/new.go b/cmd/new.go deleted file mode 100644 index fede2523..00000000 --- a/cmd/new.go +++ /dev/null @@ -1,55 +0,0 @@ -package cmd - -import ( - "github.com/mikefarah/yq/v3/pkg/yqlib" - "github.com/spf13/cobra" -) - -func createNewCmd() *cobra.Command { - var cmdNew = &cobra.Command{ - Use: "new [path] [value]", - Aliases: []string{"n"}, - Short: "yq n [--script/-s script_file] a.b.c newValue", - Example: ` -yq new 'a.b.c' cat -yq n 'a.b.c' --tag '!!str' true # force 'true' to be interpreted as a string instead of bool -yq n 'a.b[+]' cat -yq n -- '--key-starting-with-dash' cat # need to use '--' to stop processing arguments as flags -yq n --script create_script.yaml - `, - Long: `Creates a new yaml w.r.t the given path and value. -Outputs to STDOUT - -Create Scripts: -Note that you can give a create script to perform more sophisticated yaml. This follows the same format as the update script. -`, - RunE: newProperty, - } - cmdNew.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for creating yaml") - cmdNew.PersistentFlags().StringVarP(&customTag, "tag", "t", "", "set yaml tag (e.g. !!int)") - cmdNew.PersistentFlags().StringVarP(&customStyle, "style", "", "", "formatting style of the value: single, double, folded, flow, literal, tagged") - cmdNew.PersistentFlags().StringVarP(&anchorName, "anchorName", "", "", "anchor name") - cmdNew.PersistentFlags().BoolVarP(&makeAlias, "makeAlias", "", false, "create an alias using the value as the anchor name") - return cmdNew -} - -func newProperty(cmd *cobra.Command, args []string) error { - var badArgsMessage = "Must provide " - var updateCommands, updateCommandsError = readUpdateCommands(args, 2, badArgsMessage, false) - if updateCommandsError != nil { - return updateCommandsError - } - newNode := lib.New(updateCommands[0].Path) - - for _, updateCommand := range updateCommands { - - errorUpdating := lib.Update(&newNode, updateCommand, true) - - if errorUpdating != nil { - return errorUpdating - } - } - - var encoder = yqlib.NewYamlEncoder(cmd.OutOrStdout(), indent, colorsEnabled) - return encoder.Encode(&newNode) -} diff --git a/cmd/new_test.go b/cmd/new_test.go deleted file mode 100644 index bdd9f274..00000000 --- a/cmd/new_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package cmd - -import ( - "fmt" - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -func TestNewCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "new b.c 3") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestNewCmdScript(t *testing.T) { - updateScript := `- command: update - path: b.c - value: 7` - scriptFilename := test.WriteTempYamlFile(updateScript) - defer test.RemoveTempYamlFile(scriptFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("new --script %s", scriptFilename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 7 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestNewAnchorCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "new b.c 3 --anchorName=fred") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: &fred 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestNewAliasCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "new b.c foo --makeAlias") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: *foo -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestNewArrayCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "new b[0] 3") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - - 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestNewCmd_Error(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "new b.c") - if result.Error == nil { - t.Error("Expected command to fail due to missing arg") - } - expectedOutput := `Must provide ` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestNewWithTaggedStyleCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "new b.c cat --tag=!!str --style=tagged") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: !!str cat -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestNewWithDoubleQuotedStyleCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "new b.c cat --style=double") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: "cat" -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestNewWithSingleQuotedStyleCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "new b.c cat --style=single") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 'cat' -` - test.AssertResult(t, expectedOutput, result.Output) -} diff --git a/cmd/prefix.go b/cmd/prefix.go deleted file mode 100644 index ae5c28ae..00000000 --- a/cmd/prefix.go +++ /dev/null @@ -1,50 +0,0 @@ -package cmd - -import ( - "github.com/mikefarah/yq/v3/pkg/yqlib" - errors "github.com/pkg/errors" - "github.com/spf13/cobra" - yaml "gopkg.in/yaml.v3" -) - -func createPrefixCmd() *cobra.Command { - var cmdPrefix = &cobra.Command{ - Use: "prefix [yaml_file] [path]", - Aliases: []string{"p"}, - Short: "yq p [--inplace/-i] [--doc/-d index] sample.yaml a.b.c", - Example: ` -yq prefix things.yaml 'a.b.c' -yq prefix --inplace things.yaml 'a.b.c' -yq prefix --inplace -- things.yaml '--key-starting-with-dash' # need to use '--' to stop processing arguments as flags -yq p -i things.yaml 'a.b.c' -yq p --doc 2 things.yaml 'a.b.d' -yq p -d2 things.yaml 'a.b.d' - `, - Long: `Prefixes w.r.t to the yaml file at the given path. -Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. -`, - RunE: prefixProperty, - } - cmdPrefix.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") - cmdPrefix.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") - return cmdPrefix -} - -func prefixProperty(cmd *cobra.Command, args []string) error { - - if len(args) < 2 { - return errors.New("Must provide ") - } - updateCommand := yqlib.UpdateCommand{Command: "update", Path: args[1]} - log.Debugf("args %v", args) - - var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() - if errorParsingDocIndex != nil { - return errorParsingDocIndex - } - - var updateData = func(dataBucket *yaml.Node, currentIndex int) error { - return prefixDocument(updateAll, docIndexInt, currentIndex, dataBucket, updateCommand) - } - return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) -} diff --git a/cmd/prefix_test.go b/cmd/prefix_test.go deleted file mode 100644 index ee5c1989..00000000 --- a/cmd/prefix_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package cmd - -import ( - "fmt" - "runtime" - "strings" - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -func TestPrefixCmd(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("prefix %s d", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `d: - b: - c: 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestPrefixCmdArray(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("prefix %s [+].d.[+]", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `- d: - - b: - c: 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestPrefixCmd_MultiLayer(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("prefix %s d.e.f", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `d: - e: - f: - b: - c: 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestPrefixMultiCmd(t *testing.T) { - content := `b: - c: 3 ---- -apples: great -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("prefix %s -d 1 d", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 3 ---- -d: - apples: great -` - test.AssertResult(t, expectedOutput, result.Output) -} -func TestPrefixInvalidDocumentIndexCmd(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("prefix %s -df d", filename)) - if result.Error == nil { - t.Error("Expected command to fail due to invalid path") - } - expectedOutput := `Document index f is not a integer or *: strconv.ParseInt: parsing "f": invalid syntax` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestPrefixBadDocumentIndexCmd(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("prefix %s -d 1 d", filename)) - if result.Error == nil { - t.Error("Expected command to fail due to invalid path") - } - expectedOutput := `asked to process document index 1 but there are only 1 document(s)` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} -func TestPrefixMultiAllCmd(t *testing.T) { - content := `b: - c: 3 ---- -apples: great -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("prefix %s -d * d", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `d: - b: - c: 3 ---- -d: - apples: great` - test.AssertResult(t, expectedOutput, strings.Trim(result.Output, "\n ")) -} - -func TestPrefixCmd_Error(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "prefix") - if result.Error == nil { - t.Error("Expected command to fail due to missing arg") - } - expectedOutput := `Must provide ` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestPrefixCmd_ErrorUnreadableFile(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "prefix fake-unknown a.b") - if result.Error == nil { - t.Error("Expected command to fail due to unknown file") - } - var expectedOutput string - if runtime.GOOS == "windows" { - expectedOutput = `open fake-unknown: The system cannot find the file specified.` - } else { - expectedOutput = `open fake-unknown: no such file or directory` - } - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestPrefixCmd_Inplace(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("prefix -i %s d", filename)) - if result.Error != nil { - t.Error(result.Error) - } - gotOutput := test.ReadTempYamlFile(filename) - expectedOutput := `d: - b: - c: 3` - test.AssertResult(t, expectedOutput, strings.Trim(gotOutput, "\n ")) -} diff --git a/cmd/read.go b/cmd/read.go deleted file mode 100644 index 8e68f77c..00000000 --- a/cmd/read.go +++ /dev/null @@ -1,66 +0,0 @@ -package cmd - -import ( - errors "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -func createReadCmd() *cobra.Command { - var cmdRead = &cobra.Command{ - Use: "read [yaml_file] [path_expression]", - Aliases: []string{"r"}, - Short: "yq r [--printMode/-p pv] sample.yaml 'b.e(name==fr*).value'", - Example: ` -yq read things.yaml 'a.b.c' -yq r - 'a.b.c' # reads from stdin -yq r things.yaml 'a.*.c' -yq r things.yaml 'a.**.c' # deep splat -yq r things.yaml 'a.(child.subchild==co*).c' -yq r -d1 things.yaml 'a.array[0].blah' -yq r things.yaml 'a.array[*].blah' -yq r -- things.yaml '--key-starting-with-dashes.blah' - `, - Long: "Outputs the value of the given path in the yaml file to STDOUT", - RunE: readProperty, - } - cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") - cmdRead.PersistentFlags().StringVarP(&printMode, "printMode", "p", "v", "print mode (v (values, default), p (paths), pv (path and value pairs)") - cmdRead.PersistentFlags().StringVarP(&defaultValue, "defaultValue", "D", "", "default value printed when there are no results") - cmdRead.PersistentFlags().BoolVarP(&printLength, "length", "l", false, "print length of results") - cmdRead.PersistentFlags().BoolVarP(&collectIntoArray, "collect", "c", false, "collect results into array") - cmdRead.PersistentFlags().BoolVarP(&unwrapScalar, "unwrapScalar", "", true, "unwrap scalar, print the value with no quotes, colors or comments") - cmdRead.PersistentFlags().BoolVarP(&stripComments, "stripComments", "", false, "print yaml without any comments") - cmdRead.PersistentFlags().BoolVarP(&explodeAnchors, "explodeAnchors", "X", false, "explode anchors") - cmdRead.PersistentFlags().BoolVarP(&exitStatus, "exitStatus", "e", false, "set exit status if no matches are found") - return cmdRead -} - -func readProperty(cmd *cobra.Command, args []string) error { - var path = "" - - if len(args) < 1 { - return errors.New("Must provide filename") - } else if len(args) > 1 { - path = args[1] - } - - var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() - if errorParsingDocIndex != nil { - return errorParsingDocIndex - } - - matchingNodes, errorReadingStream := readYamlFile(args[0], path, updateAll, docIndexInt) - - if exitStatus && len(matchingNodes) == 0 { - cmd.SilenceUsage = true - return errors.New("No matches found") - } - - if errorReadingStream != nil { - cmd.SilenceUsage = true - return errorReadingStream - } - out := cmd.OutOrStdout() - - return printResults(matchingNodes, out) -} diff --git a/cmd/read_test.go b/cmd/read_test.go deleted file mode 100644 index 544fdb89..00000000 --- a/cmd/read_test.go +++ /dev/null @@ -1,1463 +0,0 @@ -package cmd - -import ( - "fmt" - "runtime" - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -func TestReadCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/sample.yaml b.c") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "2\n", result.Output) -} - -func TestReadCmdWithExitStatus(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/sample.yaml b.c -e") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "2\n", result.Output) -} - -func TestReadCmdWithExitStatusNotExist(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/sample.yaml caterpillar -e") - test.AssertResult(t, "No matches found", result.Error.Error()) -} - -func TestReadCmdNotExist(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/sample.yaml caterpillar") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "", result.Output) -} - -func TestReadUnwrapCmd(t *testing.T) { - - content := `b: 'frog' # my favourite` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s b --unwrapScalar=false", filename)) - - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "'frog' # my favourite\n", result.Output) -} - -func TestReadStripCommentsCmd(t *testing.T) { - - content := `# this is really cool -b: # my favourite - c: 5 # cats -# blah -` - - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s --stripComments", filename)) - - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 5 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadUnwrapJsonByDefaultCmd(t *testing.T) { - - content := `b: 'frog' # my favourite` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s b -j", filename)) - - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "\"frog\"\n", result.Output) -} - -func TestReadOutputJsonNonStringKeysCmd(t *testing.T) { - - content := ` -true: true -5: - null: - 0.1: deeply - false: things` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s -j", filename)) - - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `{"true":true,"5":{"null":{"0.1":"deeply","false":"things"}}} -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadWithAdvancedFilterCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/sample.yaml b.e(name==sam).value") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "4\n", result.Output) -} - -func TestReadWithAdvancedFilterMapCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/sample.yaml b.e[name==fr*]") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `name: fred -value: 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadWithKeyAndValueCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p pv ../examples/sample.yaml b.c") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "b.c: 2\n", result.Output) -} - -func TestReadArrayCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p pv ../examples/sample.yaml b.e[1].name") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "b.e.[1].name: sam\n", result.Output) -} - -func TestReadArrayBackwardsCmd(t *testing.T) { - content := `- one -- two -- three` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -p pv %s [-1]", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "'[-1]': three\n", result.Output) -} - -func TestReadArrayBackwardsNegative0Cmd(t *testing.T) { - content := `- one -- two -- three` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -p pv %s [-0]", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "'[0]': one\n", result.Output) -} - -func TestReadArrayBackwardsPastLimitCmd(t *testing.T) { - content := `- one -- two -- three` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -p pv %s [-4]", filename)) - expectedOutput := "Error reading path in document index 0: Index [-4] out of range, array size is 3" - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestReadArrayLengthCmd(t *testing.T) { - content := `- things -- whatever -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -l %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "2\n", result.Output) -} - -func TestReadArrayLengthDeepCmd(t *testing.T) { - content := `holder: -- things -- whatever -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -l %s holder", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "2\n", result.Output) -} - -func TestReadArrayLengthDeepMultipleCmd(t *testing.T) { - content := `holderA: -- things -- whatever -skipMe: -- yep -holderB: -- other things -- cool -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -l -c %s holder*", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "2\n", result.Output) -} - -func TestReadCollectCmd(t *testing.T) { - content := `holderA: yep -skipMe: not me -holderB: me too -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -c %s holder*", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `- yep -- me too -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCollectArrayCmd(t *testing.T) { - content := `- name: fred - value: 32 -- name: sam - value: 67 -- name: fernie - value: 103 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -c %s (name==f*)", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `- name: fred - value: 32 -- name: fernie - value: 103 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadArrayLengthDeepMultipleWithPathCmd(t *testing.T) { - content := `holderA: -- things -- whatever -holderB: -- other things -- cool -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -l %s -ppv holder*", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "holderA: 2\nholderB: 2\n", result.Output) -} - -func TestReadObjectLengthCmd(t *testing.T) { - content := `cat: meow -dog: bark -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -l %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "2\n", result.Output) -} - -func TestReadObjectLengthDeepCmd(t *testing.T) { - content := `holder: - cat: meow - dog: bark -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -l %s holder", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "2\n", result.Output) -} - -func TestReadObjectLengthDeepMultipleCmd(t *testing.T) { - content := `holderA: - cat: meow - dog: bark -holderB: - elephant: meow - zebra: bark -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -l -c %s holder*", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "2\n", result.Output) -} - -func TestReadObjectLengthDeepMultipleWithPathsCmd(t *testing.T) { - content := `holderA: - cat: meow - dog: bark -holderB: - elephant: meow - zebra: bark -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -l -ppv %s holder*", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "holderA: 2\nholderB: 2\n", result.Output) -} - -func TestReadScalarLengthCmd(t *testing.T) { - content := `meow` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -l %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "4\n", result.Output) -} - -func TestReadDoubleQuotedStringCmd(t *testing.T) { - content := `name: "meow face"` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s name", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "meow face\n", result.Output) -} - -func TestReadSingleQuotedStringCmd(t *testing.T) { - content := `name: 'meow face'` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s name", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "meow face\n", result.Output) -} - -func TestReadQuotedMultinlineStringCmd(t *testing.T) { - content := `test: | - abcdefg - hijklmno -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s test", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `abcdefg -hijklmno - -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadQuotedMultinlineNoNewLineStringCmd(t *testing.T) { - content := `test: |- - abcdefg - hijklmno -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s test", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `abcdefg -hijklmno -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadBooleanCmd(t *testing.T) { - content := `name: true` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s name", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "true\n", result.Output) -} - -func TestReadNumberCmd(t *testing.T) { - content := `name: 32.13` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s name", filename)) - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "32.13\n", result.Output) -} - -func TestReadDeepSplatCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p pv ../examples/sample.yaml b.**") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b.c: 2 -b.d.[0]: 3 -b.d.[1]: 4 -b.d.[2]: 5 -b.e.[0].name: fred -b.e.[0].value: 3 -b.e.[1].name: sam -b.e.[1].value: 4 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadDeepSplatWithSuffixCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p pv ../examples/sample.yaml b.**.name") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b.e.[0].name: fred -b.e.[1].name: sam -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadWithKeyCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p p ../examples/sample.yaml b.c") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "b.c\n", result.Output) -} - -func TestReadAnchorsCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/simple-anchor.yaml foobar.a") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "1\n", result.Output) -} - -func TestReadAnchorsWithKeyAndValueCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p pv ../examples/simple-anchor.yaml foobar.a") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "foobar.a: 1\n", result.Output) -} - -func TestReadAllAnchorsWithKeyAndValueCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p pv ../examples/merge-anchor.yaml **") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `foo.a: original -foo.thing: coolasdf -foo.thirsty: yep -bar.b: 2 -bar.thing: coconut -bar.c: oldbar -foobarList.c: newbar -foobarList.b: 2 -foobarList.thing: coconut -foobarList.a: original -foobarList.thirsty: yep -foobar.thirty: well beyond -foobar.thing: ice -foobar.c: 3 -foobar.a: original -foobar.thirsty: yep -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsOriginalCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/merge-anchor.yaml foobar.a") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "original\n", result.Output) -} - -func TestReadMergeAnchorsExplodeJsonCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -j ../examples/merge-anchor.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `{"foo":{"a":"original","thing":"coolasdf","thirsty":"yep"},"bar":{"b":2,"thing":"coconut","c":"oldbar"},"foobarList":{"c":"newbar","b":2,"thing":"coconut","a":"original","thirsty":"yep"},"foobar":{"thirty":"well beyond","thing":"ice","c":3,"a":"original","thirsty":"yep"}} -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsExplodeSimpleCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -X ../examples/simple-anchor.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `foo: - a: 1 -foobar: - a: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsExplodeSimpleValueCmd(t *testing.T) { - content := `value: &value-pointer the value -pointer: *value-pointer` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -X %s pointer", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := "the value\n" - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsExplodeMissingCmd(t *testing.T) { - content := `a: - <<: &anchor - c: d - e: f -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -X %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: - c: d - e: f -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsExplodeKeyCmd(t *testing.T) { - content := `name: &nameField Mike -*nameField: Great Guy` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -X %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `name: Mike -Mike: Great Guy -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsExplodeSimpleArrayCmd(t *testing.T) { - content := `- things` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -X %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `- things -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadNumberKeyJsonCmd(t *testing.T) { - content := `data: {"40433437326": 10.833332}` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -j %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `{"data":{"40433437326":10.833332}} -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsExplodeSimpleArrayJsonCmd(t *testing.T) { - content := `- things` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -j %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `["things"] -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsExplodeSimpleValueForValueCmd(t *testing.T) { - content := `value: &value-pointer the value -pointer: *value-pointer` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -X %s value", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := "the value\n" - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsExplodeCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -X ../examples/merge-anchor.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `foo: - a: original - thing: coolasdf - thirsty: yep -bar: - b: 2 - thing: coconut - c: oldbar -foobarList: - c: newbar - b: 2 - thing: coconut - a: original - thirsty: yep -foobar: - thirty: well beyond - thing: ice - c: 3 - a: original - thirsty: yep -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsExplodeDeepCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -X ../examples/merge-anchor.yaml foobar") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `thirty: well beyond -thing: ice -c: 3 -a: original -thirsty: yep -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsOverrideCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/merge-anchor.yaml foobar.thing") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "ice\n", result.Output) -} - -func TestReadMergeAnchorsPrefixMatchCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "r -p pv ../examples/merge-anchor.yaml foobar.th*") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `foobar.thirty: well beyond -foobar.thing: ice -foobar.thirsty: yep -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadMergeAnchorsListOriginalCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/merge-anchor.yaml foobarList.a") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "original\n", result.Output) -} - -func TestReadMergeAnchorsListOverrideInListCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/merge-anchor.yaml foobarList.thing") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "coconut\n", result.Output) -} - -func TestReadMergeAnchorsListOverrideCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/merge-anchor.yaml foobarList.c") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "newbar\n", result.Output) -} - -func TestReadInvalidDocumentIndexCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -df ../examples/sample.yaml b.c") - if result.Error == nil { - t.Error("Expected command to fail due to invalid path") - } - expectedOutput := `Document index f is not a integer or *: strconv.ParseInt: parsing "f": invalid syntax` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestReadBadDocumentIndexCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -d1 ../examples/sample.yaml b.c") - if result.Error == nil { - t.Error("Expected command to fail due to invalid path") - } - expectedOutput := `Could not process document index 1 as there are only 1 document(s)` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestReadOrderCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/order.yaml") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, - `version: 3 -application: MyApp -`, - result.Output) -} - -func TestReadMultiCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -d 1 ../examples/multiple_docs.yaml another.document") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "here\n", result.Output) -} - -func TestReadMultiWithKeyAndValueCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p vp -d 1 ../examples/multiple_docs.yaml another.document") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "another.document: here\n", result.Output) -} - -func TestReadMultiAllCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -d* ../examples/multiple_docs.yaml commonKey") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, - `first document -second document -third document -`, result.Output) -} - -func TestReadMultiAllWithKeyAndValueCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p pv -d* ../examples/multiple_docs.yaml commonKey") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, - `commonKey: first document -commonKey: second document -commonKey: third document -`, result.Output) -} - -func TestReadCmd_ArrayYaml(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/array.yaml [0].gather_facts") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "false\n", result.Output) -} - -func TestReadEmptyContentCmd(t *testing.T) { - content := `` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadEmptyNodesPrintPathCmd(t *testing.T) { - content := `map: - that: {} -array: - great: [] -null: - indeed: ~` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s -ppv **", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `map.that: {} -array.great: [] -null.indeed: ~ -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadEmptyContentWithDefaultValueCmd(t *testing.T) { - content := `` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read --defaultValue things %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `things` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadPrettyPrintCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -P ../examples/sample.json") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: Easy! as one two three -b: - c: 2 - d: - - 3 - - 4 - e: - - name: fred - value: 3 - - name: sam - value: 4 -ab: must appear last -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadNotFoundWithExitStatus(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/sample.yaml adsf -e") - if result.Error == nil { - t.Error("Expected command to fail") - } - expectedOutput := `No matches found` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestReadNotFoundWithoutExitStatus(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/sample.yaml adsf") - if result.Error != nil { - t.Error("Expected command to succeed!") - } -} - -func TestReadPrettyPrintWithIndentCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -P -I4 ../examples/sample.json") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: Easy! as one two three -b: - c: 2 - d: - - 3 - - 4 - e: - - name: fred - value: 3 - - name: sam - value: 4 -ab: must appear last -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCmd_ArrayYaml_NoPath(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/array.yaml") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `- become: true - gather_facts: false - hosts: lalaland - name: "Apply smth" - roles: - - lala - - land - serial: 1 -- become: false - gather_facts: true -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCmd_ArrayYaml_OneElement(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/array.yaml [0]") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `become: true -gather_facts: false -hosts: lalaland -name: "Apply smth" -roles: - - lala - - land -serial: 1 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCmd_ArrayYaml_SplatCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/array.yaml [*]") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `become: true -gather_facts: false -hosts: lalaland -name: "Apply smth" -roles: - - lala - - land -serial: 1 -become: false -gather_facts: true -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCmd_ArrayYaml_SplatWithKeyAndValueCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p pv ../examples/array.yaml [*]") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `'[0]': - become: true - gather_facts: false - hosts: lalaland - name: "Apply smth" - roles: - - lala - - land - serial: 1 -'[1]': - become: false - gather_facts: true -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCmd_ArrayYaml_SplatWithKeyCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p p ../examples/array.yaml [*]") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `[0] -[1] -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCmd_ArrayYaml_SplatKey(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/array.yaml [*].gather_facts") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `false -true -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCmd_ArrayYaml_ErrorBadPath(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/array.yaml [x].gather_facts") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCmd_ArrayYaml_Splat_ErrorBadPath(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ../examples/array.yaml [*].roles[x]") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadCmd_Error(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read") - if result.Error == nil { - t.Error("Expected command to fail due to missing arg") - } - expectedOutput := `Must provide filename` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestReadCmd_ErrorEmptyFilename(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read ") - if result.Error == nil { - t.Error("Expected command to fail due to missing arg") - } - expectedOutput := `Must provide filename` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestReadCmd_ErrorUnreadableFile(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read fake-unknown") - if result.Error == nil { - t.Error("Expected command to fail due to unknown file") - } - var expectedOutput string - if runtime.GOOS == "windows" { - expectedOutput = `open fake-unknown: The system cannot find the file specified.` - } else { - expectedOutput = `open fake-unknown: no such file or directory` - } - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestReadCmd_ErrorBadPath(t *testing.T) { - content := `b: - d: - e: - - 3 - - 4 - f: - - 1 - - 2 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s b.d.*.[x]", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadToJsonCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -j ../examples/sample.yaml b") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `{"c":2,"d":[3,4,5],"e":[{"name":"fred","value":3},{"name":"sam","value":4}]} -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadToJsonPrettyCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -j -P ../examples/sample.yaml b") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `{ - "c": 2, - "d": [ - 3, - 4, - 5 - ], - "e": [ - { - "name": "fred", - "value": 3 - }, - { - "name": "sam", - "value": 4 - } - ] -} -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadToJsonPrettyIndentCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "read -j -I4 -P ../examples/sample.yaml b") - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `{ - "c": 2, - "d": [ - 3, - 4, - 5 - ], - "e": [ - { - "name": "fred", - "value": 3 - }, - { - "name": "sam", - "value": 4 - } - ] -} -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadBadDataCmd(t *testing.T) { - content := `[!Whatever]` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s", filename)) - if result.Error == nil { - t.Error("Expected command to fail") - } - expectedOutput := `yaml: line 1: did not find expected ',' or ']'` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestReadDeepFromRootCmd(t *testing.T) { - content := `state: - country: - city: foo -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s (**.city==foo)", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `country: - city: foo -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadSplatPrefixCmd(t *testing.T) { - content := `a: 2 -b: - hi: - c: things - d: something else - there: - c: more things - d: more something else - there2: - c: more things also - d: more something else also -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read %s b.there*.c", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `more things -more things also -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadSplatPrefixWithKeyAndValueCmd(t *testing.T) { - content := `a: 2 -b: - hi: - c: things - d: something else - there: - c: more things - d: more something else - there2: - c: more things also - d: more something else also -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -p pv %s b.there*.c", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `b.there.c: more things -b.there2.c: more things also -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadSplatPrefixWithKeyCmd(t *testing.T) { - content := `a: 2 -b: - hi: - c: things - d: something else - there: - c: more things - d: more something else - there2: - c: more things also - d: more something else also -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -p p %s b.there*.c", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `b.there.c -b.there2.c -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadExpression(t *testing.T) { - content := `name: value` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("r %s (x==f)", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadFindValueArrayCmd(t *testing.T) { - content := `- cat -- dog -- rat -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("r %s (.==dog)", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `dog -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadFindValueDeepArrayCmd(t *testing.T) { - content := `animals: - - cat - - dog - - rat -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("r %s animals(.==dog)", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `dog -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadFindValueDeepObjectCmd(t *testing.T) { - content := `animals: - great: yes - small: sometimes -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("r %s animals(.==yes) -ppv", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `animals.great: yes -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestReadKeepsKeyOrderInJson(t *testing.T) { - const content = `{ - "z": "One", - "a": 1, - "w": ["a", "r"], - "u": {"d": "o", "0": 11.5}, -}` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("r -j %s", filename)) - if result.Error != nil { - t.Error(result.Error) - } - - expectedOutput := `{"z":"One","a":1,"w":["a","r"],"u":{"d":"o","0":11.5}} -` - test.AssertResult(t, expectedOutput, result.Output) -} diff --git a/cmd/root.go b/cmd/root.go index a7f9b6bd..9dbe6521 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,8 +18,8 @@ func New() *cobra.Command { return nil } cmd.Println(cmd.UsageString()) - return nil + }, PersistentPreRun: func(cmd *cobra.Command, args []string) { cmd.SetOut(cmd.OutOrStdout()) @@ -40,22 +40,21 @@ func New() *cobra.Command { } rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose mode") - rootCmd.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json. By default it prints a json document in one line, use the prettyPrint flag to print a formatted doc.") - rootCmd.PersistentFlags().BoolVarP(&prettyPrint, "prettyPrint", "P", false, "pretty print") + rootCmd.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json. Set indent to 0 to print json in one line.") + rootCmd.PersistentFlags().BoolVarP(&nullInput, "null-input", "n", false, "Don't read input, simply evaluate the expression given. Useful for creating yaml docs from scratch.") + rootCmd.PersistentFlags().BoolVarP(&noDocSeparators, "no-doc", "N", false, "Don't print document separators (---)") + 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.PersistentFlags().BoolVarP(&colorsEnabled, "colors", "C", false, "print with colors") + rootCmd.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace of first yaml file given.") + rootCmd.PersistentFlags().BoolVarP(&exitStatus, "exit-status", "e", false, "set exit status if there are no matches or null or false is returned") + 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( - createReadCmd(), - createCompareCmd(), - createValidateCmd(), - createWriteCmd(), - createPrefixCmd(), - createDeleteCmd(), - createNewCmd(), - createMergeCmd(), - createBashCompletionCmd(rootCmd), + createEvaluateSequenceCommand(), + createEvaluateAllCommand(), + completionCmd, ) return rootCmd } diff --git a/cmd/shell-completion.go b/cmd/shell-completion.go new file mode 100644 index 00000000..0fadfb12 --- /dev/null +++ b/cmd/shell-completion.go @@ -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 + + }, +} diff --git a/cmd/shell_completion.go b/cmd/shell_completion.go deleted file mode 100644 index d1d287b3..00000000 --- a/cmd/shell_completion.go +++ /dev/null @@ -1,57 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" -) - -var shellVariant = "bash" - -func createBashCompletionCmd(rootCmd *cobra.Command) *cobra.Command { - var completionCmd = &cobra.Command{ - Use: "shell-completion", - Short: "Generates shell completion scripts", - Long: `To load completion for: -bash: - Run - . <(yq shell-completion) - - To configure your bash shell to load completions for each session add to - your bashrc - - # ~/.bashrc or ~/.profile - . <(yq shell-completion) - -zsh: - The generated completion script should be put somewhere in your $fpath named _yq - -powershell: - Users need PowerShell version 5.0 or above, which comes with Windows 10 and - can be downloaded separately for Windows 7 or 8.1. They can then write the - completions to a file and source this file from their PowerShell profile, - which is referenced by the $Profile environment variable. - -fish: - Save the output to a fish file and add it to your completions directory. - - `, - RunE: func(cmd *cobra.Command, args []string) error { - switch shellVariant { - case "bash", "": - return rootCmd.GenBashCompletion(os.Stdout) - case "zsh": - return rootCmd.GenZshCompletion(os.Stdout) - case "fish": - return rootCmd.GenFishCompletion(os.Stdout, true) - case "powershell": - return rootCmd.GenPowerShellCompletion(os.Stdout) - default: - return fmt.Errorf("Unknown variant %v", shellVariant) - } - }, - } - completionCmd.PersistentFlags().StringVarP(&shellVariant, "variation", "V", "", "shell variation: bash (default), zsh, fish, powershell") - return completionCmd -} diff --git a/cmd/utils.go b/cmd/utils.go deleted file mode 100644 index 9b1ca726..00000000 --- a/cmd/utils.go +++ /dev/null @@ -1,619 +0,0 @@ -package cmd - -import ( - "bufio" - "fmt" - "io" - "io/ioutil" - "os" - "strconv" - - "github.com/mikefarah/yq/v3/pkg/yqlib" - errors "github.com/pkg/errors" - yaml "gopkg.in/yaml.v3" -) - -type readDataFn func(dataBucket *yaml.Node) ([]*yqlib.NodeContext, error) - -func createReadFunction(path string) func(*yaml.Node) ([]*yqlib.NodeContext, error) { - return func(dataBucket *yaml.Node) ([]*yqlib.NodeContext, error) { - return lib.Get(dataBucket, path) - } -} - -func readYamlFile(filename string, path string, updateAll bool, docIndexInt int) ([]*yqlib.NodeContext, error) { - return doReadYamlFile(filename, createReadFunction(path), updateAll, docIndexInt) -} - -func doReadYamlFile(filename string, readFn readDataFn, updateAll bool, docIndexInt int) ([]*yqlib.NodeContext, error) { - var matchingNodes []*yqlib.NodeContext - - var currentIndex = 0 - var errorReadingStream = readStream(filename, func(decoder *yaml.Decoder) error { - for { - var dataBucket yaml.Node - errorReading := decoder.Decode(&dataBucket) - - if errorReading == io.EOF { - return handleEOF(updateAll, docIndexInt, currentIndex) - } else if errorReading != nil { - return errorReading - } - - var errorParsing error - matchingNodes, errorParsing = appendDocument(matchingNodes, dataBucket, readFn, updateAll, docIndexInt, currentIndex) - if errorParsing != nil { - return errorParsing - } - if !updateAll && currentIndex == docIndexInt { - log.Debug("all done") - return nil - } - currentIndex = currentIndex + 1 - } - }) - return matchingNodes, errorReadingStream -} - -func handleEOF(updateAll bool, docIndexInt int, currentIndex int) error { - log.Debugf("done %v / %v", currentIndex, docIndexInt) - if !updateAll && currentIndex <= docIndexInt && docIndexInt != 0 { - return fmt.Errorf("Could not process document index %v as there are only %v document(s)", docIndex, currentIndex) - } - return nil -} - -func appendDocument(originalMatchingNodes []*yqlib.NodeContext, dataBucket yaml.Node, readFn readDataFn, updateAll bool, docIndexInt int, currentIndex int) ([]*yqlib.NodeContext, error) { - log.Debugf("processing document %v - requested index %v", currentIndex, docIndexInt) - yqlib.DebugNode(&dataBucket) - if !updateAll && currentIndex != docIndexInt { - return originalMatchingNodes, nil - } - log.Debugf("reading in document %v", currentIndex) - matchingNodes, errorParsing := readFn(&dataBucket) - if errorParsing != nil { - return nil, errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex) - } - return append(originalMatchingNodes, matchingNodes...), nil -} - -func lengthOf(node *yaml.Node) int { - kindToCheck := node.Kind - if node.Kind == yaml.DocumentNode && len(node.Content) == 1 { - log.Debugf("length of document node, calculating length of child") - kindToCheck = node.Content[0].Kind - } - switch kindToCheck { - case yaml.ScalarNode: - return len(node.Value) - case yaml.MappingNode: - return len(node.Content) / 2 - default: - return len(node.Content) - } -} - -// transforms node before printing, if required -func transformNode(node *yaml.Node) *yaml.Node { - if printLength { - return &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", lengthOf(node))} - } - return node -} - -func printNode(node *yaml.Node, writer io.Writer) error { - var encoder yqlib.Encoder - if node.Kind == yaml.ScalarNode && unwrapScalar && !outputToJSON { - return writeString(writer, node.Value+"\n") - } - if outputToJSON { - encoder = yqlib.NewJsonEncoder(writer, prettyPrint, indent) - } else { - encoder = yqlib.NewYamlEncoder(writer, indent, colorsEnabled) - } - return encoder.Encode(node) -} - -func removeComments(matchingNodes []*yqlib.NodeContext) { - for _, nodeContext := range matchingNodes { - removeCommentOfNode(nodeContext.Node) - } -} - -func removeCommentOfNode(node *yaml.Node) { - node.HeadComment = "" - node.LineComment = "" - node.FootComment = "" - - for _, child := range node.Content { - removeCommentOfNode(child) - } -} - -func setStyle(matchingNodes []*yqlib.NodeContext, style yaml.Style) { - for _, nodeContext := range matchingNodes { - updateStyleOfNode(nodeContext.Node, style) - } -} - -func updateStyleOfNode(node *yaml.Node, style yaml.Style) { - node.Style = style - - for _, child := range node.Content { - updateStyleOfNode(child, style) - } -} - -func writeString(writer io.Writer, txt string) error { - _, errorWriting := writer.Write([]byte(txt)) - return errorWriting -} - -func setIfNotThere(node *yaml.Node, key string, value *yaml.Node) { - for index := 0; index < len(node.Content); index = index + 2 { - keyNode := node.Content[index] - if keyNode.Value == key { - return - } - } - // need to add it to the map - mapEntryKey := yaml.Node{Value: key, Kind: yaml.ScalarNode} - node.Content = append(node.Content, &mapEntryKey) - node.Content = append(node.Content, value) -} - -func applyAlias(node *yaml.Node, alias *yaml.Node) { - if alias == nil { - return - } - for index := 0; index < len(alias.Content); index = index + 2 { - keyNode := alias.Content[index] - log.Debugf("applying alias key %v", keyNode.Value) - valueNode := alias.Content[index+1] - setIfNotThere(node, keyNode.Value, valueNode) - } -} - -func explodeNode(node *yaml.Node) error { - node.Anchor = "" - switch node.Kind { - case yaml.SequenceNode, yaml.DocumentNode: - for index, contentNode := range node.Content { - log.Debugf("exploding index %v", index) - errorInContent := explodeNode(contentNode) - if errorInContent != nil { - return errorInContent - } - } - return nil - case yaml.AliasNode: - log.Debugf("its an alias!") - if node.Alias != nil { - node.Kind = node.Alias.Kind - node.Style = node.Alias.Style - node.Tag = node.Alias.Tag - node.Content = node.Alias.Content - node.Value = node.Alias.Value - node.Alias = nil - } - return nil - case yaml.MappingNode: - for index := 0; index < len(node.Content); index = index + 2 { - keyNode := node.Content[index] - valueNode := node.Content[index+1] - log.Debugf("traversing %v", keyNode.Value) - if keyNode.Value != "<<" { - errorInContent := explodeNode(valueNode) - if errorInContent != nil { - return errorInContent - } - errorInContent = explodeNode(keyNode) - if errorInContent != nil { - return errorInContent - } - } else { - if valueNode.Kind == yaml.SequenceNode { - log.Debugf("an alias merge list!") - for index := len(valueNode.Content) - 1; index >= 0; index = index - 1 { - aliasNode := valueNode.Content[index] - applyAlias(node, aliasNode.Alias) - } - } else { - log.Debugf("an alias merge!") - applyAlias(node, valueNode.Alias) - } - node.Content = append(node.Content[:index], node.Content[index+2:]...) - //replay that index, since the array is shorter now. - index = index - 2 - } - } - - return nil - default: - return nil - } -} - -func explode(matchingNodes []*yqlib.NodeContext) error { - log.Debug("exploding nodes") - for _, nodeContext := range matchingNodes { - log.Debugf("exploding %v", nodeContext.Head) - errorExplodingNode := explodeNode(nodeContext.Node) - if errorExplodingNode != nil { - return errorExplodingNode - } - } - return nil -} - -func printResults(matchingNodes []*yqlib.NodeContext, writer io.Writer) error { - if prettyPrint { - setStyle(matchingNodes, 0) - } - - if stripComments { - removeComments(matchingNodes) - } - - //always explode anchors when printing json - if explodeAnchors || outputToJSON { - errorExploding := explode(matchingNodes) - if errorExploding != nil { - return errorExploding - } - } - - bufferedWriter := bufio.NewWriter(writer) - defer safelyFlush(bufferedWriter) - - if len(matchingNodes) == 0 { - log.Debug("no matching results, nothing to print") - if defaultValue != "" { - return writeString(bufferedWriter, defaultValue) - } - return nil - } - var errorWriting error - - var arrayCollection = yaml.Node{Kind: yaml.SequenceNode} - - for _, mappedDoc := range matchingNodes { - switch printMode { - case "p": - errorWriting = writeString(bufferedWriter, lib.PathStackToString(mappedDoc.PathStack)+"\n") - if errorWriting != nil { - return errorWriting - } - case "pv", "vp": - // put it into a node and print that. - var parentNode = yaml.Node{Kind: yaml.MappingNode} - parentNode.Content = make([]*yaml.Node, 2) - parentNode.Content[0] = &yaml.Node{Kind: yaml.ScalarNode, Value: lib.PathStackToString(mappedDoc.PathStack)} - parentNode.Content[1] = transformNode(mappedDoc.Node) - if collectIntoArray { - arrayCollection.Content = append(arrayCollection.Content, &parentNode) - } else if err := printNode(&parentNode, bufferedWriter); err != nil { - return err - } - default: - if collectIntoArray { - arrayCollection.Content = append(arrayCollection.Content, mappedDoc.Node) - } else if err := printNode(transformNode(mappedDoc.Node), bufferedWriter); err != nil { - return err - } - } - } - - if collectIntoArray { - if err := printNode(transformNode(&arrayCollection), bufferedWriter); err != nil { - return err - } - } - - return nil -} - -func parseDocumentIndex() (bool, int, error) { - if docIndex == "*" { - return true, -1, nil - } - docIndexInt64, err := strconv.ParseInt(docIndex, 10, 32) - if err != nil { - return false, -1, errors.Wrapf(err, "Document index %v is not a integer or *", docIndex) - } - return false, int(docIndexInt64), nil -} - -type updateDataFn func(dataBucket *yaml.Node, currentIndex int) error - -func isNullDocument(dataBucket *yaml.Node) bool { - return dataBucket.Kind == yaml.DocumentNode && (len(dataBucket.Content) == 0 || - dataBucket.Content[0].Kind == yaml.ScalarNode && dataBucket.Content[0].Tag == "!!null") -} - -func mapYamlDecoder(updateData updateDataFn, encoder yqlib.Encoder) yamlDecoderFn { - return func(decoder *yaml.Decoder) error { - var dataBucket yaml.Node - var errorReading error - var errorWriting error - var errorUpdating error - var currentIndex = 0 - - var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() - if errorParsingDocIndex != nil { - return errorParsingDocIndex - } - - for { - log.Debugf("Read doc %v", currentIndex) - errorReading = decoder.Decode(&dataBucket) - - if errorReading == io.EOF && docIndexInt == 0 && currentIndex == 0 { - //empty document, lets just make one - dataBucket = yaml.Node{Kind: yaml.DocumentNode, Content: make([]*yaml.Node, 1)} - child := yaml.Node{Kind: yaml.MappingNode} - dataBucket.Content[0] = &child - } else if isNullDocument(&dataBucket) && (updateAll || docIndexInt == currentIndex) { - child := yaml.Node{Kind: yaml.MappingNode} - dataBucket.Content[0] = &child - } else if errorReading == io.EOF { - if !updateAll && currentIndex <= docIndexInt { - return fmt.Errorf("asked to process document index %v but there are only %v document(s)", docIndex, currentIndex) - } - return nil - } else if errorReading != nil { - return errors.Wrapf(errorReading, "Error reading document at index %v, %v", currentIndex, errorReading) - } - errorUpdating = updateData(&dataBucket, currentIndex) - if errorUpdating != nil { - return errors.Wrapf(errorUpdating, "Error updating document at index %v", currentIndex) - } - - if prettyPrint { - updateStyleOfNode(&dataBucket, 0) - } - - errorWriting = encoder.Encode(&dataBucket) - - if errorWriting != nil { - return errors.Wrapf(errorWriting, "Error writing document at index %v, %v", currentIndex, errorWriting) - } - currentIndex = currentIndex + 1 - } - } -} - -func prefixDocument(updateAll bool, docIndexInt int, currentIndex int, dataBucket *yaml.Node, updateCommand yqlib.UpdateCommand) error { - if updateAll || currentIndex == docIndexInt { - log.Debugf("Prefixing document %v", currentIndex) - yqlib.DebugNode(dataBucket) - updateCommand.Value = dataBucket.Content[0] - dataBucket.Content = make([]*yaml.Node, 1) - - newNode := lib.New(updateCommand.Path) - dataBucket.Content[0] = &newNode - - errorUpdating := lib.Update(dataBucket, updateCommand, true) - if errorUpdating != nil { - return errorUpdating - } - } - return nil -} - -func updateDoc(inputFile string, updateCommands []yqlib.UpdateCommand, writer io.Writer) error { - var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() - if errorParsingDocIndex != nil { - return errorParsingDocIndex - } - - var updateData = func(dataBucket *yaml.Node, currentIndex int) error { - if updateAll || currentIndex == docIndexInt { - log.Debugf("Updating doc %v", currentIndex) - for _, updateCommand := range updateCommands { - log.Debugf("Processing update to Path %v", updateCommand.Path) - errorUpdating := lib.Update(dataBucket, updateCommand, autoCreateFlag) - if errorUpdating != nil { - return errorUpdating - } - } - } - return nil - } - return readAndUpdate(writer, inputFile, updateData) -} - -func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) error { - var destination io.Writer - var destinationName string - var completedSuccessfully = false - if writeInplace { - info, err := os.Stat(inputFile) - if err != nil { - return err - } - // mkdir temp dir as some docker images does not have temp dir - _, err = os.Stat(os.TempDir()) - if os.IsNotExist(err) { - err = os.Mkdir(os.TempDir(), 0700) - if err != nil { - return err - } - } else if err != nil { - return err - } - tempFile, err := ioutil.TempFile("", "temp") - if err != nil { - return err - } - destinationName = tempFile.Name() - err = os.Chmod(destinationName, info.Mode()) - if err != nil { - return err - } - destination = tempFile - defer func() { - safelyCloseFile(tempFile) - if completedSuccessfully { - safelyRenameFile(tempFile.Name(), inputFile) - } - }() - } else { - destination = stdOut - destinationName = "Stdout" - } - - log.Debugf("Writing to %v from %v", destinationName, inputFile) - - bufferedWriter := bufio.NewWriter(destination) - defer safelyFlush(bufferedWriter) - - var encoder yqlib.Encoder - if outputToJSON { - encoder = yqlib.NewJsonEncoder(bufferedWriter, prettyPrint, indent) - } else { - encoder = yqlib.NewYamlEncoder(bufferedWriter, indent, colorsEnabled) - } - - var errorProcessing = readStream(inputFile, mapYamlDecoder(updateData, encoder)) - completedSuccessfully = errorProcessing == nil - return errorProcessing -} - -type updateCommandParsed struct { - Command string - Path string - Value yaml.Node -} - -func readUpdateCommands(args []string, expectedArgs int, badArgsMessage string, allowNoValue bool) ([]yqlib.UpdateCommand, error) { - var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0) - if writeScript != "" { - var parsedCommands = make([]updateCommandParsed, 0) - - err := readData(writeScript, 0, &parsedCommands) - - if err != nil && err != io.EOF { - return nil, err - } - - log.Debugf("Read write commands file '%v'", parsedCommands) - for index := range parsedCommands { - parsedCommand := parsedCommands[index] - updateCommand := yqlib.UpdateCommand{Command: parsedCommand.Command, Path: parsedCommand.Path, Value: &parsedCommand.Value, Overwrite: true} - updateCommands = append(updateCommands, updateCommand) - } - - log.Debugf("Read write commands file '%v'", updateCommands) - } else if sourceYamlFile != "" && len(args) == expectedArgs-1 { - log.Debugf("Reading value from %v", sourceYamlFile) - var value yaml.Node - err := readData(sourceYamlFile, 0, &value) - if err != nil && err != io.EOF { - return nil, err - } - log.Debug("args %v", args[expectedArgs-2]) - updateCommands = make([]yqlib.UpdateCommand, 1) - updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: value.Content[0], Overwrite: true} - } else if len(args) == expectedArgs { - updateCommands = make([]yqlib.UpdateCommand, 1) - log.Debug("args %v", args) - log.Debug("path %v", args[expectedArgs-2]) - log.Debug("Value %v", args[expectedArgs-1]) - value := valueParser.Parse(args[expectedArgs-1], customTag, customStyle, anchorName, makeAlias) - updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: value, Overwrite: true, CommentsMergeStrategy: yqlib.IgnoreCommentsMergeStrategy} - } else if len(args) == expectedArgs-1 && allowNoValue { - // don't update the value - updateCommands = make([]yqlib.UpdateCommand, 1) - log.Debug("args %v", args) - log.Debug("path %v", args[expectedArgs-2]) - updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: valueParser.Parse("", customTag, customStyle, anchorName, makeAlias), Overwrite: true, DontUpdateNodeValue: true} - } else { - return nil, errors.New(badArgsMessage) - } - return updateCommands, nil -} - -func safelyRenameFile(from string, to string) { - if renameError := os.Rename(from, to); renameError != nil { - log.Debugf("Error renaming from %v to %v, attempting to copy contents", from, to) - log.Debug(renameError.Error()) - // can't do this rename when running in docker to a file targeted in a mounted volume, - // so gracefully degrade to copying the entire contents. - if copyError := copyFileContents(from, to); copyError != nil { - log.Errorf("Failed copying from %v to %v", from, to) - log.Error(copyError.Error()) - } else { - removeErr := os.Remove(from) - if removeErr != nil { - log.Errorf("failed removing original file: %s", from) - } - } - } -} - -// 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 { - return err - } - defer safelyCloseFile(in) - out, err := os.Create(dst) - if err != nil { - return err - } - defer safelyCloseFile(out) - if _, err = io.Copy(out, in); err != nil { - return err - } - return out.Sync() -} - -func safelyFlush(writer *bufio.Writer) { - if err := writer.Flush(); err != nil { - log.Error("Error flushing writer!") - log.Error(err.Error()) - } - -} -func safelyCloseFile(file *os.File) { - err := file.Close() - if err != nil { - log.Error("Error closing file!") - log.Error(err.Error()) - } -} - -type yamlDecoderFn func(*yaml.Decoder) error - -func readStream(filename string, yamlDecoder yamlDecoderFn) error { - if filename == "" { - return errors.New("Must provide filename") - } - - var stream io.Reader - if filename == "-" { - stream = bufio.NewReader(os.Stdin) - } else { - file, err := os.Open(filename) // nolint gosec - if err != nil { - return err - } - defer safelyCloseFile(file) - stream = file - } - return yamlDecoder(yaml.NewDecoder(stream)) -} - -func readData(filename string, indexToRead int, parsedData interface{}) error { - return readStream(filename, func(decoder *yaml.Decoder) error { - for currentIndex := 0; currentIndex < indexToRead; currentIndex++ { - errorSkipping := decoder.Decode(parsedData) - if errorSkipping != nil { - return errors.Wrapf(errorSkipping, "Error processing document at index %v, %v", currentIndex, errorSkipping) - } - } - return decoder.Decode(parsedData) - }) -} diff --git a/cmd/validate.go b/cmd/validate.go deleted file mode 100644 index 5e3a9301..00000000 --- a/cmd/validate.go +++ /dev/null @@ -1,37 +0,0 @@ -package cmd - -import ( - errors "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -func createValidateCmd() *cobra.Command { - var cmdRead = &cobra.Command{ - Use: "validate [yaml_file]", - Aliases: []string{"v"}, - Short: "yq v sample.yaml", - Example: ` -yq v - # reads from stdin -`, - RunE: validateProperty, - SilenceUsage: true, - SilenceErrors: false, - } - cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") - return cmdRead -} - -func validateProperty(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("Must provide filename") - } - - var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() - if errorParsingDocIndex != nil { - return errorParsingDocIndex - } - - _, errorReadingStream := readYamlFile(args[0], "", updateAll, docIndexInt) - - return errorReadingStream -} diff --git a/cmd/validate_test.go b/cmd/validate_test.go deleted file mode 100644 index 378f0b1f..00000000 --- a/cmd/validate_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package cmd - -import ( - "fmt" - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -func TestValidateCmd(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "validate ../examples/sample.yaml b.c") - if result.Error != nil { - t.Error(result.Error) - } - test.AssertResult(t, "", result.Output) -} - -func TestValidateBadDataCmd(t *testing.T) { - content := `[!Whatever]` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("validate %s", filename)) - if result.Error == nil { - t.Error("Expected command to fail") - } - expectedOutput := `yaml: line 1: did not find expected ',' or ']'` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} diff --git a/cmd/version.go b/cmd/version.go index 33705373..167f356d 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -11,7 +11,7 @@ var ( GitDescribe string // Version is main version number that is being run at the moment. - Version = "3.4.1" + Version = "4.0.0" // VersionPrerelease is a pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff --git a/cmd/write.go b/cmd/write.go deleted file mode 100644 index be14a132..00000000 --- a/cmd/write.go +++ /dev/null @@ -1,61 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -func createWriteCmd() *cobra.Command { - var cmdWrite = &cobra.Command{ - Use: "write [yaml_file] [path_expression] [value]", - Aliases: []string{"w"}, - Short: "yq w [--inplace/-i] [--script/-s script_file] [--doc/-d index] sample.yaml 'b.e(name==fr*).value' newValue", - Example: ` -yq write things.yaml 'a.b.c' true -yq write things.yaml 'a.*.c' true -yq write things.yaml 'a.**' true -yq write things.yaml 'a.(child.subchild==co*).c' true -yq write things.yaml 'a.b.c' --tag '!!str' true # force 'true' to be interpreted as a string instead of bool -yq write things.yaml 'a.b.c' --tag '!!float' 3 -yq write --inplace -- things.yaml 'a.b.c' '--cat' # need to use '--' to stop processing arguments as flags -yq w -i things.yaml 'a.b.c' cat -yq w -i -s update_script.yaml things.yaml -yq w things.yaml 'a.b.d[+]' foo # appends a new node to the 'd' array -yq w --doc 2 things.yaml 'a.b.d[+]' foo # updates the 3rd document of the yaml file - `, - Long: `Updates the yaml file w.r.t the given path and value. -Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. - -Append value to array adds the value to the end of array. - -Update Scripts: -Note that you can give an update script to perform more sophisticated update. Update script -format is list of update commands (update or delete) like so: ---- -- command: update - path: b.c - value: - #great - things: frog # wow! -- command: delete - path: b.d -`, - RunE: writeProperty, - } - cmdWrite.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") - cmdWrite.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml") - cmdWrite.PersistentFlags().StringVarP(&sourceYamlFile, "from", "f", "", "yaml file for updating yaml (as-is)") - cmdWrite.PersistentFlags().StringVarP(&customTag, "tag", "t", "", "set yaml tag (e.g. !!int)") - cmdWrite.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") - cmdWrite.PersistentFlags().StringVarP(&customStyle, "style", "", "", "formatting style of the value: single, double, folded, flow, literal, tagged") - cmdWrite.PersistentFlags().StringVarP(&anchorName, "anchorName", "", "", "anchor name") - cmdWrite.PersistentFlags().BoolVarP(&makeAlias, "makeAlias", "", false, "create an alias using the value as the anchor name") - return cmdWrite -} - -func writeProperty(cmd *cobra.Command, args []string) error { - var updateCommands, updateCommandsError = readUpdateCommands(args, 3, "Must provide ", true) - if updateCommandsError != nil { - return updateCommandsError - } - return updateDoc(args[0], updateCommands, cmd.OutOrStdout()) -} diff --git a/cmd/write_test.go b/cmd/write_test.go deleted file mode 100644 index 8249a890..00000000 --- a/cmd/write_test.go +++ /dev/null @@ -1,611 +0,0 @@ -package cmd - -import ( - "fmt" - "runtime" - "strings" - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -func TestWriteCmd(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c 7", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 7 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteKeepCommentsCmd(t *testing.T) { - content := `b: - c: 3 # comment -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c 7", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 7 # comment -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteWithTaggedStyleCmd(t *testing.T) { - content := `b: - c: dog -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c cat --tag=!!str --style=tagged", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: !!str cat -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteWithDoubleQuotedStyleCmd(t *testing.T) { - content := `b: - c: dog -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c cat --style=double", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: "cat" -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteUpdateStyleOnlyCmd(t *testing.T) { - content := `b: - c: dog - d: things -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.* --style=single", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 'dog' - d: 'things' -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteUpdateTagOnlyCmd(t *testing.T) { - content := `b: - c: true - d: false -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.* --tag=!!str", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: "true" - d: "false" -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteWithSingleQuotedStyleCmd(t *testing.T) { - content := `b: - c: dog -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c cat --style=single", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 'cat' -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteWithLiteralStyleCmd(t *testing.T) { - content := `b: - c: dog -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c cat --style=literal", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: |- - cat -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteWithFoldedStyleCmd(t *testing.T) { - content := `b: - c: dog -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c cat --style=folded", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: >- - cat -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteEmptyMultiDocCmd(t *testing.T) { - content := `# this is empty ---- -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s c 7", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `c: 7 - -# this is empty -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteSurroundingEmptyMultiDocCmd(t *testing.T) { - content := `--- -# empty ---- -cat: frog ---- - -# empty -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s -d1 c 7", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := ` - -# empty ---- -cat: frog -c: 7 ---- - - -# empty -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteFromFileCmd(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - source := `kittens: are cute # sure are!` - fromFilename := test.WriteTempYamlFile(source) - defer test.RemoveTempYamlFile(fromFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c -f %s", filename, fromFilename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: - kittens: are cute # sure are! -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteEmptyCmd(t *testing.T) { - content := `` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c 7", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 7 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteAutoCreateCmd(t *testing.T) { - content := `applications: - - name: app - env:` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s applications[0].env.hello world", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `applications: - - name: app - env: - hello: world -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteCmdScript(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - updateScript := `- command: update - path: b.c - value: 7` - scriptFilename := test.WriteTempYamlFile(updateScript) - defer test.RemoveTempYamlFile(scriptFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write --script %s %s", scriptFilename, filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 7 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteCmdEmptyScript(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - updateScript := `` - scriptFilename := test.WriteTempYamlFile(updateScript) - defer test.RemoveTempYamlFile(scriptFilename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write --script %s %s", scriptFilename, filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 3 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteMultiCmd(t *testing.T) { - content := `b: - c: 3 ---- -apples: great -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s -d 1 apples ok", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 3 ---- -apples: ok -` - test.AssertResult(t, expectedOutput, result.Output) -} -func TestWriteInvalidDocumentIndexCmd(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s -df apples ok", filename)) - if result.Error == nil { - t.Error("Expected command to fail due to invalid path") - } - expectedOutput := `Document index f is not a integer or *: strconv.ParseInt: parsing "f": invalid syntax` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestWriteBadDocumentIndexCmd(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s -d 1 apples ok", filename)) - if result.Error == nil { - t.Error("Expected command to fail due to invalid path") - } - expectedOutput := `asked to process document index 1 but there are only 1 document(s)` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} -func TestWriteMultiAllCmd(t *testing.T) { - content := `b: - c: 3 ---- -apples: great -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s -d * apples ok", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: 3 -apples: ok ---- -apples: ok` - test.AssertResult(t, expectedOutput, strings.Trim(result.Output, "\n ")) -} - -func TestWriteCmd_EmptyArray(t *testing.T) { - content := `b: 3` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s a []", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: 3 -a: [] -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteCmd_Error(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "write") - if result.Error == nil { - t.Error("Expected command to fail due to missing arg") - } - expectedOutput := `Must provide ` - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestWriteCmd_ErrorUnreadableFile(t *testing.T) { - cmd := getRootCommand() - result := test.RunCmd(cmd, "write fake-unknown a.b 3") - if result.Error == nil { - t.Error("Expected command to fail due to unknown file") - } - var expectedOutput string - if runtime.GOOS == "windows" { - expectedOutput = `open fake-unknown: The system cannot find the file specified.` - } else { - expectedOutput = `open fake-unknown: no such file or directory` - } - test.AssertResult(t, expectedOutput, result.Error.Error()) -} - -func TestWriteCmd_Inplace(t *testing.T) { - content := `b: - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write -i %s b.c 7", filename)) - if result.Error != nil { - t.Error(result.Error) - } - gotOutput := test.ReadTempYamlFile(filename) - expectedOutput := `b: - c: 7` - test.AssertResult(t, expectedOutput, strings.Trim(gotOutput, "\n ")) -} - -func TestWriteCmd_InplaceError(t *testing.T) { - content := `b: cat - c: 3 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write -i %s b.c 7", filename)) - if result.Error == nil { - t.Error("Expected Error to occur!") - } - gotOutput := test.ReadTempYamlFile(filename) - test.AssertResult(t, content, gotOutput) -} - -func TestWriteCmd_Append(t *testing.T) { - content := `b: - - foo -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b[+] 7", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - - foo - - 7 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteCmd_AppendInline(t *testing.T) { - content := `b: [foo]` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b[+] 7", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: [foo, 7] -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteCmd_AppendInlinePretty(t *testing.T) { - content := `b: [foo]` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s -P b[+] 7", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - - foo - - 7 -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteCmd_AppendEmptyArray(t *testing.T) { - content := `a: 2 -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b[+] v", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `a: 2 -b: - - v -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteCmd_SplatArray(t *testing.T) { - content := `b: -- c: thing -- c: another thing -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b[*].c new", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - - c: new - - c: new -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteCmd_SplatMap(t *testing.T) { - content := `b: - c: thing - d: another thing -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.* new", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: new - d: new -` - test.AssertResult(t, expectedOutput, result.Output) -} - -func TestWriteCmd_SplatMapEmpty(t *testing.T) { - content := `b: - c: thing - d: another thing -` - filename := test.WriteTempYamlFile(content) - defer test.RemoveTempYamlFile(filename) - - cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("write %s b.c.* new", filename)) - if result.Error != nil { - t.Error(result.Error) - } - expectedOutput := `b: - c: {} - d: another thing -` - test.AssertResult(t, expectedOutput, result.Output) -} diff --git a/compare.sh b/compare.sh deleted file mode 100755 index fc8cd8bf..00000000 --- a/compare.sh +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/404.html b/docs/404.html deleted file mode 100644 index a7879f36..00000000 --- a/docs/404.html +++ /dev/null @@ -1,268 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Yq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - - -
-
- -

404 - Not found

- - - - - - -
-
-
-
- - - - -
- - - - - - - - \ No newline at end of file diff --git a/docs/assets/fonts/font-awesome.css b/docs/assets/fonts/font-awesome.css deleted file mode 100644 index b476b53e..00000000 --- a/docs/assets/fonts/font-awesome.css +++ /dev/null @@ -1,4 +0,0 @@ -/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url("specimen/FontAwesome.woff2") format("woff2"),url("specimen/FontAwesome.woff") format("woff"),url("specimen/FontAwesome.ttf") format("truetype")}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} \ No newline at end of file diff --git a/docs/assets/fonts/material-icons.css b/docs/assets/fonts/material-icons.css deleted file mode 100644 index d23d365e..00000000 --- a/docs/assets/fonts/material-icons.css +++ /dev/null @@ -1,13 +0,0 @@ -/*! - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, SOFTWARE - * DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - * SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS AND - * LIMITATIONS UNDER THE LICENSE. - */@font-face{font-family:"Material Icons";font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url("specimen/MaterialIcons-Regular.woff2") format("woff2"),url("specimen/MaterialIcons-Regular.woff") format("woff"),url("specimen/MaterialIcons-Regular.ttf") format("truetype")} \ No newline at end of file diff --git a/docs/assets/fonts/specimen/FontAwesome.ttf b/docs/assets/fonts/specimen/FontAwesome.ttf deleted file mode 100644 index 35acda2f..00000000 Binary files a/docs/assets/fonts/specimen/FontAwesome.ttf and /dev/null differ diff --git a/docs/assets/fonts/specimen/FontAwesome.woff b/docs/assets/fonts/specimen/FontAwesome.woff deleted file mode 100644 index 400014a4..00000000 Binary files a/docs/assets/fonts/specimen/FontAwesome.woff and /dev/null differ diff --git a/docs/assets/fonts/specimen/FontAwesome.woff2 b/docs/assets/fonts/specimen/FontAwesome.woff2 deleted file mode 100644 index 4d13fc60..00000000 Binary files a/docs/assets/fonts/specimen/FontAwesome.woff2 and /dev/null differ diff --git a/docs/assets/fonts/specimen/MaterialIcons-Regular.ttf b/docs/assets/fonts/specimen/MaterialIcons-Regular.ttf deleted file mode 100644 index 7015564a..00000000 Binary files a/docs/assets/fonts/specimen/MaterialIcons-Regular.ttf and /dev/null differ diff --git a/docs/assets/fonts/specimen/MaterialIcons-Regular.woff b/docs/assets/fonts/specimen/MaterialIcons-Regular.woff deleted file mode 100644 index b648a3ee..00000000 Binary files a/docs/assets/fonts/specimen/MaterialIcons-Regular.woff and /dev/null differ diff --git a/docs/assets/fonts/specimen/MaterialIcons-Regular.woff2 b/docs/assets/fonts/specimen/MaterialIcons-Regular.woff2 deleted file mode 100644 index 9fa21125..00000000 Binary files a/docs/assets/fonts/specimen/MaterialIcons-Regular.woff2 and /dev/null differ diff --git a/docs/assets/images/favicon.png b/docs/assets/images/favicon.png deleted file mode 100644 index 76d17f57..00000000 Binary files a/docs/assets/images/favicon.png and /dev/null differ diff --git a/docs/assets/images/icons/bitbucket.1b09e088.svg b/docs/assets/images/icons/bitbucket.1b09e088.svg deleted file mode 100644 index cf58c14f..00000000 --- a/docs/assets/images/icons/bitbucket.1b09e088.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/assets/images/icons/github.f0b8504a.svg b/docs/assets/images/icons/github.f0b8504a.svg deleted file mode 100644 index 3d13b197..00000000 --- a/docs/assets/images/icons/github.f0b8504a.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/assets/images/icons/gitlab.6dd19c00.svg b/docs/assets/images/icons/gitlab.6dd19c00.svg deleted file mode 100644 index 1d9fffa7..00000000 --- a/docs/assets/images/icons/gitlab.6dd19c00.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/assets/javascripts/application.808e90bb.js b/docs/assets/javascripts/application.808e90bb.js deleted file mode 100644 index cd28ce28..00000000 --- a/docs/assets/javascripts/application.808e90bb.js +++ /dev/null @@ -1,60 +0,0 @@ -!function(e,t){for(var n in t)e[n]=t[n]}(window,function(n){var r={};function i(e){if(r[e])return r[e].exports;var t=r[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,i),t.l=!0,t.exports}return i.m=n,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)i.d(n,r,function(e){return t[e]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i(i.s=13)}([function(e,t,n){"use strict";var r={Listener:function(){function e(e,t,n){var r=this;this.els_=Array.prototype.slice.call("string"==typeof e?document.querySelectorAll(e):[].concat(e)),this.handler_="function"==typeof n?{update:n}:n,this.events_=[].concat(t),this.update_=function(e){return r.handler_.update(e)}}var t=e.prototype;return t.listen=function(){var n=this;this.els_.forEach(function(t){n.events_.forEach(function(e){t.addEventListener(e,n.update_,!1)})}),"function"==typeof this.handler_.setup&&this.handler_.setup()},t.unlisten=function(){var n=this;this.els_.forEach(function(t){n.events_.forEach(function(e){t.removeEventListener(e,n.update_)})}),"function"==typeof this.handler_.reset&&this.handler_.reset()},e}(),MatchMedia:function(e,t){this.handler_=function(e){e.matches?t.listen():t.unlisten()};var n=window.matchMedia(e);n.addListener(this.handler_),this.handler_(n)}},i={Shadow:function(){function e(e,t){var n="string"==typeof e?document.querySelector(e):e;if(!(n instanceof HTMLElement&&n.parentNode instanceof HTMLElement))throw new ReferenceError;if(this.el_=n.parentNode,!((n="string"==typeof t?document.querySelector(t):t)instanceof HTMLElement))throw new ReferenceError;this.header_=n,this.height_=0,this.active_=!1}var t=e.prototype;return t.setup=function(){for(var e=this.el_;e=e.previousElementSibling;){if(!(e instanceof HTMLElement))throw new ReferenceError;this.height_+=e.offsetHeight}this.update()},t.update=function(e){if(!e||"resize"!==e.type&&"orientationchange"!==e.type){var t=window.pageYOffset>=this.height_;t!==this.active_&&(this.header_.dataset.mdState=(this.active_=t)?"shadow":"")}else this.height_=0,this.setup()},t.reset=function(){this.header_.dataset.mdState="",this.height_=0,this.active_=!1},e}(),Title:function(){function e(e,t){var n="string"==typeof e?document.querySelector(e):e;if(!(n instanceof HTMLElement))throw new ReferenceError;if(this.el_=n,!((n="string"==typeof t?document.querySelector(t):t)instanceof HTMLHeadingElement))throw new ReferenceError;this.header_=n,this.active_=!1}var t=e.prototype;return t.setup=function(){var t=this;Array.prototype.forEach.call(this.el_.children,function(e){e.style.width=t.el_.offsetWidth-20+"px"})},t.update=function(e){var t=this,n=window.pageYOffset>=this.header_.offsetTop;n!==this.active_&&(this.el_.dataset.mdState=(this.active_=n)?"active":""),"resize"!==e.type&&"orientationchange"!==e.type||Array.prototype.forEach.call(this.el_.children,function(e){e.style.width=t.el_.offsetWidth-20+"px"})},t.reset=function(){this.el_.dataset.mdState="",this.el_.style.width="",this.active_=!1},e}()},o={Blur:function(){function e(e){this.els_="string"==typeof e?document.querySelectorAll(e):e,this.index_=0,this.offset_=window.pageYOffset,this.dir_=!1,this.anchors_=[].reduce.call(this.els_,function(e,t){var n=decodeURIComponent(t.hash);return e.concat(document.getElementById(n.substring(1))||[])},[])}var t=e.prototype;return t.setup=function(){this.update()},t.update=function(){var e=window.pageYOffset,t=this.offset_-e<0;if(this.dir_!==t&&(this.index_=this.index_=t?0:this.els_.length-1),0!==this.anchors_.length){if(this.offset_<=e)for(var n=this.index_+1;ne)){this.index_=r;break}0=this.offset_?"lock"!==this.el_.dataset.mdState&&(this.el_.dataset.mdState="lock"):"lock"===this.el_.dataset.mdState&&(this.el_.dataset.mdState="")},t.reset=function(){this.el_.dataset.mdState="",this.el_.style.height="",this.height_=0},e}()},c=n(6),l=n.n(c);var u={Adapter:{GitHub:function(o){var e,t;function n(e){var t;t=o.call(this,e)||this;var n=/^.+github\.com\/([^/]+)\/?([^/]+)?.*$/.exec(t.base_);if(n&&3===n.length){var r=n[1],i=n[2];t.base_="https://api.github.com/users/"+r+"/repos",t.name_=i}return t}return t=o,(e=n).prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t,n.prototype.fetch_=function(){var i=this;return function n(r){return void 0===r&&(r=0),fetch(i.base_+"?per_page=100&sort=updated&page="+r).then(function(e){return e.json()}).then(function(e){if(!(e instanceof Array))return[];if(i.name_){var t=e.find(function(e){return e.name===i.name_});return t||30!==e.length?t?[i.format_(t.stargazers_count)+" Stars",i.format_(t.forks_count)+" Forks"]:[]:n(r+1)}return[e.length+" Repositories"]})}()},n}(function(){function e(e){var t="string"==typeof e?document.querySelector(e):e;if(!(t instanceof HTMLAnchorElement))throw new ReferenceError;this.el_=t,this.base_=this.el_.href,this.salt_=this.hash_(this.base_)}var t=e.prototype;return t.fetch=function(){var n=this;return new Promise(function(t){var e=l.a.getJSON(n.salt_+".cache-source");void 0!==e?t(e):n.fetch_().then(function(e){l.a.set(n.salt_+".cache-source",e,{expires:1/96}),t(e)})})},t.fetch_=function(){throw new Error("fetch_(): Not implemented")},t.format_=function(e){return 1e4=this.el_.children[0].offsetTop+(5-this.height_);e!==this.active_&&(this.el_.dataset.mdState=(this.active_=e)?"hidden":"")},t.reset=function(){this.el_.dataset.mdState="",this.active_=!1},e}()};t.a={Event:r,Header:i,Nav:o,Search:a,Sidebar:s,Source:u,Tabs:f}},function(t,e,n){(function(e){t.exports=e.lunr=n(24)}).call(this,n(4))},function(e,d,h){"use strict";(function(t){var e=h(8),n=setTimeout;function c(e){return Boolean(e&&void 0!==e.length)}function r(){}function o(e){if(!(this instanceof o))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],f(e,this)}function i(n,r){for(;3===n._state;)n=n._value;0!==n._state?(n._handled=!0,o._immediateFn(function(){var e=1===n._state?r.onFulfilled:r.onRejected;if(null!==e){var t;try{t=e(n._value)}catch(e){return void s(r.promise,e)}a(r.promise,t)}else(1===n._state?a:s)(r.promise,n._value)})):n._deferreds.push(r)}function a(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var n=e.then;if(e instanceof o)return t._state=3,t._value=e,void l(t);if("function"==typeof n)return void f((r=n,i=e,function(){r.apply(i,arguments)}),t)}t._state=1,t._value=e,l(t)}catch(e){s(t,e)}var r,i}function s(e,t){e._state=2,e._value=t,l(e)}function l(e){2===e._state&&0===e._deferreds.length&&o._immediateFn(function(){e._handled||o._unhandledRejectionFn(e._value)});for(var t=0,n=e._deferreds.length;t"+n+""};this.stack_=[],r.forEach(function(e,t){var n,r=a.docs_.get(t),i=f.createElement("li",{class:"md-search-result__item"},f.createElement("a",{href:r.location,title:r.title,class:"md-search-result__link",tabindex:"-1"},f.createElement("article",{class:"md-search-result__article md-search-result__article--document"},f.createElement("h1",{class:"md-search-result__title"},{__html:r.title.replace(s,c)}),r.text.length?f.createElement("p",{class:"md-search-result__teaser"},{__html:r.text.replace(s,c)}):{}))),o=e.map(function(t){return function(){var e=a.docs_.get(t.ref);i.appendChild(f.createElement("a",{href:e.location,title:e.title,class:"md-search-result__link","data-md-rel":"anchor",tabindex:"-1"},f.createElement("article",{class:"md-search-result__article"},f.createElement("h1",{class:"md-search-result__title"},{__html:e.title.replace(s,c)}),e.text.length?f.createElement("p",{class:"md-search-result__teaser"},{__html:function(e,t){var n=t;if(e.length>n){for(;" "!==e[n]&&0<--n;);return e.substring(0,n)+"..."}return e}(e.text.replace(s,c),400)}):{})))}});(n=a.stack_).push.apply(n,[function(){return a.list_.appendChild(i)}].concat(o))});var o=this.el_.parentNode;if(!(o instanceof HTMLElement))throw new ReferenceError;for(;this.stack_.length&&o.offsetHeight>=o.scrollHeight-16;)this.stack_.shift()();var l=this.list_.querySelectorAll("[data-md-rel=anchor]");switch(Array.prototype.forEach.call(l,function(r){["click","keydown"].forEach(function(n){r.addEventListener(n,function(e){if("keydown"!==n||13===e.keyCode){var t=document.querySelector("[data-md-toggle=search]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t.checked&&(t.checked=!1,t.dispatchEvent(new CustomEvent("change"))),e.preventDefault(),setTimeout(function(){document.location.href=r.href},100)}})})}),r.size){case 0:this.meta_.textContent=this.message_.none;break;case 1:this.meta_.textContent=this.message_.one;break;default:this.meta_.textContent=this.message_.other.replace("#",r.size)}}}else{var u=function(e){a.docs_=e.reduce(function(e,t){var n,r,i,o=t.location.split("#"),a=o[0],s=o[1];return t.text=(n=t.text,r=document.createTextNode(n),(i=document.createElement("p")).appendChild(r),i.innerHTML),s&&(t.parent=e.get(a),t.parent&&!t.parent.done&&(t.parent.title=t.title,t.parent.text=t.text,t.parent.done=!0)),t.text=t.text.replace(/\n/g," ").replace(/\s+/g," ").replace(/\s+([,.:;!?])/g,function(e,t){return t}),t.parent&&t.parent.title===t.title||e.set(t.location,t),e},new Map);var i=a.docs_,o=a.lang_;a.stack_=[],a.index_=d()(function(){var e,t=this,n={"search.pipeline.trimmer":d.a.trimmer,"search.pipeline.stopwords":d.a.stopWordFilter},r=Object.keys(n).reduce(function(e,t){return h(t).match(/^false$/i)||e.push(n[t]),e},[]);this.pipeline.reset(),r&&(e=this.pipeline).add.apply(e,r),1===o.length&&"en"!==o[0]&&d.a[o[0]]?this.use(d.a[o[0]]):1=t.scrollHeight-16;)a.stack_.splice(0,10).forEach(function(e){return e()})})};setTimeout(function(){return"function"==typeof a.data_?a.data_().then(u):u(a.data_)},250)}},e}()}).call(this,r(3))},function(e,n,r){"use strict";(function(t){r.d(n,"a",function(){return e});var e=function(){function e(e){var t="string"==typeof e?document.querySelector(e):e;if(!(t instanceof HTMLElement))throw new ReferenceError;this.el_=t}return e.prototype.initialize=function(e){e.length&&this.el_.children.length&&this.el_.children[this.el_.children.length-1].appendChild(t.createElement("ul",{class:"md-source__facts"},e.map(function(e){return t.createElement("li",{class:"md-source__fact"},e)}))),this.el_.dataset.mdState="done"},e}()}).call(this,r(3))},,,function(e,n,c){"use strict";c.r(n),function(o){c.d(n,"app",function(){return t});c(14),c(15),c(16),c(17),c(18),c(19),c(20);var r=c(2),e=c(5),a=c.n(e),i=c(0);window.Promise=window.Promise||r.a;var s=function(e){var t=document.getElementsByName("lang:"+e)[0];if(!(t instanceof HTMLMetaElement))throw new ReferenceError;return t.content};var t={initialize:function(t){new i.a.Event.Listener(document,"DOMContentLoaded",function(){if(!(document.body instanceof HTMLElement))throw new ReferenceError;Modernizr.addTest("ios",function(){return!!navigator.userAgent.match(/(iPad|iPhone|iPod)/g)});var e=document.querySelectorAll("table:not([class])");if(Array.prototype.forEach.call(e,function(e){var t=o.createElement("div",{class:"md-typeset__scrollwrap"},o.createElement("div",{class:"md-typeset__table"}));e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t),t.children[0].appendChild(e)}),a.a.isSupported()){var t=document.querySelectorAll(".codehilite > pre, pre > code");Array.prototype.forEach.call(t,function(e,t){var n="__code_"+t,r=o.createElement("button",{class:"md-clipboard",title:s("clipboard.copy"),"data-clipboard-target":"#"+n+" pre, #"+n+" code"},o.createElement("span",{class:"md-clipboard__message"})),i=e.parentNode;i.id=n,i.insertBefore(r,e)}),new a.a(".md-clipboard").on("success",function(e){var t=e.trigger.querySelector(".md-clipboard__message");if(!(t instanceof HTMLElement))throw new ReferenceError;e.clearSelection(),t.dataset.mdTimer&&clearTimeout(parseInt(t.dataset.mdTimer,10)),t.classList.add("md-clipboard__message--active"),t.innerHTML=s("clipboard.copied"),t.dataset.mdTimer=setTimeout(function(){t.classList.remove("md-clipboard__message--active"),t.dataset.mdTimer=""},2e3).toString()})}if(!Modernizr.details){var n=document.querySelectorAll("details > summary");Array.prototype.forEach.call(n,function(e){e.addEventListener("click",function(e){var t=e.target.parentNode;t.hasAttribute("open")?t.removeAttribute("open"):t.setAttribute("open","")})})}var r=function(){if(document.location.hash){var e=document.getElementById(document.location.hash.substring(1));if(!e)return;for(var t=e.parentNode;t&&!(t instanceof HTMLDetailsElement);)t=t.parentNode;if(t&&!t.open){t.open=!0;var n=location.hash;location.hash=" ",location.hash=n}}};if(window.addEventListener("hashchange",r),r(),Modernizr.ios){var i=document.querySelectorAll("[data-md-scrollfix]");Array.prototype.forEach.call(i,function(t){t.addEventListener("touchstart",function(){var e=t.scrollTop;0===e?t.scrollTop=1:e+t.offsetHeight===t.scrollHeight&&(t.scrollTop=e-1)})})}}).listen(),new i.a.Event.Listener(window,["scroll","resize","orientationchange"],new i.a.Header.Shadow("[data-md-component=container]","[data-md-component=header]")).listen(),new i.a.Event.Listener(window,["scroll","resize","orientationchange"],new i.a.Header.Title("[data-md-component=title]",".md-typeset h1")).listen(),document.querySelector("[data-md-component=hero]")&&new i.a.Event.Listener(window,["scroll","resize","orientationchange"],new i.a.Tabs.Toggle("[data-md-component=hero]")).listen(),document.querySelector("[data-md-component=tabs]")&&new i.a.Event.Listener(window,["scroll","resize","orientationchange"],new i.a.Tabs.Toggle("[data-md-component=tabs]")).listen(),new i.a.Event.MatchMedia("(min-width: 1220px)",new i.a.Event.Listener(window,["scroll","resize","orientationchange"],new i.a.Sidebar.Position("[data-md-component=navigation]","[data-md-component=header]"))),document.querySelector("[data-md-component=toc]")&&new i.a.Event.MatchMedia("(min-width: 960px)",new i.a.Event.Listener(window,["scroll","resize","orientationchange"],new i.a.Sidebar.Position("[data-md-component=toc]","[data-md-component=header]"))),new i.a.Event.MatchMedia("(min-width: 960px)",new i.a.Event.Listener(window,"scroll",new i.a.Nav.Blur("[data-md-component=toc] .md-nav__link")));var e=document.querySelectorAll("[data-md-component=collapsible]");Array.prototype.forEach.call(e,function(e){new i.a.Event.MatchMedia("(min-width: 1220px)",new i.a.Event.Listener(e.previousElementSibling,"click",new i.a.Nav.Collapse(e)))}),new i.a.Event.MatchMedia("(max-width: 1219px)",new i.a.Event.Listener("[data-md-component=navigation] [data-md-toggle]","change",new i.a.Nav.Scrolling("[data-md-component=navigation] nav"))),document.querySelector("[data-md-component=search]")&&(new i.a.Event.MatchMedia("(max-width: 959px)",new i.a.Event.Listener("[data-md-toggle=search]","change",new i.a.Search.Lock("[data-md-toggle=search]"))),new i.a.Event.Listener("[data-md-component=query]",["focus","keyup","change"],new i.a.Search.Result("[data-md-component=result]",function(){return fetch(t.url.base+"/search/search_index.json",{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return e.docs.map(function(e){return e.location=t.url.base+"/"+e.location,e})})})).listen(),new i.a.Event.Listener("[data-md-component=reset]","click",function(){setTimeout(function(){var e=document.querySelector("[data-md-component=query]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.focus()},10)}).listen(),new i.a.Event.Listener("[data-md-toggle=search]","change",function(e){setTimeout(function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t.focus()}},400,e.target)}).listen(),new i.a.Event.Listener("[data-md-component=query]","focus",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked||(e.checked=!0,e.dispatchEvent(new CustomEvent("change")))}).listen(),new i.a.Event.Listener(window,"keydown",function(e){var t=document.querySelector("[data-md-toggle=search]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;var n=document.querySelector("[data-md-component=query]");if(!(n instanceof HTMLInputElement))throw new ReferenceError;if(!(document.activeElement instanceof HTMLElement&&document.activeElement.isContentEditable||e.metaKey||e.ctrlKey))if(t.checked){if(13===e.keyCode){if(n===document.activeElement){e.preventDefault();var r=document.querySelector("[data-md-component=search] [href][data-md-state=active]");r instanceof HTMLLinkElement&&(window.location=r.getAttribute("href"),t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur())}}else if(9===e.keyCode||27===e.keyCode)t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur();else if(-1!==[8,37,39].indexOf(e.keyCode))n!==document.activeElement&&n.focus();else if(-1!==[38,40].indexOf(e.keyCode)){var i=e.keyCode,o=Array.prototype.slice.call(document.querySelectorAll("[data-md-component=query], [data-md-component=search] [href]")),a=o.find(function(e){if(!(e instanceof HTMLElement))throw new ReferenceError;return"active"===e.dataset.mdState});a&&(a.dataset.mdState="");var s=Math.max(0,(o.indexOf(a)+o.length+(38===i?-1:1))%o.length);return o[s]&&(o[s].dataset.mdState="active",o[s].focus()),e.preventDefault(),e.stopPropagation(),!1}}else if(document.activeElement&&!document.activeElement.form){if("TEXTAREA"===document.activeElement.tagName||"INPUT"===document.activeElement.tagName)return;70!==e.keyCode&&83!==e.keyCode||(n.focus(),e.preventDefault())}}).listen(),new i.a.Event.Listener(window,"keypress",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t!==document.activeElement&&t.focus()}}).listen()),new i.a.Event.Listener(document.body,"keydown",function(e){if(9===e.keyCode){var t=document.querySelectorAll("[data-md-component=navigation] .md-nav__link[for]:not([tabindex])");Array.prototype.forEach.call(t,function(e){e.offsetHeight&&(e.tabIndex=0)})}}).listen(),new i.a.Event.Listener(document.body,"mousedown",function(){var e=document.querySelectorAll("[data-md-component=navigation] .md-nav__link[tabindex]");Array.prototype.forEach.call(e,function(e){e.removeAttribute("tabIndex")})}).listen(),document.body.addEventListener("click",function(){"tabbing"===document.body.dataset.mdState&&(document.body.dataset.mdState="")}),new i.a.Event.MatchMedia("(max-width: 959px)",new i.a.Event.Listener("[data-md-component=navigation] [href^='#']","click",function(){var e=document.querySelector("[data-md-toggle=drawer]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked&&(e.checked=!1,e.dispatchEvent(new CustomEvent("change")))})),function(){var e=document.querySelector("[data-md-source]");if(!e)return r.a.resolve([]);if(!(e instanceof HTMLAnchorElement))throw new ReferenceError;switch(e.dataset.mdSource){case"github":return new i.a.Source.Adapter.GitHub(e).fetch();default:return r.a.resolve([])}}().then(function(t){var e=document.querySelectorAll("[data-md-source]");Array.prototype.forEach.call(e,function(e){new i.a.Source.Repository(e).initialize(t)})});var n=function(){var e=document.querySelectorAll("details");Array.prototype.forEach.call(e,function(e){e.setAttribute("open","")})};new i.a.Event.MatchMedia("print",{listen:n,unlisten:function(){}}),window.onbeforeprint=n}}}.call(this,c(3))},function(e,t,n){"use strict";n.p},function(e,t,n){"use strict";n.p},function(e,t,n){"use strict";n.p},function(e,t,n){"use strict"},function(e,t,n){"use strict"},function(e,t){!function(){if("undefined"!=typeof window)try{var e=new window.CustomEvent("test",{cancelable:!0});if(e.preventDefault(),!0!==e.defaultPrevented)throw new Error("Could not prevent default")}catch(e){var t=function(e,t){var n,r;return(t=t||{}).bubbles=!!t.bubbles,t.cancelable=!!t.cancelable,(n=document.createEvent("CustomEvent")).initCustomEvent(e,t.bubbles,t.cancelable,t.detail),r=n.preventDefault,n.preventDefault=function(){r.call(this);try{Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}})}catch(e){this.defaultPrevented=!0}},n};t.prototype=window.Event.prototype,window.CustomEvent=t}}()},function(e,t,n){window.fetch||(window.fetch=n(7).default||n(7))},function(e,i,o){(function(e){var t=void 0!==e&&e||"undefined"!=typeof self&&self||window,n=Function.prototype.apply;function r(e,t){this._id=e,this._clearFn=t}i.setTimeout=function(){return new r(n.call(setTimeout,t,arguments),clearTimeout)},i.setInterval=function(){return new r(n.call(setInterval,t,arguments),clearInterval)},i.clearTimeout=i.clearInterval=function(e){e&&e.close()},r.prototype.unref=r.prototype.ref=function(){},r.prototype.close=function(){this._clearFn.call(t,this._id)},i.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},i.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},i._unrefActive=i.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;0<=t&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},o(22),i.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,i.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,o(4))},function(e,t,n){(function(e,p){!function(n,r){"use strict";if(!n.setImmediate){var i,o,t,a,e,s=1,c={},l=!1,u=n.document,f=Object.getPrototypeOf&&Object.getPrototypeOf(n);f=f&&f.setTimeout?f:n,i="[object process]"==={}.toString.call(n.process)?function(e){p.nextTick(function(){h(e)})}:function(){if(n.postMessage&&!n.importScripts){var e=!0,t=n.onmessage;return n.onmessage=function(){e=!1},n.postMessage("","*"),n.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",e=function(e){e.source===n&&"string"==typeof e.data&&0===e.data.indexOf(a)&&h(+e.data.slice(a.length))},n.addEventListener?n.addEventListener("message",e,!1):n.attachEvent("onmessage",e),function(e){n.postMessage(a+e,"*")}):n.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){h(e.data)},function(e){t.port2.postMessage(e)}):u&&"onreadystatechange"in u.createElement("script")?(o=u.documentElement,function(e){var t=u.createElement("script");t.onreadystatechange=function(){h(e),t.onreadystatechange=null,o.removeChild(t),t=null},o.appendChild(t)}):function(e){setTimeout(h,0,e)},f.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n=this.length)return D.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},D.QueryLexer.prototype.width=function(){return this.pos-this.start},D.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},D.QueryLexer.prototype.backup=function(){this.pos-=1},D.QueryLexer.prototype.acceptDigitRun=function(){for(var e,t;47<(t=(e=this.next()).charCodeAt(0))&&t<58;);e!=D.QueryLexer.EOS&&this.backup()},D.QueryLexer.prototype.more=function(){return this.pos=t&&(e=c.limit_backward,c.limit_backward=t,c.ket=c.cursor,c.find_among_b(o,4)?(c.bra=c.cursor,c.limit_backward=e,c.cursor=c.limit-r,c.cursor>c.limit_backward&&(c.cursor--,c.bra=c.cursor,c.slice_del())):c.limit_backward=e)}this.setCurrent=function(e){c.setCurrent(e)},this.getCurrent=function(){return c.getCurrent()},this.stem=function(){var e,r=c.cursor;return function(){var e,r=c.cursor+3;if(t=c.limit,0<=r&&r<=c.limit){for(i=r;;){if(e=c.cursor,c.in_grouping(d,97,248)){c.cursor=e;break}if((c.cursor=e)>=c.limit)return;c.cursor++}for(;!c.out_grouping(d,97,248);){if(c.cursor>=c.limit)return;c.cursor++}(t=c.cursor)=t&&(r=c.limit_backward,c.limit_backward=t,c.ket=c.cursor,e=c.find_among_b(s,32),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del();break;case 2:c.in_grouping_b(u,97,229)&&c.slice_del()}}(),c.cursor=c.limit,l(),c.cursor=c.limit,function(){var e,r,i,n=c.limit-c.cursor;if(c.ket=c.cursor,c.eq_s_b(2,"st")&&(c.bra=c.cursor,c.eq_s_b(2,"ig")&&c.slice_del()),c.cursor=c.limit-n,c.cursor>=t&&(r=c.limit_backward,c.limit_backward=t,c.ket=c.cursor,e=c.find_among_b(a,5),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del(),i=c.limit-c.cursor,l(),c.cursor=c.limit-i;break;case 2:c.slice_from("løs")}}(),c.cursor=c.limit,c.cursor>=t&&(e=c.limit_backward,c.limit_backward=t,c.ket=c.cursor,c.out_grouping_b(d,97,248)?(c.bra=c.cursor,n=c.slice_to(n),c.limit_backward=e,c.eq_v_b(n)&&c.slice_del()):c.limit_backward=e),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.de.js b/docs/assets/javascripts/lunr/lunr.de.js deleted file mode 100644 index 73e55eb0..00000000 --- a/docs/assets/javascripts/lunr/lunr.de.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `German` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var _,p,r;e.de=function(){this.pipeline.reset(),this.pipeline.add(e.de.trimmer,e.de.stopWordFilter,e.de.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.de.stemmer))},e.de.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.de.trimmer=e.trimmerSupport.generateTrimmer(e.de.wordCharacters),e.Pipeline.registerFunction(e.de.trimmer,"trimmer-de"),e.de.stemmer=(_=e.stemmerSupport.Among,p=e.stemmerSupport.SnowballProgram,r=new function(){var r,n,i,s=[new _("",-1,6),new _("U",0,2),new _("Y",0,1),new _("ä",0,3),new _("ö",0,4),new _("ü",0,5)],o=[new _("e",-1,2),new _("em",-1,1),new _("en",-1,2),new _("ern",-1,1),new _("er",-1,1),new _("s",-1,3),new _("es",5,2)],c=[new _("en",-1,1),new _("er",-1,1),new _("st",-1,2),new _("est",2,1)],u=[new _("ig",-1,1),new _("lich",-1,1)],a=[new _("end",-1,1),new _("ig",-1,2),new _("ung",-1,1),new _("lich",-1,3),new _("isch",-1,2),new _("ik",-1,2),new _("heit",-1,3),new _("keit",-1,4)],t=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32,8],d=[117,30,5],l=[117,30,4],m=new p;function h(e,r,n){return!(!m.eq_s(1,e)||(m.ket=m.cursor,!m.in_grouping(t,97,252)))&&(m.slice_from(r),m.cursor=n,!0)}function w(){for(;!m.in_grouping(t,97,252);){if(m.cursor>=m.limit)return!0;m.cursor++}for(;!m.out_grouping(t,97,252);){if(m.cursor>=m.limit)return!0;m.cursor++}return!1}function f(){return i<=m.cursor}function b(){return n<=m.cursor}this.setCurrent=function(e){m.setCurrent(e)},this.getCurrent=function(){return m.getCurrent()},this.stem=function(){var e=m.cursor;return function(){for(var e,r,n,i,s=m.cursor;;)if(e=m.cursor,m.bra=e,m.eq_s(1,"ß"))m.ket=m.cursor,m.slice_from("ss");else{if(e>=m.limit)break;m.cursor=e+1}for(m.cursor=s;;)for(r=m.cursor;;){if(n=m.cursor,m.in_grouping(t,97,252)){if(i=m.cursor,m.bra=i,h("u","U",n))break;if(m.cursor=i,h("y","Y",n))break}if(n>=m.limit)return m.cursor=r;m.cursor=n+1}}(),m.cursor=e,function(){i=m.limit,n=i;var e=m.cursor+3;0<=e&&e<=m.limit&&(r=e,w()||((i=m.cursor)=m.limit)return;m.cursor++}}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return r.setCurrent(e),r.stem(),r.getCurrent()}):(r.setCurrent(e),r.stem(),r.getCurrent())}),e.Pipeline.registerFunction(e.de.stemmer,"stemmer-de"),e.de.stopWordFilter=e.generateStopWordFilter("aber alle allem allen aller alles als also am an ander andere anderem anderen anderer anderes anderm andern anderr anders auch auf aus bei bin bis bist da damit dann das dasselbe dazu daß dein deine deinem deinen deiner deines dem demselben den denn denselben der derer derselbe derselben des desselben dessen dich die dies diese dieselbe dieselben diesem diesen dieser dieses dir doch dort du durch ein eine einem einen einer eines einig einige einigem einigen einiger einiges einmal er es etwas euch euer eure eurem euren eurer eures für gegen gewesen hab habe haben hat hatte hatten hier hin hinter ich ihm ihn ihnen ihr ihre ihrem ihren ihrer ihres im in indem ins ist jede jedem jeden jeder jedes jene jenem jenen jener jenes jetzt kann kein keine keinem keinen keiner keines können könnte machen man manche manchem manchen mancher manches mein meine meinem meinen meiner meines mich mir mit muss musste nach nicht nichts noch nun nur ob oder ohne sehr sein seine seinem seinen seiner seines selbst sich sie sind so solche solchem solchen solcher solches soll sollte sondern sonst um und uns unse unsem unsen unser unses unter viel vom von vor war waren warst was weg weil weiter welche welchem welchen welcher welches wenn werde werden wie wieder will wir wird wirst wo wollen wollte während würde würden zu zum zur zwar zwischen über".split(" ")),e.Pipeline.registerFunction(e.de.stopWordFilter,"stopWordFilter-de")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.du.js b/docs/assets/javascripts/lunr/lunr.du.js deleted file mode 100644 index e9c67299..00000000 --- a/docs/assets/javascripts/lunr/lunr.du.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Dutch` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var v,q,r;console.warn('[Lunr Languages] Please use the "nl" instead of the "du". The "nl" code is the standard code for Dutch language, and "du" will be removed in the next major versions.'),e.du=function(){this.pipeline.reset(),this.pipeline.add(e.du.trimmer,e.du.stopWordFilter,e.du.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.du.stemmer))},e.du.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.du.trimmer=e.trimmerSupport.generateTrimmer(e.du.wordCharacters),e.Pipeline.registerFunction(e.du.trimmer,"trimmer-du"),e.du.stemmer=(v=e.stemmerSupport.Among,q=e.stemmerSupport.SnowballProgram,r=new function(){var r,i,u,o=[new v("",-1,6),new v("á",0,1),new v("ä",0,1),new v("é",0,2),new v("ë",0,2),new v("í",0,3),new v("ï",0,3),new v("ó",0,4),new v("ö",0,4),new v("ú",0,5),new v("ü",0,5)],n=[new v("",-1,3),new v("I",0,2),new v("Y",0,1)],t=[new v("dd",-1,-1),new v("kk",-1,-1),new v("tt",-1,-1)],c=[new v("ene",-1,2),new v("se",-1,3),new v("en",-1,2),new v("heden",2,1),new v("s",-1,3)],a=[new v("end",-1,1),new v("ig",-1,2),new v("ing",-1,1),new v("lijk",-1,3),new v("baar",-1,4),new v("bar",-1,5)],l=[new v("aa",-1,-1),new v("ee",-1,-1),new v("oo",-1,-1),new v("uu",-1,-1)],m=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],d=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],f=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],_=new q;function s(e){return(_.cursor=e)>=_.limit||(_.cursor++,!1)}function w(){for(;!_.in_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}for(;!_.out_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}return!1}function b(){return i<=_.cursor}function p(){return r<=_.cursor}function g(){var e=_.limit-_.cursor;_.find_among_b(t,3)&&(_.cursor=_.limit-e,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del()))}function h(){var e;u=!1,_.ket=_.cursor,_.eq_s_b(1,"e")&&(_.bra=_.cursor,b()&&(e=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-e,_.slice_del(),u=!0,g())))}function k(){var e;b()&&(e=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-e,_.eq_s_b(3,"gem")||(_.cursor=_.limit-e,_.slice_del(),g())))}this.setCurrent=function(e){_.setCurrent(e)},this.getCurrent=function(){return _.getCurrent()},this.stem=function(){var e=_.cursor;return function(){for(var e,r,i,n=_.cursor;;){if(_.bra=_.cursor,e=_.find_among(o,11))switch(_.ket=_.cursor,e){case 1:_.slice_from("a");continue;case 2:_.slice_from("e");continue;case 3:_.slice_from("i");continue;case 4:_.slice_from("o");continue;case 5:_.slice_from("u");continue;case 6:if(_.cursor>=_.limit)break;_.cursor++;continue}break}for(_.cursor=n,_.bra=n,_.eq_s(1,"y")?(_.ket=_.cursor,_.slice_from("Y")):_.cursor=n;;)if(r=_.cursor,_.in_grouping(m,97,232)){if(i=_.cursor,_.bra=i,_.eq_s(1,"i"))_.ket=_.cursor,_.in_grouping(m,97,232)&&(_.slice_from("I"),_.cursor=r);else if(_.cursor=i,_.eq_s(1,"y"))_.ket=_.cursor,_.slice_from("Y"),_.cursor=r;else if(s(r))break}else if(s(r))break}(),_.cursor=e,i=_.limit,r=i,w()||((i=_.cursor)<3&&(i=3),w()||(r=_.cursor)),_.limit_backward=e,_.cursor=_.limit,function(){var e,r,i,n,o,t,s=_.limit-_.cursor;if(_.ket=_.cursor,e=_.find_among_b(c,5))switch(_.bra=_.cursor,e){case 1:b()&&_.slice_from("heid");break;case 2:k();break;case 3:b()&&_.out_grouping_b(f,97,232)&&_.slice_del()}if(_.cursor=_.limit-s,h(),_.cursor=_.limit-s,_.ket=_.cursor,_.eq_s_b(4,"heid")&&(_.bra=_.cursor,p()&&(r=_.limit-_.cursor,_.eq_s_b(1,"c")||(_.cursor=_.limit-r,_.slice_del(),_.ket=_.cursor,_.eq_s_b(2,"en")&&(_.bra=_.cursor,k())))),_.cursor=_.limit-s,_.ket=_.cursor,e=_.find_among_b(a,6))switch(_.bra=_.cursor,e){case 1:if(p()){if(_.slice_del(),i=_.limit-_.cursor,_.ket=_.cursor,_.eq_s_b(2,"ig")&&(_.bra=_.cursor,p()&&(n=_.limit-_.cursor,!_.eq_s_b(1,"e")))){_.cursor=_.limit-n,_.slice_del();break}_.cursor=_.limit-i,g()}break;case 2:p()&&(o=_.limit-_.cursor,_.eq_s_b(1,"e")||(_.cursor=_.limit-o,_.slice_del()));break;case 3:p()&&(_.slice_del(),h());break;case 4:p()&&_.slice_del();break;case 5:p()&&u&&_.slice_del()}_.cursor=_.limit-s,_.out_grouping_b(d,73,232)&&(t=_.limit-_.cursor,_.find_among_b(l,4)&&_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-t,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del())))}(),_.cursor=_.limit_backward,function(){for(var e;;)if(_.bra=_.cursor,e=_.find_among(n,3))switch(_.ket=_.cursor,e){case 1:_.slice_from("y");break;case 2:_.slice_from("i");break;case 3:if(_.cursor>=_.limit)return;_.cursor++}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return r.setCurrent(e),r.stem(),r.getCurrent()}):(r.setCurrent(e),r.stem(),r.getCurrent())}),e.Pipeline.registerFunction(e.du.stemmer,"stemmer-du"),e.du.stopWordFilter=e.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),e.Pipeline.registerFunction(e.du.stopWordFilter,"stopWordFilter-du")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.es.js b/docs/assets/javascripts/lunr/lunr.es.js deleted file mode 100644 index 2918bd19..00000000 --- a/docs/assets/javascripts/lunr/lunr.es.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Spanish` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,s){"function"==typeof define&&define.amd?define(s):"object"==typeof exports?module.exports=s():s()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var C,P,s;e.es=function(){this.pipeline.reset(),this.pipeline.add(e.es.trimmer,e.es.stopWordFilter,e.es.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.es.stemmer))},e.es.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.es.trimmer=e.trimmerSupport.generateTrimmer(e.es.wordCharacters),e.Pipeline.registerFunction(e.es.trimmer,"trimmer-es"),e.es.stemmer=(C=e.stemmerSupport.Among,P=e.stemmerSupport.SnowballProgram,s=new function(){var r,n,i,a=[new C("",-1,6),new C("á",0,1),new C("é",0,2),new C("í",0,3),new C("ó",0,4),new C("ú",0,5)],t=[new C("la",-1,-1),new C("sela",0,-1),new C("le",-1,-1),new C("me",-1,-1),new C("se",-1,-1),new C("lo",-1,-1),new C("selo",5,-1),new C("las",-1,-1),new C("selas",7,-1),new C("les",-1,-1),new C("los",-1,-1),new C("selos",10,-1),new C("nos",-1,-1)],o=[new C("ando",-1,6),new C("iendo",-1,6),new C("yendo",-1,7),new C("ándo",-1,2),new C("iéndo",-1,1),new C("ar",-1,6),new C("er",-1,6),new C("ir",-1,6),new C("ár",-1,3),new C("ér",-1,4),new C("ír",-1,5)],s=[new C("ic",-1,-1),new C("ad",-1,-1),new C("os",-1,-1),new C("iv",-1,1)],u=[new C("able",-1,1),new C("ible",-1,1),new C("ante",-1,1)],w=[new C("ic",-1,1),new C("abil",-1,1),new C("iv",-1,1)],c=[new C("ica",-1,1),new C("ancia",-1,2),new C("encia",-1,5),new C("adora",-1,2),new C("osa",-1,1),new C("ista",-1,1),new C("iva",-1,9),new C("anza",-1,1),new C("logía",-1,3),new C("idad",-1,8),new C("able",-1,1),new C("ible",-1,1),new C("ante",-1,2),new C("mente",-1,7),new C("amente",13,6),new C("ación",-1,2),new C("ución",-1,4),new C("ico",-1,1),new C("ismo",-1,1),new C("oso",-1,1),new C("amiento",-1,1),new C("imiento",-1,1),new C("ivo",-1,9),new C("ador",-1,2),new C("icas",-1,1),new C("ancias",-1,2),new C("encias",-1,5),new C("adoras",-1,2),new C("osas",-1,1),new C("istas",-1,1),new C("ivas",-1,9),new C("anzas",-1,1),new C("logías",-1,3),new C("idades",-1,8),new C("ables",-1,1),new C("ibles",-1,1),new C("aciones",-1,2),new C("uciones",-1,4),new C("adores",-1,2),new C("antes",-1,2),new C("icos",-1,1),new C("ismos",-1,1),new C("osos",-1,1),new C("amientos",-1,1),new C("imientos",-1,1),new C("ivos",-1,9)],m=[new C("ya",-1,1),new C("ye",-1,1),new C("yan",-1,1),new C("yen",-1,1),new C("yeron",-1,1),new C("yendo",-1,1),new C("yo",-1,1),new C("yas",-1,1),new C("yes",-1,1),new C("yais",-1,1),new C("yamos",-1,1),new C("yó",-1,1)],l=[new C("aba",-1,2),new C("ada",-1,2),new C("ida",-1,2),new C("ara",-1,2),new C("iera",-1,2),new C("ía",-1,2),new C("aría",5,2),new C("ería",5,2),new C("iría",5,2),new C("ad",-1,2),new C("ed",-1,2),new C("id",-1,2),new C("ase",-1,2),new C("iese",-1,2),new C("aste",-1,2),new C("iste",-1,2),new C("an",-1,2),new C("aban",16,2),new C("aran",16,2),new C("ieran",16,2),new C("ían",16,2),new C("arían",20,2),new C("erían",20,2),new C("irían",20,2),new C("en",-1,1),new C("asen",24,2),new C("iesen",24,2),new C("aron",-1,2),new C("ieron",-1,2),new C("arán",-1,2),new C("erán",-1,2),new C("irán",-1,2),new C("ado",-1,2),new C("ido",-1,2),new C("ando",-1,2),new C("iendo",-1,2),new C("ar",-1,2),new C("er",-1,2),new C("ir",-1,2),new C("as",-1,2),new C("abas",39,2),new C("adas",39,2),new C("idas",39,2),new C("aras",39,2),new C("ieras",39,2),new C("ías",39,2),new C("arías",45,2),new C("erías",45,2),new C("irías",45,2),new C("es",-1,1),new C("ases",49,2),new C("ieses",49,2),new C("abais",-1,2),new C("arais",-1,2),new C("ierais",-1,2),new C("íais",-1,2),new C("aríais",55,2),new C("eríais",55,2),new C("iríais",55,2),new C("aseis",-1,2),new C("ieseis",-1,2),new C("asteis",-1,2),new C("isteis",-1,2),new C("áis",-1,2),new C("éis",-1,1),new C("aréis",64,2),new C("eréis",64,2),new C("iréis",64,2),new C("ados",-1,2),new C("idos",-1,2),new C("amos",-1,2),new C("ábamos",70,2),new C("áramos",70,2),new C("iéramos",70,2),new C("íamos",70,2),new C("aríamos",74,2),new C("eríamos",74,2),new C("iríamos",74,2),new C("emos",-1,1),new C("aremos",78,2),new C("eremos",78,2),new C("iremos",78,2),new C("ásemos",78,2),new C("iésemos",78,2),new C("imos",-1,2),new C("arás",-1,2),new C("erás",-1,2),new C("irás",-1,2),new C("ís",-1,2),new C("ará",-1,2),new C("erá",-1,2),new C("irá",-1,2),new C("aré",-1,2),new C("eré",-1,2),new C("iré",-1,2),new C("ió",-1,2)],d=[new C("a",-1,1),new C("e",-1,2),new C("o",-1,1),new C("os",-1,1),new C("á",-1,1),new C("é",-1,2),new C("í",-1,1),new C("ó",-1,1)],b=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,4,10],f=new P;function _(){if(f.out_grouping(b,97,252)){for(;!f.in_grouping(b,97,252);){if(f.cursor>=f.limit)return!0;f.cursor++}return!1}return!0}function h(){var e,s=f.cursor;if(function(){if(f.in_grouping(b,97,252)){var e=f.cursor;if(_()){if(f.cursor=e,!f.in_grouping(b,97,252))return!0;for(;!f.out_grouping(b,97,252);){if(f.cursor>=f.limit)return!0;f.cursor++}}return!1}return!0}()){if(f.cursor=s,!f.out_grouping(b,97,252))return;if(e=f.cursor,_()){if(f.cursor=e,!f.in_grouping(b,97,252)||f.cursor>=f.limit)return;f.cursor++}}i=f.cursor}function v(){for(;!f.in_grouping(b,97,252);){if(f.cursor>=f.limit)return!1;f.cursor++}for(;!f.out_grouping(b,97,252);){if(f.cursor>=f.limit)return!1;f.cursor++}return!0}function p(){return i<=f.cursor}function g(){return r<=f.cursor}function k(e,s){if(!g())return!0;f.slice_del(),f.ket=f.cursor;var r=f.find_among_b(e,s);return r&&(f.bra=f.cursor,1==r&&g()&&f.slice_del()),!1}function y(e){return!g()||(f.slice_del(),f.ket=f.cursor,f.eq_s_b(2,e)&&(f.bra=f.cursor,g()&&f.slice_del()),!1)}function q(){var e;if(f.ket=f.cursor,e=f.find_among_b(c,46)){switch(f.bra=f.cursor,e){case 1:if(!g())return!1;f.slice_del();break;case 2:if(y("ic"))return!1;break;case 3:if(!g())return!1;f.slice_from("log");break;case 4:if(!g())return!1;f.slice_from("u");break;case 5:if(!g())return!1;f.slice_from("ente");break;case 6:if(!(n<=f.cursor))return!1;f.slice_del(),f.ket=f.cursor,(e=f.find_among_b(s,4))&&(f.bra=f.cursor,g()&&(f.slice_del(),1==e&&(f.ket=f.cursor,f.eq_s_b(2,"at")&&(f.bra=f.cursor,g()&&f.slice_del()))));break;case 7:if(k(u,3))return!1;break;case 8:if(k(w,3))return!1;break;case 9:if(y("at"))return!1}return!0}return!1}this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var e,s=f.cursor;return e=f.cursor,i=f.limit,r=n=i,h(),f.cursor=e,v()&&(n=f.cursor,v()&&(r=f.cursor)),f.limit_backward=s,f.cursor=f.limit,function(){var e;if(f.ket=f.cursor,f.find_among_b(t,13)&&(f.bra=f.cursor,(e=f.find_among_b(o,11))&&p()))switch(e){case 1:f.bra=f.cursor,f.slice_from("iendo");break;case 2:f.bra=f.cursor,f.slice_from("ando");break;case 3:f.bra=f.cursor,f.slice_from("ar");break;case 4:f.bra=f.cursor,f.slice_from("er");break;case 5:f.bra=f.cursor,f.slice_from("ir");break;case 6:f.slice_del();break;case 7:f.eq_s_b(1,"u")&&f.slice_del()}}(),f.cursor=f.limit,q()||(f.cursor=f.limit,function(){var e,s;if(f.cursor>=i&&(s=f.limit_backward,f.limit_backward=i,f.ket=f.cursor,e=f.find_among_b(m,12),f.limit_backward=s,e)){if(f.bra=f.cursor,1==e){if(!f.eq_s_b(1,"u"))return!1;f.slice_del()}return!0}return!1}()||(f.cursor=f.limit,function(){var e,s,r,n;if(f.cursor>=i&&(s=f.limit_backward,f.limit_backward=i,f.ket=f.cursor,e=f.find_among_b(l,96),f.limit_backward=s,e))switch(f.bra=f.cursor,e){case 1:r=f.limit-f.cursor,f.eq_s_b(1,"u")?(n=f.limit-f.cursor,f.eq_s_b(1,"g")?f.cursor=f.limit-n:f.cursor=f.limit-r):f.cursor=f.limit-r,f.bra=f.cursor;case 2:f.slice_del()}}())),f.cursor=f.limit,function(){var e,s;if(f.ket=f.cursor,e=f.find_among_b(d,8))switch(f.bra=f.cursor,e){case 1:p()&&f.slice_del();break;case 2:p()&&(f.slice_del(),f.ket=f.cursor,f.eq_s_b(1,"u")&&(f.bra=f.cursor,s=f.limit-f.cursor,f.eq_s_b(1,"g")&&(f.cursor=f.limit-s,p()&&f.slice_del())))}}(),f.cursor=f.limit_backward,function(){for(var e;;){if(f.bra=f.cursor,e=f.find_among(a,6))switch(f.ket=f.cursor,e){case 1:f.slice_from("a");continue;case 2:f.slice_from("e");continue;case 3:f.slice_from("i");continue;case 4:f.slice_from("o");continue;case 5:f.slice_from("u");continue;case 6:if(f.cursor>=f.limit)break;f.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return s.setCurrent(e),s.stem(),s.getCurrent()}):(s.setCurrent(e),s.stem(),s.getCurrent())}),e.Pipeline.registerFunction(e.es.stemmer,"stemmer-es"),e.es.stopWordFilter=e.generateStopWordFilter("a al algo algunas algunos ante antes como con contra cual cuando de del desde donde durante e el ella ellas ellos en entre era erais eran eras eres es esa esas ese eso esos esta estaba estabais estaban estabas estad estada estadas estado estados estamos estando estar estaremos estará estarán estarás estaré estaréis estaría estaríais estaríamos estarían estarías estas este estemos esto estos estoy estuve estuviera estuvierais estuvieran estuvieras estuvieron estuviese estuvieseis estuviesen estuvieses estuvimos estuviste estuvisteis estuviéramos estuviésemos estuvo está estábamos estáis están estás esté estéis estén estés fue fuera fuerais fueran fueras fueron fuese fueseis fuesen fueses fui fuimos fuiste fuisteis fuéramos fuésemos ha habida habidas habido habidos habiendo habremos habrá habrán habrás habré habréis habría habríais habríamos habrían habrías habéis había habíais habíamos habían habías han has hasta hay haya hayamos hayan hayas hayáis he hemos hube hubiera hubierais hubieran hubieras hubieron hubiese hubieseis hubiesen hubieses hubimos hubiste hubisteis hubiéramos hubiésemos hubo la las le les lo los me mi mis mucho muchos muy más mí mía mías mío míos nada ni no nos nosotras nosotros nuestra nuestras nuestro nuestros o os otra otras otro otros para pero poco por porque que quien quienes qué se sea seamos sean seas seremos será serán serás seré seréis sería seríais seríamos serían serías seáis sido siendo sin sobre sois somos son soy su sus suya suyas suyo suyos sí también tanto te tendremos tendrá tendrán tendrás tendré tendréis tendría tendríais tendríamos tendrían tendrías tened tenemos tenga tengamos tengan tengas tengo tengáis tenida tenidas tenido tenidos teniendo tenéis tenía teníais teníamos tenían tenías ti tiene tienen tienes todo todos tu tus tuve tuviera tuvierais tuvieran tuvieras tuvieron tuviese tuvieseis tuviesen tuvieses tuvimos tuviste tuvisteis tuviéramos tuviésemos tuvo tuya tuyas tuyo tuyos tú un una uno unos vosotras vosotros vuestra vuestras vuestro vuestros y ya yo él éramos".split(" ")),e.Pipeline.registerFunction(e.es.stopWordFilter,"stopWordFilter-es")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.fi.js b/docs/assets/javascripts/lunr/lunr.fi.js deleted file mode 100644 index f34d10e0..00000000 --- a/docs/assets/javascripts/lunr/lunr.fi.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Finnish` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(i,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(i.lunr)}(this,function(){return function(i){if(void 0===i)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===i.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var v,C,e;i.fi=function(){this.pipeline.reset(),this.pipeline.add(i.fi.trimmer,i.fi.stopWordFilter,i.fi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(i.fi.stemmer))},i.fi.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",i.fi.trimmer=i.trimmerSupport.generateTrimmer(i.fi.wordCharacters),i.Pipeline.registerFunction(i.fi.trimmer,"trimmer-fi"),i.fi.stemmer=(v=i.stemmerSupport.Among,C=i.stemmerSupport.SnowballProgram,e=new function(){var n,t,l,o,r=[new v("pa",-1,1),new v("sti",-1,2),new v("kaan",-1,1),new v("han",-1,1),new v("kin",-1,1),new v("hän",-1,1),new v("kään",-1,1),new v("ko",-1,1),new v("pä",-1,1),new v("kö",-1,1)],s=[new v("lla",-1,-1),new v("na",-1,-1),new v("ssa",-1,-1),new v("ta",-1,-1),new v("lta",3,-1),new v("sta",3,-1)],a=[new v("llä",-1,-1),new v("nä",-1,-1),new v("ssä",-1,-1),new v("tä",-1,-1),new v("ltä",3,-1),new v("stä",3,-1)],u=[new v("lle",-1,-1),new v("ine",-1,-1)],c=[new v("nsa",-1,3),new v("mme",-1,3),new v("nne",-1,3),new v("ni",-1,2),new v("si",-1,1),new v("an",-1,4),new v("en",-1,6),new v("än",-1,5),new v("nsä",-1,3)],i=[new v("aa",-1,-1),new v("ee",-1,-1),new v("ii",-1,-1),new v("oo",-1,-1),new v("uu",-1,-1),new v("ää",-1,-1),new v("öö",-1,-1)],m=[new v("a",-1,8),new v("lla",0,-1),new v("na",0,-1),new v("ssa",0,-1),new v("ta",0,-1),new v("lta",4,-1),new v("sta",4,-1),new v("tta",4,9),new v("lle",-1,-1),new v("ine",-1,-1),new v("ksi",-1,-1),new v("n",-1,7),new v("han",11,1),new v("den",11,-1,q),new v("seen",11,-1,j),new v("hen",11,2),new v("tten",11,-1,q),new v("hin",11,3),new v("siin",11,-1,q),new v("hon",11,4),new v("hän",11,5),new v("hön",11,6),new v("ä",-1,8),new v("llä",22,-1),new v("nä",22,-1),new v("ssä",22,-1),new v("tä",22,-1),new v("ltä",26,-1),new v("stä",26,-1),new v("ttä",26,9)],w=[new v("eja",-1,-1),new v("mma",-1,1),new v("imma",1,-1),new v("mpa",-1,1),new v("impa",3,-1),new v("mmi",-1,1),new v("immi",5,-1),new v("mpi",-1,1),new v("impi",7,-1),new v("ejä",-1,-1),new v("mmä",-1,1),new v("immä",10,-1),new v("mpä",-1,1),new v("impä",12,-1)],_=[new v("i",-1,-1),new v("j",-1,-1)],k=[new v("mma",-1,1),new v("imma",0,-1)],b=[17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],e=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],f=[17,97,24,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],h=new C;function p(){for(var i;i=h.cursor,!h.in_grouping(d,97,246);){if((h.cursor=i)>=h.limit)return!0;h.cursor++}for(h.cursor=i;!h.out_grouping(d,97,246);){if(h.cursor>=h.limit)return!0;h.cursor++}return!1}function g(){var i,e;if(h.cursor>=o)if(e=h.limit_backward,h.limit_backward=o,h.ket=h.cursor,i=h.find_among_b(r,10)){switch(h.bra=h.cursor,h.limit_backward=e,i){case 1:if(!h.in_grouping_b(f,97,246))return;break;case 2:if(!(l<=h.cursor))return}h.slice_del()}else h.limit_backward=e}function j(){return h.find_among_b(i,7)}function q(){return h.eq_s_b(1,"i")&&h.in_grouping_b(e,97,246)}this.setCurrent=function(i){h.setCurrent(i)},this.getCurrent=function(){return h.getCurrent()},this.stem=function(){var i,e=h.cursor;return o=h.limit,l=o,p()||(o=h.cursor,p()||(l=h.cursor)),n=!1,h.limit_backward=e,h.cursor=h.limit,g(),h.cursor=h.limit,function(){var i,e,r;if(h.cursor>=o)if(e=h.limit_backward,h.limit_backward=o,h.ket=h.cursor,i=h.find_among_b(c,9))switch(h.bra=h.cursor,h.limit_backward=e,i){case 1:r=h.limit-h.cursor,h.eq_s_b(1,"k")||(h.cursor=h.limit-r,h.slice_del());break;case 2:h.slice_del(),h.ket=h.cursor,h.eq_s_b(3,"kse")&&(h.bra=h.cursor,h.slice_from("ksi"));break;case 3:h.slice_del();break;case 4:h.find_among_b(s,6)&&h.slice_del();break;case 5:h.find_among_b(a,6)&&h.slice_del();break;case 6:h.find_among_b(u,2)&&h.slice_del()}else h.limit_backward=e}(),h.cursor=h.limit,function(){var i,e,r;if(h.cursor>=o)if(e=h.limit_backward,h.limit_backward=o,h.ket=h.cursor,i=h.find_among_b(m,30)){switch(h.bra=h.cursor,h.limit_backward=e,i){case 1:if(!h.eq_s_b(1,"a"))return;break;case 2:case 9:if(!h.eq_s_b(1,"e"))return;break;case 3:if(!h.eq_s_b(1,"i"))return;break;case 4:if(!h.eq_s_b(1,"o"))return;break;case 5:if(!h.eq_s_b(1,"ä"))return;break;case 6:if(!h.eq_s_b(1,"ö"))return;break;case 7:if(r=h.limit-h.cursor,!j()&&(h.cursor=h.limit-r,!h.eq_s_b(2,"ie"))){h.cursor=h.limit-r;break}if(h.cursor=h.limit-r,h.cursor<=h.limit_backward){h.cursor=h.limit-r;break}h.cursor--,h.bra=h.cursor;break;case 8:if(!h.in_grouping_b(d,97,246)||!h.out_grouping_b(d,97,246))return}h.slice_del(),n=!0}else h.limit_backward=e}(),h.cursor=h.limit,function(){var i,e,r;if(h.cursor>=l)if(e=h.limit_backward,h.limit_backward=l,h.ket=h.cursor,i=h.find_among_b(w,14)){if(h.bra=h.cursor,h.limit_backward=e,1==i){if(r=h.limit-h.cursor,h.eq_s_b(2,"po"))return;h.cursor=h.limit-r}h.slice_del()}else h.limit_backward=e}(),h.cursor=h.limit,h.cursor=(n?h.cursor>=o&&(i=h.limit_backward,h.limit_backward=o,h.ket=h.cursor,h.find_among_b(_,2)?(h.bra=h.cursor,h.limit_backward=i,h.slice_del()):h.limit_backward=i):(h.cursor=h.limit,function(){var i,e,r,n,t,s;if(h.cursor>=o){if(e=h.limit_backward,h.limit_backward=o,h.ket=h.cursor,h.eq_s_b(1,"t")&&(h.bra=h.cursor,r=h.limit-h.cursor,h.in_grouping_b(d,97,246)&&(h.cursor=h.limit-r,h.slice_del(),h.limit_backward=e,n=h.limit-h.cursor,h.cursor>=l&&(h.cursor=l,t=h.limit_backward,h.limit_backward=h.cursor,h.cursor=h.limit-n,h.ket=h.cursor,i=h.find_among_b(k,2))))){if(h.bra=h.cursor,h.limit_backward=t,1==i){if(s=h.limit-h.cursor,h.eq_s_b(2,"po"))return;h.cursor=h.limit-s}return h.slice_del()}h.limit_backward=e}}()),h.limit),function(){var i,e,r,n;if(h.cursor>=o){for(i=h.limit_backward,h.limit_backward=o,e=h.limit-h.cursor,j()&&(h.cursor=h.limit-e,h.ket=h.cursor,h.cursor>h.limit_backward&&(h.cursor--,h.bra=h.cursor,h.slice_del())),h.cursor=h.limit-e,h.ket=h.cursor,h.in_grouping_b(b,97,228)&&(h.bra=h.cursor,h.out_grouping_b(d,97,246)&&h.slice_del()),h.cursor=h.limit-e,h.ket=h.cursor,h.eq_s_b(1,"j")&&(h.bra=h.cursor,r=h.limit-h.cursor,h.eq_s_b(1,"o")?h.slice_del():(h.cursor=h.limit-r,h.eq_s_b(1,"u")&&h.slice_del())),h.cursor=h.limit-e,h.ket=h.cursor,h.eq_s_b(1,"o")&&(h.bra=h.cursor,h.eq_s_b(1,"j")&&h.slice_del()),h.cursor=h.limit-e,h.limit_backward=i;;){if(n=h.limit-h.cursor,h.out_grouping_b(d,97,246)){h.cursor=h.limit-n;break}if(h.cursor=h.limit-n,h.cursor<=h.limit_backward)return;h.cursor--}h.ket=h.cursor,h.cursor>h.limit_backward&&(h.cursor--,h.bra=h.cursor,t=h.slice_to(),h.eq_v_b(t)&&h.slice_del())}}(),!0}},function(i){return"function"==typeof i.update?i.update(function(i){return e.setCurrent(i),e.stem(),e.getCurrent()}):(e.setCurrent(i),e.stem(),e.getCurrent())}),i.Pipeline.registerFunction(i.fi.stemmer,"stemmer-fi"),i.fi.stopWordFilter=i.generateStopWordFilter("ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli".split(" ")),i.Pipeline.registerFunction(i.fi.stopWordFilter,"stopWordFilter-fi")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.fr.js b/docs/assets/javascripts/lunr/lunr.fr.js deleted file mode 100644 index d043ec65..00000000 --- a/docs/assets/javascripts/lunr/lunr.fr.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `French` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,y,s;e.fr=function(){this.pipeline.reset(),this.pipeline.add(e.fr.trimmer,e.fr.stopWordFilter,e.fr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.fr.stemmer))},e.fr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.fr.trimmer=e.trimmerSupport.generateTrimmer(e.fr.wordCharacters),e.Pipeline.registerFunction(e.fr.trimmer,"trimmer-fr"),e.fr.stemmer=(r=e.stemmerSupport.Among,y=e.stemmerSupport.SnowballProgram,s=new function(){var s,i,t,n=[new r("col",-1,-1),new r("par",-1,-1),new r("tap",-1,-1)],u=[new r("",-1,4),new r("I",0,1),new r("U",0,2),new r("Y",0,3)],o=[new r("iqU",-1,3),new r("abl",-1,3),new r("Ièr",-1,4),new r("ièr",-1,4),new r("eus",-1,2),new r("iv",-1,1)],c=[new r("ic",-1,2),new r("abil",-1,1),new r("iv",-1,3)],a=[new r("iqUe",-1,1),new r("atrice",-1,2),new r("ance",-1,1),new r("ence",-1,5),new r("logie",-1,3),new r("able",-1,1),new r("isme",-1,1),new r("euse",-1,11),new r("iste",-1,1),new r("ive",-1,8),new r("if",-1,8),new r("usion",-1,4),new r("ation",-1,2),new r("ution",-1,4),new r("ateur",-1,2),new r("iqUes",-1,1),new r("atrices",-1,2),new r("ances",-1,1),new r("ences",-1,5),new r("logies",-1,3),new r("ables",-1,1),new r("ismes",-1,1),new r("euses",-1,11),new r("istes",-1,1),new r("ives",-1,8),new r("ifs",-1,8),new r("usions",-1,4),new r("ations",-1,2),new r("utions",-1,4),new r("ateurs",-1,2),new r("ments",-1,15),new r("ements",30,6),new r("issements",31,12),new r("ités",-1,7),new r("ment",-1,15),new r("ement",34,6),new r("issement",35,12),new r("amment",34,13),new r("emment",34,14),new r("aux",-1,10),new r("eaux",39,9),new r("eux",-1,1),new r("ité",-1,7)],l=[new r("ira",-1,1),new r("ie",-1,1),new r("isse",-1,1),new r("issante",-1,1),new r("i",-1,1),new r("irai",4,1),new r("ir",-1,1),new r("iras",-1,1),new r("ies",-1,1),new r("îmes",-1,1),new r("isses",-1,1),new r("issantes",-1,1),new r("îtes",-1,1),new r("is",-1,1),new r("irais",13,1),new r("issais",13,1),new r("irions",-1,1),new r("issions",-1,1),new r("irons",-1,1),new r("issons",-1,1),new r("issants",-1,1),new r("it",-1,1),new r("irait",21,1),new r("issait",21,1),new r("issant",-1,1),new r("iraIent",-1,1),new r("issaIent",-1,1),new r("irent",-1,1),new r("issent",-1,1),new r("iront",-1,1),new r("ît",-1,1),new r("iriez",-1,1),new r("issiez",-1,1),new r("irez",-1,1),new r("issez",-1,1)],w=[new r("a",-1,3),new r("era",0,2),new r("asse",-1,3),new r("ante",-1,3),new r("ée",-1,2),new r("ai",-1,3),new r("erai",5,2),new r("er",-1,2),new r("as",-1,3),new r("eras",8,2),new r("âmes",-1,3),new r("asses",-1,3),new r("antes",-1,3),new r("âtes",-1,3),new r("ées",-1,2),new r("ais",-1,3),new r("erais",15,2),new r("ions",-1,1),new r("erions",17,2),new r("assions",17,3),new r("erons",-1,2),new r("ants",-1,3),new r("és",-1,2),new r("ait",-1,3),new r("erait",23,2),new r("ant",-1,3),new r("aIent",-1,3),new r("eraIent",26,2),new r("èrent",-1,2),new r("assent",-1,3),new r("eront",-1,2),new r("ât",-1,3),new r("ez",-1,2),new r("iez",32,2),new r("eriez",33,2),new r("assiez",33,3),new r("erez",32,2),new r("é",-1,2)],f=[new r("e",-1,3),new r("Ière",0,2),new r("ière",0,2),new r("ion",-1,1),new r("Ier",-1,2),new r("ier",-1,2),new r("ë",-1,4)],m=[new r("ell",-1,-1),new r("eill",-1,-1),new r("enn",-1,-1),new r("onn",-1,-1),new r("ett",-1,-1)],_=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,128,130,103,8,5],b=[1,65,20,0,0,0,0,0,0,0,0,0,0,0,0,0,128],d=new y;function k(e,r,s){return!(!d.eq_s(1,e)||(d.ket=d.cursor,!d.in_grouping(_,97,251)))&&(d.slice_from(r),d.cursor=s,!0)}function p(e,r,s){return!!d.eq_s(1,e)&&(d.ket=d.cursor,d.slice_from(r),d.cursor=s,!0)}function g(){for(;!d.in_grouping(_,97,251);){if(d.cursor>=d.limit)return!0;d.cursor++}for(;!d.out_grouping(_,97,251);){if(d.cursor>=d.limit)return!0;d.cursor++}return!1}function q(){return t<=d.cursor}function v(){return i<=d.cursor}function h(){return s<=d.cursor}function z(){if(!function(){var e,r;if(d.ket=d.cursor,e=d.find_among_b(a,43)){switch(d.bra=d.cursor,e){case 1:if(!h())return!1;d.slice_del();break;case 2:if(!h())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,h()?d.slice_del():d.slice_from("iqU"));break;case 3:if(!h())return!1;d.slice_from("log");break;case 4:if(!h())return!1;d.slice_from("u");break;case 5:if(!h())return!1;d.slice_from("ent");break;case 6:if(!q())return!1;if(d.slice_del(),d.ket=d.cursor,e=d.find_among_b(o,6))switch(d.bra=d.cursor,e){case 1:h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,h()&&d.slice_del()));break;case 2:h()?d.slice_del():v()&&d.slice_from("eux");break;case 3:h()&&d.slice_del();break;case 4:q()&&d.slice_from("i")}break;case 7:if(!h())return!1;if(d.slice_del(),d.ket=d.cursor,e=d.find_among_b(c,3))switch(d.bra=d.cursor,e){case 1:h()?d.slice_del():d.slice_from("abl");break;case 2:h()?d.slice_del():d.slice_from("iqU");break;case 3:h()&&d.slice_del()}break;case 8:if(!h())return!1;if(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")))){d.bra=d.cursor,h()?d.slice_del():d.slice_from("iqU");break}break;case 9:d.slice_from("eau");break;case 10:if(!v())return!1;d.slice_from("al");break;case 11:if(h())d.slice_del();else{if(!v())return!1;d.slice_from("eux")}break;case 12:if(!v()||!d.out_grouping_b(_,97,251))return!1;d.slice_del();break;case 13:return q()&&d.slice_from("ant"),!1;case 14:return q()&&d.slice_from("ent"),!1;case 15:return r=d.limit-d.cursor,d.in_grouping_b(_,97,251)&&q()&&(d.cursor=d.limit-r,d.slice_del()),!1}return!0}return!1}()&&(d.cursor=d.limit,!function(){var e,r;if(d.cursor=t){if(s=d.limit_backward,d.limit_backward=t,d.ket=d.cursor,e=d.find_among_b(f,7))switch(d.bra=d.cursor,e){case 1:if(h()){if(i=d.limit-d.cursor,!d.eq_s_b(1,"s")&&(d.cursor=d.limit-i,!d.eq_s_b(1,"t")))break;d.slice_del()}break;case 2:d.slice_from("i");break;case 3:d.slice_del();break;case 4:d.eq_s_b(2,"gu")&&d.slice_del()}d.limit_backward=s}}();d.cursor=d.limit,d.ket=d.cursor,d.eq_s_b(1,"Y")?(d.bra=d.cursor,d.slice_from("i")):(d.cursor=d.limit,d.eq_s_b(1,"ç")&&(d.bra=d.cursor,d.slice_from("c")))}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var e,r=d.cursor;return function(){for(var e,r;;){if(e=d.cursor,d.in_grouping(_,97,251)){if(d.bra=d.cursor,r=d.cursor,k("u","U",e))continue;if(d.cursor=r,k("i","I",e))continue;if(d.cursor=r,p("y","Y",e))continue}if(d.cursor=e,!k("y","Y",d.bra=e)){if(d.cursor=e,d.eq_s(1,"q")&&(d.bra=d.cursor,p("u","U",e)))continue;if((d.cursor=e)>=d.limit)return;d.cursor++}}}(),d.cursor=r,function(){var e=d.cursor;if(t=d.limit,s=i=t,d.in_grouping(_,97,251)&&d.in_grouping(_,97,251)&&d.cursor=d.limit){d.cursor=t;break}d.cursor++}while(!d.in_grouping(_,97,251))}t=d.cursor,d.cursor=e,g()||(i=d.cursor,g()||(s=d.cursor))}(),d.limit_backward=r,d.cursor=d.limit,z(),d.cursor=d.limit,e=d.limit-d.cursor,d.find_among_b(m,5)&&(d.cursor=d.limit-e,d.ket=d.cursor,d.cursor>d.limit_backward&&(d.cursor--,d.bra=d.cursor,d.slice_del())),d.cursor=d.limit,function(){for(var e,r=1;d.out_grouping_b(_,97,251);)r--;if(r<=0){if(d.ket=d.cursor,e=d.limit-d.cursor,!d.eq_s_b(1,"é")&&(d.cursor=d.limit-e,!d.eq_s_b(1,"è")))return;d.bra=d.cursor,d.slice_from("e")}}(),d.cursor=d.limit_backward,function(){for(var e,r;r=d.cursor,d.bra=r,e=d.find_among(u,4);)switch(d.ket=d.cursor,e){case 1:d.slice_from("i");break;case 2:d.slice_from("u");break;case 3:d.slice_from("y");break;case 4:if(d.cursor>=d.limit)return;d.cursor++}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return s.setCurrent(e),s.stem(),s.getCurrent()}):(s.setCurrent(e),s.stem(),s.getCurrent())}),e.Pipeline.registerFunction(e.fr.stemmer,"stemmer-fr"),e.fr.stopWordFilter=e.generateStopWordFilter("ai aie aient aies ait as au aura aurai auraient aurais aurait auras aurez auriez aurions aurons auront aux avaient avais avait avec avez aviez avions avons ayant ayez ayons c ce ceci celà ces cet cette d dans de des du elle en es est et eu eue eues eurent eus eusse eussent eusses eussiez eussions eut eux eûmes eût eûtes furent fus fusse fussent fusses fussiez fussions fut fûmes fût fûtes ici il ils j je l la le les leur leurs lui m ma mais me mes moi mon même n ne nos notre nous on ont ou par pas pour qu que quel quelle quelles quels qui s sa sans se sera serai seraient serais serait seras serez seriez serions serons seront ses soi soient sois soit sommes son sont soyez soyons suis sur t ta te tes toi ton tu un une vos votre vous y à étaient étais était étant étiez étions été étée étées étés êtes".split(" ")),e.Pipeline.registerFunction(e.fr.stopWordFilter,"stopWordFilter-fr")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.hu.js b/docs/assets/javascripts/lunr/lunr.hu.js deleted file mode 100644 index bfc68db8..00000000 --- a/docs/assets/javascripts/lunr/lunr.hu.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Hungarian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var p,_,n;e.hu=function(){this.pipeline.reset(),this.pipeline.add(e.hu.trimmer,e.hu.stopWordFilter,e.hu.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hu.stemmer))},e.hu.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.hu.trimmer=e.trimmerSupport.generateTrimmer(e.hu.wordCharacters),e.Pipeline.registerFunction(e.hu.trimmer,"trimmer-hu"),e.hu.stemmer=(p=e.stemmerSupport.Among,_=e.stemmerSupport.SnowballProgram,n=new function(){var r,i=[new p("cs",-1,-1),new p("dzs",-1,-1),new p("gy",-1,-1),new p("ly",-1,-1),new p("ny",-1,-1),new p("sz",-1,-1),new p("ty",-1,-1),new p("zs",-1,-1)],n=[new p("á",-1,1),new p("é",-1,2)],a=[new p("bb",-1,-1),new p("cc",-1,-1),new p("dd",-1,-1),new p("ff",-1,-1),new p("gg",-1,-1),new p("jj",-1,-1),new p("kk",-1,-1),new p("ll",-1,-1),new p("mm",-1,-1),new p("nn",-1,-1),new p("pp",-1,-1),new p("rr",-1,-1),new p("ccs",-1,-1),new p("ss",-1,-1),new p("zzs",-1,-1),new p("tt",-1,-1),new p("vv",-1,-1),new p("ggy",-1,-1),new p("lly",-1,-1),new p("nny",-1,-1),new p("tty",-1,-1),new p("ssz",-1,-1),new p("zz",-1,-1)],t=[new p("al",-1,1),new p("el",-1,2)],e=[new p("ba",-1,-1),new p("ra",-1,-1),new p("be",-1,-1),new p("re",-1,-1),new p("ig",-1,-1),new p("nak",-1,-1),new p("nek",-1,-1),new p("val",-1,-1),new p("vel",-1,-1),new p("ul",-1,-1),new p("nál",-1,-1),new p("nél",-1,-1),new p("ból",-1,-1),new p("ról",-1,-1),new p("tól",-1,-1),new p("bõl",-1,-1),new p("rõl",-1,-1),new p("tõl",-1,-1),new p("ül",-1,-1),new p("n",-1,-1),new p("an",19,-1),new p("ban",20,-1),new p("en",19,-1),new p("ben",22,-1),new p("képpen",22,-1),new p("on",19,-1),new p("ön",19,-1),new p("képp",-1,-1),new p("kor",-1,-1),new p("t",-1,-1),new p("at",29,-1),new p("et",29,-1),new p("ként",29,-1),new p("anként",32,-1),new p("enként",32,-1),new p("onként",32,-1),new p("ot",29,-1),new p("ért",29,-1),new p("öt",29,-1),new p("hez",-1,-1),new p("hoz",-1,-1),new p("höz",-1,-1),new p("vá",-1,-1),new p("vé",-1,-1)],s=[new p("án",-1,2),new p("én",-1,1),new p("ánként",-1,3)],c=[new p("stul",-1,2),new p("astul",0,1),new p("ástul",0,3),new p("stül",-1,2),new p("estül",3,1),new p("éstül",3,4)],w=[new p("á",-1,1),new p("é",-1,2)],o=[new p("k",-1,7),new p("ak",0,4),new p("ek",0,6),new p("ok",0,5),new p("ák",0,1),new p("ék",0,2),new p("ök",0,3)],l=[new p("éi",-1,7),new p("áéi",0,6),new p("ééi",0,5),new p("é",-1,9),new p("ké",3,4),new p("aké",4,1),new p("eké",4,1),new p("oké",4,1),new p("áké",4,3),new p("éké",4,2),new p("öké",4,1),new p("éé",3,8)],u=[new p("a",-1,18),new p("ja",0,17),new p("d",-1,16),new p("ad",2,13),new p("ed",2,13),new p("od",2,13),new p("ád",2,14),new p("éd",2,15),new p("öd",2,13),new p("e",-1,18),new p("je",9,17),new p("nk",-1,4),new p("unk",11,1),new p("ánk",11,2),new p("énk",11,3),new p("ünk",11,1),new p("uk",-1,8),new p("juk",16,7),new p("ájuk",17,5),new p("ük",-1,8),new p("jük",19,7),new p("éjük",20,6),new p("m",-1,12),new p("am",22,9),new p("em",22,9),new p("om",22,9),new p("ám",22,10),new p("ém",22,11),new p("o",-1,18),new p("á",-1,19),new p("é",-1,20)],m=[new p("id",-1,10),new p("aid",0,9),new p("jaid",1,6),new p("eid",0,9),new p("jeid",3,6),new p("áid",0,7),new p("éid",0,8),new p("i",-1,15),new p("ai",7,14),new p("jai",8,11),new p("ei",7,14),new p("jei",10,11),new p("ái",7,12),new p("éi",7,13),new p("itek",-1,24),new p("eitek",14,21),new p("jeitek",15,20),new p("éitek",14,23),new p("ik",-1,29),new p("aik",18,26),new p("jaik",19,25),new p("eik",18,26),new p("jeik",21,25),new p("áik",18,27),new p("éik",18,28),new p("ink",-1,20),new p("aink",25,17),new p("jaink",26,16),new p("eink",25,17),new p("jeink",28,16),new p("áink",25,18),new p("éink",25,19),new p("aitok",-1,21),new p("jaitok",32,20),new p("áitok",-1,22),new p("im",-1,5),new p("aim",35,4),new p("jaim",36,1),new p("eim",35,4),new p("jeim",38,1),new p("áim",35,2),new p("éim",35,3)],k=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,52,14],f=new _;function b(){return r<=f.cursor}function d(){var e=f.limit-f.cursor;return!!f.find_among_b(a,23)&&(f.cursor=f.limit-e,!0)}function g(){if(f.cursor>f.limit_backward){f.cursor--,f.ket=f.cursor;var e=f.cursor-1;f.limit_backward<=e&&e<=f.limit&&(f.cursor=e,f.bra=e,f.slice_del())}}function h(){f.ket=f.cursor,f.find_among_b(e,44)&&(f.bra=f.cursor,b()&&(f.slice_del(),function(){var e;if(f.ket=f.cursor,(e=f.find_among_b(n,2))&&(f.bra=f.cursor,b()))switch(e){case 1:f.slice_from("a");break;case 2:f.slice_from("e")}}()))}this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var e=f.cursor;return function(){var e,n=f.cursor;if(r=f.limit,f.in_grouping(k,97,252))for(;;){if(e=f.cursor,f.out_grouping(k,97,252))return f.cursor=e,f.find_among(i,8)||(f.cursor=e)=f.limit)return r=e;f.cursor++}if(f.cursor=n,f.out_grouping(k,97,252)){for(;!f.in_grouping(k,97,252);){if(f.cursor>=f.limit)return;f.cursor++}r=f.cursor}}(),f.limit_backward=e,f.cursor=f.limit,function(){var e;if(f.ket=f.cursor,(e=f.find_among_b(t,2))&&(f.bra=f.cursor,b())){if((1==e||2==e)&&!d())return;f.slice_del(),g()}}(),f.cursor=f.limit,h(),f.cursor=f.limit,function(){var e;if(f.ket=f.cursor,(e=f.find_among_b(s,3))&&(f.bra=f.cursor,b()))switch(e){case 1:f.slice_from("e");break;case 2:case 3:f.slice_from("a")}}(),f.cursor=f.limit,function(){var e;if(f.ket=f.cursor,(e=f.find_among_b(c,6))&&(f.bra=f.cursor,b()))switch(e){case 1:case 2:f.slice_del();break;case 3:f.slice_from("a");break;case 4:f.slice_from("e")}}(),f.cursor=f.limit,function(){var e;if(f.ket=f.cursor,(e=f.find_among_b(w,2))&&(f.bra=f.cursor,b())){if((1==e||2==e)&&!d())return;f.slice_del(),g()}}(),f.cursor=f.limit,function(){var e;if(f.ket=f.cursor,(e=f.find_among_b(l,12))&&(f.bra=f.cursor,b()))switch(e){case 1:case 4:case 7:case 9:f.slice_del();break;case 2:case 5:case 8:f.slice_from("e");break;case 3:case 6:f.slice_from("a")}}(),f.cursor=f.limit,function(){var e;if(f.ket=f.cursor,(e=f.find_among_b(u,31))&&(f.bra=f.cursor,b()))switch(e){case 1:case 4:case 7:case 8:case 9:case 12:case 13:case 16:case 17:case 18:f.slice_del();break;case 2:case 5:case 10:case 14:case 19:f.slice_from("a");break;case 3:case 6:case 11:case 15:case 20:f.slice_from("e")}}(),f.cursor=f.limit,function(){var e;if(f.ket=f.cursor,(e=f.find_among_b(m,42))&&(f.bra=f.cursor,b()))switch(e){case 1:case 4:case 5:case 6:case 9:case 10:case 11:case 14:case 15:case 16:case 17:case 20:case 21:case 24:case 25:case 26:case 29:f.slice_del();break;case 2:case 7:case 12:case 18:case 22:case 27:f.slice_from("a");break;case 3:case 8:case 13:case 19:case 23:case 28:f.slice_from("e")}}(),f.cursor=f.limit,function(){var e;if(f.ket=f.cursor,(e=f.find_among_b(o,7))&&(f.bra=f.cursor,b()))switch(e){case 1:f.slice_from("a");break;case 2:f.slice_from("e");break;case 3:case 4:case 5:case 6:case 7:f.slice_del()}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.hu.stemmer,"stemmer-hu"),e.hu.stopWordFilter=e.generateStopWordFilter("a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra".split(" ")),e.Pipeline.registerFunction(e.hu.stopWordFilter,"stopWordFilter-hu")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.it.js b/docs/assets/javascripts/lunr/lunr.it.js deleted file mode 100644 index 58a46fb6..00000000 --- a/docs/assets/javascripts/lunr/lunr.it.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Italian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var z,P,r;e.it=function(){this.pipeline.reset(),this.pipeline.add(e.it.trimmer,e.it.stopWordFilter,e.it.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.it.stemmer))},e.it.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.it.trimmer=e.trimmerSupport.generateTrimmer(e.it.wordCharacters),e.Pipeline.registerFunction(e.it.trimmer,"trimmer-it"),e.it.stemmer=(z=e.stemmerSupport.Among,P=e.stemmerSupport.SnowballProgram,r=new function(){var o,t,s,a=[new z("",-1,7),new z("qu",0,6),new z("á",0,1),new z("é",0,2),new z("í",0,3),new z("ó",0,4),new z("ú",0,5)],u=[new z("",-1,3),new z("I",0,1),new z("U",0,2)],c=[new z("la",-1,-1),new z("cela",0,-1),new z("gliela",0,-1),new z("mela",0,-1),new z("tela",0,-1),new z("vela",0,-1),new z("le",-1,-1),new z("cele",6,-1),new z("gliele",6,-1),new z("mele",6,-1),new z("tele",6,-1),new z("vele",6,-1),new z("ne",-1,-1),new z("cene",12,-1),new z("gliene",12,-1),new z("mene",12,-1),new z("sene",12,-1),new z("tene",12,-1),new z("vene",12,-1),new z("ci",-1,-1),new z("li",-1,-1),new z("celi",20,-1),new z("glieli",20,-1),new z("meli",20,-1),new z("teli",20,-1),new z("veli",20,-1),new z("gli",20,-1),new z("mi",-1,-1),new z("si",-1,-1),new z("ti",-1,-1),new z("vi",-1,-1),new z("lo",-1,-1),new z("celo",31,-1),new z("glielo",31,-1),new z("melo",31,-1),new z("telo",31,-1),new z("velo",31,-1)],w=[new z("ando",-1,1),new z("endo",-1,1),new z("ar",-1,2),new z("er",-1,2),new z("ir",-1,2)],r=[new z("ic",-1,-1),new z("abil",-1,-1),new z("os",-1,-1),new z("iv",-1,1)],n=[new z("ic",-1,1),new z("abil",-1,1),new z("iv",-1,1)],i=[new z("ica",-1,1),new z("logia",-1,3),new z("osa",-1,1),new z("ista",-1,1),new z("iva",-1,9),new z("anza",-1,1),new z("enza",-1,5),new z("ice",-1,1),new z("atrice",7,1),new z("iche",-1,1),new z("logie",-1,3),new z("abile",-1,1),new z("ibile",-1,1),new z("usione",-1,4),new z("azione",-1,2),new z("uzione",-1,4),new z("atore",-1,2),new z("ose",-1,1),new z("ante",-1,1),new z("mente",-1,1),new z("amente",19,7),new z("iste",-1,1),new z("ive",-1,9),new z("anze",-1,1),new z("enze",-1,5),new z("ici",-1,1),new z("atrici",25,1),new z("ichi",-1,1),new z("abili",-1,1),new z("ibili",-1,1),new z("ismi",-1,1),new z("usioni",-1,4),new z("azioni",-1,2),new z("uzioni",-1,4),new z("atori",-1,2),new z("osi",-1,1),new z("anti",-1,1),new z("amenti",-1,6),new z("imenti",-1,6),new z("isti",-1,1),new z("ivi",-1,9),new z("ico",-1,1),new z("ismo",-1,1),new z("oso",-1,1),new z("amento",-1,6),new z("imento",-1,6),new z("ivo",-1,9),new z("ità",-1,8),new z("istà",-1,1),new z("istè",-1,1),new z("istì",-1,1)],l=[new z("isca",-1,1),new z("enda",-1,1),new z("ata",-1,1),new z("ita",-1,1),new z("uta",-1,1),new z("ava",-1,1),new z("eva",-1,1),new z("iva",-1,1),new z("erebbe",-1,1),new z("irebbe",-1,1),new z("isce",-1,1),new z("ende",-1,1),new z("are",-1,1),new z("ere",-1,1),new z("ire",-1,1),new z("asse",-1,1),new z("ate",-1,1),new z("avate",16,1),new z("evate",16,1),new z("ivate",16,1),new z("ete",-1,1),new z("erete",20,1),new z("irete",20,1),new z("ite",-1,1),new z("ereste",-1,1),new z("ireste",-1,1),new z("ute",-1,1),new z("erai",-1,1),new z("irai",-1,1),new z("isci",-1,1),new z("endi",-1,1),new z("erei",-1,1),new z("irei",-1,1),new z("assi",-1,1),new z("ati",-1,1),new z("iti",-1,1),new z("eresti",-1,1),new z("iresti",-1,1),new z("uti",-1,1),new z("avi",-1,1),new z("evi",-1,1),new z("ivi",-1,1),new z("isco",-1,1),new z("ando",-1,1),new z("endo",-1,1),new z("Yamo",-1,1),new z("iamo",-1,1),new z("avamo",-1,1),new z("evamo",-1,1),new z("ivamo",-1,1),new z("eremo",-1,1),new z("iremo",-1,1),new z("assimo",-1,1),new z("ammo",-1,1),new z("emmo",-1,1),new z("eremmo",54,1),new z("iremmo",54,1),new z("immo",-1,1),new z("ano",-1,1),new z("iscano",58,1),new z("avano",58,1),new z("evano",58,1),new z("ivano",58,1),new z("eranno",-1,1),new z("iranno",-1,1),new z("ono",-1,1),new z("iscono",65,1),new z("arono",65,1),new z("erono",65,1),new z("irono",65,1),new z("erebbero",-1,1),new z("irebbero",-1,1),new z("assero",-1,1),new z("essero",-1,1),new z("issero",-1,1),new z("ato",-1,1),new z("ito",-1,1),new z("uto",-1,1),new z("avo",-1,1),new z("evo",-1,1),new z("ivo",-1,1),new z("ar",-1,1),new z("ir",-1,1),new z("erà",-1,1),new z("irà",-1,1),new z("erò",-1,1),new z("irò",-1,1)],m=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2,1],f=[17,65,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2],v=[17],b=new P;function d(e,r,n){return!(!b.eq_s(1,e)||(b.ket=b.cursor,!b.in_grouping(m,97,249)))&&(b.slice_from(r),b.cursor=n,!0)}function _(e){if(b.cursor=e,!b.in_grouping(m,97,249))return!1;for(;!b.out_grouping(m,97,249);){if(b.cursor>=b.limit)return!1;b.cursor++}return!0}function g(){var e,r=b.cursor;if(!function(){if(b.in_grouping(m,97,249)){var e=b.cursor;if(b.out_grouping(m,97,249)){for(;!b.in_grouping(m,97,249);){if(b.cursor>=b.limit)return _(e);b.cursor++}return!0}return _(e)}return!1}()){if(b.cursor=r,!b.out_grouping(m,97,249))return;if(e=b.cursor,b.out_grouping(m,97,249)){for(;!b.in_grouping(m,97,249);){if(b.cursor>=b.limit)return b.cursor=e,void(b.in_grouping(m,97,249)&&b.cursor=b.limit)return;b.cursor++}s=b.cursor}function p(){for(;!b.in_grouping(m,97,249);){if(b.cursor>=b.limit)return!1;b.cursor++}for(;!b.out_grouping(m,97,249);){if(b.cursor>=b.limit)return!1;b.cursor++}return!0}function k(){return s<=b.cursor}function h(){return o<=b.cursor}function q(){var e;if(b.ket=b.cursor,!(e=b.find_among_b(i,51)))return!1;switch(b.bra=b.cursor,e){case 1:if(!h())return!1;b.slice_del();break;case 2:if(!h())return!1;b.slice_del(),b.ket=b.cursor,b.eq_s_b(2,"ic")&&(b.bra=b.cursor,h()&&b.slice_del());break;case 3:if(!h())return!1;b.slice_from("log");break;case 4:if(!h())return!1;b.slice_from("u");break;case 5:if(!h())return!1;b.slice_from("ente");break;case 6:if(!k())return!1;b.slice_del();break;case 7:if(!(t<=b.cursor))return!1;b.slice_del(),b.ket=b.cursor,(e=b.find_among_b(r,4))&&(b.bra=b.cursor,h()&&(b.slice_del(),1==e&&(b.ket=b.cursor,b.eq_s_b(2,"at")&&(b.bra=b.cursor,h()&&b.slice_del()))));break;case 8:if(!h())return!1;b.slice_del(),b.ket=b.cursor,(e=b.find_among_b(n,3))&&(b.bra=b.cursor,1==e&&h()&&b.slice_del());break;case 9:if(!h())return!1;b.slice_del(),b.ket=b.cursor,b.eq_s_b(2,"at")&&(b.bra=b.cursor,h()&&(b.slice_del(),b.ket=b.cursor,b.eq_s_b(2,"ic")&&(b.bra=b.cursor,h()&&b.slice_del())))}return!0}function C(){var e;e=b.limit-b.cursor,b.ket=b.cursor,b.in_grouping_b(f,97,242)&&(b.bra=b.cursor,k()&&(b.slice_del(),b.ket=b.cursor,b.eq_s_b(1,"i")&&(b.bra=b.cursor,k())))?b.slice_del():b.cursor=b.limit-e,b.ket=b.cursor,b.eq_s_b(1,"h")&&(b.bra=b.cursor,b.in_grouping_b(v,99,103)&&k()&&b.slice_del())}this.setCurrent=function(e){b.setCurrent(e)},this.getCurrent=function(){return b.getCurrent()},this.stem=function(){var e,r,n,i=b.cursor;return function(){for(var e,r,n,i,o=b.cursor;;){if(b.bra=b.cursor,e=b.find_among(a,7))switch(b.ket=b.cursor,e){case 1:b.slice_from("à");continue;case 2:b.slice_from("è");continue;case 3:b.slice_from("ì");continue;case 4:b.slice_from("ò");continue;case 5:b.slice_from("ù");continue;case 6:b.slice_from("qU");continue;case 7:if(b.cursor>=b.limit)break;b.cursor++;continue}break}for(b.cursor=o;;)for(r=b.cursor;;){if(n=b.cursor,b.in_grouping(m,97,249)){if(b.bra=b.cursor,i=b.cursor,d("u","U",n))break;if(b.cursor=i,d("i","I",n))break}if(b.cursor=n,b.cursor>=b.limit)return b.cursor=r;b.cursor++}}(),b.cursor=i,e=b.cursor,s=b.limit,o=t=s,g(),b.cursor=e,p()&&(t=b.cursor,p()&&(o=b.cursor)),b.limit_backward=i,b.cursor=b.limit,function(){var e;if(b.ket=b.cursor,b.find_among_b(c,37)&&(b.bra=b.cursor,(e=b.find_among_b(w,5))&&k()))switch(e){case 1:b.slice_del();break;case 2:b.slice_from("e")}}(),b.cursor=b.limit,q()||(b.cursor=b.limit,b.cursor>=s&&(n=b.limit_backward,b.limit_backward=s,b.ket=b.cursor,(r=b.find_among_b(l,87))&&(b.bra=b.cursor,1==r&&b.slice_del()),b.limit_backward=n)),b.cursor=b.limit,C(),b.cursor=b.limit_backward,function(){for(var e;b.bra=b.cursor,e=b.find_among(u,3);)switch(b.ket=b.cursor,e){case 1:b.slice_from("i");break;case 2:b.slice_from("u");break;case 3:if(b.cursor>=b.limit)return;b.cursor++}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return r.setCurrent(e),r.stem(),r.getCurrent()}):(r.setCurrent(e),r.stem(),r.getCurrent())}),e.Pipeline.registerFunction(e.it.stemmer,"stemmer-it"),e.it.stopWordFilter=e.generateStopWordFilter("a abbia abbiamo abbiano abbiate ad agl agli ai al all alla alle allo anche avemmo avendo avesse avessero avessi avessimo aveste avesti avete aveva avevamo avevano avevate avevi avevo avrai avranno avrebbe avrebbero avrei avremmo avremo avreste avresti avrete avrà avrò avuta avute avuti avuto c che chi ci coi col come con contro cui da dagl dagli dai dal dall dalla dalle dallo degl degli dei del dell della delle dello di dov dove e ebbe ebbero ebbi ed era erano eravamo eravate eri ero essendo faccia facciamo facciano facciate faccio facemmo facendo facesse facessero facessi facessimo faceste facesti faceva facevamo facevano facevate facevi facevo fai fanno farai faranno farebbe farebbero farei faremmo faremo fareste faresti farete farà farò fece fecero feci fosse fossero fossi fossimo foste fosti fu fui fummo furono gli ha hai hanno ho i il in io l la le lei li lo loro lui ma mi mia mie miei mio ne negl negli nei nel nell nella nelle nello noi non nostra nostre nostri nostro o per perché più quale quanta quante quanti quanto quella quelle quelli quello questa queste questi questo sarai saranno sarebbe sarebbero sarei saremmo saremo sareste saresti sarete sarà sarò se sei si sia siamo siano siate siete sono sta stai stando stanno starai staranno starebbe starebbero starei staremmo staremo stareste staresti starete starà starò stava stavamo stavano stavate stavi stavo stemmo stesse stessero stessi stessimo steste stesti stette stettero stetti stia stiamo stiano stiate sto su sua sue sugl sugli sui sul sull sulla sulle sullo suo suoi ti tra tu tua tue tuo tuoi tutti tutto un una uno vi voi vostra vostre vostri vostro è".split(" ")),e.Pipeline.registerFunction(e.it.stopWordFilter,"stopWordFilter-it")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.ja.js b/docs/assets/javascripts/lunr/lunr.ja.js deleted file mode 100644 index 715b834a..00000000 --- a/docs/assets/javascripts/lunr/lunr.ja.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Japanese` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Chad Liu - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(m){if(void 0===m)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===m.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var l="2"==m.version[0];m.ja=function(){this.pipeline.reset(),this.pipeline.add(m.ja.trimmer,m.ja.stopWordFilter,m.ja.stemmer),l?this.tokenizer=m.ja.tokenizer:(m.tokenizer&&(m.tokenizer=m.ja.tokenizer),this.tokenizerFn&&(this.tokenizerFn=m.ja.tokenizer))};var j=new m.TinySegmenter;m.ja.tokenizer=function(e){var r,t,i,n,o,s,p,a,u;if(!arguments.length||null==e||null==e)return[];if(Array.isArray(e))return e.map(function(e){return l?new m.Token(e.toLowerCase()):e.toLowerCase()});for(r=(t=e.toString().toLowerCase().replace(/^\s+/,"")).length-1;0<=r;r--)if(/\S/.test(t.charAt(r))){t=t.substring(0,r+1);break}for(o=[],i=t.length,p=a=0;a<=i;a++)if(s=a-p,t.charAt(a).match(/\s/)||a==i){if(0=_.limit||(_.cursor++,!1)}function w(){for(;!_.in_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}for(;!_.out_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}return!1}function b(){return i<=_.cursor}function p(){return e<=_.cursor}function g(){var r=_.limit-_.cursor;_.find_among_b(t,3)&&(_.cursor=_.limit-r,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del()))}function h(){var r;u=!1,_.ket=_.cursor,_.eq_s_b(1,"e")&&(_.bra=_.cursor,b()&&(r=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-r,_.slice_del(),u=!0,g())))}function k(){var r;b()&&(r=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-r,_.eq_s_b(3,"gem")||(_.cursor=_.limit-r,_.slice_del(),g())))}this.setCurrent=function(r){_.setCurrent(r)},this.getCurrent=function(){return _.getCurrent()},this.stem=function(){var r=_.cursor;return function(){for(var r,e,i,n=_.cursor;;){if(_.bra=_.cursor,r=_.find_among(o,11))switch(_.ket=_.cursor,r){case 1:_.slice_from("a");continue;case 2:_.slice_from("e");continue;case 3:_.slice_from("i");continue;case 4:_.slice_from("o");continue;case 5:_.slice_from("u");continue;case 6:if(_.cursor>=_.limit)break;_.cursor++;continue}break}for(_.cursor=n,_.bra=n,_.eq_s(1,"y")?(_.ket=_.cursor,_.slice_from("Y")):_.cursor=n;;)if(e=_.cursor,_.in_grouping(m,97,232)){if(i=_.cursor,_.bra=i,_.eq_s(1,"i"))_.ket=_.cursor,_.in_grouping(m,97,232)&&(_.slice_from("I"),_.cursor=e);else if(_.cursor=i,_.eq_s(1,"y"))_.ket=_.cursor,_.slice_from("Y"),_.cursor=e;else if(s(e))break}else if(s(e))break}(),_.cursor=r,i=_.limit,e=i,w()||((i=_.cursor)<3&&(i=3),w()||(e=_.cursor)),_.limit_backward=r,_.cursor=_.limit,function(){var r,e,i,n,o,t,s=_.limit-_.cursor;if(_.ket=_.cursor,r=_.find_among_b(c,5))switch(_.bra=_.cursor,r){case 1:b()&&_.slice_from("heid");break;case 2:k();break;case 3:b()&&_.out_grouping_b(f,97,232)&&_.slice_del()}if(_.cursor=_.limit-s,h(),_.cursor=_.limit-s,_.ket=_.cursor,_.eq_s_b(4,"heid")&&(_.bra=_.cursor,p()&&(e=_.limit-_.cursor,_.eq_s_b(1,"c")||(_.cursor=_.limit-e,_.slice_del(),_.ket=_.cursor,_.eq_s_b(2,"en")&&(_.bra=_.cursor,k())))),_.cursor=_.limit-s,_.ket=_.cursor,r=_.find_among_b(a,6))switch(_.bra=_.cursor,r){case 1:if(p()){if(_.slice_del(),i=_.limit-_.cursor,_.ket=_.cursor,_.eq_s_b(2,"ig")&&(_.bra=_.cursor,p()&&(n=_.limit-_.cursor,!_.eq_s_b(1,"e")))){_.cursor=_.limit-n,_.slice_del();break}_.cursor=_.limit-i,g()}break;case 2:p()&&(o=_.limit-_.cursor,_.eq_s_b(1,"e")||(_.cursor=_.limit-o,_.slice_del()));break;case 3:p()&&(_.slice_del(),h());break;case 4:p()&&_.slice_del();break;case 5:p()&&u&&_.slice_del()}_.cursor=_.limit-s,_.out_grouping_b(d,73,232)&&(t=_.limit-_.cursor,_.find_among_b(l,4)&&_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-t,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del())))}(),_.cursor=_.limit_backward,function(){for(var r;;)if(_.bra=_.cursor,r=_.find_among(n,3))switch(_.ket=_.cursor,r){case 1:_.slice_from("y");break;case 2:_.slice_from("i");break;case 3:if(_.cursor>=_.limit)return;_.cursor++}}(),!0}},function(r){return"function"==typeof r.update?r.update(function(r){return e.setCurrent(r),e.stem(),e.getCurrent()}):(e.setCurrent(r),e.stem(),e.getCurrent())}),r.Pipeline.registerFunction(r.nl.stemmer,"stemmer-nl"),r.nl.stopWordFilter=r.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),r.Pipeline.registerFunction(r.nl.stopWordFilter,"stopWordFilter-nl")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.no.js b/docs/assets/javascripts/lunr/lunr.no.js deleted file mode 100644 index 031e4b20..00000000 --- a/docs/assets/javascripts/lunr/lunr.no.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Norwegian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,n,i;e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=(r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){var o,s,a=[new r("a",-1,1),new r("e",-1,1),new r("ede",1,1),new r("ande",1,1),new r("ende",1,1),new r("ane",1,1),new r("ene",1,1),new r("hetene",6,1),new r("erte",1,3),new r("en",-1,1),new r("heten",9,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",12,1),new r("s",-1,2),new r("as",14,1),new r("es",14,1),new r("edes",16,1),new r("endes",16,1),new r("enes",16,1),new r("hetenes",19,1),new r("ens",14,1),new r("hetens",21,1),new r("ers",14,1),new r("ets",14,1),new r("et",-1,1),new r("het",25,1),new r("ert",-1,3),new r("ast",-1,1)],m=[new r("dt",-1,-1),new r("vt",-1,-1)],l=[new r("leg",-1,1),new r("eleg",0,1),new r("ig",-1,1),new r("eig",2,1),new r("lig",2,1),new r("elig",4,1),new r("els",-1,1),new r("lov",-1,1),new r("elov",7,1),new r("slov",7,1),new r("hetslov",9,1)],u=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],d=[119,125,149,1],c=new n;this.setCurrent=function(e){c.setCurrent(e)},this.getCurrent=function(){return c.getCurrent()},this.stem=function(){var e,r,n,i,t=c.cursor;return function(){var e,r=c.cursor+3;if(s=c.limit,0<=r||r<=c.limit){for(o=r;;){if(e=c.cursor,c.in_grouping(u,97,248)){c.cursor=e;break}if(e>=c.limit)return;c.cursor=e+1}for(;!c.out_grouping(u,97,248);){if(c.cursor>=c.limit)return;c.cursor++}(s=c.cursor)=s&&(r=c.limit_backward,c.limit_backward=s,c.ket=c.cursor,e=c.find_among_b(a,29),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del();break;case 2:n=c.limit-c.cursor,c.in_grouping_b(d,98,122)?c.slice_del():(c.cursor=c.limit-n,c.eq_s_b(1,"k")&&c.out_grouping_b(u,97,248)&&c.slice_del());break;case 3:c.slice_from("er")}}(),c.cursor=c.limit,r=c.limit-c.cursor,c.cursor>=s&&(e=c.limit_backward,c.limit_backward=s,c.ket=c.cursor,c.find_among_b(m,2)?(c.bra=c.cursor,c.limit_backward=e,c.cursor=c.limit-r,c.cursor>c.limit_backward&&(c.cursor--,c.bra=c.cursor,c.slice_del())):c.limit_backward=e),c.cursor=c.limit,c.cursor>=s&&(i=c.limit_backward,c.limit_backward=s,c.ket=c.cursor,(n=c.find_among_b(l,11))?(c.bra=c.cursor,c.limit_backward=i,1==n&&c.slice_del()):c.limit_backward=i),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.pt.js b/docs/assets/javascripts/lunr/lunr.pt.js deleted file mode 100644 index 59e766fe..00000000 --- a/docs/assets/javascripts/lunr/lunr.pt.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Portuguese` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var j,C,r;e.pt=function(){this.pipeline.reset(),this.pipeline.add(e.pt.trimmer,e.pt.stopWordFilter,e.pt.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.pt.stemmer))},e.pt.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.pt.trimmer=e.trimmerSupport.generateTrimmer(e.pt.wordCharacters),e.Pipeline.registerFunction(e.pt.trimmer,"trimmer-pt"),e.pt.stemmer=(j=e.stemmerSupport.Among,C=e.stemmerSupport.SnowballProgram,r=new function(){var s,n,i,o=[new j("",-1,3),new j("ã",0,1),new j("õ",0,2)],a=[new j("",-1,3),new j("a~",0,1),new j("o~",0,2)],r=[new j("ic",-1,-1),new j("ad",-1,-1),new j("os",-1,-1),new j("iv",-1,1)],t=[new j("ante",-1,1),new j("avel",-1,1),new j("ível",-1,1)],u=[new j("ic",-1,1),new j("abil",-1,1),new j("iv",-1,1)],w=[new j("ica",-1,1),new j("ância",-1,1),new j("ência",-1,4),new j("ira",-1,9),new j("adora",-1,1),new j("osa",-1,1),new j("ista",-1,1),new j("iva",-1,8),new j("eza",-1,1),new j("logía",-1,2),new j("idade",-1,7),new j("ante",-1,1),new j("mente",-1,6),new j("amente",12,5),new j("ável",-1,1),new j("ível",-1,1),new j("ución",-1,3),new j("ico",-1,1),new j("ismo",-1,1),new j("oso",-1,1),new j("amento",-1,1),new j("imento",-1,1),new j("ivo",-1,8),new j("aça~o",-1,1),new j("ador",-1,1),new j("icas",-1,1),new j("ências",-1,4),new j("iras",-1,9),new j("adoras",-1,1),new j("osas",-1,1),new j("istas",-1,1),new j("ivas",-1,8),new j("ezas",-1,1),new j("logías",-1,2),new j("idades",-1,7),new j("uciones",-1,3),new j("adores",-1,1),new j("antes",-1,1),new j("aço~es",-1,1),new j("icos",-1,1),new j("ismos",-1,1),new j("osos",-1,1),new j("amentos",-1,1),new j("imentos",-1,1),new j("ivos",-1,8)],m=[new j("ada",-1,1),new j("ida",-1,1),new j("ia",-1,1),new j("aria",2,1),new j("eria",2,1),new j("iria",2,1),new j("ara",-1,1),new j("era",-1,1),new j("ira",-1,1),new j("ava",-1,1),new j("asse",-1,1),new j("esse",-1,1),new j("isse",-1,1),new j("aste",-1,1),new j("este",-1,1),new j("iste",-1,1),new j("ei",-1,1),new j("arei",16,1),new j("erei",16,1),new j("irei",16,1),new j("am",-1,1),new j("iam",20,1),new j("ariam",21,1),new j("eriam",21,1),new j("iriam",21,1),new j("aram",20,1),new j("eram",20,1),new j("iram",20,1),new j("avam",20,1),new j("em",-1,1),new j("arem",29,1),new j("erem",29,1),new j("irem",29,1),new j("assem",29,1),new j("essem",29,1),new j("issem",29,1),new j("ado",-1,1),new j("ido",-1,1),new j("ando",-1,1),new j("endo",-1,1),new j("indo",-1,1),new j("ara~o",-1,1),new j("era~o",-1,1),new j("ira~o",-1,1),new j("ar",-1,1),new j("er",-1,1),new j("ir",-1,1),new j("as",-1,1),new j("adas",47,1),new j("idas",47,1),new j("ias",47,1),new j("arias",50,1),new j("erias",50,1),new j("irias",50,1),new j("aras",47,1),new j("eras",47,1),new j("iras",47,1),new j("avas",47,1),new j("es",-1,1),new j("ardes",58,1),new j("erdes",58,1),new j("irdes",58,1),new j("ares",58,1),new j("eres",58,1),new j("ires",58,1),new j("asses",58,1),new j("esses",58,1),new j("isses",58,1),new j("astes",58,1),new j("estes",58,1),new j("istes",58,1),new j("is",-1,1),new j("ais",71,1),new j("eis",71,1),new j("areis",73,1),new j("ereis",73,1),new j("ireis",73,1),new j("áreis",73,1),new j("éreis",73,1),new j("íreis",73,1),new j("ásseis",73,1),new j("ésseis",73,1),new j("ísseis",73,1),new j("áveis",73,1),new j("íeis",73,1),new j("aríeis",84,1),new j("eríeis",84,1),new j("iríeis",84,1),new j("ados",-1,1),new j("idos",-1,1),new j("amos",-1,1),new j("áramos",90,1),new j("éramos",90,1),new j("íramos",90,1),new j("ávamos",90,1),new j("íamos",90,1),new j("aríamos",95,1),new j("eríamos",95,1),new j("iríamos",95,1),new j("emos",-1,1),new j("aremos",99,1),new j("eremos",99,1),new j("iremos",99,1),new j("ássemos",99,1),new j("êssemos",99,1),new j("íssemos",99,1),new j("imos",-1,1),new j("armos",-1,1),new j("ermos",-1,1),new j("irmos",-1,1),new j("ámos",-1,1),new j("arás",-1,1),new j("erás",-1,1),new j("irás",-1,1),new j("eu",-1,1),new j("iu",-1,1),new j("ou",-1,1),new j("ará",-1,1),new j("erá",-1,1),new j("irá",-1,1)],c=[new j("a",-1,1),new j("i",-1,1),new j("o",-1,1),new j("os",-1,1),new j("á",-1,1),new j("í",-1,1),new j("ó",-1,1)],l=[new j("e",-1,1),new j("ç",-1,2),new j("é",-1,1),new j("ê",-1,1)],f=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,3,19,12,2],d=new C;function v(){if(d.out_grouping(f,97,250)){for(;!d.in_grouping(f,97,250);){if(d.cursor>=d.limit)return!0;d.cursor++}return!1}return!0}function p(){var e,r,s=d.cursor;if(d.in_grouping(f,97,250))if(e=d.cursor,v()){if(d.cursor=e,function(){if(d.in_grouping(f,97,250))for(;!d.out_grouping(f,97,250);){if(d.cursor>=d.limit)return!1;d.cursor++}return i=d.cursor,!0}())return}else i=d.cursor;if(d.cursor=s,d.out_grouping(f,97,250)){if(r=d.cursor,v()){if(d.cursor=r,!d.in_grouping(f,97,250)||d.cursor>=d.limit)return;d.cursor++}i=d.cursor}}function _(){for(;!d.in_grouping(f,97,250);){if(d.cursor>=d.limit)return!1;d.cursor++}for(;!d.out_grouping(f,97,250);){if(d.cursor>=d.limit)return!1;d.cursor++}return!0}function h(){return i<=d.cursor}function b(){return s<=d.cursor}function g(){var e;if(d.ket=d.cursor,!(e=d.find_among_b(w,45)))return!1;switch(d.bra=d.cursor,e){case 1:if(!b())return!1;d.slice_del();break;case 2:if(!b())return!1;d.slice_from("log");break;case 3:if(!b())return!1;d.slice_from("u");break;case 4:if(!b())return!1;d.slice_from("ente");break;case 5:if(!(n<=d.cursor))return!1;d.slice_del(),d.ket=d.cursor,(e=d.find_among_b(r,4))&&(d.bra=d.cursor,b()&&(d.slice_del(),1==e&&(d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,b()&&d.slice_del()))));break;case 6:if(!b())return!1;d.slice_del(),d.ket=d.cursor,(e=d.find_among_b(t,3))&&(d.bra=d.cursor,1==e&&b()&&d.slice_del());break;case 7:if(!b())return!1;d.slice_del(),d.ket=d.cursor,(e=d.find_among_b(u,3))&&(d.bra=d.cursor,1==e&&b()&&d.slice_del());break;case 8:if(!b())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,b()&&d.slice_del());break;case 9:if(!h()||!d.eq_s_b(1,"e"))return!1;d.slice_from("ir")}return!0}function k(e,r){if(d.eq_s_b(1,e)){d.bra=d.cursor;var s=d.limit-d.cursor;if(d.eq_s_b(1,r))return d.cursor=d.limit-s,h()&&d.slice_del(),!1}return!0}function q(){if(!g()&&(d.cursor=d.limit,!function(){var e,r;if(d.cursor>=i){if(r=d.limit_backward,d.limit_backward=i,d.ket=d.cursor,e=d.find_among_b(m,120))return d.bra=d.cursor,1==e&&d.slice_del(),d.limit_backward=r,!0;d.limit_backward=r}return!1}()))return d.cursor=d.limit,d.ket=d.cursor,void((e=d.find_among_b(c,7))&&(d.bra=d.cursor,1==e&&h()&&d.slice_del()));var e;d.cursor=d.limit,d.ket=d.cursor,d.eq_s_b(1,"i")&&(d.bra=d.cursor,d.eq_s_b(1,"c")&&(d.cursor=d.limit,h()&&d.slice_del()))}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var e,r=d.cursor;return function(){for(var e;;){if(d.bra=d.cursor,e=d.find_among(o,3))switch(d.ket=d.cursor,e){case 1:d.slice_from("a~");continue;case 2:d.slice_from("o~");continue;case 3:if(d.cursor>=d.limit)break;d.cursor++;continue}break}}(),d.cursor=r,e=d.cursor,i=d.limit,s=n=i,p(),d.cursor=e,_()&&(n=d.cursor,_()&&(s=d.cursor)),d.limit_backward=r,d.cursor=d.limit,q(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,e=d.find_among_b(l,4))switch(d.bra=d.cursor,e){case 1:h()&&(d.slice_del(),d.ket=d.cursor,d.limit,d.cursor,k("u","g")&&k("i","c"));break;case 2:d.slice_from("c")}}(),d.cursor=d.limit_backward,function(){for(var e;;){if(d.bra=d.cursor,e=d.find_among(a,3))switch(d.ket=d.cursor,e){case 1:d.slice_from("ã");continue;case 2:d.slice_from("õ");continue;case 3:if(d.cursor>=d.limit)break;d.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return r.setCurrent(e),r.stem(),r.getCurrent()}):(r.setCurrent(e),r.stem(),r.getCurrent())}),e.Pipeline.registerFunction(e.pt.stemmer,"stemmer-pt"),e.pt.stopWordFilter=e.generateStopWordFilter("a ao aos aquela aquelas aquele aqueles aquilo as até com como da das de dela delas dele deles depois do dos e ela elas ele eles em entre era eram essa essas esse esses esta estamos estas estava estavam este esteja estejam estejamos estes esteve estive estivemos estiver estivera estiveram estiverem estivermos estivesse estivessem estivéramos estivéssemos estou está estávamos estão eu foi fomos for fora foram forem formos fosse fossem fui fôramos fôssemos haja hajam hajamos havemos hei houve houvemos houver houvera houveram houverei houverem houveremos houveria houveriam houvermos houverá houverão houveríamos houvesse houvessem houvéramos houvéssemos há hão isso isto já lhe lhes mais mas me mesmo meu meus minha minhas muito na nas nem no nos nossa nossas nosso nossos num numa não nós o os ou para pela pelas pelo pelos por qual quando que quem se seja sejam sejamos sem serei seremos seria seriam será serão seríamos seu seus somos sou sua suas são só também te tem temos tenha tenham tenhamos tenho terei teremos teria teriam terá terão teríamos teu teus teve tinha tinham tive tivemos tiver tivera tiveram tiverem tivermos tivesse tivessem tivéramos tivéssemos tu tua tuas tém tínhamos um uma você vocês vos à às éramos".split(" ")),e.Pipeline.registerFunction(e.pt.stopWordFilter,"stopWordFilter-pt")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.ro.js b/docs/assets/javascripts/lunr/lunr.ro.js deleted file mode 100644 index c5ecc96c..00000000 --- a/docs/assets/javascripts/lunr/lunr.ro.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Romanian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var h,z,i;e.ro=function(){this.pipeline.reset(),this.pipeline.add(e.ro.trimmer,e.ro.stopWordFilter,e.ro.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ro.stemmer))},e.ro.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.ro.trimmer=e.trimmerSupport.generateTrimmer(e.ro.wordCharacters),e.Pipeline.registerFunction(e.ro.trimmer,"trimmer-ro"),e.ro.stemmer=(h=e.stemmerSupport.Among,z=e.stemmerSupport.SnowballProgram,i=new function(){var r,n,t,a,o=[new h("",-1,3),new h("I",0,1),new h("U",0,2)],s=[new h("ea",-1,3),new h("aţia",-1,7),new h("aua",-1,2),new h("iua",-1,4),new h("aţie",-1,7),new h("ele",-1,3),new h("ile",-1,5),new h("iile",6,4),new h("iei",-1,4),new h("atei",-1,6),new h("ii",-1,4),new h("ului",-1,1),new h("ul",-1,1),new h("elor",-1,3),new h("ilor",-1,4),new h("iilor",14,4)],c=[new h("icala",-1,4),new h("iciva",-1,4),new h("ativa",-1,5),new h("itiva",-1,6),new h("icale",-1,4),new h("aţiune",-1,5),new h("iţiune",-1,6),new h("atoare",-1,5),new h("itoare",-1,6),new h("ătoare",-1,5),new h("icitate",-1,4),new h("abilitate",-1,1),new h("ibilitate",-1,2),new h("ivitate",-1,3),new h("icive",-1,4),new h("ative",-1,5),new h("itive",-1,6),new h("icali",-1,4),new h("atori",-1,5),new h("icatori",18,4),new h("itori",-1,6),new h("ători",-1,5),new h("icitati",-1,4),new h("abilitati",-1,1),new h("ivitati",-1,3),new h("icivi",-1,4),new h("ativi",-1,5),new h("itivi",-1,6),new h("icităi",-1,4),new h("abilităi",-1,1),new h("ivităi",-1,3),new h("icităţi",-1,4),new h("abilităţi",-1,1),new h("ivităţi",-1,3),new h("ical",-1,4),new h("ator",-1,5),new h("icator",35,4),new h("itor",-1,6),new h("ător",-1,5),new h("iciv",-1,4),new h("ativ",-1,5),new h("itiv",-1,6),new h("icală",-1,4),new h("icivă",-1,4),new h("ativă",-1,5),new h("itivă",-1,6)],u=[new h("ica",-1,1),new h("abila",-1,1),new h("ibila",-1,1),new h("oasa",-1,1),new h("ata",-1,1),new h("ita",-1,1),new h("anta",-1,1),new h("ista",-1,3),new h("uta",-1,1),new h("iva",-1,1),new h("ic",-1,1),new h("ice",-1,1),new h("abile",-1,1),new h("ibile",-1,1),new h("isme",-1,3),new h("iune",-1,2),new h("oase",-1,1),new h("ate",-1,1),new h("itate",17,1),new h("ite",-1,1),new h("ante",-1,1),new h("iste",-1,3),new h("ute",-1,1),new h("ive",-1,1),new h("ici",-1,1),new h("abili",-1,1),new h("ibili",-1,1),new h("iuni",-1,2),new h("atori",-1,1),new h("osi",-1,1),new h("ati",-1,1),new h("itati",30,1),new h("iti",-1,1),new h("anti",-1,1),new h("isti",-1,3),new h("uti",-1,1),new h("işti",-1,3),new h("ivi",-1,1),new h("ităi",-1,1),new h("oşi",-1,1),new h("ităţi",-1,1),new h("abil",-1,1),new h("ibil",-1,1),new h("ism",-1,3),new h("ator",-1,1),new h("os",-1,1),new h("at",-1,1),new h("it",-1,1),new h("ant",-1,1),new h("ist",-1,3),new h("ut",-1,1),new h("iv",-1,1),new h("ică",-1,1),new h("abilă",-1,1),new h("ibilă",-1,1),new h("oasă",-1,1),new h("ată",-1,1),new h("ită",-1,1),new h("antă",-1,1),new h("istă",-1,3),new h("ută",-1,1),new h("ivă",-1,1)],w=[new h("ea",-1,1),new h("ia",-1,1),new h("esc",-1,1),new h("ăsc",-1,1),new h("ind",-1,1),new h("ând",-1,1),new h("are",-1,1),new h("ere",-1,1),new h("ire",-1,1),new h("âre",-1,1),new h("se",-1,2),new h("ase",10,1),new h("sese",10,2),new h("ise",10,1),new h("use",10,1),new h("âse",10,1),new h("eşte",-1,1),new h("ăşte",-1,1),new h("eze",-1,1),new h("ai",-1,1),new h("eai",19,1),new h("iai",19,1),new h("sei",-1,2),new h("eşti",-1,1),new h("ăşti",-1,1),new h("ui",-1,1),new h("ezi",-1,1),new h("âi",-1,1),new h("aşi",-1,1),new h("seşi",-1,2),new h("aseşi",29,1),new h("seseşi",29,2),new h("iseşi",29,1),new h("useşi",29,1),new h("âseşi",29,1),new h("işi",-1,1),new h("uşi",-1,1),new h("âşi",-1,1),new h("aţi",-1,2),new h("eaţi",38,1),new h("iaţi",38,1),new h("eţi",-1,2),new h("iţi",-1,2),new h("âţi",-1,2),new h("arăţi",-1,1),new h("serăţi",-1,2),new h("aserăţi",45,1),new h("seserăţi",45,2),new h("iserăţi",45,1),new h("userăţi",45,1),new h("âserăţi",45,1),new h("irăţi",-1,1),new h("urăţi",-1,1),new h("ârăţi",-1,1),new h("am",-1,1),new h("eam",54,1),new h("iam",54,1),new h("em",-1,2),new h("asem",57,1),new h("sesem",57,2),new h("isem",57,1),new h("usem",57,1),new h("âsem",57,1),new h("im",-1,2),new h("âm",-1,2),new h("ăm",-1,2),new h("arăm",65,1),new h("serăm",65,2),new h("aserăm",67,1),new h("seserăm",67,2),new h("iserăm",67,1),new h("userăm",67,1),new h("âserăm",67,1),new h("irăm",65,1),new h("urăm",65,1),new h("ârăm",65,1),new h("au",-1,1),new h("eau",76,1),new h("iau",76,1),new h("indu",-1,1),new h("ându",-1,1),new h("ez",-1,1),new h("ească",-1,1),new h("ară",-1,1),new h("seră",-1,2),new h("aseră",84,1),new h("seseră",84,2),new h("iseră",84,1),new h("useră",84,1),new h("âseră",84,1),new h("iră",-1,1),new h("ură",-1,1),new h("âră",-1,1),new h("ează",-1,1)],i=[new h("a",-1,1),new h("e",-1,1),new h("ie",1,1),new h("i",-1,1),new h("ă",-1,1)],m=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,32,0,0,4],l=new z;function f(e,i){l.eq_s(1,e)&&(l.ket=l.cursor,l.in_grouping(m,97,259)&&l.slice_from(i))}function p(){if(l.out_grouping(m,97,259)){for(;!l.in_grouping(m,97,259);){if(l.cursor>=l.limit)return!0;l.cursor++}return!1}return!0}function d(){var e,i,r=l.cursor;if(l.in_grouping(m,97,259)){if(e=l.cursor,!p())return void(a=l.cursor);if(l.cursor=e,!function(){if(l.in_grouping(m,97,259))for(;!l.out_grouping(m,97,259);){if(l.cursor>=l.limit)return!0;l.cursor++}return!1}())return void(a=l.cursor)}l.cursor=r,l.out_grouping(m,97,259)&&(i=l.cursor,p()&&(l.cursor=i,l.in_grouping(m,97,259)&&l.cursor=l.limit)return!1;l.cursor++}for(;!l.out_grouping(m,97,259);){if(l.cursor>=l.limit)return!1;l.cursor++}return!0}function v(){return t<=l.cursor}function _(){var e,i=l.limit-l.cursor;if(l.ket=l.cursor,(e=l.find_among_b(c,46))&&(l.bra=l.cursor,v())){switch(e){case 1:l.slice_from("abil");break;case 2:l.slice_from("ibil");break;case 3:l.slice_from("iv");break;case 4:l.slice_from("ic");break;case 5:l.slice_from("at");break;case 6:l.slice_from("it")}return r=!0,l.cursor=l.limit-i,!0}return!1}function g(){var e,i;for(r=!1;;)if(i=l.limit-l.cursor,!_()){l.cursor=l.limit-i;break}if(l.ket=l.cursor,(e=l.find_among_b(u,62))&&(l.bra=l.cursor,n<=l.cursor)){switch(e){case 1:l.slice_del();break;case 2:l.eq_s_b(1,"ţ")&&(l.bra=l.cursor,l.slice_from("t"));break;case 3:l.slice_from("ist")}r=!0}}function k(){var e;l.ket=l.cursor,(e=l.find_among_b(i,5))&&(l.bra=l.cursor,a<=l.cursor&&1==e&&l.slice_del())}this.setCurrent=function(e){l.setCurrent(e)},this.getCurrent=function(){return l.getCurrent()},this.stem=function(){var e,i=l.cursor;return function(){for(var e,i;e=l.cursor,l.in_grouping(m,97,259)&&(i=l.cursor,l.bra=i,f("u","U"),l.cursor=i,f("i","I")),l.cursor=e,!(l.cursor>=l.limit);)l.cursor++}(),l.cursor=i,e=l.cursor,a=l.limit,n=t=a,d(),l.cursor=e,b()&&(t=l.cursor,b()&&(n=l.cursor)),l.limit_backward=i,l.cursor=l.limit,function(){var e,i;if(l.ket=l.cursor,(e=l.find_among_b(s,16))&&(l.bra=l.cursor,v()))switch(e){case 1:l.slice_del();break;case 2:l.slice_from("a");break;case 3:l.slice_from("e");break;case 4:l.slice_from("i");break;case 5:i=l.limit-l.cursor,l.eq_s_b(2,"ab")||(l.cursor=l.limit-i,l.slice_from("i"));break;case 6:l.slice_from("at");break;case 7:l.slice_from("aţi")}}(),l.cursor=l.limit,g(),l.cursor=l.limit,r||(l.cursor=l.limit,function(){var e,i,r;if(l.cursor>=a){if(i=l.limit_backward,l.limit_backward=a,l.ket=l.cursor,e=l.find_among_b(w,94))switch(l.bra=l.cursor,e){case 1:if(r=l.limit-l.cursor,!l.out_grouping_b(m,97,259)&&(l.cursor=l.limit-r,!l.eq_s_b(1,"u")))break;case 2:l.slice_del()}l.limit_backward=i}}(),l.cursor=l.limit),k(),l.cursor=l.limit_backward,function(){for(var e;;){if(l.bra=l.cursor,e=l.find_among(o,3))switch(l.ket=l.cursor,e){case 1:l.slice_from("i");continue;case 2:l.slice_from("u");continue;case 3:if(l.cursor>=l.limit)break;l.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.ro.stemmer,"stemmer-ro"),e.ro.stopWordFilter=e.generateStopWordFilter("acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ale alea altceva altcineva am ar are asemenea asta astea astăzi asupra au avea avem aveţi azi aş aşadar aţi bine bucur bună ca care caut ce cel ceva chiar cinci cine cineva contra cu cum cumva curând curînd când cât câte câtva câţi cînd cît cîte cîtva cîţi că căci cărei căror cărui către da dacă dar datorită dată dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după dă ea ei el ele eram este eu eşti face fata fi fie fiecare fii fim fiu fiţi frumos fără graţie halbă iar ieri la le li lor lui lângă lîngă mai mea mei mele mereu meu mi mie mine mult multă mulţi mulţumesc mâine mîine mă ne nevoie nici nicăieri nimeni nimeri nimic nişte noastre noastră noi noroc nostru nouă noştri nu opt ori oricare orice oricine oricum oricând oricât oricînd oricît oriunde patra patru patrulea pe pentru peste pic poate pot prea prima primul prin puţin puţina puţină până pînă rog sa sale sau se spate spre sub sunt suntem sunteţi sută sînt sîntem sînteţi să săi său ta tale te timp tine toate toată tot totuşi toţi trei treia treilea tu tăi tău un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vi voastre voastră voi vostru vouă voştri vreme vreo vreun vă zece zero zi zice îi îl îmi împotriva în înainte înaintea încotro încât încît între întrucât întrucît îţi ăla ălea ăsta ăstea ăştia şapte şase şi ştiu ţi ţie".split(" ")),e.Pipeline.registerFunction(e.ro.stopWordFilter,"stopWordFilter-ro")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.ru.js b/docs/assets/javascripts/lunr/lunr.ru.js deleted file mode 100644 index 104bc6e8..00000000 --- a/docs/assets/javascripts/lunr/lunr.ru.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Russian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var h,g,n;e.ru=function(){this.pipeline.reset(),this.pipeline.add(e.ru.trimmer,e.ru.stopWordFilter,e.ru.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ru.stemmer))},e.ru.wordCharacters="Ѐ-҄҇-ԯᴫᵸⷠ-ⷿꙀ-ꚟ︮︯",e.ru.trimmer=e.trimmerSupport.generateTrimmer(e.ru.wordCharacters),e.Pipeline.registerFunction(e.ru.trimmer,"trimmer-ru"),e.ru.stemmer=(h=e.stemmerSupport.Among,g=e.stemmerSupport.SnowballProgram,n=new function(){var n,e,r=[new h("в",-1,1),new h("ив",0,2),new h("ыв",0,2),new h("вши",-1,1),new h("ивши",3,2),new h("ывши",3,2),new h("вшись",-1,1),new h("ившись",6,2),new h("ывшись",6,2)],t=[new h("ее",-1,1),new h("ие",-1,1),new h("ое",-1,1),new h("ые",-1,1),new h("ими",-1,1),new h("ыми",-1,1),new h("ей",-1,1),new h("ий",-1,1),new h("ой",-1,1),new h("ый",-1,1),new h("ем",-1,1),new h("им",-1,1),new h("ом",-1,1),new h("ым",-1,1),new h("его",-1,1),new h("ого",-1,1),new h("ему",-1,1),new h("ому",-1,1),new h("их",-1,1),new h("ых",-1,1),new h("ею",-1,1),new h("ою",-1,1),new h("ую",-1,1),new h("юю",-1,1),new h("ая",-1,1),new h("яя",-1,1)],w=[new h("ем",-1,1),new h("нн",-1,1),new h("вш",-1,1),new h("ивш",2,2),new h("ывш",2,2),new h("щ",-1,1),new h("ющ",5,1),new h("ующ",6,2)],i=[new h("сь",-1,1),new h("ся",-1,1)],u=[new h("ла",-1,1),new h("ила",0,2),new h("ыла",0,2),new h("на",-1,1),new h("ена",3,2),new h("ете",-1,1),new h("ите",-1,2),new h("йте",-1,1),new h("ейте",7,2),new h("уйте",7,2),new h("ли",-1,1),new h("или",10,2),new h("ыли",10,2),new h("й",-1,1),new h("ей",13,2),new h("уй",13,2),new h("л",-1,1),new h("ил",16,2),new h("ыл",16,2),new h("ем",-1,1),new h("им",-1,2),new h("ым",-1,2),new h("н",-1,1),new h("ен",22,2),new h("ло",-1,1),new h("ило",24,2),new h("ыло",24,2),new h("но",-1,1),new h("ено",27,2),new h("нно",27,1),new h("ет",-1,1),new h("ует",30,2),new h("ит",-1,2),new h("ыт",-1,2),new h("ют",-1,1),new h("уют",34,2),new h("ят",-1,2),new h("ны",-1,1),new h("ены",37,2),new h("ть",-1,1),new h("ить",39,2),new h("ыть",39,2),new h("ешь",-1,1),new h("ишь",-1,2),new h("ю",-1,2),new h("ую",44,2)],s=[new h("а",-1,1),new h("ев",-1,1),new h("ов",-1,1),new h("е",-1,1),new h("ие",3,1),new h("ье",3,1),new h("и",-1,1),new h("еи",6,1),new h("ии",6,1),new h("ами",6,1),new h("ями",6,1),new h("иями",10,1),new h("й",-1,1),new h("ей",12,1),new h("ией",13,1),new h("ий",12,1),new h("ой",12,1),new h("ам",-1,1),new h("ем",-1,1),new h("ием",18,1),new h("ом",-1,1),new h("ям",-1,1),new h("иям",21,1),new h("о",-1,1),new h("у",-1,1),new h("ах",-1,1),new h("ях",-1,1),new h("иях",26,1),new h("ы",-1,1),new h("ь",-1,1),new h("ю",-1,1),new h("ию",30,1),new h("ью",30,1),new h("я",-1,1),new h("ия",33,1),new h("ья",33,1)],o=[new h("ост",-1,1),new h("ость",-1,1)],c=[new h("ейше",-1,1),new h("н",-1,2),new h("ейш",-1,1),new h("ь",-1,3)],m=[33,65,8,232],l=new g;function f(){for(;!l.in_grouping(m,1072,1103);){if(l.cursor>=l.limit)return!1;l.cursor++}return!0}function a(){for(;!l.out_grouping(m,1072,1103);){if(l.cursor>=l.limit)return!1;l.cursor++}return!0}function p(e,n){var r,t;if(l.ket=l.cursor,r=l.find_among_b(e,n)){switch(l.bra=l.cursor,r){case 1:if(t=l.limit-l.cursor,!l.eq_s_b(1,"а")&&(l.cursor=l.limit-t,!l.eq_s_b(1,"я")))return!1;case 2:l.slice_del()}return!0}return!1}function d(e,n){var r;return l.ket=l.cursor,!!(r=l.find_among_b(e,n))&&(l.bra=l.cursor,1==r&&l.slice_del(),!0)}function _(){return!!d(t,26)&&(p(w,8),!0)}function b(){var e;l.ket=l.cursor,(e=l.find_among_b(o,2))&&(l.bra=l.cursor,n<=l.cursor&&1==e&&l.slice_del())}this.setCurrent=function(e){l.setCurrent(e)},this.getCurrent=function(){return l.getCurrent()},this.stem=function(){return e=l.limit,n=e,f()&&(e=l.cursor,a()&&f()&&a()&&(n=l.cursor)),l.cursor=l.limit,!(l.cursor>3]&1<<(7&s))return this.cursor++,!0}return!1},in_grouping_b:function(r,t,i){if(this.cursor>this.limit_backward){var s=b.charCodeAt(this.cursor-1);if(s<=i&&t<=s&&r[(s-=t)>>3]&1<<(7&s))return this.cursor--,!0}return!1},out_grouping:function(r,t,i){if(this.cursor>3]&1<<(7&s)))return this.cursor++,!0}return!1},out_grouping_b:function(r,t,i){if(this.cursor>this.limit_backward){var s=b.charCodeAt(this.cursor-1);if(i>3]&1<<(7&s)))return this.cursor--,!0}return!1},eq_s:function(r,t){if(this.limit-this.cursor>1),a=0,f=u=(l=r[i]).s_size){if(this.cursor=e+l.s_size,!l.method)return l.result;var m=l.method();if(this.cursor=e+l.s_size,m)return l.result}if((i=l.substring_i)<0)return 0}},find_among_b:function(r,t){for(var i=0,s=t,e=this.cursor,n=this.limit_backward,u=0,o=0,h=!1;;){for(var c=i+(s-i>>1),a=0,f=u=(_=r[i]).s_size){if(this.cursor=e-_.s_size,!_.method)return _.result;var m=_.method();if(this.cursor=e-_.s_size,m)return _.result}if((i=_.substring_i)<0)return 0}},replace_s:function(r,t,i){var s=i.length-(t-r);return b=b.substring(0,r)+i+b.substring(t),this.limit+=s,this.cursor>=t?this.cursor+=s:this.cursor>r&&(this.cursor=r),s},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>b.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),b.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.sv.js b/docs/assets/javascripts/lunr/lunr.sv.js deleted file mode 100644 index a46a4e70..00000000 --- a/docs/assets/javascripts/lunr/lunr.sv.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Swedish` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,l,n;e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=(r=e.stemmerSupport.Among,l=e.stemmerSupport.SnowballProgram,n=new function(){var n,t,i=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],s=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],a=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],o=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],u=[119,127,149],m=new l;this.setCurrent=function(e){m.setCurrent(e)},this.getCurrent=function(){return m.getCurrent()},this.stem=function(){var e,r=m.cursor;return function(){var e,r=m.cursor+3;if(t=m.limit,0<=r||r<=m.limit){for(n=r;;){if(e=m.cursor,m.in_grouping(o,97,246)){m.cursor=e;break}if(m.cursor=e,m.cursor>=m.limit)return;m.cursor++}for(;!m.out_grouping(o,97,246);){if(m.cursor>=m.limit)return;m.cursor++}(t=m.cursor)=t&&(m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(i,37),m.limit_backward=r,e))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.in_grouping_b(u,98,121)&&m.slice_del()}}(),m.cursor=m.limit,e=m.limit_backward,m.cursor>=t&&(m.limit_backward=t,m.cursor=m.limit,m.find_among_b(s,7)&&(m.cursor=m.limit,m.ket=m.cursor,m.cursor>m.limit_backward&&(m.bra=--m.cursor,m.slice_del())),m.limit_backward=e),m.cursor=m.limit,function(){var e,r;if(m.cursor>=t){if(r=m.limit_backward,m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(a,5))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.slice_from("lös");break;case 3:m.slice_from("full")}m.limit_backward=r}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.th.js b/docs/assets/javascripts/lunr/lunr.th.js deleted file mode 100644 index 7f9887f7..00000000 --- a/docs/assets/javascripts/lunr/lunr.th.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Thai` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2017, Keerati Thiwanruk - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(t){if(void 0===t)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===t.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i="2"==t.version[0];t.th=function(){this.pipeline.reset(),this.pipeline.add(t.th.trimmer),i?this.tokenizer=t.th.tokenizer:(t.tokenizer&&(t.tokenizer=t.th.tokenizer),this.tokenizerFn&&(this.tokenizerFn=t.th.tokenizer))},t.th.wordCharacters="[฀-๿]",t.th.trimmer=t.trimmerSupport.generateTrimmer(t.th.wordCharacters),t.Pipeline.registerFunction(t.th.trimmer,"trimmer-th");var n=t.wordcut;n.init(),t.th.tokenizer=function(e){if(!arguments.length||null==e||null==e)return[];if(Array.isArray(e))return e.map(function(e){return i?new t.Token(e):e});var r=e.toString().replace(/^\s+/,"");return n.cut(r).split("|")}}}); \ No newline at end of file diff --git a/docs/assets/javascripts/lunr/lunr.tr.js b/docs/assets/javascripts/lunr/lunr.tr.js deleted file mode 100644 index 64ba95cb..00000000 --- a/docs/assets/javascripts/lunr/lunr.tr.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * Lunr languages, `Turkish` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ -!function(r,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var mr,dr,i;r.tr=function(){this.pipeline.reset(),this.pipeline.add(r.tr.trimmer,r.tr.stopWordFilter,r.tr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.tr.stemmer))},r.tr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.tr.trimmer=r.trimmerSupport.generateTrimmer(r.tr.wordCharacters),r.Pipeline.registerFunction(r.tr.trimmer,"trimmer-tr"),r.tr.stemmer=(mr=r.stemmerSupport.Among,dr=r.stemmerSupport.SnowballProgram,i=new function(){var t,r=[new mr("m",-1,-1),new mr("n",-1,-1),new mr("miz",-1,-1),new mr("niz",-1,-1),new mr("muz",-1,-1),new mr("nuz",-1,-1),new mr("müz",-1,-1),new mr("nüz",-1,-1),new mr("mız",-1,-1),new mr("nız",-1,-1)],i=[new mr("leri",-1,-1),new mr("ları",-1,-1)],e=[new mr("ni",-1,-1),new mr("nu",-1,-1),new mr("nü",-1,-1),new mr("nı",-1,-1)],n=[new mr("in",-1,-1),new mr("un",-1,-1),new mr("ün",-1,-1),new mr("ın",-1,-1)],u=[new mr("a",-1,-1),new mr("e",-1,-1)],o=[new mr("na",-1,-1),new mr("ne",-1,-1)],s=[new mr("da",-1,-1),new mr("ta",-1,-1),new mr("de",-1,-1),new mr("te",-1,-1)],c=[new mr("nda",-1,-1),new mr("nde",-1,-1)],l=[new mr("dan",-1,-1),new mr("tan",-1,-1),new mr("den",-1,-1),new mr("ten",-1,-1)],a=[new mr("ndan",-1,-1),new mr("nden",-1,-1)],m=[new mr("la",-1,-1),new mr("le",-1,-1)],d=[new mr("ca",-1,-1),new mr("ce",-1,-1)],f=[new mr("im",-1,-1),new mr("um",-1,-1),new mr("üm",-1,-1),new mr("ım",-1,-1)],b=[new mr("sin",-1,-1),new mr("sun",-1,-1),new mr("sün",-1,-1),new mr("sın",-1,-1)],w=[new mr("iz",-1,-1),new mr("uz",-1,-1),new mr("üz",-1,-1),new mr("ız",-1,-1)],_=[new mr("siniz",-1,-1),new mr("sunuz",-1,-1),new mr("sünüz",-1,-1),new mr("sınız",-1,-1)],k=[new mr("lar",-1,-1),new mr("ler",-1,-1)],p=[new mr("niz",-1,-1),new mr("nuz",-1,-1),new mr("nüz",-1,-1),new mr("nız",-1,-1)],g=[new mr("dir",-1,-1),new mr("tir",-1,-1),new mr("dur",-1,-1),new mr("tur",-1,-1),new mr("dür",-1,-1),new mr("tür",-1,-1),new mr("dır",-1,-1),new mr("tır",-1,-1)],y=[new mr("casına",-1,-1),new mr("cesine",-1,-1)],z=[new mr("di",-1,-1),new mr("ti",-1,-1),new mr("dik",-1,-1),new mr("tik",-1,-1),new mr("duk",-1,-1),new mr("tuk",-1,-1),new mr("dük",-1,-1),new mr("tük",-1,-1),new mr("dık",-1,-1),new mr("tık",-1,-1),new mr("dim",-1,-1),new mr("tim",-1,-1),new mr("dum",-1,-1),new mr("tum",-1,-1),new mr("düm",-1,-1),new mr("tüm",-1,-1),new mr("dım",-1,-1),new mr("tım",-1,-1),new mr("din",-1,-1),new mr("tin",-1,-1),new mr("dun",-1,-1),new mr("tun",-1,-1),new mr("dün",-1,-1),new mr("tün",-1,-1),new mr("dın",-1,-1),new mr("tın",-1,-1),new mr("du",-1,-1),new mr("tu",-1,-1),new mr("dü",-1,-1),new mr("tü",-1,-1),new mr("dı",-1,-1),new mr("tı",-1,-1)],h=[new mr("sa",-1,-1),new mr("se",-1,-1),new mr("sak",-1,-1),new mr("sek",-1,-1),new mr("sam",-1,-1),new mr("sem",-1,-1),new mr("san",-1,-1),new mr("sen",-1,-1)],v=[new mr("miş",-1,-1),new mr("muş",-1,-1),new mr("müş",-1,-1),new mr("mış",-1,-1)],q=[new mr("b",-1,1),new mr("c",-1,2),new mr("d",-1,3),new mr("ğ",-1,4)],C=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,8,0,0,0,0,0,0,1],P=[1,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,1],F=[65],S=[65],W=[["a",[1,64,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],97,305],["e",[17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130],101,252],["ı",[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],97,305],["i",[17],101,105],["o",F,111,117],["ö",S,246,252],["u",F,111,117]],L=new dr;function x(r,i,e){for(;;){var n=L.limit-L.cursor;if(L.in_grouping_b(r,i,e)){L.cursor=L.limit-n;break}if(L.cursor=L.limit-n,L.cursor<=L.limit_backward)return!1;L.cursor--}return!0}function A(){var r,i;r=L.limit-L.cursor,x(C,97,305);for(var e=0;eL.limit_backward&&(L.cursor--,e=L.limit-L.cursor,i()))?(L.cursor=L.limit-e,!0):(L.cursor=L.limit-n,r()?(L.cursor=L.limit-n,!1):(L.cursor=L.limit-n,!(L.cursor<=L.limit_backward)&&(L.cursor--,!!i()&&(L.cursor=L.limit-n,!0))))}function j(r){return E(r,function(){return L.in_grouping_b(C,97,305)})}function T(){return j(function(){return L.eq_s_b(1,"n")})}function Z(){return j(function(){return L.eq_s_b(1,"y")})}function B(){return L.find_among_b(r,10)&&E(function(){return L.in_grouping_b(P,105,305)},function(){return L.out_grouping_b(C,97,305)})}function D(){return A()&&L.in_grouping_b(P,105,305)&&j(function(){return L.eq_s_b(1,"s")})}function G(){return L.find_among_b(i,2)}function H(){return A()&&L.find_among_b(n,4)&&T()}function I(){return A()&&L.find_among_b(s,4)}function J(){return A()&&L.find_among_b(c,2)}function K(){return A()&&L.find_among_b(f,4)&&Z()}function M(){return A()&&L.find_among_b(b,4)}function N(){return A()&&L.find_among_b(w,4)&&Z()}function O(){return L.find_among_b(_,4)}function Q(){return A()&&L.find_among_b(k,2)}function R(){return A()&&L.find_among_b(g,8)}function U(){return A()&&L.find_among_b(z,32)&&Z()}function V(){return L.find_among_b(h,8)&&Z()}function X(){return A()&&L.find_among_b(v,4)&&Z()}function Y(){var r=L.limit-L.cursor;return!(X()||(L.cursor=L.limit-r,U()||(L.cursor=L.limit-r,V()||(L.cursor=L.limit-r,L.eq_s_b(3,"ken")&&Z()))))}function $(){if(L.find_among_b(y,2)){var r=L.limit-L.cursor;if(O()||(L.cursor=L.limit-r,Q()||(L.cursor=L.limit-r,K()||(L.cursor=L.limit-r,M()||(L.cursor=L.limit-r,N()||(L.cursor=L.limit-r))))),X())return!1}return!0}function rr(){if(!A()||!L.find_among_b(p,4))return!0;var r=L.limit-L.cursor;return!U()&&(L.cursor=L.limit-r,!V())}function ir(){var r,i,e,n=L.limit-L.cursor;if(L.ket=L.cursor,t=!0,Y()&&(L.cursor=L.limit-n,$()&&(L.cursor=L.limit-n,function(){if(Q()){L.bra=L.cursor,L.slice_del();var r=L.limit-L.cursor;return L.ket=L.cursor,R()||(L.cursor=L.limit-r,U()||(L.cursor=L.limit-r,V()||(L.cursor=L.limit-r,X()||(L.cursor=L.limit-r)))),t=!1}return!0}()&&(L.cursor=L.limit-n,rr()&&(L.cursor=L.limit-n,e=L.limit-L.cursor,!(O()||(L.cursor=L.limit-e,N()||(L.cursor=L.limit-e,M()||(L.cursor=L.limit-e,K()))))||(L.bra=L.cursor,L.slice_del(),i=L.limit-L.cursor,L.ket=L.cursor,X()||(L.cursor=L.limit-i),0)))))){if(L.cursor=L.limit-n,!R())return;L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,r=L.limit-L.cursor,O()||(L.cursor=L.limit-r,Q()||(L.cursor=L.limit-r,K()||(L.cursor=L.limit-r,M()||(L.cursor=L.limit-r,N()||(L.cursor=L.limit-r))))),X()||(L.cursor=L.limit-r)}L.bra=L.cursor,L.slice_del()}function er(){var r,i,e,n;if(L.ket=L.cursor,L.eq_s_b(2,"ki")){if(r=L.limit-L.cursor,I())return L.bra=L.cursor,L.slice_del(),i=L.limit-L.cursor,L.ket=L.cursor,Q()?(L.bra=L.cursor,L.slice_del(),er()):(L.cursor=L.limit-i,B()&&(L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,Q()&&(L.bra=L.cursor,L.slice_del(),er()))),!0;if(L.cursor=L.limit-r,H()){if(L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,e=L.limit-L.cursor,G())L.bra=L.cursor,L.slice_del();else{if(L.cursor=L.limit-e,L.ket=L.cursor,!B()&&(L.cursor=L.limit-e,!D()&&(L.cursor=L.limit-e,!er())))return!0;L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,Q()&&(L.bra=L.cursor,L.slice_del(),er())}return!0}if(L.cursor=L.limit-r,J()){if(n=L.limit-L.cursor,G())L.bra=L.cursor,L.slice_del();else if(L.cursor=L.limit-n,D())L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,Q()&&(L.bra=L.cursor,L.slice_del(),er());else if(L.cursor=L.limit-n,!er())return!1;return!0}}return!1}function nr(r){if(L.ket=L.cursor,!J()&&(L.cursor=L.limit-r,!A()||!L.find_among_b(o,2)))return!1;var i=L.limit-L.cursor;if(G())L.bra=L.cursor,L.slice_del();else if(L.cursor=L.limit-i,D())L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,Q()&&(L.bra=L.cursor,L.slice_del(),er());else if(L.cursor=L.limit-i,!er())return!1;return!0}function tr(r){if(L.ket=L.cursor,!(A()&&L.find_among_b(a,2)||(L.cursor=L.limit-r,A()&&L.find_among_b(e,4))))return!1;var i=L.limit-L.cursor;return!(!D()&&(L.cursor=L.limit-i,!G()))&&(L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,Q()&&(L.bra=L.cursor,L.slice_del(),er()),!0)}function ur(){var r,i=L.limit-L.cursor;return L.ket=L.cursor,!!(H()||(L.cursor=L.limit-i,A()&&L.find_among_b(m,2)&&Z()))&&(L.bra=L.cursor,L.slice_del(),r=L.limit-L.cursor,L.ket=L.cursor,!(!Q()||(L.bra=L.cursor,L.slice_del(),!er()))||(L.cursor=L.limit-r,L.ket=L.cursor,(B()||(L.cursor=L.limit-r,D()||(L.cursor=L.limit-r,er())))&&(L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,Q()&&(L.bra=L.cursor,L.slice_del(),er())),!0))}function or(){var r,i,e=L.limit-L.cursor;if(L.ket=L.cursor,!(I()||(L.cursor=L.limit-e,A()&&L.in_grouping_b(P,105,305)&&Z()||(L.cursor=L.limit-e,A()&&L.find_among_b(u,2)&&Z()))))return!1;if(L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,r=L.limit-L.cursor,B())L.bra=L.cursor,L.slice_del(),i=L.limit-L.cursor,L.ket=L.cursor,Q()||(L.cursor=L.limit-i);else if(L.cursor=L.limit-r,!Q())return!0;return L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,er(),!0}function sr(){var r,i,e=L.limit-L.cursor;if(L.ket=L.cursor,Q())return L.bra=L.cursor,L.slice_del(),void er();if(L.cursor=L.limit-e,L.ket=L.cursor,A()&&L.find_among_b(d,2)&&T())if(L.bra=L.cursor,L.slice_del(),r=L.limit-L.cursor,L.ket=L.cursor,G())L.bra=L.cursor,L.slice_del();else{if(L.cursor=L.limit-r,L.ket=L.cursor,!B()&&(L.cursor=L.limit-r,!D())){if(L.cursor=L.limit-r,L.ket=L.cursor,!Q())return;if(L.bra=L.cursor,L.slice_del(),!er())return}L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,Q()&&(L.bra=L.cursor,L.slice_del(),er())}else if(L.cursor=L.limit-e,!nr(e)&&(L.cursor=L.limit-e,!tr(e))){if(L.cursor=L.limit-e,L.ket=L.cursor,A()&&L.find_among_b(l,4))return L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,i=L.limit-L.cursor,void(B()?(L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,Q()&&(L.bra=L.cursor,L.slice_del(),er())):(L.cursor=L.limit-i,Q()?(L.bra=L.cursor,L.slice_del()):L.cursor=L.limit-i,er()));if(L.cursor=L.limit-e,!ur()){if(L.cursor=L.limit-e,G())return L.bra=L.cursor,void L.slice_del();L.cursor=L.limit-e,er()||(L.cursor=L.limit-e,or()||(L.cursor=L.limit-e,L.ket=L.cursor,(B()||(L.cursor=L.limit-e,D()))&&(L.bra=L.cursor,L.slice_del(),L.ket=L.cursor,Q()&&(L.bra=L.cursor,L.slice_del(),er()))))}}}function cr(r,i,e){if(L.cursor=L.limit-r,function(){for(;;){var r=L.limit-L.cursor;if(L.in_grouping_b(C,97,305)){L.cursor=L.limit-r;break}if(L.cursor=L.limit-r,L.cursor<=L.limit_backward)return!1;L.cursor--}return!0}()){var n=L.limit-L.cursor;if(!L.eq_s_b(1,i)&&(L.cursor=L.limit-n,!L.eq_s_b(1,e)))return!0;L.cursor=L.limit-r;var t=L.cursor;return L.insert(L.cursor,L.cursor,e),L.cursor=t,!1}return!0}function lr(r,i,e){for(;!L.eq_s(i,e);){if(L.cursor>=L.limit)return!0;L.cursor++}return i!=L.limit||(L.cursor=r,!1)}function ar(){var r,i,e=L.cursor;return!(!lr(r=L.cursor,2,"ad")||!lr(L.cursor=r,5,"soyad"))&&(L.limit_backward=e,L.cursor=L.limit,i=L.limit-L.cursor,(L.eq_s_b(1,"d")||(L.cursor=L.limit-i,L.eq_s_b(1,"g")))&&cr(i,"a","ı")&&cr(i,"e","i")&&cr(i,"o","u")&&cr(i,"ö","ü"),L.cursor=L.limit,function(){var r;if(L.ket=L.cursor,r=L.find_among_b(q,4))switch(L.bra=L.cursor,r){case 1:L.slice_from("p");break;case 2:L.slice_from("ç");break;case 3:L.slice_from("t");break;case 4:L.slice_from("k")}}(),!0)}this.setCurrent=function(r){L.setCurrent(r)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){return!!(function(){for(var r,i=L.cursor,e=2;;){for(r=L.cursor;!L.in_grouping(C,97,305);){if(L.cursor>=L.limit)return L.cursor=r,!(0e&&(this._events[n].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[n].length),"function"==typeof console.trace&&console.trace()));return this},r.prototype.once=function(n,t){if(!a(t))throw TypeError("listener must be a function");var e=!1;function r(){this.removeListener(n,r),e||(e=!0,t.apply(this,arguments))}return r.listener=t,this.on(n,r),this},r.prototype.removeListener=function(n,t){var e,r,i,o;if(!a(t))throw TypeError("listener must be a function");if(!this._events||!this._events[n])return this;if(i=(e=this._events[n]).length,r=-1,e===t||a(e.listener)&&e.listener===t)delete this._events[n],this._events.removeListener&&this.emit("removeListener",n,t);else if(c(e)){for(o=i;0this.maxLength)return i();if(!this.stat&&p(this.cache,o)){var t=this.cache[o];if(Array.isArray(t)&&(t="DIR"),!n||"DIR"===t)return i(null,t);if(n&&"FILE"===t)return i()}var e=this.statCache[o];if(void 0!==e){if(!1===e)return i(null,e);var s=e.isDirectory()?"DIR":"FILE";return n&&"FILE"===s?i():i(null,s,e)}var a=this,c=d("stat\0"+o,function(n,e){{if(e&&e.isSymbolicLink())return u.stat(o,function(n,t){n?a._stat2(r,o,null,e,i):a._stat2(r,o,n,t,i)});a._stat2(r,o,n,e,i)}});c&&u.lstat(o,c)},b.prototype._stat2=function(n,t,e,r,i){if(e)return this.statCache[t]=!1,i();var o="/"===n.slice(-1);if(this.statCache[t]=r,"/"===t.slice(-1)&&!r.isDirectory())return i(null,!1,r);var s=r.isDirectory()?"DIR":"FILE";return this.cache[t]=this.cache[t]||s,o&&"DIR"!==s?i():i(null,s,r)}}).call(this,_("_process"))},{"./common.js":15,"./sync.js":17,_process:24,assert:9,events:14,fs:12,inflight:18,inherits:19,minimatch:20,once:21,path:22,"path-is-absolute":23,util:28}],17:[function(e,r,n){(function(i){(r.exports=n).GlobSync=h;var s=e("fs"),c=e("minimatch"),g=(c.Minimatch,e("./glob.js").Glob,e("util"),e("path")),u=e("assert"),l=e("path-is-absolute"),t=e("./common.js"),o=(t.alphasort,t.alphasorti,t.setopts),a=t.ownProp,f=t.childrenIgnored;function n(n,t){if("function"==typeof t||3===arguments.length)throw new TypeError("callback provided to sync glob\nSee: https://github.com/isaacs/node-glob/issues/167");return new h(n,t).found}function h(n,t){if(!n)throw new Error("must provide pattern");if("function"==typeof t||3===arguments.length)throw new TypeError("callback provided to sync glob\nSee: https://github.com/isaacs/node-glob/issues/167");if(!(this instanceof h))return new h(n,t);if(o(this,n,t),this.noprocess)return this;var e=this.minimatch.set.length;this.matches=new Array(e);for(var r=0;rthis.maxLength)return!1;if(!this.stat&&a(this.cache,t)){var r=this.cache[t];if(Array.isArray(r)&&(r="DIR"),!e||"DIR"===r)return r;if(e&&"FILE"===r)return!1}var i=this.statCache[t];if(!i){var o;try{o=s.lstatSync(t)}catch(n){return!1}if(o.isSymbolicLink())try{i=s.statSync(t)}catch(n){i=o}else i=o}r=(this.statCache[t]=i).isDirectory()?"DIR":"FILE";return this.cache[t]=this.cache[t]||r,(!e||"DIR"===r)&&r},h.prototype._mark=function(n){return t.mark(this,n)},h.prototype._makeAbs=function(n){return t.makeAbs(this,n)}}).call(this,e("_process"))},{"./common.js":15,"./glob.js":16,_process:24,assert:9,fs:12,minimatch:20,path:22,"path-is-absolute":23,util:28}],18:[function(t,r,n){(function(s){var n=t("wrappy"),a=Object.create(null),e=t("once");r.exports=n(function(n,t){return a[n]?(a[n].push(t),null):(a[n]=[t],o=n,e(function n(){var t=a[o],e=t.length,r=function(n){for(var t=n.length,e=[],r=0;re?(t.splice(0,e),s.nextTick(function(){n.apply(null,r)})):delete a[o]}}));var o})}).call(this,t("_process"))},{_process:24,once:21,wrappy:29}],19:[function(n,t,e){"function"==typeof Object.create?t.exports=function(n,t){n.super_=t,n.prototype=Object.create(t.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}})}:t.exports=function(n,t){n.super_=t;var e=function(){};e.prototype=t.prototype,n.prototype=new e,n.prototype.constructor=n}},{}],20:[function(n,t,e){(t.exports=s).Minimatch=i;var u={sep:"/"};try{u=n("path")}catch(n){}var M=s.GLOBSTAR=i.GLOBSTAR={},r=n("brace-expansion"),C={"!":{open:"(?:(?!(?:",close:"))[^/]*?)"},"?":{open:"(?:",close:")?"},"+":{open:"(?:",close:")+"},"*":{open:"(?:",close:")*"},"@":{open:"(?:",close:")"}},P="[^/]",z=P+"*?",B="().*{}+?[]^$\\!".split("").reduce(function(n,t){return n[t]=!0,n},{});var l=/\/+/;function o(t,e){t=t||{},e=e||{};var r={};return Object.keys(e).forEach(function(n){r[n]=e[n]}),Object.keys(t).forEach(function(n){r[n]=t[n]}),r}function s(n,t,e){if("string"!=typeof t)throw new TypeError("glob pattern string required");return e||(e={}),!(!e.nocomment&&"#"===t.charAt(0))&&(""===t.trim()?""===n:new i(t,e).match(n))}function i(n,t){if(!(this instanceof i))return new i(n,t);if("string"!=typeof n)throw new TypeError("glob pattern string required");t||(t={}),n=n.trim(),"/"!==u.sep&&(n=n.split(u.sep).join("/")),this.options=t,this.set=[],this.pattern=n,this.regexp=null,this.negate=!1,this.comment=!1,this.empty=!1,this.make()}function a(n,t){if(t||(t=this instanceof i?this.options:{}),void 0===(n=void 0===n?this.pattern:n))throw new TypeError("undefined pattern");return t.nobrace||!n.match(/\{.*\}/)?[n]:r(n)}s.filter=function(r,i){return i=i||{},function(n,t,e){return s(n,r,i)}},s.defaults=function(r){if(!r||!Object.keys(r).length)return s;var i=s,n=function(n,t,e){return i.minimatch(n,t,o(r,e))};return n.Minimatch=function(n,t){return new i.Minimatch(n,o(r,t))},n},i.defaults=function(n){return n&&Object.keys(n).length?s.defaults(n).Minimatch:i},i.prototype.debug=function(){},i.prototype.make=function(){if(this._made)return;var n=this.pattern,t=this.options;if(!t.nocomment&&"#"===n.charAt(0))return void(this.comment=!0);if(!n)return void(this.empty=!0);this.parseNegate();var e=this.globSet=this.braceExpand();t.debug&&(this.debug=console.error);this.debug(this.pattern,e),e=this.globParts=e.map(function(n){return n.split(l)}),this.debug(this.pattern,e),e=e.map(function(n,t,e){return n.map(this.parse,this)},this),this.debug(this.pattern,e),e=e.filter(function(n){return-1===n.indexOf(!1)}),this.debug(this.pattern,e),this.set=e},i.prototype.parseNegate=function(){var n=this.pattern,t=!1,e=this.options,r=0;if(e.nonegate)return;for(var i=0,o=n.length;i>> no match, partial?",n,f,t,h),f!==s))}if("string"==typeof u?(c=r.nocase?l.toLowerCase()===u.toLowerCase():l===u,this.debug("string match",u,l,c)):(c=l.match(u),this.debug("pattern match",u,l,c)),!c)return!1}if(i===s&&o===a)return!0;if(i===s)return e;if(o===a)return i===s-1&&""===n[i];throw new Error("wtf?")}},{"brace-expansion":11,path:22}],21:[function(n,t,e){var r=n("wrappy");function i(n){var t=function(){return t.called?t.value:(t.called=!0,t.value=n.apply(this,arguments))};return t.called=!1,t}function o(n){var t=function(){if(t.called)throw new Error(t.onceError);return t.called=!0,t.value=n.apply(this,arguments)},e=n.name||"Function wrapped with `once`";return t.onceError=e+" shouldn't be called more than once",t.called=!1,t}t.exports=r(i),t.exports.strict=r(o),i.proto=i(function(){Object.defineProperty(Function.prototype,"once",{value:function(){return i(this)},configurable:!0}),Object.defineProperty(Function.prototype,"onceStrict",{value:function(){return o(this)},configurable:!0})})},{wrappy:29}],22:[function(n,t,u){(function(i){function o(n,t){for(var e=0,r=n.length-1;0<=r;r--){var i=n[r];"."===i?n.splice(r,1):".."===i?(n.splice(r,1),e++):e&&(n.splice(r,1),e--)}if(t)for(;e--;e)n.unshift("..");return n}var t=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/,s=function(n){return t.exec(n).slice(1)};function a(n,t){if(n.filter)return n.filter(t);for(var e=[],r=0;r":">",'"':""","'":"'","`":"`"},D=d.invert(N),F=function(t){var e=function(n){return t[n]},n="(?:"+d.keys(t).join("|")+")",r=RegExp(n),i=RegExp(n,"g");return function(n){return n=null==n?"":""+n,r.test(n)?n.replace(i,e):n}};d.escape=F(N),d.unescape=F(D),d.result=function(n,t,e){var r=null==n?void 0:n[t];return void 0===r&&(r=e),d.isFunction(r)?r.call(n):r};var M=0;d.uniqueId=function(n){var t=++M+"";return n?n+t:t},d.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var C=/(.)^/,P={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},z=/\\|'|\r|\n|\u2028|\u2029/g,B=function(n){return"\\"+P[n]};d.template=function(o,n,t){!n&&t&&(n=t),n=d.defaults({},n,d.templateSettings);var e=RegExp([(n.escape||C).source,(n.interpolate||C).source,(n.evaluate||C).source].join("|")+"|$","g"),s=0,a="__p+='";o.replace(e,function(n,t,e,r,i){return a+=o.slice(s,i).replace(z,B),s=i+n.length,t?a+="'+\n((__t=("+t+"))==null?'':_.escape(__t))+\n'":e?a+="'+\n((__t=("+e+"))==null?'':__t)+\n'":r&&(a+="';\n"+r+"\n__p+='"),n}),a+="';\n",n.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{var r=new Function(n.variable||"obj","_",a)}catch(n){throw n.source=a,n}var i=function(n){return r.call(this,n,d)},c=n.variable||"obj";return i.source="function("+c+"){\n"+a+"}",i},d.chain=function(n){var t=d(n);return t._chain=!0,t};var U=function(n,t){return n._chain?d(t).chain():t};d.mixin=function(e){d.each(d.functions(e),function(n){var t=d[n]=e[n];d.prototype[n]=function(){var n=[this._wrapped];return i.apply(n,arguments),U(this,t.apply(d,n))}})},d.mixin(d),d.each(["pop","push","reverse","shift","sort","splice","unshift"],function(t){var e=r[t];d.prototype[t]=function(){var n=this._wrapped;return e.apply(n,arguments),"shift"!==t&&"splice"!==t||0!==n.length||delete n[0],U(this,n)}}),d.each(["concat","join","slice"],function(n){var t=r[n];d.prototype[n]=function(){return U(this,t.apply(this._wrapped,arguments))}}),d.prototype.value=function(){return this._wrapped},d.prototype.valueOf=d.prototype.toJSON=d.prototype.value,d.prototype.toString=function(){return""+this._wrapped}}).call(this)},{}],26:[function(n,t,e){arguments[4][19][0].apply(e,arguments)},{dup:19}],27:[function(n,t,e){t.exports=function(n){return n&&"object"==typeof n&&"function"==typeof n.copy&&"function"==typeof n.fill&&"function"==typeof n.readUInt8}},{}],28:[function(h,n,k){(function(r,i){var a=/%[sdj%]/g;k.format=function(n){if(!_(n)){for(var t=[],e=0;e.md-nav__link{color:inherit}button[data-md-color-primary=pink]{background-color:#e91e63}[data-md-color-primary=pink] .md-typeset a{color:#e91e63}[data-md-color-primary=pink] .md-header,[data-md-color-primary=pink] .md-hero{background-color:#e91e63}[data-md-color-primary=pink] .md-nav__link--active,[data-md-color-primary=pink] .md-nav__link:active{color:#e91e63}[data-md-color-primary=pink] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=purple]{background-color:#ab47bc}[data-md-color-primary=purple] .md-typeset a{color:#ab47bc}[data-md-color-primary=purple] .md-header,[data-md-color-primary=purple] .md-hero{background-color:#ab47bc}[data-md-color-primary=purple] .md-nav__link--active,[data-md-color-primary=purple] .md-nav__link:active{color:#ab47bc}[data-md-color-primary=purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-purple]{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-typeset a{color:#7e57c2}[data-md-color-primary=deep-purple] .md-header,[data-md-color-primary=deep-purple] .md-hero{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__link--active,[data-md-color-primary=deep-purple] .md-nav__link:active{color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=indigo]{background-color:#3f51b5}[data-md-color-primary=indigo] .md-typeset a{color:#3f51b5}[data-md-color-primary=indigo] .md-header,[data-md-color-primary=indigo] .md-hero{background-color:#3f51b5}[data-md-color-primary=indigo] .md-nav__link--active,[data-md-color-primary=indigo] .md-nav__link:active{color:#3f51b5}[data-md-color-primary=indigo] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue]{background-color:#2196f3}[data-md-color-primary=blue] .md-typeset a{color:#2196f3}[data-md-color-primary=blue] .md-header,[data-md-color-primary=blue] .md-hero{background-color:#2196f3}[data-md-color-primary=blue] .md-nav__link--active,[data-md-color-primary=blue] .md-nav__link:active{color:#2196f3}[data-md-color-primary=blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-blue]{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-typeset a{color:#03a9f4}[data-md-color-primary=light-blue] .md-header,[data-md-color-primary=light-blue] .md-hero{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__link--active,[data-md-color-primary=light-blue] .md-nav__link:active{color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=cyan]{background-color:#00bcd4}[data-md-color-primary=cyan] .md-typeset a{color:#00bcd4}[data-md-color-primary=cyan] .md-header,[data-md-color-primary=cyan] .md-hero{background-color:#00bcd4}[data-md-color-primary=cyan] .md-nav__link--active,[data-md-color-primary=cyan] .md-nav__link:active{color:#00bcd4}[data-md-color-primary=cyan] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=teal]{background-color:#009688}[data-md-color-primary=teal] .md-typeset a{color:#009688}[data-md-color-primary=teal] .md-header,[data-md-color-primary=teal] .md-hero{background-color:#009688}[data-md-color-primary=teal] .md-nav__link--active,[data-md-color-primary=teal] .md-nav__link:active{color:#009688}[data-md-color-primary=teal] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=green]{background-color:#4caf50}[data-md-color-primary=green] .md-typeset a{color:#4caf50}[data-md-color-primary=green] .md-header,[data-md-color-primary=green] .md-hero{background-color:#4caf50}[data-md-color-primary=green] .md-nav__link--active,[data-md-color-primary=green] .md-nav__link:active{color:#4caf50}[data-md-color-primary=green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-green]{background-color:#7cb342}[data-md-color-primary=light-green] .md-typeset a{color:#7cb342}[data-md-color-primary=light-green] .md-header,[data-md-color-primary=light-green] .md-hero{background-color:#7cb342}[data-md-color-primary=light-green] .md-nav__link--active,[data-md-color-primary=light-green] .md-nav__link:active{color:#7cb342}[data-md-color-primary=light-green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=lime]{background-color:#c0ca33}[data-md-color-primary=lime] .md-typeset a{color:#c0ca33}[data-md-color-primary=lime] .md-header,[data-md-color-primary=lime] .md-hero{background-color:#c0ca33}[data-md-color-primary=lime] .md-nav__link--active,[data-md-color-primary=lime] .md-nav__link:active{color:#c0ca33}[data-md-color-primary=lime] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=yellow]{background-color:#f9a825}[data-md-color-primary=yellow] .md-typeset a{color:#f9a825}[data-md-color-primary=yellow] .md-header,[data-md-color-primary=yellow] .md-hero{background-color:#f9a825}[data-md-color-primary=yellow] .md-nav__link--active,[data-md-color-primary=yellow] .md-nav__link:active{color:#f9a825}[data-md-color-primary=yellow] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=amber]{background-color:#ffa000}[data-md-color-primary=amber] .md-typeset a{color:#ffa000}[data-md-color-primary=amber] .md-header,[data-md-color-primary=amber] .md-hero{background-color:#ffa000}[data-md-color-primary=amber] .md-nav__link--active,[data-md-color-primary=amber] .md-nav__link:active{color:#ffa000}[data-md-color-primary=amber] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=orange]{background-color:#fb8c00}[data-md-color-primary=orange] .md-typeset a{color:#fb8c00}[data-md-color-primary=orange] .md-header,[data-md-color-primary=orange] .md-hero{background-color:#fb8c00}[data-md-color-primary=orange] .md-nav__link--active,[data-md-color-primary=orange] .md-nav__link:active{color:#fb8c00}[data-md-color-primary=orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-orange]{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-typeset a{color:#ff7043}[data-md-color-primary=deep-orange] .md-header,[data-md-color-primary=deep-orange] .md-hero{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__link--active,[data-md-color-primary=deep-orange] .md-nav__link:active{color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=brown]{background-color:#795548}[data-md-color-primary=brown] .md-typeset a{color:#795548}[data-md-color-primary=brown] .md-header,[data-md-color-primary=brown] .md-hero{background-color:#795548}[data-md-color-primary=brown] .md-nav__link--active,[data-md-color-primary=brown] .md-nav__link:active{color:#795548}[data-md-color-primary=brown] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=grey]{background-color:#757575}[data-md-color-primary=grey] .md-typeset a{color:#757575}[data-md-color-primary=grey] .md-header,[data-md-color-primary=grey] .md-hero{background-color:#757575}[data-md-color-primary=grey] .md-nav__link--active,[data-md-color-primary=grey] .md-nav__link:active{color:#757575}[data-md-color-primary=grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue-grey]{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-typeset a{color:#546e7a}[data-md-color-primary=blue-grey] .md-header,[data-md-color-primary=blue-grey] .md-hero{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__link--active,[data-md-color-primary=blue-grey] .md-nav__link:active{color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=white]{box-shadow:inset 0 0 .05rem rgba(0,0,0,.54)}[data-md-color-primary=white] .md-header,[data-md-color-primary=white] .md-hero,button[data-md-color-primary=white]{background-color:#fff;color:rgba(0,0,0,.87)}[data-md-color-primary=white] .md-hero--expand{border-bottom:.05rem solid rgba(0,0,0,.07)}[data-md-color-primary=black] .md-header,[data-md-color-primary=black] .md-hero,button[data-md-color-primary=black]{background-color:#000}button[data-md-color-accent=red]{background-color:#ff1744}[data-md-color-accent=red] .md-typeset a:active,[data-md-color-accent=red] .md-typeset a:hover{color:#ff1744}[data-md-color-accent=red] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=red] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-nav__link:focus,[data-md-color-accent=red] .md-nav__link:hover,[data-md-color-accent=red] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=red] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=red] .md-typeset .md-clipboard:active:before,[data-md-color-accent=red] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=red] .md-typeset [id] .headerlink:focus,[data-md-color-accent=red] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=red] .md-typeset [id]:target .headerlink{color:#ff1744}[data-md-color-accent=red] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-search-result__link:hover,[data-md-color-accent=red] .md-search-result__link[data-md-state=active]{background-color:rgba(255,23,68,.1)}[data-md-color-accent=red] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-source-file:hover:before{background-color:#ff1744}button[data-md-color-accent=pink]{background-color:#f50057}[data-md-color-accent=pink] .md-typeset a:active,[data-md-color-accent=pink] .md-typeset a:hover{color:#f50057}[data-md-color-accent=pink] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=pink] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-nav__link:focus,[data-md-color-accent=pink] .md-nav__link:hover,[data-md-color-accent=pink] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=pink] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=pink] .md-typeset .md-clipboard:active:before,[data-md-color-accent=pink] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=pink] .md-typeset [id] .headerlink:focus,[data-md-color-accent=pink] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=pink] .md-typeset [id]:target .headerlink{color:#f50057}[data-md-color-accent=pink] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-search-result__link:hover,[data-md-color-accent=pink] .md-search-result__link[data-md-state=active]{background-color:rgba(245,0,87,.1)}[data-md-color-accent=pink] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-source-file:hover:before{background-color:#f50057}button[data-md-color-accent=purple]{background-color:#e040fb}[data-md-color-accent=purple] .md-typeset a:active,[data-md-color-accent=purple] .md-typeset a:hover{color:#e040fb}[data-md-color-accent=purple] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=purple] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-nav__link:focus,[data-md-color-accent=purple] .md-nav__link:hover,[data-md-color-accent=purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=purple] .md-typeset .md-clipboard:active:before,[data-md-color-accent=purple] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=purple] .md-typeset [id]:target .headerlink{color:#e040fb}[data-md-color-accent=purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-search-result__link:hover,[data-md-color-accent=purple] .md-search-result__link[data-md-state=active]{background-color:rgba(224,64,251,.1)}[data-md-color-accent=purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-source-file:hover:before{background-color:#e040fb}button[data-md-color-accent=deep-purple]{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset a:active,[data-md-color-accent=deep-purple] .md-typeset a:hover{color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-purple] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-nav__link:focus,[data-md-color-accent=deep-purple] .md-nav__link:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-purple] .md-typeset .md-clipboard:active:before,[data-md-color-accent=deep-purple] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=deep-purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-purple] .md-typeset [id]:target .headerlink{color:#7c4dff}[data-md-color-accent=deep-purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-search-result__link:hover,[data-md-color-accent=deep-purple] .md-search-result__link[data-md-state=active]{background-color:rgba(124,77,255,.1)}[data-md-color-accent=deep-purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-source-file:hover:before{background-color:#7c4dff}button[data-md-color-accent=indigo]{background-color:#536dfe}[data-md-color-accent=indigo] .md-typeset a:active,[data-md-color-accent=indigo] .md-typeset a:hover{color:#536dfe}[data-md-color-accent=indigo] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=indigo] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-nav__link:focus,[data-md-color-accent=indigo] .md-nav__link:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=indigo] .md-typeset .md-clipboard:active:before,[data-md-color-accent=indigo] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=indigo] .md-typeset [id] .headerlink:focus,[data-md-color-accent=indigo] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=indigo] .md-typeset [id]:target .headerlink{color:#536dfe}[data-md-color-accent=indigo] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-search-result__link:hover,[data-md-color-accent=indigo] .md-search-result__link[data-md-state=active]{background-color:rgba(83,109,254,.1)}[data-md-color-accent=indigo] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-source-file:hover:before{background-color:#536dfe}button[data-md-color-accent=blue]{background-color:#448aff}[data-md-color-accent=blue] .md-typeset a:active,[data-md-color-accent=blue] .md-typeset a:hover{color:#448aff}[data-md-color-accent=blue] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=blue] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-nav__link:focus,[data-md-color-accent=blue] .md-nav__link:hover,[data-md-color-accent=blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=blue] .md-typeset .md-clipboard:active:before,[data-md-color-accent=blue] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=blue] .md-typeset [id]:target .headerlink{color:#448aff}[data-md-color-accent=blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-search-result__link:hover,[data-md-color-accent=blue] .md-search-result__link[data-md-state=active]{background-color:rgba(68,138,255,.1)}[data-md-color-accent=blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-source-file:hover:before{background-color:#448aff}button[data-md-color-accent=light-blue]{background-color:#0091ea}[data-md-color-accent=light-blue] .md-typeset a:active,[data-md-color-accent=light-blue] .md-typeset a:hover{color:#0091ea}[data-md-color-accent=light-blue] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-blue] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-nav__link:focus,[data-md-color-accent=light-blue] .md-nav__link:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-blue] .md-typeset .md-clipboard:active:before,[data-md-color-accent=light-blue] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=light-blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-blue] .md-typeset [id]:target .headerlink{color:#0091ea}[data-md-color-accent=light-blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-search-result__link:hover,[data-md-color-accent=light-blue] .md-search-result__link[data-md-state=active]{background-color:rgba(0,145,234,.1)}[data-md-color-accent=light-blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-source-file:hover:before{background-color:#0091ea}button[data-md-color-accent=cyan]{background-color:#00b8d4}[data-md-color-accent=cyan] .md-typeset a:active,[data-md-color-accent=cyan] .md-typeset a:hover{color:#00b8d4}[data-md-color-accent=cyan] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=cyan] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-nav__link:focus,[data-md-color-accent=cyan] .md-nav__link:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=cyan] .md-typeset .md-clipboard:active:before,[data-md-color-accent=cyan] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=cyan] .md-typeset [id] .headerlink:focus,[data-md-color-accent=cyan] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=cyan] .md-typeset [id]:target .headerlink{color:#00b8d4}[data-md-color-accent=cyan] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-search-result__link:hover,[data-md-color-accent=cyan] .md-search-result__link[data-md-state=active]{background-color:rgba(0,184,212,.1)}[data-md-color-accent=cyan] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-source-file:hover:before{background-color:#00b8d4}button[data-md-color-accent=teal]{background-color:#00bfa5}[data-md-color-accent=teal] .md-typeset a:active,[data-md-color-accent=teal] .md-typeset a:hover{color:#00bfa5}[data-md-color-accent=teal] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=teal] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-nav__link:focus,[data-md-color-accent=teal] .md-nav__link:hover,[data-md-color-accent=teal] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=teal] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=teal] .md-typeset .md-clipboard:active:before,[data-md-color-accent=teal] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=teal] .md-typeset [id] .headerlink:focus,[data-md-color-accent=teal] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=teal] .md-typeset [id]:target .headerlink{color:#00bfa5}[data-md-color-accent=teal] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-search-result__link:hover,[data-md-color-accent=teal] .md-search-result__link[data-md-state=active]{background-color:rgba(0,191,165,.1)}[data-md-color-accent=teal] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-source-file:hover:before{background-color:#00bfa5}button[data-md-color-accent=green]{background-color:#00c853}[data-md-color-accent=green] .md-typeset a:active,[data-md-color-accent=green] .md-typeset a:hover{color:#00c853}[data-md-color-accent=green] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=green] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-nav__link:focus,[data-md-color-accent=green] .md-nav__link:hover,[data-md-color-accent=green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=green] .md-typeset .md-clipboard:active:before,[data-md-color-accent=green] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=green] .md-typeset [id]:target .headerlink{color:#00c853}[data-md-color-accent=green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-search-result__link:hover,[data-md-color-accent=green] .md-search-result__link[data-md-state=active]{background-color:rgba(0,200,83,.1)}[data-md-color-accent=green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-source-file:hover:before{background-color:#00c853}button[data-md-color-accent=light-green]{background-color:#64dd17}[data-md-color-accent=light-green] .md-typeset a:active,[data-md-color-accent=light-green] .md-typeset a:hover{color:#64dd17}[data-md-color-accent=light-green] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-green] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-nav__link:focus,[data-md-color-accent=light-green] .md-nav__link:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-green] .md-typeset .md-clipboard:active:before,[data-md-color-accent=light-green] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=light-green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-green] .md-typeset [id]:target .headerlink{color:#64dd17}[data-md-color-accent=light-green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-search-result__link:hover,[data-md-color-accent=light-green] .md-search-result__link[data-md-state=active]{background-color:rgba(100,221,23,.1)}[data-md-color-accent=light-green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-source-file:hover:before{background-color:#64dd17}button[data-md-color-accent=lime]{background-color:#aeea00}[data-md-color-accent=lime] .md-typeset a:active,[data-md-color-accent=lime] .md-typeset a:hover{color:#aeea00}[data-md-color-accent=lime] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=lime] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-nav__link:focus,[data-md-color-accent=lime] .md-nav__link:hover,[data-md-color-accent=lime] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=lime] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=lime] .md-typeset .md-clipboard:active:before,[data-md-color-accent=lime] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=lime] .md-typeset [id] .headerlink:focus,[data-md-color-accent=lime] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=lime] .md-typeset [id]:target .headerlink{color:#aeea00}[data-md-color-accent=lime] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-search-result__link:hover,[data-md-color-accent=lime] .md-search-result__link[data-md-state=active]{background-color:rgba(174,234,0,.1)}[data-md-color-accent=lime] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-source-file:hover:before{background-color:#aeea00}button[data-md-color-accent=yellow]{background-color:#ffd600}[data-md-color-accent=yellow] .md-typeset a:active,[data-md-color-accent=yellow] .md-typeset a:hover{color:#ffd600}[data-md-color-accent=yellow] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=yellow] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-nav__link:focus,[data-md-color-accent=yellow] .md-nav__link:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=yellow] .md-typeset .md-clipboard:active:before,[data-md-color-accent=yellow] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=yellow] .md-typeset [id] .headerlink:focus,[data-md-color-accent=yellow] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=yellow] .md-typeset [id]:target .headerlink{color:#ffd600}[data-md-color-accent=yellow] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-search-result__link:hover,[data-md-color-accent=yellow] .md-search-result__link[data-md-state=active]{background-color:rgba(255,214,0,.1)}[data-md-color-accent=yellow] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-source-file:hover:before{background-color:#ffd600}button[data-md-color-accent=amber]{background-color:#ffab00}[data-md-color-accent=amber] .md-typeset a:active,[data-md-color-accent=amber] .md-typeset a:hover{color:#ffab00}[data-md-color-accent=amber] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=amber] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-nav__link:focus,[data-md-color-accent=amber] .md-nav__link:hover,[data-md-color-accent=amber] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=amber] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=amber] .md-typeset .md-clipboard:active:before,[data-md-color-accent=amber] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=amber] .md-typeset [id] .headerlink:focus,[data-md-color-accent=amber] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=amber] .md-typeset [id]:target .headerlink{color:#ffab00}[data-md-color-accent=amber] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-search-result__link:hover,[data-md-color-accent=amber] .md-search-result__link[data-md-state=active]{background-color:rgba(255,171,0,.1)}[data-md-color-accent=amber] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-source-file:hover:before{background-color:#ffab00}button[data-md-color-accent=orange]{background-color:#ff9100}[data-md-color-accent=orange] .md-typeset a:active,[data-md-color-accent=orange] .md-typeset a:hover{color:#ff9100}[data-md-color-accent=orange] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=orange] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-nav__link:focus,[data-md-color-accent=orange] .md-nav__link:hover,[data-md-color-accent=orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=orange] .md-typeset .md-clipboard:active:before,[data-md-color-accent=orange] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=orange] .md-typeset [id]:target .headerlink{color:#ff9100}[data-md-color-accent=orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-search-result__link:hover,[data-md-color-accent=orange] .md-search-result__link[data-md-state=active]{background-color:rgba(255,145,0,.1)}[data-md-color-accent=orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-source-file:hover:before{background-color:#ff9100}button[data-md-color-accent=deep-orange]{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset a:active,[data-md-color-accent=deep-orange] .md-typeset a:hover{color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-orange] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-nav__link:focus,[data-md-color-accent=deep-orange] .md-nav__link:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-orange] .md-typeset .md-clipboard:active:before,[data-md-color-accent=deep-orange] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=deep-orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-orange] .md-typeset [id]:target .headerlink{color:#ff6e40}[data-md-color-accent=deep-orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-search-result__link:hover,[data-md-color-accent=deep-orange] .md-search-result__link[data-md-state=active]{background-color:rgba(255,110,64,.1)}[data-md-color-accent=deep-orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-source-file:hover:before{background-color:#ff6e40}@media only screen and (max-width:59.9375em){[data-md-color-primary=red] .md-nav__source{background-color:rgba(190,66,64,.9675)}[data-md-color-primary=pink] .md-nav__source{background-color:rgba(185,24,79,.9675)}[data-md-color-primary=purple] .md-nav__source{background-color:rgba(136,57,150,.9675)}[data-md-color-primary=deep-purple] .md-nav__source{background-color:rgba(100,69,154,.9675)}[data-md-color-primary=indigo] .md-nav__source{background-color:rgba(50,64,144,.9675)}[data-md-color-primary=blue] .md-nav__source{background-color:rgba(26,119,193,.9675)}[data-md-color-primary=light-blue] .md-nav__source{background-color:rgba(2,134,194,.9675)}[data-md-color-primary=cyan] .md-nav__source{background-color:rgba(0,150,169,.9675)}[data-md-color-primary=teal] .md-nav__source{background-color:rgba(0,119,108,.9675)}[data-md-color-primary=green] .md-nav__source{background-color:rgba(60,139,64,.9675)}[data-md-color-primary=light-green] .md-nav__source{background-color:rgba(99,142,53,.9675)}[data-md-color-primary=lime] .md-nav__source{background-color:rgba(153,161,41,.9675)}[data-md-color-primary=yellow] .md-nav__source{background-color:rgba(198,134,29,.9675)}[data-md-color-primary=amber] .md-nav__source{background-color:rgba(203,127,0,.9675)}[data-md-color-primary=orange] .md-nav__source{background-color:rgba(200,111,0,.9675)}[data-md-color-primary=deep-orange] .md-nav__source{background-color:rgba(203,89,53,.9675)}[data-md-color-primary=brown] .md-nav__source{background-color:rgba(96,68,57,.9675)}[data-md-color-primary=grey] .md-nav__source{background-color:rgba(93,93,93,.9675)}[data-md-color-primary=blue-grey] .md-nav__source{background-color:rgba(67,88,97,.9675)}[data-md-color-primary=white] .md-nav__source{background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.87)}[data-md-color-primary=black] .md-nav__source{background-color:#404040}}@media only screen and (max-width:76.1875em){html [data-md-color-primary=red] .md-nav--primary .md-nav__title--site{background-color:#ef5350}html [data-md-color-primary=pink] .md-nav--primary .md-nav__title--site{background-color:#e91e63}html [data-md-color-primary=purple] .md-nav--primary .md-nav__title--site{background-color:#ab47bc}html [data-md-color-primary=deep-purple] .md-nav--primary .md-nav__title--site{background-color:#7e57c2}html [data-md-color-primary=indigo] .md-nav--primary .md-nav__title--site{background-color:#3f51b5}html [data-md-color-primary=blue] .md-nav--primary .md-nav__title--site{background-color:#2196f3}html [data-md-color-primary=light-blue] .md-nav--primary .md-nav__title--site{background-color:#03a9f4}html [data-md-color-primary=cyan] .md-nav--primary .md-nav__title--site{background-color:#00bcd4}html [data-md-color-primary=teal] .md-nav--primary .md-nav__title--site{background-color:#009688}html [data-md-color-primary=green] .md-nav--primary .md-nav__title--site{background-color:#4caf50}html [data-md-color-primary=light-green] .md-nav--primary .md-nav__title--site{background-color:#7cb342}html [data-md-color-primary=lime] .md-nav--primary .md-nav__title--site{background-color:#c0ca33}html [data-md-color-primary=yellow] .md-nav--primary .md-nav__title--site{background-color:#f9a825}html [data-md-color-primary=amber] .md-nav--primary .md-nav__title--site{background-color:#ffa000}html [data-md-color-primary=orange] .md-nav--primary .md-nav__title--site{background-color:#fb8c00}html [data-md-color-primary=deep-orange] .md-nav--primary .md-nav__title--site{background-color:#ff7043}html [data-md-color-primary=brown] .md-nav--primary .md-nav__title--site{background-color:#795548}html [data-md-color-primary=grey] .md-nav--primary .md-nav__title--site{background-color:#757575}html [data-md-color-primary=blue-grey] .md-nav--primary .md-nav__title--site{background-color:#546e7a}html [data-md-color-primary=white] .md-nav--primary .md-nav__title--site{background-color:#fff;color:rgba(0,0,0,.87)}[data-md-color-primary=white] .md-hero{border-bottom:.05rem solid rgba(0,0,0,.07)}html [data-md-color-primary=black] .md-nav--primary .md-nav__title--site{background-color:#000}}@media only screen and (min-width:76.25em){[data-md-color-primary=red] .md-tabs{background-color:#ef5350}[data-md-color-primary=pink] .md-tabs{background-color:#e91e63}[data-md-color-primary=purple] .md-tabs{background-color:#ab47bc}[data-md-color-primary=deep-purple] .md-tabs{background-color:#7e57c2}[data-md-color-primary=indigo] .md-tabs{background-color:#3f51b5}[data-md-color-primary=blue] .md-tabs{background-color:#2196f3}[data-md-color-primary=light-blue] .md-tabs{background-color:#03a9f4}[data-md-color-primary=cyan] .md-tabs{background-color:#00bcd4}[data-md-color-primary=teal] .md-tabs{background-color:#009688}[data-md-color-primary=green] .md-tabs{background-color:#4caf50}[data-md-color-primary=light-green] .md-tabs{background-color:#7cb342}[data-md-color-primary=lime] .md-tabs{background-color:#c0ca33}[data-md-color-primary=yellow] .md-tabs{background-color:#f9a825}[data-md-color-primary=amber] .md-tabs{background-color:#ffa000}[data-md-color-primary=orange] .md-tabs{background-color:#fb8c00}[data-md-color-primary=deep-orange] .md-tabs{background-color:#ff7043}[data-md-color-primary=brown] .md-tabs{background-color:#795548}[data-md-color-primary=grey] .md-tabs{background-color:#757575}[data-md-color-primary=blue-grey] .md-tabs{background-color:#546e7a}[data-md-color-primary=white] .md-tabs{border-bottom:.05rem solid rgba(0,0,0,.07);background-color:#fff;color:rgba(0,0,0,.87)}[data-md-color-primary=black] .md-tabs{background-color:#000}}@media only screen and (min-width:60em){[data-md-color-primary=white] .md-search__input{background-color:rgba(0,0,0,.07)}[data-md-color-primary=white] .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input::-moz-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input:-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input::-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input::placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=black] .md-search__input{background-color:hsla(0,0%,100%,.3)}} \ No newline at end of file diff --git a/docs/assets/stylesheets/application.1b62728e.css b/docs/assets/stylesheets/application.1b62728e.css deleted file mode 100644 index 3840dd13..00000000 --- a/docs/assets/stylesheets/application.1b62728e.css +++ /dev/null @@ -1 +0,0 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none}body{margin:0}hr{overflow:visible;box-sizing:content-box}a{-webkit-text-decoration-skip:objects}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}small,sub,sup{font-size:80%}sub,sup{position:relative;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}table{border-collapse:separate;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{margin:0;padding:0;border:0;outline-style:none;background:transparent;font-size:inherit}input{border:0;outline:0}.md-clipboard:before,.md-icon,.md-nav__button,.md-nav__link:after,.md-nav__title:before,.md-search-result__article--document:before,.md-source-file:before,.md-typeset .admonition>.admonition-title:before,.md-typeset .admonition>summary:before,.md-typeset .critic.comment:before,.md-typeset .footnote-backref,.md-typeset .task-list-control .task-list-indicator:before,.md-typeset details>.admonition-title:before,.md-typeset details>summary:before,.md-typeset summary:after{font-family:Material Icons;font-style:normal;font-variant:normal;font-weight:400;line-height:1;text-transform:none;white-space:nowrap;speak:none;word-wrap:normal;direction:ltr}.md-content__icon,.md-footer-nav__button,.md-header-nav__button,.md-nav__button,.md-nav__title:before,.md-search-result__article--document:before{display:inline-block;margin:.2rem;padding:.4rem;font-size:1.2rem;cursor:pointer}.md-icon--arrow-back:before{content:""}.md-icon--arrow-forward:before{content:""}.md-icon--menu:before{content:""}.md-icon--search:before{content:""}[dir=rtl] .md-icon--arrow-back:before{content:""}[dir=rtl] .md-icon--arrow-forward:before{content:""}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{color:rgba(0,0,0,.87);font-feature-settings:"kern","liga";font-family:Helvetica Neue,Helvetica,Arial,sans-serif}code,kbd,pre{color:rgba(0,0,0,.87);font-feature-settings:"kern";font-family:Courier New,Courier,monospace}.md-typeset{font-size:.8rem;line-height:1.6;-webkit-print-color-adjust:exact}.md-typeset blockquote,.md-typeset ol,.md-typeset p,.md-typeset ul{margin:1em 0}.md-typeset h1{margin:0 0 2rem;color:rgba(0,0,0,.54);font-size:1.5625rem;line-height:1.3}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{margin:2rem 0 .8rem;font-size:1.25rem;line-height:1.4}.md-typeset h3{margin:1.6rem 0 .8rem;font-size:1rem;font-weight:400;letter-spacing:-.01em;line-height:1.5}.md-typeset h2+h3{margin-top:.8rem}.md-typeset h4{font-size:.8rem}.md-typeset h4,.md-typeset h5,.md-typeset h6{margin:.8rem 0;font-weight:700;letter-spacing:-.01em}.md-typeset h5,.md-typeset h6{color:rgba(0,0,0,.54);font-size:.64rem}.md-typeset h5{text-transform:uppercase}.md-typeset hr{margin:1.5em 0;border-bottom:.05rem dotted rgba(0,0,0,.26)}.md-typeset a{color:#3f51b5;word-break:break-word}.md-typeset a,.md-typeset a:before{-webkit-transition:color .125s;transition:color .125s}.md-typeset a:active,.md-typeset a:hover{color:#536dfe}.md-typeset code,.md-typeset pre{background-color:hsla(0,0%,92.5%,.5);color:#37474f;font-size:85%;direction:ltr}.md-typeset code{margin:0 .29412em;padding:.07353em 0;border-radius:.1rem;box-shadow:.29412em 0 0 hsla(0,0%,92.5%,.5),-.29412em 0 0 hsla(0,0%,92.5%,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{margin:0;background-color:transparent;box-shadow:none}.md-typeset a>code{margin:inherit;padding:inherit;border-radius:initial;background-color:inherit;color:inherit;box-shadow:none}.md-typeset pre{position:relative;margin:1em 0;border-radius:.1rem;line-height:1.4;-webkit-overflow-scrolling:touch}.md-typeset pre>code{display:block;margin:0;padding:.525rem .6rem;background-color:transparent;font-size:inherit;box-shadow:none;-webkit-box-decoration-break:slice;box-decoration-break:slice;overflow:auto}.md-typeset pre>code::-webkit-scrollbar{width:.2rem;height:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset kbd{padding:0 .29412em;border-radius:.15rem;border:.05rem solid #c9c9c9;border-bottom-color:#bcbcbc;background-color:#fcfcfc;color:#555;font-size:85%;box-shadow:0 .05rem 0 #b0b0b0;word-break:break-word}.md-typeset mark{margin:0 .25em;padding:.0625em 0;border-radius:.1rem;background-color:rgba(255,235,59,.5);box-shadow:.25em 0 0 rgba(255,235,59,.5),-.25em 0 0 rgba(255,235,59,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset abbr{border-bottom:.05rem dotted rgba(0,0,0,.54);text-decoration:none;cursor:help}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.07812em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.07812em;margin-left:0}.md-typeset blockquote{padding-left:.6rem;border-left:.2rem solid rgba(0,0,0,.26);color:rgba(0,0,0,.54)}[dir=rtl] .md-typeset blockquote{padding-right:.6rem;padding-left:0;border-right:.2rem solid rgba(0,0,0,.26);border-left:initial}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{margin-left:.625em;padding:0}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em;margin-left:0}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em;margin-left:0}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em;margin-left:0}.md-typeset dd{margin:1em 0 1em 1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em;margin-left:0}.md-typeset iframe,.md-typeset img,.md-typeset svg{max-width:100%}.md-typeset table:not([class]){box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);display:inline-block;max-width:100%;border-radius:.1rem;font-size:.64rem;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{min-width:5rem;padding:.6rem .8rem;background-color:rgba(0,0,0,.54);color:#fff;vertical-align:top}.md-typeset table:not([class]) td{padding:.6rem .8rem;border-top:.05rem solid rgba(0,0,0,.07);vertical-align:top}.md-typeset table:not([class]) tr{-webkit-transition:background-color .125s;transition:background-color .125s}.md-typeset table:not([class]) tr:hover{background-color:rgba(0,0,0,.035);box-shadow:inset 0 .05rem 0 #fff}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;-webkit-overflow-scrolling:touch}.md-typeset .md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}.md-typeset .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{font-size:125%;overflow-x:hidden}body,html{height:100%}body{position:relative;font-size:.5rem}hr{display:block;height:.05rem;padding:0;border:0}.md-svg{display:none}.md-grid{max-width:61rem;margin-right:auto;margin-left:auto}.md-container,.md-main{overflow:auto}.md-container{display:table;width:100%;height:100%;padding-top:2.4rem;table-layout:fixed}.md-main{display:table-row;height:100%}.md-main__inner{height:100%;padding-top:1.5rem;padding-bottom:.05rem}.md-toggle{display:none}.md-overlay{position:fixed;top:0;width:0;height:0;-webkit-transition:width 0s .25s,height 0s .25s,opacity .25s;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);opacity:0;z-index:3}.md-flex{display:table}.md-flex__cell{display:table-cell;position:relative;vertical-align:top}.md-flex__cell--shrink{width:0}.md-flex__cell--stretch{display:table;width:100%;table-layout:fixed}.md-flex__ellipsis{display:table-cell;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md-skip{position:fixed;width:.05rem;height:.05rem;margin:.5rem;padding:.3rem .5rem;-webkit-transform:translateY(.4rem);transform:translateY(.4rem);border-radius:.1rem;background-color:rgba(0,0,0,.87);color:#fff;font-size:.64rem;opacity:0;overflow:hidden}.md-skip:focus{width:auto;height:auto;clip:auto;-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1;z-index:10}@page{margin:25mm}.md-clipboard{position:absolute;top:.3rem;right:.3rem;width:1.4rem;height:1.4rem;border-radius:.1rem;font-size:.8rem;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-clipboard:before{-webkit-transition:color .25s,opacity .25s;transition:color .25s,opacity .25s;color:rgba(0,0,0,.07);content:"\E14D"}.codehilite:hover .md-clipboard:before,.md-typeset .highlight:hover .md-clipboard:before,pre:hover .md-clipboard:before{color:rgba(0,0,0,.54)}.md-clipboard:focus:before,.md-clipboard:hover:before{color:#536dfe}.md-clipboard__message{display:block;position:absolute;top:0;right:1.7rem;padding:.3rem .5rem;-webkit-transform:translateX(.4rem);transform:translateX(.4rem);-webkit-transition:opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);transition:opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s;transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);border-radius:.1rem;background-color:rgba(0,0,0,.54);color:#fff;font-size:.64rem;white-space:nowrap;opacity:0;pointer-events:none}.md-clipboard__message--active{-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1;pointer-events:auto}.md-clipboard__message:before{content:attr(aria-label)}.md-clipboard__message:after{display:block;position:absolute;top:50%;right:-.2rem;width:0;margin-top:-.2rem;border-color:transparent rgba(0,0,0,.54);border-style:solid;border-width:.2rem 0 .2rem .2rem;content:""}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}.md-content__inner:before{display:block;height:.4rem;content:""}.md-content__inner>:last-child{margin-bottom:0}.md-content__icon{position:relative;margin:.4rem 0;padding:0;float:right}.md-typeset .md-content__icon{color:rgba(0,0,0,.26)}.md-header{position:fixed;top:0;right:0;left:0;height:2.4rem;-webkit-transition:background-color .25s,color .25s;transition:background-color .25s,color .25s;background-color:#3f51b5;color:#fff;box-shadow:none;z-index:2;-webkit-backface-visibility:hidden;backface-visibility:hidden}.no-js .md-header{-webkit-transition:none;transition:none;box-shadow:none}.md-header[data-md-state=shadow]{-webkit-transition:background-color .25s,color .25s,box-shadow .25s;transition:background-color .25s,color .25s,box-shadow .25s;box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2)}.md-header-nav{padding:0 .2rem}.md-header-nav__button{position:relative;-webkit-transition:opacity .25s;transition:opacity .25s;z-index:1}.md-header-nav__button:hover{opacity:.7}.md-header-nav__button.md-logo *{display:block}.no-js .md-header-nav__button.md-icon--search{display:none}.md-header-nav__topic{display:block;position:absolute;-webkit-transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(1.25rem);transform:translateX(1.25rem);-webkit-transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);opacity:0;z-index:-1;pointer-events:none}[dir=rtl] .md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(-1.25rem);transform:translateX(-1.25rem)}.no-js .md-header-nav__topic{position:static}.no-js .md-header-nav__topic+.md-header-nav__topic{display:none}.md-header-nav__title{padding:0 1rem;font-size:.9rem;line-height:2.4rem}.md-header-nav__title[data-md-state=active] .md-header-nav__topic{-webkit-transform:translateX(-1.25rem);transform:translateX(-1.25rem);-webkit-transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);opacity:0;z-index:-1;pointer-events:none}[dir=rtl] .md-header-nav__title[data-md-state=active] .md-header-nav__topic{-webkit-transform:translateX(1.25rem);transform:translateX(1.25rem)}.md-header-nav__title[data-md-state=active] .md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);opacity:1;z-index:0;pointer-events:auto}.md-header-nav__source{display:none}.md-hero{-webkit-transition:background .25s;transition:background .25s;background-color:#3f51b5;color:#fff;font-size:1rem;overflow:hidden}.md-hero__inner{margin-top:1rem;padding:.8rem .8rem .4rem;-webkit-transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);-webkit-transition-delay:.1s;transition-delay:.1s}[data-md-state=hidden] .md-hero__inner{pointer-events:none;-webkit-transform:translateY(.625rem);transform:translateY(.625rem);-webkit-transition:opacity .1s 0s,-webkit-transform 0s .4s;transition:opacity .1s 0s,-webkit-transform 0s .4s;transition:transform 0s .4s,opacity .1s 0s;transition:transform 0s .4s,opacity .1s 0s,-webkit-transform 0s .4s;opacity:0}.md-hero--expand .md-hero__inner{margin-bottom:1.2rem}.md-footer-nav{background-color:rgba(0,0,0,.87);color:#fff}.md-footer-nav__inner{padding:.2rem;overflow:auto}.md-footer-nav__link{padding-top:1.4rem;padding-bottom:.4rem;-webkit-transition:opacity .25s;transition:opacity .25s}.md-footer-nav__link:hover{opacity:.7}.md-footer-nav__link--prev{width:25%;float:left}[dir=rtl] .md-footer-nav__link--prev{float:right}.md-footer-nav__link--next{width:75%;float:right;text-align:right}[dir=rtl] .md-footer-nav__link--next{float:left;text-align:left}.md-footer-nav__button{-webkit-transition:background .25s;transition:background .25s}.md-footer-nav__title{position:relative;padding:0 1rem;font-size:.9rem;line-height:2.4rem}.md-footer-nav__direction{position:absolute;right:0;left:0;margin-top:-1rem;padding:0 1rem;color:hsla(0,0%,100%,.7);font-size:.75rem}.md-footer-meta{background-color:rgba(0,0,0,.895)}.md-footer-meta__inner{padding:.2rem;overflow:auto}html .md-footer-meta.md-typeset a{color:hsla(0,0%,100%,.7)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:#fff}.md-footer-copyright{margin:0 .6rem;padding:.4rem 0;color:hsla(0,0%,100%,.3);font-size:.64rem}.md-footer-copyright__highlight{color:hsla(0,0%,100%,.7)}.md-footer-social{margin:0 .4rem;padding:.2rem 0 .6rem}.md-footer-social__link{display:inline-block;width:1.6rem;height:1.6rem;font-size:.8rem;text-align:center}.md-footer-social__link:before{line-height:1.9}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{display:block;padding:0 .6rem;font-weight:700;text-overflow:ellipsis;overflow:hidden}.md-nav__title:before{display:none;content:"\E5C4"}[dir=rtl] .md-nav__title:before{content:"\E5C8"}.md-nav__title .md-nav__button{display:none}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 .6rem}.md-nav__item:last-child{padding-bottom:.6rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-right:.6rem;padding-left:0}.md-nav__item .md-nav__item:last-child{padding-bottom:0}.md-nav__button img{width:100%;height:auto}.md-nav__link{display:block;margin-top:.625em;-webkit-transition:color .125s;transition:color .125s;text-overflow:ellipsis;cursor:pointer;overflow:hidden}.md-nav__item--nested>.md-nav__link:after{content:"\E313"}html .md-nav__link[for=__toc],html .md-nav__link[for=__toc]+.md-nav__link:after,html .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__link[data-md-state=blur]{color:rgba(0,0,0,.54)}.md-nav__link--active,.md-nav__link:active{color:#3f51b5}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:#536dfe}.md-nav__source,.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}.md-search__form{position:relative}.md-search__input{position:relative;padding:0 2.2rem 0 3.6rem;text-overflow:ellipsis;z-index:2}[dir=rtl] .md-search__input{padding:0 3.6rem 0 2.2rem}.md-search__input::-webkit-input-placeholder{-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::-moz-placeholder{-moz-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input:-ms-input-placeholder{-ms-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::-ms-input-placeholder{-ms-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::placeholder{-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}.md-search__input::-moz-placeholder{color:rgba(0,0,0,.54)}.md-search__input:-ms-input-placeholder{color:rgba(0,0,0,.54)}.md-search__input::-ms-input-placeholder{color:rgba(0,0,0,.54)}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::-ms-clear{display:none}.md-search__icon{position:absolute;-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:color .25s cubic-bezier(.1,.7,.1,1),opacity .25s;font-size:1.2rem;cursor:pointer;z-index:2}.md-search__icon:hover{opacity:.7}.md-search__icon[for=__search]{top:.3rem;left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem;left:auto}.md-search__icon[for=__search]:before{content:"\E8B6"}.md-search__icon[type=reset]{top:.3rem;right:.5rem;-webkit-transform:scale(.125);transform:scale(.125);-webkit-transition:opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);transition:opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);opacity:0}[dir=rtl] .md-search__icon[type=reset]{right:auto;left:.5rem}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]{-webkit-transform:scale(1);transform:scale(1);opacity:1}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]:hover{opacity:.7}.md-search__output{position:absolute;width:100%;border-radius:0 0 .1rem .1rem;overflow:hidden;z-index:1}.md-search__scrollwrap{height:100%;background-color:#fff;box-shadow:inset 0 .05rem 0 rgba(0,0,0,.07);overflow-y:auto;-webkit-overflow-scrolling:touch}.md-search-result{color:rgba(0,0,0,.87);word-break:break-word}.md-search-result__meta{padding:0 .8rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-size:.64rem;line-height:1.8rem}.md-search-result__list{margin:0;padding:0;border-top:.05rem solid rgba(0,0,0,.07);list-style:none}.md-search-result__item{box-shadow:0 -.05rem 0 rgba(0,0,0,.07)}.md-search-result__link{display:block;-webkit-transition:background .25s;transition:background .25s;outline:0;overflow:hidden}.md-search-result__link:hover,.md-search-result__link[data-md-state=active]{background-color:rgba(83,109,254,.1)}.md-search-result__link:hover .md-search-result__article:before,.md-search-result__link[data-md-state=active] .md-search-result__article:before{opacity:.7}.md-search-result__link:last-child .md-search-result__teaser{margin-bottom:.6rem}.md-search-result__article{position:relative;padding:0 .8rem;overflow:auto}.md-search-result__article--document:before{position:absolute;left:0;margin:.1rem;-webkit-transition:opacity .25s;transition:opacity .25s;color:rgba(0,0,0,.54);content:"\E880"}[dir=rtl] .md-search-result__article--document:before{right:0;left:auto}.md-search-result__article--document .md-search-result__title{margin:.55rem 0;font-size:.8rem;font-weight:400;line-height:1.4}.md-search-result__title{margin:.5em 0;font-size:.64rem;font-weight:700;line-height:1.4}.md-search-result__teaser{display:-webkit-box;max-height:1.65rem;margin:.5em 0;color:rgba(0,0,0,.54);font-size:.64rem;line-height:1.4;text-overflow:ellipsis;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2}.md-search-result em{font-style:normal;font-weight:700;text-decoration:underline}.md-sidebar{position:absolute;width:12.1rem;padding:1.2rem 0;overflow:hidden}.md-sidebar[data-md-state=lock]{position:fixed;top:2.4rem}.md-sidebar--secondary{display:none}.md-sidebar__scrollwrap{max-height:100%;margin:0 .2rem;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}@-webkit-keyframes md-source__facts--done{0%{height:0}to{height:.65rem}}@keyframes md-source__facts--done{0%{height:0}to{height:.65rem}}@-webkit-keyframes md-source__fact--done{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes md-source__fact--done{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}.md-source{display:block;padding-right:.6rem;-webkit-transition:opacity .25s;transition:opacity .25s;font-size:.65rem;line-height:1.2;white-space:nowrap}[dir=rtl] .md-source{padding-right:0;padding-left:.6rem}.md-source:hover{opacity:.7}.md-source:after,.md-source__icon{display:inline-block;height:2.4rem;content:"";vertical-align:middle}.md-source__icon{width:2.4rem}.md-source__icon svg{width:1.2rem;height:1.2rem;margin-top:.6rem;margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem;margin-left:0}.md-source__icon+.md-source__repository{margin-left:-2rem;padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem;margin-left:0;padding-right:2rem;padding-left:0}.md-source__repository{display:inline-block;max-width:100%;margin-left:.6rem;font-weight:700;text-overflow:ellipsis;overflow:hidden;vertical-align:middle}.md-source__facts{margin:0;padding:0;font-size:.55rem;font-weight:700;list-style-type:none;opacity:.75;overflow:hidden}[data-md-state=done] .md-source__facts{-webkit-animation:md-source__facts--done .25s ease-in;animation:md-source__facts--done .25s ease-in}.md-source__fact{float:left}[dir=rtl] .md-source__fact{float:right}[data-md-state=done] .md-source__fact{-webkit-animation:md-source__fact--done .4s ease-out;animation:md-source__fact--done .4s ease-out}.md-source__fact:before{margin:0 .1rem;content:"\00B7"}.md-source__fact:first-child:before{display:none}.md-source-file{display:inline-block;margin:1em .5em 1em 0;padding-right:.25rem;border-radius:.1rem;background-color:rgba(0,0,0,.07);font-size:.64rem;list-style-type:none;cursor:pointer;overflow:hidden}.md-source-file:before{display:inline-block;margin-right:.25rem;padding:.25rem;background-color:rgba(0,0,0,.26);color:#fff;font-size:.8rem;content:"\E86F";vertical-align:middle}html .md-source-file{-webkit-transition:background .4s,color .4s,box-shadow .4s cubic-bezier(.4,0,.2,1);transition:background .4s,color .4s,box-shadow .4s cubic-bezier(.4,0,.2,1)}html .md-source-file:before{-webkit-transition:inherit;transition:inherit}html body .md-typeset .md-source-file{color:rgba(0,0,0,.54)}.md-source-file:hover{box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36)}.md-source-file:hover:before{background-color:#536dfe}.md-tabs{width:100%;-webkit-transition:background .25s;transition:background .25s;background-color:#3f51b5;color:#fff;overflow:auto}.md-tabs__list{margin:0 0 0 .2rem;padding:0;list-style:none;white-space:nowrap}.md-tabs__item{display:inline-block;height:2.4rem;padding-right:.6rem;padding-left:.6rem}.md-tabs__link{display:block;margin-top:.8rem;-webkit-transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);font-size:.7rem;opacity:.7}.md-tabs__link--active,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{-webkit-transition-delay:.02s;transition-delay:.02s}.md-tabs__item:nth-child(3) .md-tabs__link{-webkit-transition-delay:.04s;transition-delay:.04s}.md-tabs__item:nth-child(4) .md-tabs__link{-webkit-transition-delay:.06s;transition-delay:.06s}.md-tabs__item:nth-child(5) .md-tabs__link{-webkit-transition-delay:.08s;transition-delay:.08s}.md-tabs__item:nth-child(6) .md-tabs__link{-webkit-transition-delay:.1s;transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{-webkit-transition-delay:.12s;transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{-webkit-transition-delay:.14s;transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{-webkit-transition-delay:.16s;transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{-webkit-transition-delay:.18s;transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{-webkit-transition-delay:.2s;transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{-webkit-transition-delay:.22s;transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{-webkit-transition-delay:.24s;transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{-webkit-transition-delay:.26s;transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{-webkit-transition-delay:.28s;transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{-webkit-transition-delay:.3s;transition-delay:.3s}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs[data-md-state=hidden] .md-tabs__link{-webkit-transform:translateY(50%);transform:translateY(50%);-webkit-transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,transform 0s .4s,opacity .1s;transition:color .25s,transform 0s .4s,opacity .1s,-webkit-transform 0s .4s;opacity:0}.md-typeset .admonition,.md-typeset details{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:relative;margin:1.5625em 0;padding:0 .6rem;border-left:.2rem solid #448aff;border-radius:.1rem;font-size:.64rem;overflow:auto}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-right:.2rem solid #448aff;border-left:none}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin:1em 0}.md-typeset .admonition>.admonition-title,.md-typeset .admonition>summary,.md-typeset details>.admonition-title,.md-typeset details>summary{margin:0 -.6rem;padding:.4rem .6rem .4rem 2rem;border-bottom:.05rem solid rgba(68,138,255,.1);background-color:rgba(68,138,255,.1);font-weight:700}[dir=rtl] .md-typeset .admonition>.admonition-title,[dir=rtl] .md-typeset .admonition>summary,[dir=rtl] .md-typeset details>.admonition-title,[dir=rtl] .md-typeset details>summary{padding:.4rem 2rem .4rem .6rem}.md-typeset .admonition>.admonition-title:last-child,.md-typeset .admonition>summary:last-child,.md-typeset details>.admonition-title:last-child,.md-typeset details>summary:last-child{margin-bottom:0}.md-typeset .admonition>.admonition-title:before,.md-typeset .admonition>summary:before,.md-typeset details>.admonition-title:before,.md-typeset details>summary:before{position:absolute;left:.6rem;color:#448aff;font-size:1rem;content:"\E3C9"}[dir=rtl] .md-typeset .admonition>.admonition-title:before,[dir=rtl] .md-typeset .admonition>summary:before,[dir=rtl] .md-typeset details>.admonition-title:before,[dir=rtl] .md-typeset details>summary:before{right:.6rem;left:auto}.md-typeset .admonition.abstract,.md-typeset .admonition.summary,.md-typeset .admonition.tldr,.md-typeset details.abstract,.md-typeset details.summary,.md-typeset details.tldr{border-left-color:#00b0ff}[dir=rtl] .md-typeset .admonition.abstract,[dir=rtl] .md-typeset .admonition.summary,[dir=rtl] .md-typeset .admonition.tldr,[dir=rtl] .md-typeset details.abstract,[dir=rtl] .md-typeset details.summary,[dir=rtl] .md-typeset details.tldr{border-right-color:#00b0ff}.md-typeset .admonition.abstract>.admonition-title,.md-typeset .admonition.abstract>summary,.md-typeset .admonition.summary>.admonition-title,.md-typeset .admonition.summary>summary,.md-typeset .admonition.tldr>.admonition-title,.md-typeset .admonition.tldr>summary,.md-typeset details.abstract>.admonition-title,.md-typeset details.abstract>summary,.md-typeset details.summary>.admonition-title,.md-typeset details.summary>summary,.md-typeset details.tldr>.admonition-title,.md-typeset details.tldr>summary{border-bottom-color:rgba(0,176,255,.1);background-color:rgba(0,176,255,.1)}.md-typeset .admonition.abstract>.admonition-title:before,.md-typeset .admonition.abstract>summary:before,.md-typeset .admonition.summary>.admonition-title:before,.md-typeset .admonition.summary>summary:before,.md-typeset .admonition.tldr>.admonition-title:before,.md-typeset .admonition.tldr>summary:before,.md-typeset details.abstract>.admonition-title:before,.md-typeset details.abstract>summary:before,.md-typeset details.summary>.admonition-title:before,.md-typeset details.summary>summary:before,.md-typeset details.tldr>.admonition-title:before,.md-typeset details.tldr>summary:before{color:#00b0ff;content:""}.md-typeset .admonition.info,.md-typeset .admonition.todo,.md-typeset details.info,.md-typeset details.todo{border-left-color:#00b8d4}[dir=rtl] .md-typeset .admonition.info,[dir=rtl] .md-typeset .admonition.todo,[dir=rtl] .md-typeset details.info,[dir=rtl] .md-typeset details.todo{border-right-color:#00b8d4}.md-typeset .admonition.info>.admonition-title,.md-typeset .admonition.info>summary,.md-typeset .admonition.todo>.admonition-title,.md-typeset .admonition.todo>summary,.md-typeset details.info>.admonition-title,.md-typeset details.info>summary,.md-typeset details.todo>.admonition-title,.md-typeset details.todo>summary{border-bottom-color:rgba(0,184,212,.1);background-color:rgba(0,184,212,.1)}.md-typeset .admonition.info>.admonition-title:before,.md-typeset .admonition.info>summary:before,.md-typeset .admonition.todo>.admonition-title:before,.md-typeset .admonition.todo>summary:before,.md-typeset details.info>.admonition-title:before,.md-typeset details.info>summary:before,.md-typeset details.todo>.admonition-title:before,.md-typeset details.todo>summary:before{color:#00b8d4;content:""}.md-typeset .admonition.hint,.md-typeset .admonition.important,.md-typeset .admonition.tip,.md-typeset details.hint,.md-typeset details.important,.md-typeset details.tip{border-left-color:#00bfa5}[dir=rtl] .md-typeset .admonition.hint,[dir=rtl] .md-typeset .admonition.important,[dir=rtl] .md-typeset .admonition.tip,[dir=rtl] .md-typeset details.hint,[dir=rtl] .md-typeset details.important,[dir=rtl] .md-typeset details.tip{border-right-color:#00bfa5}.md-typeset .admonition.hint>.admonition-title,.md-typeset .admonition.hint>summary,.md-typeset .admonition.important>.admonition-title,.md-typeset .admonition.important>summary,.md-typeset .admonition.tip>.admonition-title,.md-typeset .admonition.tip>summary,.md-typeset details.hint>.admonition-title,.md-typeset details.hint>summary,.md-typeset details.important>.admonition-title,.md-typeset details.important>summary,.md-typeset details.tip>.admonition-title,.md-typeset details.tip>summary{border-bottom-color:rgba(0,191,165,.1);background-color:rgba(0,191,165,.1)}.md-typeset .admonition.hint>.admonition-title:before,.md-typeset .admonition.hint>summary:before,.md-typeset .admonition.important>.admonition-title:before,.md-typeset .admonition.important>summary:before,.md-typeset .admonition.tip>.admonition-title:before,.md-typeset .admonition.tip>summary:before,.md-typeset details.hint>.admonition-title:before,.md-typeset details.hint>summary:before,.md-typeset details.important>.admonition-title:before,.md-typeset details.important>summary:before,.md-typeset details.tip>.admonition-title:before,.md-typeset details.tip>summary:before{color:#00bfa5;content:""}.md-typeset .admonition.check,.md-typeset .admonition.done,.md-typeset .admonition.success,.md-typeset details.check,.md-typeset details.done,.md-typeset details.success{border-left-color:#00c853}[dir=rtl] .md-typeset .admonition.check,[dir=rtl] .md-typeset .admonition.done,[dir=rtl] .md-typeset .admonition.success,[dir=rtl] .md-typeset details.check,[dir=rtl] .md-typeset details.done,[dir=rtl] .md-typeset details.success{border-right-color:#00c853}.md-typeset .admonition.check>.admonition-title,.md-typeset .admonition.check>summary,.md-typeset .admonition.done>.admonition-title,.md-typeset .admonition.done>summary,.md-typeset .admonition.success>.admonition-title,.md-typeset .admonition.success>summary,.md-typeset details.check>.admonition-title,.md-typeset details.check>summary,.md-typeset details.done>.admonition-title,.md-typeset details.done>summary,.md-typeset details.success>.admonition-title,.md-typeset details.success>summary{border-bottom-color:rgba(0,200,83,.1);background-color:rgba(0,200,83,.1)}.md-typeset .admonition.check>.admonition-title:before,.md-typeset .admonition.check>summary:before,.md-typeset .admonition.done>.admonition-title:before,.md-typeset .admonition.done>summary:before,.md-typeset .admonition.success>.admonition-title:before,.md-typeset .admonition.success>summary:before,.md-typeset details.check>.admonition-title:before,.md-typeset details.check>summary:before,.md-typeset details.done>.admonition-title:before,.md-typeset details.done>summary:before,.md-typeset details.success>.admonition-title:before,.md-typeset details.success>summary:before{color:#00c853;content:""}.md-typeset .admonition.faq,.md-typeset .admonition.help,.md-typeset .admonition.question,.md-typeset details.faq,.md-typeset details.help,.md-typeset details.question{border-left-color:#64dd17}[dir=rtl] .md-typeset .admonition.faq,[dir=rtl] .md-typeset .admonition.help,[dir=rtl] .md-typeset .admonition.question,[dir=rtl] .md-typeset details.faq,[dir=rtl] .md-typeset details.help,[dir=rtl] .md-typeset details.question{border-right-color:#64dd17}.md-typeset .admonition.faq>.admonition-title,.md-typeset .admonition.faq>summary,.md-typeset .admonition.help>.admonition-title,.md-typeset .admonition.help>summary,.md-typeset .admonition.question>.admonition-title,.md-typeset .admonition.question>summary,.md-typeset details.faq>.admonition-title,.md-typeset details.faq>summary,.md-typeset details.help>.admonition-title,.md-typeset details.help>summary,.md-typeset details.question>.admonition-title,.md-typeset details.question>summary{border-bottom-color:rgba(100,221,23,.1);background-color:rgba(100,221,23,.1)}.md-typeset .admonition.faq>.admonition-title:before,.md-typeset .admonition.faq>summary:before,.md-typeset .admonition.help>.admonition-title:before,.md-typeset .admonition.help>summary:before,.md-typeset .admonition.question>.admonition-title:before,.md-typeset .admonition.question>summary:before,.md-typeset details.faq>.admonition-title:before,.md-typeset details.faq>summary:before,.md-typeset details.help>.admonition-title:before,.md-typeset details.help>summary:before,.md-typeset details.question>.admonition-title:before,.md-typeset details.question>summary:before{color:#64dd17;content:""}.md-typeset .admonition.attention,.md-typeset .admonition.caution,.md-typeset .admonition.warning,.md-typeset details.attention,.md-typeset details.caution,.md-typeset details.warning{border-left-color:#ff9100}[dir=rtl] .md-typeset .admonition.attention,[dir=rtl] .md-typeset .admonition.caution,[dir=rtl] .md-typeset .admonition.warning,[dir=rtl] .md-typeset details.attention,[dir=rtl] .md-typeset details.caution,[dir=rtl] .md-typeset details.warning{border-right-color:#ff9100}.md-typeset .admonition.attention>.admonition-title,.md-typeset .admonition.attention>summary,.md-typeset .admonition.caution>.admonition-title,.md-typeset .admonition.caution>summary,.md-typeset .admonition.warning>.admonition-title,.md-typeset .admonition.warning>summary,.md-typeset details.attention>.admonition-title,.md-typeset details.attention>summary,.md-typeset details.caution>.admonition-title,.md-typeset details.caution>summary,.md-typeset details.warning>.admonition-title,.md-typeset details.warning>summary{border-bottom-color:rgba(255,145,0,.1);background-color:rgba(255,145,0,.1)}.md-typeset .admonition.attention>.admonition-title:before,.md-typeset .admonition.attention>summary:before,.md-typeset .admonition.caution>.admonition-title:before,.md-typeset .admonition.caution>summary:before,.md-typeset .admonition.warning>.admonition-title:before,.md-typeset .admonition.warning>summary:before,.md-typeset details.attention>.admonition-title:before,.md-typeset details.attention>summary:before,.md-typeset details.caution>.admonition-title:before,.md-typeset details.caution>summary:before,.md-typeset details.warning>.admonition-title:before,.md-typeset details.warning>summary:before{color:#ff9100;content:""}.md-typeset .admonition.fail,.md-typeset .admonition.failure,.md-typeset .admonition.missing,.md-typeset details.fail,.md-typeset details.failure,.md-typeset details.missing{border-left-color:#ff5252}[dir=rtl] .md-typeset .admonition.fail,[dir=rtl] .md-typeset .admonition.failure,[dir=rtl] .md-typeset .admonition.missing,[dir=rtl] .md-typeset details.fail,[dir=rtl] .md-typeset details.failure,[dir=rtl] .md-typeset details.missing{border-right-color:#ff5252}.md-typeset .admonition.fail>.admonition-title,.md-typeset .admonition.fail>summary,.md-typeset .admonition.failure>.admonition-title,.md-typeset .admonition.failure>summary,.md-typeset .admonition.missing>.admonition-title,.md-typeset .admonition.missing>summary,.md-typeset details.fail>.admonition-title,.md-typeset details.fail>summary,.md-typeset details.failure>.admonition-title,.md-typeset details.failure>summary,.md-typeset details.missing>.admonition-title,.md-typeset details.missing>summary{border-bottom-color:rgba(255,82,82,.1);background-color:rgba(255,82,82,.1)}.md-typeset .admonition.fail>.admonition-title:before,.md-typeset .admonition.fail>summary:before,.md-typeset .admonition.failure>.admonition-title:before,.md-typeset .admonition.failure>summary:before,.md-typeset .admonition.missing>.admonition-title:before,.md-typeset .admonition.missing>summary:before,.md-typeset details.fail>.admonition-title:before,.md-typeset details.fail>summary:before,.md-typeset details.failure>.admonition-title:before,.md-typeset details.failure>summary:before,.md-typeset details.missing>.admonition-title:before,.md-typeset details.missing>summary:before{color:#ff5252;content:""}.md-typeset .admonition.danger,.md-typeset .admonition.error,.md-typeset details.danger,.md-typeset details.error{border-left-color:#ff1744}[dir=rtl] .md-typeset .admonition.danger,[dir=rtl] .md-typeset .admonition.error,[dir=rtl] .md-typeset details.danger,[dir=rtl] .md-typeset details.error{border-right-color:#ff1744}.md-typeset .admonition.danger>.admonition-title,.md-typeset .admonition.danger>summary,.md-typeset .admonition.error>.admonition-title,.md-typeset .admonition.error>summary,.md-typeset details.danger>.admonition-title,.md-typeset details.danger>summary,.md-typeset details.error>.admonition-title,.md-typeset details.error>summary{border-bottom-color:rgba(255,23,68,.1);background-color:rgba(255,23,68,.1)}.md-typeset .admonition.danger>.admonition-title:before,.md-typeset .admonition.danger>summary:before,.md-typeset .admonition.error>.admonition-title:before,.md-typeset .admonition.error>summary:before,.md-typeset details.danger>.admonition-title:before,.md-typeset details.danger>summary:before,.md-typeset details.error>.admonition-title:before,.md-typeset details.error>summary:before{color:#ff1744;content:""}.md-typeset .admonition.bug,.md-typeset details.bug{border-left-color:#f50057}[dir=rtl] .md-typeset .admonition.bug,[dir=rtl] .md-typeset details.bug{border-right-color:#f50057}.md-typeset .admonition.bug>.admonition-title,.md-typeset .admonition.bug>summary,.md-typeset details.bug>.admonition-title,.md-typeset details.bug>summary{border-bottom-color:rgba(245,0,87,.1);background-color:rgba(245,0,87,.1)}.md-typeset .admonition.bug>.admonition-title:before,.md-typeset .admonition.bug>summary:before,.md-typeset details.bug>.admonition-title:before,.md-typeset details.bug>summary:before{color:#f50057;content:""}.md-typeset .admonition.example,.md-typeset details.example{border-left-color:#651fff}[dir=rtl] .md-typeset .admonition.example,[dir=rtl] .md-typeset details.example{border-right-color:#651fff}.md-typeset .admonition.example>.admonition-title,.md-typeset .admonition.example>summary,.md-typeset details.example>.admonition-title,.md-typeset details.example>summary{border-bottom-color:rgba(101,31,255,.1);background-color:rgba(101,31,255,.1)}.md-typeset .admonition.example>.admonition-title:before,.md-typeset .admonition.example>summary:before,.md-typeset details.example>.admonition-title:before,.md-typeset details.example>summary:before{color:#651fff;content:""}.md-typeset .admonition.cite,.md-typeset .admonition.quote,.md-typeset details.cite,.md-typeset details.quote{border-left-color:#9e9e9e}[dir=rtl] .md-typeset .admonition.cite,[dir=rtl] .md-typeset .admonition.quote,[dir=rtl] .md-typeset details.cite,[dir=rtl] .md-typeset details.quote{border-right-color:#9e9e9e}.md-typeset .admonition.cite>.admonition-title,.md-typeset .admonition.cite>summary,.md-typeset .admonition.quote>.admonition-title,.md-typeset .admonition.quote>summary,.md-typeset details.cite>.admonition-title,.md-typeset details.cite>summary,.md-typeset details.quote>.admonition-title,.md-typeset details.quote>summary{border-bottom-color:hsla(0,0%,62%,.1);background-color:hsla(0,0%,62%,.1)}.md-typeset .admonition.cite>.admonition-title:before,.md-typeset .admonition.cite>summary:before,.md-typeset .admonition.quote>.admonition-title:before,.md-typeset .admonition.quote>summary:before,.md-typeset details.cite>.admonition-title:before,.md-typeset details.cite>summary:before,.md-typeset details.quote>.admonition-title:before,.md-typeset details.quote>summary:before{color:#9e9e9e;content:""}.codehilite .o,.codehilite .ow,.md-typeset .highlight .o,.md-typeset .highlight .ow{color:inherit}.codehilite .ge,.md-typeset .highlight .ge{color:#000}.codehilite .gr,.md-typeset .highlight .gr{color:#a00}.codehilite .gh,.md-typeset .highlight .gh{color:#999}.codehilite .go,.md-typeset .highlight .go{color:#888}.codehilite .gp,.md-typeset .highlight .gp{color:#555}.codehilite .gs,.md-typeset .highlight .gs{color:inherit}.codehilite .gu,.md-typeset .highlight .gu{color:#aaa}.codehilite .gt,.md-typeset .highlight .gt{color:#a00}.codehilite .gd,.md-typeset .highlight .gd{background-color:#fdd}.codehilite .gi,.md-typeset .highlight .gi{background-color:#dfd}.codehilite .k,.md-typeset .highlight .k{color:#3b78e7}.codehilite .kc,.md-typeset .highlight .kc{color:#a71d5d}.codehilite .kd,.codehilite .kn,.md-typeset .highlight .kd,.md-typeset .highlight .kn{color:#3b78e7}.codehilite .kp,.md-typeset .highlight .kp{color:#a71d5d}.codehilite .kr,.codehilite .kt,.md-typeset .highlight .kr,.md-typeset .highlight .kt{color:#3e61a2}.codehilite .c,.codehilite .cm,.md-typeset .highlight .c,.md-typeset .highlight .cm{color:#999}.codehilite .cp,.md-typeset .highlight .cp{color:#666}.codehilite .c1,.codehilite .ch,.codehilite .cs,.md-typeset .highlight .c1,.md-typeset .highlight .ch,.md-typeset .highlight .cs{color:#999}.codehilite .na,.codehilite .nb,.md-typeset .highlight .na,.md-typeset .highlight .nb{color:#c2185b}.codehilite .bp,.md-typeset .highlight .bp{color:#3e61a2}.codehilite .nc,.md-typeset .highlight .nc{color:#c2185b}.codehilite .no,.md-typeset .highlight .no{color:#3e61a2}.codehilite .nd,.codehilite .ni,.md-typeset .highlight .nd,.md-typeset .highlight .ni{color:#666}.codehilite .ne,.codehilite .nf,.md-typeset .highlight .ne,.md-typeset .highlight .nf{color:#c2185b}.codehilite .nl,.md-typeset .highlight .nl{color:#3b5179}.codehilite .nn,.md-typeset .highlight .nn{color:#ec407a}.codehilite .nt,.md-typeset .highlight .nt{color:#3b78e7}.codehilite .nv,.codehilite .vc,.codehilite .vg,.codehilite .vi,.md-typeset .highlight .nv,.md-typeset .highlight .vc,.md-typeset .highlight .vg,.md-typeset .highlight .vi{color:#3e61a2}.codehilite .nx,.md-typeset .highlight .nx{color:#ec407a}.codehilite .il,.codehilite .m,.codehilite .mf,.codehilite .mh,.codehilite .mi,.codehilite .mo,.md-typeset .highlight .il,.md-typeset .highlight .m,.md-typeset .highlight .mf,.md-typeset .highlight .mh,.md-typeset .highlight .mi,.md-typeset .highlight .mo{color:#e74c3c}.codehilite .s,.codehilite .sb,.codehilite .sc,.md-typeset .highlight .s,.md-typeset .highlight .sb,.md-typeset .highlight .sc{color:#0d904f}.codehilite .sd,.md-typeset .highlight .sd{color:#999}.codehilite .s2,.md-typeset .highlight .s2{color:#0d904f}.codehilite .se,.codehilite .sh,.codehilite .si,.codehilite .sx,.md-typeset .highlight .se,.md-typeset .highlight .sh,.md-typeset .highlight .si,.md-typeset .highlight .sx{color:#183691}.codehilite .sr,.md-typeset .highlight .sr{color:#009926}.codehilite .s1,.codehilite .ss,.md-typeset .highlight .s1,.md-typeset .highlight .ss{color:#0d904f}.codehilite .err,.md-typeset .highlight .err{color:#a61717}.codehilite .w,.md-typeset .highlight .w{color:transparent}.codehilite .hll,.md-typeset .highlight .hll{display:block;margin:0 -.6rem;padding:0 .6rem;background-color:rgba(255,235,59,.5)}.md-typeset .codehilite,.md-typeset .highlight{position:relative;margin:1em 0;padding:0;border-radius:.1rem;background-color:hsla(0,0%,92.5%,.5);color:#37474f;line-height:1.4;-webkit-overflow-scrolling:touch}.md-typeset .codehilite code,.md-typeset .codehilite pre,.md-typeset .highlight code,.md-typeset .highlight pre{display:block;margin:0;padding:.525rem .6rem;background-color:transparent;overflow:auto;vertical-align:top}.md-typeset .codehilite code::-webkit-scrollbar,.md-typeset .codehilite pre::-webkit-scrollbar,.md-typeset .highlight code::-webkit-scrollbar,.md-typeset .highlight pre::-webkit-scrollbar{width:.2rem;height:.2rem}.md-typeset .codehilite code::-webkit-scrollbar-thumb,.md-typeset .codehilite pre::-webkit-scrollbar-thumb,.md-typeset .highlight code::-webkit-scrollbar-thumb,.md-typeset .highlight pre::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset .codehilite code::-webkit-scrollbar-thumb:hover,.md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,.md-typeset .highlight code::-webkit-scrollbar-thumb:hover,.md-typeset .highlight pre::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset pre.codehilite,.md-typeset pre.highlight{overflow:visible}.md-typeset pre.codehilite code,.md-typeset pre.highlight code{display:block;padding:.525rem .6rem;overflow:auto}.md-typeset .codehilitetable,.md-typeset .highlighttable{display:block;margin:1em 0;border-radius:.2em;font-size:.8rem;overflow:hidden}.md-typeset .codehilitetable tbody,.md-typeset .codehilitetable td,.md-typeset .highlighttable tbody,.md-typeset .highlighttable td{display:block;padding:0}.md-typeset .codehilitetable tr,.md-typeset .highlighttable tr{display:-webkit-box;display:flex}.md-typeset .codehilitetable .codehilite,.md-typeset .codehilitetable .highlight,.md-typeset .codehilitetable .linenodiv,.md-typeset .highlighttable .codehilite,.md-typeset .highlighttable .highlight,.md-typeset .highlighttable .linenodiv{margin:0;border-radius:0}.md-typeset .codehilitetable .linenodiv,.md-typeset .highlighttable .linenodiv{padding:.525rem .6rem}.md-typeset .codehilitetable .linenos,.md-typeset .highlighttable .linenos{background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.26);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.md-typeset .codehilitetable .linenos pre,.md-typeset .highlighttable .linenos pre{margin:0;padding:0;background-color:transparent;color:inherit;text-align:right}.md-typeset .codehilitetable .code,.md-typeset .highlighttable .code{-webkit-box-flex:1;flex:1;overflow:hidden}.md-typeset>.codehilitetable,.md-typeset>.highlighttable{box-shadow:none}.md-typeset [id^="fnref:"]{display:inline-block}.md-typeset [id^="fnref:"]:target{margin-top:-3.8rem;padding-top:3.8rem;pointer-events:none}.md-typeset [id^="fn:"]:before{display:none;height:0;content:""}.md-typeset [id^="fn:"]:target:before{display:block;margin-top:-3.5rem;padding-top:3.5rem;pointer-events:none}.md-typeset .footnote{color:rgba(0,0,0,.54);font-size:.64rem}.md-typeset .footnote ol{margin-left:0}.md-typeset .footnote li{-webkit-transition:color .25s;transition:color .25s}.md-typeset .footnote li:target{color:rgba(0,0,0,.87)}.md-typeset .footnote li :first-child{margin-top:0}.md-typeset .footnote li:hover .footnote-backref,.md-typeset .footnote li:target .footnote-backref{-webkit-transform:translateX(0);transform:translateX(0);opacity:1}.md-typeset .footnote li:hover .footnote-backref:hover,.md-typeset .footnote li:target .footnote-backref{color:#536dfe}.md-typeset .footnote-ref{display:inline-block;pointer-events:auto}.md-typeset .footnote-ref:before{display:inline;margin:0 .2em;border-left:.05rem solid rgba(0,0,0,.26);font-size:1.25em;content:"";vertical-align:-.25rem}.md-typeset .footnote-backref{display:inline-block;-webkit-transform:translateX(.25rem);transform:translateX(.25rem);-webkit-transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s,-webkit-transform .25s .125s;color:rgba(0,0,0,.26);font-size:0;opacity:0;vertical-align:text-bottom}[dir=rtl] .md-typeset .footnote-backref{-webkit-transform:translateX(-.25rem);transform:translateX(-.25rem)}.md-typeset .footnote-backref:before{display:inline-block;font-size:.8rem;content:"\E31B"}[dir=rtl] .md-typeset .footnote-backref:before{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.md-typeset .headerlink{display:inline-block;margin-left:.5rem;-webkit-transform:translateY(.25rem);transform:translateY(.25rem);-webkit-transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s,-webkit-transform .25s .25s;opacity:0}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem;margin-left:0}html body .md-typeset .headerlink{color:rgba(0,0,0,.26)}.md-typeset h1[id]:before{display:block;margin-top:-9px;padding-top:9px;content:""}.md-typeset h1[id]:target:before{margin-top:-3.45rem;padding-top:3.45rem}.md-typeset h1[id] .headerlink:focus,.md-typeset h1[id]:hover .headerlink,.md-typeset h1[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h1[id] .headerlink:focus,.md-typeset h1[id]:hover .headerlink:hover,.md-typeset h1[id]:target .headerlink{color:#536dfe}.md-typeset h2[id]:before{display:block;margin-top:-8px;padding-top:8px;content:""}.md-typeset h2[id]:target:before{margin-top:-3.4rem;padding-top:3.4rem}.md-typeset h2[id] .headerlink:focus,.md-typeset h2[id]:hover .headerlink,.md-typeset h2[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h2[id] .headerlink:focus,.md-typeset h2[id]:hover .headerlink:hover,.md-typeset h2[id]:target .headerlink{color:#536dfe}.md-typeset h3[id]:before{display:block;margin-top:-9px;padding-top:9px;content:""}.md-typeset h3[id]:target:before{margin-top:-3.45rem;padding-top:3.45rem}.md-typeset h3[id] .headerlink:focus,.md-typeset h3[id]:hover .headerlink,.md-typeset h3[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h3[id] .headerlink:focus,.md-typeset h3[id]:hover .headerlink:hover,.md-typeset h3[id]:target .headerlink{color:#536dfe}.md-typeset h4[id]:before{display:block;margin-top:-9px;padding-top:9px;content:""}.md-typeset h4[id]:target:before{margin-top:-3.45rem;padding-top:3.45rem}.md-typeset h4[id] .headerlink:focus,.md-typeset h4[id]:hover .headerlink,.md-typeset h4[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h4[id] .headerlink:focus,.md-typeset h4[id]:hover .headerlink:hover,.md-typeset h4[id]:target .headerlink{color:#536dfe}.md-typeset h5[id]:before{display:block;margin-top:-11px;padding-top:11px;content:""}.md-typeset h5[id]:target:before{margin-top:-3.55rem;padding-top:3.55rem}.md-typeset h5[id] .headerlink:focus,.md-typeset h5[id]:hover .headerlink,.md-typeset h5[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h5[id] .headerlink:focus,.md-typeset h5[id]:hover .headerlink:hover,.md-typeset h5[id]:target .headerlink{color:#536dfe}.md-typeset h6[id]:before{display:block;margin-top:-11px;padding-top:11px;content:""}.md-typeset h6[id]:target:before{margin-top:-3.55rem;padding-top:3.55rem}.md-typeset h6[id] .headerlink:focus,.md-typeset h6[id]:hover .headerlink,.md-typeset h6[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h6[id] .headerlink:focus,.md-typeset h6[id]:hover .headerlink:hover,.md-typeset h6[id]:target .headerlink{color:#536dfe}.md-typeset .MJXc-display{margin:.75em 0;padding:.75em 0;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset .MathJax_CHTML{outline:0}.md-typeset .critic.comment,.md-typeset del.critic,.md-typeset ins.critic{margin:0 .25em;padding:.0625em 0;border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:#fdd;box-shadow:.25em 0 0 #fdd,-.25em 0 0 #fdd}.md-typeset ins.critic{background-color:#dfd;box-shadow:.25em 0 0 #dfd,-.25em 0 0 #dfd}.md-typeset .critic.comment{background-color:hsla(0,0%,92.5%,.5);color:#37474f;box-shadow:.25em 0 0 hsla(0,0%,92.5%,.5),-.25em 0 0 hsla(0,0%,92.5%,.5)}.md-typeset .critic.comment:before{padding-right:.125em;color:rgba(0,0,0,.26);content:"\E0B7";vertical-align:-.125em}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:.8rem;padding-left:.8rem;box-shadow:none}.md-typeset .critic.block :first-child{margin-top:.5em}.md-typeset .critic.block :last-child{margin-bottom:.5em}.md-typeset details{display:block;padding-top:0}.md-typeset details[open]>summary:after{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.md-typeset details:not([open]){padding-bottom:0}.md-typeset details:not([open])>summary{border-bottom:none}.md-typeset details summary{padding-right:2rem}[dir=rtl] .md-typeset details summary{padding-left:2rem}.no-details .md-typeset details:not([open])>*{display:none}.no-details .md-typeset details:not([open]) summary{display:block}.md-typeset summary{display:block;outline:none;cursor:pointer}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset summary:after{position:absolute;top:.4rem;right:.6rem;color:rgba(0,0,0,.26);font-size:1rem;content:"\E313"}[dir=rtl] .md-typeset summary:after{right:auto;left:.6rem}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{width:1rem;vertical-align:text-top}.md-typeset code.codehilite,.md-typeset code.highlight{margin:0 .29412em;padding:.07353em 0}.md-typeset .superfences-content{display:none;-webkit-box-ordinal-group:100;order:99;width:100%;background-color:#fff}.md-typeset .superfences-content>*{margin:0;border-radius:0}.md-typeset .superfences-tabs{display:-webkit-box;display:flex;position:relative;flex-wrap:wrap;margin:1em 0;border:.05rem solid rgba(0,0,0,.07);border-radius:.2em}.md-typeset .superfences-tabs>input{display:none}.md-typeset .superfences-tabs>input:checked+label{font-weight:700}.md-typeset .superfences-tabs>input:checked+label+.superfences-content{display:block}.md-typeset .superfences-tabs>label{width:auto;padding:.6rem;-webkit-transition:color .125s;transition:color .125s;font-size:.64rem;cursor:pointer}html .md-typeset .superfences-tabs>label:hover{color:#536dfe}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em;left:auto}.md-typeset .task-list-control .task-list-indicator:before{position:absolute;top:.15em;left:-1.25em;color:rgba(0,0,0,.26);font-size:1.25em;content:"\E835";vertical-align:-.25em}[dir=rtl] .md-typeset .task-list-control .task-list-indicator:before{right:-1.25em;left:auto}.md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator:before{content:"\E834"}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}@media print{.md-typeset a:after{color:rgba(0,0,0,.54);content:" [" attr(href) "]"}.md-typeset code,.md-typeset pre{white-space:pre-wrap}.md-typeset code{box-shadow:none;-webkit-box-decoration-break:initial;box-decoration-break:slice}.md-clipboard,.md-content__icon,.md-footer,.md-header,.md-sidebar,.md-tabs,.md-typeset .headerlink{display:none}}@media only screen and (max-width:44.9375em){.md-typeset pre{margin:1em -.8rem;border-radius:0}.md-typeset pre>code{padding:.525rem .8rem}.md-footer-nav__link--prev .md-footer-nav__title{display:none}.md-search-result__teaser{max-height:2.5rem;-webkit-line-clamp:3}.codehilite .hll,.md-typeset .highlight .hll{margin:0 -.8rem;padding:0 .8rem}.md-typeset>.codehilite,.md-typeset>.highlight{margin:1em -.8rem;border-radius:0}.md-typeset>.codehilite code,.md-typeset>.codehilite pre,.md-typeset>.highlight code,.md-typeset>.highlight pre{padding:.525rem .8rem}.md-typeset>.codehilitetable,.md-typeset>.highlighttable{margin:1em -.8rem;border-radius:0}.md-typeset>.codehilitetable .codehilite>code,.md-typeset>.codehilitetable .codehilite>pre,.md-typeset>.codehilitetable .highlight>code,.md-typeset>.codehilitetable .highlight>pre,.md-typeset>.codehilitetable .linenodiv,.md-typeset>.highlighttable .codehilite>code,.md-typeset>.highlighttable .codehilite>pre,.md-typeset>.highlighttable .highlight>code,.md-typeset>.highlighttable .highlight>pre,.md-typeset>.highlighttable .linenodiv{padding:.5rem .8rem}.md-typeset>p>.MJXc-display{margin:.75em -.8rem;padding:.25em .8rem}.md-typeset>.superfences-tabs{margin:1em -.8rem;border:0;border-top:.05rem solid rgba(0,0,0,.07);border-radius:0}.md-typeset>.superfences-tabs code,.md-typeset>.superfences-tabs pre{padding:.525rem .8rem}}@media only screen and (min-width:100em){html{font-size:137.5%}}@media only screen and (min-width:125em){html{font-size:150%}}@media only screen and (max-width:59.9375em){body[data-md-state=lock]{overflow:hidden}.ios body[data-md-state=lock] .md-container{display:none}html .md-nav__link[for=__toc]{display:block;padding-right:2.4rem}html .md-nav__link[for=__toc]:after{color:inherit;content:"\E8DE"}html .md-nav__link[for=__toc]+.md-nav__link{display:none}html .md-nav__link[for=__toc]~.md-nav{display:-webkit-box;display:flex}html [dir=rtl] .md-nav__link{padding-right:.8rem;padding-left:2.4rem}.md-nav__source{display:block;padding:0 .2rem;background-color:rgba(50,64,144,.9675);color:#fff}.md-search__overlay{position:absolute;top:.2rem;left:.2rem;width:1.8rem;height:1.8rem;-webkit-transform-origin:center;transform-origin:center;-webkit-transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:transform .3s .1s,opacity .2s .2s;transition:transform .3s .1s,opacity .2s .2s,-webkit-transform .3s .1s;border-radius:1rem;background-color:#fff;overflow:hidden;pointer-events:none}[dir=rtl] .md-search__overlay{right:.2rem;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transition:opacity .1s,-webkit-transform .4s;transition:opacity .1s,-webkit-transform .4s;transition:transform .4s,opacity .1s;transition:transform .4s,opacity .1s,-webkit-transform .4s;opacity:1}.md-search__inner{position:fixed;top:0;left:100%;width:100%;height:100%;-webkit-transform:translateX(5%);transform:translateX(5%);-webkit-transition:right 0s .3s,left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:right 0s .3s,left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:right 0s .3s,left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;transition:right 0s .3s,left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;opacity:0;z-index:2}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:right 0s 0s,left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:right 0s 0s,left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:right 0s 0s,left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;transition:right 0s 0s,left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;opacity:1}[dir=rtl] [data-md-toggle=search]:checked~.md-header .md-search__inner{right:0;left:auto}html [dir=rtl] .md-search__inner{right:100%;left:auto;-webkit-transform:translateX(-5%);transform:translateX(-5%)}.md-search__input{width:100%;height:2.4rem;font-size:.9rem}.md-search__icon[for=__search]{top:.6rem;left:.8rem}.md-search__icon[for=__search][for=__search]:before{content:"\E5C4"}[dir=rtl] .md-search__icon[for=__search][for=__search]:before{content:"\E5C8"}.md-search__icon[type=reset]{top:.6rem;right:.8rem}.md-search__output{top:2.4rem;bottom:0}.md-search-result__article--document:before{display:none}}@media only screen and (max-width:76.1875em){[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;-webkit-transition:width 0s,height 0s,opacity .25s;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-header-nav__button.md-icon--home,.md-header-nav__button.md-logo{display:none}.md-hero__inner{margin-top:2.4rem;margin-bottom:1.2rem}.md-nav{background-color:#fff}.md-nav--primary,.md-nav--primary .md-nav{display:-webkit-box;display:flex;position:absolute;top:0;right:0;left:0;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;height:100%;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}html .md-nav--primary .md-nav__title{position:relative;height:5.6rem;padding:3rem .8rem .2rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-weight:400;line-height:2.4rem;white-space:nowrap;cursor:pointer}html .md-nav--primary .md-nav__title:before{display:block;position:absolute;top:.2rem;left:.2rem;width:2rem;height:2rem;color:rgba(0,0,0,.54)}html .md-nav--primary .md-nav__title~.md-nav__list{background-color:#fff;box-shadow:inset 0 .05rem 0 rgba(0,0,0,.07)}html .md-nav--primary .md-nav__title~.md-nav__list>.md-nav__item:first-child{border-top:0}html .md-nav--primary .md-nav__title--site{position:relative;background-color:#3f51b5;color:#fff}html .md-nav--primary .md-nav__title--site .md-nav__button{display:block;position:absolute;top:.2rem;left:.2rem;width:3.2rem;height:3.2rem;font-size:2.4rem}html .md-nav--primary .md-nav__title--site:before{display:none}html [dir=rtl] .md-nav--primary .md-nav__title--site .md-nav__button,html [dir=rtl] .md-nav--primary .md-nav__title:before{right:.2rem;left:auto}.md-nav--primary .md-nav__list{-webkit-box-flex:1;flex:1;overflow-y:auto}.md-nav--primary .md-nav__item{padding:0;border-top:.05rem solid rgba(0,0,0,.07)}[dir=rtl] .md-nav--primary .md-nav__item{padding:0}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:2.4rem}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:.8rem;padding-left:2.4rem}.md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"\E315"}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"\E314"}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link:after{position:absolute;top:50%;right:.6rem;margin-top:-.6rem;color:inherit;font-size:1.2rem}[dir=rtl] .md-nav--primary .md-nav__link:after{right:auto;left:.6rem}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{position:static;background-color:transparent}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem;padding-left:0}.md-nav__toggle~.md-nav{display:-webkit-box;display:flex;-webkit-transform:translateX(100%);transform:translateX(100%);-webkit-transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s;transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);opacity:0}[dir=rtl] .md-nav__toggle~.md-nav{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.no-csstransforms3d .md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav{-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1}.no-csstransforms3d .md-nav__toggle:checked~.md-nav{display:-webkit-box;display:flex}.md-sidebar--primary{position:fixed;top:0;left:-12.1rem;width:12.1rem;height:100%;-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);background-color:#fff;z-index:3}[dir=rtl] .md-sidebar--primary{right:-12.1rem;left:auto}.no-csstransforms3d .md-sidebar--primary{display:none}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);-webkit-transform:translateX(12.1rem);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{-webkit-transform:translateX(-12.1rem);transform:translateX(-12.1rem)}.no-csstransforms3d [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{display:block}.md-sidebar--primary .md-sidebar__scrollwrap{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;margin:0}.md-tabs{display:none}}@media only screen and (min-width:60em){.md-content{margin-right:12.1rem}[dir=rtl] .md-content{margin-right:0;margin-left:12.1rem}.md-header-nav__button.md-icon--search{display:none}.md-header-nav__source{display:block;width:11.7rem;max-width:11.7rem;padding-right:.6rem}[dir=rtl] .md-header-nav__source{padding-right:0;padding-left:.6rem}.md-search{padding:.2rem}.md-search__overlay{position:fixed;top:0;left:0;width:0;height:0;-webkit-transition:width 0s .25s,height 0s .25s,opacity .25s;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);cursor:pointer}[dir=rtl] .md-search__overlay{right:0;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{width:100%;height:100%;-webkit-transition:width 0s,height 0s,opacity .25s;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-search__inner{position:relative;width:11.5rem;margin-right:.8rem;padding:.1rem 0;float:right;-webkit-transition:width .25s cubic-bezier(.1,.7,.1,1);transition:width .25s cubic-bezier(.1,.7,.1,1)}[dir=rtl] .md-search__inner{margin-right:0;margin-left:.8rem;float:left}.md-search__form,.md-search__input{border-radius:.1rem}.md-search__input{width:100%;height:1.8rem;padding-left:2.2rem;-webkit-transition:background-color .25s cubic-bezier(.1,.7,.1,1),color .25s cubic-bezier(.1,.7,.1,1);transition:background-color .25s cubic-bezier(.1,.7,.1,1),color .25s cubic-bezier(.1,.7,.1,1);background-color:rgba(0,0,0,.26);color:inherit;font-size:.8rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input+.md-search__icon{color:inherit}.md-search__input::-webkit-input-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input::-moz-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input:-ms-input-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input::-ms-input-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input::placeholder{color:hsla(0,0%,100%,.7)}.md-search__input:hover{background-color:hsla(0,0%,100%,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{border-radius:.1rem .1rem 0 0;background-color:#fff;color:rgba(0,0,0,.87);text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input::-moz-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input:-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input::-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:rgba(0,0,0,.54)}.md-search__output{top:1.9rem;-webkit-transition:opacity .4s;transition:opacity .4s;opacity:0}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}.md-search__scrollwrap{max-height:0}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem;padding-left:0}.md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem;padding-left:.8rem}.md-sidebar--secondary{display:block;margin-left:100%;-webkit-transform:translate(-100%);transform:translate(-100%)}[dir=rtl] .md-sidebar--secondary{margin-right:100%;margin-left:0;-webkit-transform:translate(100%);transform:translate(100%)}}@media only screen and (min-width:76.25em){.md-content{margin-left:12.1rem}[dir=rtl] .md-content{margin-right:12.1rem}.md-content__inner{margin-right:1.2rem;margin-left:1.2rem}.md-header-nav__button.md-icon--menu{display:none}.md-nav[data-md-state=animate]{-webkit-transition:max-height .25s cubic-bezier(.86,0,.07,1);transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav__toggle~.md-nav{max-height:0;overflow:hidden}.no-js .md-nav__toggle~.md-nav{display:none}.md-nav[data-md-state=expand],.md-nav__toggle:checked~.md-nav{max-height:100%}.no-js .md-nav[data-md-state=expand],.no-js .md-nav__toggle:checked~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--nested>.md-nav__link:after{display:inline-block;-webkit-transform-origin:.45em .45em;transform-origin:.45em .45em;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;vertical-align:-.125em}.js .md-nav__item--nested>.md-nav__link:after{-webkit-transition:-webkit-transform .4s;transition:-webkit-transform .4s;transition:transform .4s;transition:transform .4s,-webkit-transform .4s}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link:after{-webkit-transform:rotateX(180deg);transform:rotateX(180deg)}.md-search__inner{margin-right:1.2rem}[dir=rtl] .md-search__inner{margin-left:1.2rem}.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}.md-sidebar--secondary{margin-left:61rem}[dir=rtl] .md-sidebar--secondary{margin-right:61rem;margin-left:0}.md-tabs~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{font-size:0;visibility:hidden}.md-tabs--active~.md-main .md-nav--primary .md-nav__title{display:block;padding:0}.md-tabs--active~.md-main .md-nav--primary .md-nav__title--site{display:none}.no-js .md-tabs--active~.md-main .md-nav--primary .md-nav{display:block}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item{font-size:0;visibility:hidden}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{display:none;font-size:.7rem;overflow:auto;visibility:visible}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested>.md-nav__link{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active{display:block}.md-tabs--active~.md-main .md-nav[data-md-level="1"]{max-height:none;overflow:visible}.md-tabs--active~.md-main .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-left:0}.md-tabs--active~.md-main .md-nav[data-md-level="1"] .md-nav .md-nav__title{display:none}}@media only screen and (min-width:45em){.md-footer-nav__link{width:50%}.md-footer-copyright{max-width:75%;float:left}[dir=rtl] .md-footer-copyright{float:right}.md-footer-social{padding:.6rem 0;float:right}[dir=rtl] .md-footer-social{float:left}}@media only screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(45);transform:scale(45)}}@media only screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(60);transform:scale(60)}}@media only screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(75);transform:scale(75)}}@media only screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}.md-search-result__teaser{max-height:2.5rem;-webkit-line-clamp:3}} \ No newline at end of file diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index e66dceed..00000000 --- a/docs/index.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Yq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - - - - -
-
- - - -

New documentation website

- -

User docs are better than ever, and have been moved here

- - - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - \ No newline at end of file diff --git a/docs/search/search_index.json b/docs/search/search_index.json deleted file mode 100644 index d033dff2..00000000 --- a/docs/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"New documentation website User docs are better than ever, and have been moved here","title":"Home"},{"location":"#new-documentation-website","text":"User docs are better than ever, and have been moved here","title":"New documentation website"}]} \ No newline at end of file diff --git a/docs/sitemap.xml b/docs/sitemap.xml deleted file mode 100644 index 364990d7..00000000 --- a/docs/sitemap.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - None - 2020-01-30 - daily - - \ No newline at end of file diff --git a/docs/sitemap.xml.gz b/docs/sitemap.xml.gz deleted file mode 100644 index 005f405c..00000000 Binary files a/docs/sitemap.xml.gz and /dev/null differ diff --git a/examples/merge-anchor.yaml b/examples/merge-anchor.yaml index 6d3f426d..61041bd5 100644 --- a/examples/merge-anchor.yaml +++ b/examples/merge-anchor.yaml @@ -1,19 +1,19 @@ foo: &foo - a: original - thing: coolasdf - thirsty: yep + a: foo_a + thing: foo_thing + c: foo_c bar: &bar - b: 2 - thing: coconut - c: oldbar + b: bar_b + thing: bar_thing + c: bar_c foobarList: + b: foobarList_b <<: [*foo,*bar] - c: newbar + c: foobarList_c foobar: + c: foobar_c <<: *foo - thirty: well beyond - thing: ice - c: 3 + thing: foobar_thing \ No newline at end of file diff --git a/examples/sample.yaml b/examples/sample.yaml index 1fb93352..9b925d75 100644 --- a/examples/sample.yaml +++ b/examples/sample.yaml @@ -1,3 +1,5 @@ +# Some doc + a: true b: c: 2 diff --git a/go.mod b/go.mod index b2d77dc9..ea84d979 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,15 @@ -module github.com/mikefarah/yq/v3 +module github.com/mikefarah/yq/v4 require ( + github.com/elliotchance/orderedmap v1.3.0 github.com/fatih/color v1.9.0 github.com/goccy/go-yaml v1.8.1 - github.com/kylelemons/godebug v1.1.0 + github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a github.com/mattn/go-colorable v0.1.7 // indirect - github.com/pkg/errors v0.9.1 - github.com/spf13/cobra v1.0.0 - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/cobra v1.1.1 + github.com/timtadh/data-structures v0.5.3 // indirect + github.com/timtadh/lexmachine v0.2.2 + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 diff --git a/go.sum b/go.sum index 807e8640..b4d8e675 100644 --- a/go.sum +++ b/go.sum @@ -1,28 +1,49 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/elliotchance/orderedmap v1.3.0 h1:k6m77/d0zCXTjsk12nX40TkEBkSICq8T4s6R6bpCqU0= +github.com/elliotchance/orderedmap v1.3.0/go.mod h1:8hdSl6jmveQw8ScByd3AaNHNk51RhbTazdqtTty+NFw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -36,18 +57,54 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= +github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -58,14 +115,14 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= @@ -73,15 +130,26 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -92,52 +160,104 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/timtadh/data-structures v0.5.3 h1:F2tEjoG9qWIyUjbvXVgJqEOGJPMIiYn7U5W5mE+i/vQ= +github.com/timtadh/data-structures v0.5.3/go.mod h1:9R4XODhJ8JdWFEI8P/HJKqxuJctfBQw6fDibMQny2oU= +github.com/timtadh/lexmachine v0.2.2 h1:g55RnjdYazm5wnKv59pwFcBJHOyvTPfDEoz21s4PHmY= +github.com/timtadh/lexmachine v0.2.2/go.mod h1:GBJvD5OAfRn/gnp92zb9KTgHLB7akKyxmVivoYCcjQI= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= @@ -145,29 +265,66 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE= gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -175,6 +332,13 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/pkg/yqlib/all_at_once_evaluator.go b/pkg/yqlib/all_at_once_evaluator.go new file mode 100644 index 00000000..4082c7e2 --- /dev/null +++ b/pkg/yqlib/all_at_once_evaluator.go @@ -0,0 +1,45 @@ +package yqlib + +import "container/list" + +/** + Loads all yaml documents of all files given into memory, then runs the given expression once. +**/ +type Evaluator interface { + EvaluateFiles(expression string, filenames []string, printer Printer) error +} + +type allAtOnceEvaluator struct { + treeNavigator DataTreeNavigator + treeCreator PathTreeCreator +} + +func NewAllAtOnceEvaluator() Evaluator { + return &allAtOnceEvaluator{treeNavigator: NewDataTreeNavigator(), treeCreator: NewPathTreeCreator()} +} + +func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer) error { + fileIndex := 0 + node, err := treeCreator.ParsePath(expression) + if err != nil { + return err + } + var allDocuments *list.List = list.New() + for _, filename := range filenames { + reader, err := readStream(filename) + if err != nil { + return err + } + fileDocuments, err := readDocuments(reader, filename, fileIndex) + if err != nil { + return err + } + allDocuments.PushBackList(fileDocuments) + fileIndex = fileIndex + 1 + } + matches, err := treeNavigator.GetMatchingNodes(allDocuments, node) + if err != nil { + return err + } + return printer.PrintResults(matches) +} diff --git a/pkg/yqlib/candidate_node.go b/pkg/yqlib/candidate_node.go new file mode 100644 index 00000000..603bd9b8 --- /dev/null +++ b/pkg/yqlib/candidate_node.go @@ -0,0 +1,102 @@ +package yqlib + +import ( + "fmt" + "strconv" + "strings" + + "github.com/jinzhu/copier" + yaml "gopkg.in/yaml.v3" +) + +type CandidateNode struct { + Node *yaml.Node // the actual node + Path []interface{} /// the path we took to get to this node + Document uint // the document index of this node + Filename string + FileIndex int +} + +func (n *CandidateNode) GetKey() string { + return fmt.Sprintf("%v - %v", n.Document, n.Path) +} + +func (n *CandidateNode) Copy() (*CandidateNode, error) { + clone := &CandidateNode{} + err := copier.Copy(clone, n) + if err != nil { + return nil, err + } + return clone, nil +} + +// updates this candidate from the given candidate node +func (n *CandidateNode) UpdateFrom(other *CandidateNode) { + n.UpdateAttributesFrom(other) + n.Node.Content = other.Node.Content + n.Node.Value = other.Node.Value + n.Node.Alias = other.Node.Alias +} + +func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) { + if n.Node.Kind != other.Node.Kind { + // clear out the contents when switching to a different type + // e.g. map to array + n.Node.Content = make([]*yaml.Node, 0) + n.Node.Value = "" + } + n.Node.Kind = other.Node.Kind + n.Node.Tag = other.Node.Tag + + // merge will pickup the style of the new thing + // when autocreating nodes + if n.Node.Style == 0 { + n.Node.Style = other.Node.Style + } + n.Node.FootComment = n.Node.FootComment + other.Node.FootComment + n.Node.HeadComment = n.Node.HeadComment + other.Node.HeadComment + n.Node.LineComment = n.Node.LineComment + other.Node.LineComment +} + +func (n *CandidateNode) PathStackToString() string { + return mergePathStackToString(n.Path) +} + +func mergePathStackToString(pathStack []interface{}) string { + var sb strings.Builder + for index, path := range pathStack { + switch path.(type) { + case int, int64: + // if arrayMergeStrategy == AppendArrayMergeStrategy { + // sb.WriteString("[+]") + // } else { + sb.WriteString(fmt.Sprintf("[%v]", path)) + // } + + default: + s := fmt.Sprintf("%v", path) + var _, errParsingInt = strconv.ParseInt(s, 10, 64) // nolint + + hasSpecial := strings.Contains(s, ".") || strings.Contains(s, "[") || strings.Contains(s, "]") || strings.Contains(s, "\"") + hasDoubleQuotes := strings.Contains(s, "\"") + wrappingCharacterStart := "\"" + wrappingCharacterEnd := "\"" + if hasDoubleQuotes { + wrappingCharacterStart = "(" + wrappingCharacterEnd = ")" + } + if hasSpecial || errParsingInt == nil { + sb.WriteString(wrappingCharacterStart) + } + sb.WriteString(s) + if hasSpecial || errParsingInt == nil { + sb.WriteString(wrappingCharacterEnd) + } + } + + if index < len(pathStack)-1 { + sb.WriteString(".") + } + } + return sb.String() +} diff --git a/pkg/yqlib/data_navigator.go b/pkg/yqlib/data_navigator.go deleted file mode 100644 index a7c41c64..00000000 --- a/pkg/yqlib/data_navigator.go +++ /dev/null @@ -1,295 +0,0 @@ -package yqlib - -import ( - "fmt" - "strconv" - - yaml "gopkg.in/yaml.v3" -) - -type DataNavigator interface { - Traverse(value *yaml.Node, path []interface{}) error -} - -type navigator struct { - navigationStrategy NavigationStrategy -} - -func NewDataNavigator(NavigationStrategy NavigationStrategy) DataNavigator { - return &navigator{ - navigationStrategy: NavigationStrategy, - } -} - -func (n *navigator) Traverse(value *yaml.Node, path []interface{}) error { - emptyArray := make([]interface{}, 0) - log.Debugf("Traversing path %v", pathStackToString(path)) - return n.doTraverse(value, "", path, emptyArray) -} - -func (n *navigator) doTraverse(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error { - - log.Debug("head %v", head) - DebugNode(value) - var nodeContext = NewNodeContext(value, head, tail, pathStack) - - var errorDeepSplatting error - // no need to deeply traverse the DocumentNode, as it's already covered by its first child. - if head == "**" && value.Kind != yaml.DocumentNode && value.Kind != yaml.ScalarNode && n.navigationStrategy.ShouldDeeplyTraverse(nodeContext) { - if len(pathStack) == 0 || pathStack[len(pathStack)-1] != "<<" { - errorDeepSplatting = n.recurse(value, head, tail, pathStack) - } - // ignore errors here, we are deep splatting so we may accidently give a string key - // to an array sequence - if len(tail) > 0 { - _ = n.recurse(value, tail[0], tail[1:], pathStack) - } - return errorDeepSplatting - } - - if value.Kind == yaml.DocumentNode { - log.Debugf("its a document, diving into %v", head) - DebugNode(value) - return n.recurse(value, head, tail, pathStack) - } else if len(tail) > 0 && value.Kind != yaml.ScalarNode { - log.Debugf("diving into %v", tail[0]) - DebugNode(value) - return n.recurse(value, tail[0], tail[1:], pathStack) - } - return n.navigationStrategy.Visit(nodeContext) -} - -func (n *navigator) getOrReplace(original *yaml.Node, expectedKind yaml.Kind) *yaml.Node { - if original.Kind != expectedKind { - log.Debug("wanted %v but it was %v, overriding", KindString(expectedKind), KindString(original.Kind)) - return &yaml.Node{Kind: expectedKind} - } - return original -} - -func (n *navigator) recurse(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error { - log.Debug("recursing, processing %v, pathStack %v", head, pathStackToString(pathStack)) - - nodeContext := NewNodeContext(value, head, tail, pathStack) - - if head == "**" && !n.navigationStrategy.ShouldOnlyDeeplyVisitLeaves(nodeContext) { - nodeContext.IsMiddleNode = true - errorVisitingDeeply := n.navigationStrategy.Visit(nodeContext) - if errorVisitingDeeply != nil { - return errorVisitingDeeply - } - } - - switch value.Kind { - case yaml.MappingNode: - log.Debug("its a map with %v entries", len(value.Content)/2) - headString := fmt.Sprintf("%v", head) - return n.recurseMap(value, headString, tail, pathStack) - - case yaml.SequenceNode: - log.Debug("its a sequence of %v things!", len(value.Content)) - - switch head := head.(type) { - case int64: - return n.recurseArray(value, head, head, tail, pathStack) - default: - - if head == "+" { - return n.appendArray(value, head, tail, pathStack) - } else if len(value.Content) == 0 && head == "**" { - return n.navigationStrategy.Visit(nodeContext) - } - return n.splatArray(value, head, tail, pathStack) - } - case yaml.AliasNode: - log.Debug("its an alias!") - DebugNode(value.Alias) - if n.navigationStrategy.FollowAlias(nodeContext) { - log.Debug("following the alias") - return n.recurse(value.Alias, head, tail, pathStack) - } - return nil - case yaml.DocumentNode: - return n.doTraverse(value.Content[0], head, tail, pathStack) - default: - return n.navigationStrategy.Visit(nodeContext) - } -} - -func (n *navigator) recurseMap(value *yaml.Node, head string, tail []interface{}, pathStack []interface{}) error { - traversedEntry := false - errorVisiting := n.visitMatchingEntries(value, head, tail, pathStack, func(contents []*yaml.Node, indexInMap int) error { - log.Debug("recurseMap: visitMatchingEntries for %v", contents[indexInMap].Value) - n.navigationStrategy.DebugVisitedNodes() - newPathStack := append(pathStack, contents[indexInMap].Value) - log.Debug("should I traverse? head: %v, path: %v", head, pathStackToString(newPathStack)) - DebugNode(value) - if n.navigationStrategy.ShouldTraverse(NewNodeContext(contents[indexInMap+1], head, tail, newPathStack), contents[indexInMap].Value) { - log.Debug("recurseMap: Going to traverse") - traversedEntry = true - contents[indexInMap+1] = n.getOrReplace(contents[indexInMap+1], guessKind(head, tail, contents[indexInMap+1].Kind)) - errorTraversing := n.doTraverse(contents[indexInMap+1], head, tail, newPathStack) - log.Debug("recurseMap: Finished traversing") - n.navigationStrategy.DebugVisitedNodes() - return errorTraversing - } else { - log.Debug("nope not traversing") - } - return nil - }) - - if errorVisiting != nil { - return errorVisiting - } - - if len(value.Content) == 0 && head == "**" { - return n.navigationStrategy.Visit(NewNodeContext(value, head, tail, pathStack)) - } else if traversedEntry || n.navigationStrategy.GetPathParser().IsPathExpression(head) || !n.navigationStrategy.AutoCreateMap(NewNodeContext(value, head, tail, pathStack)) { - return nil - } - - _, errorParsingInt := strconv.ParseInt(head, 10, 64) - - mapEntryKey := yaml.Node{Value: head, Kind: yaml.ScalarNode} - - if errorParsingInt == nil { - // fixes a json encoding problem where keys that look like numbers - // get treated as numbers and cannot be used in a json map - mapEntryKey.Style = yaml.LiteralStyle - } - - value.Content = append(value.Content, &mapEntryKey) - mapEntryValue := yaml.Node{Kind: guessKind(head, tail, 0)} - value.Content = append(value.Content, &mapEntryValue) - log.Debug("adding a new node %v - def a string", head) - return n.doTraverse(&mapEntryValue, head, tail, append(pathStack, head)) -} - -// need to pass the node in, as it may be aliased -type mapVisitorFn func(contents []*yaml.Node, index int) error - -func (n *navigator) visitDirectMatchingEntries(node *yaml.Node, head string, tail []interface{}, pathStack []interface{}, visit mapVisitorFn) error { - var contents = node.Content - for index := 0; index < len(contents); index = index + 2 { - content := contents[index] - - log.Debug("index %v, checking %v, %v", index, content.Value, content.Tag) - n.navigationStrategy.DebugVisitedNodes() - errorVisiting := visit(contents, index) - if errorVisiting != nil { - return errorVisiting - } - } - return nil -} - -func (n *navigator) visitMatchingEntries(node *yaml.Node, head string, tail []interface{}, pathStack []interface{}, visit mapVisitorFn) error { - var contents = node.Content - log.Debug("visitMatchingEntries %v", head) - DebugNode(node) - // value.Content is a concatenated array of key, value, - // so keys are in the even indexes, values in odd. - // merge aliases are defined first, but we only want to traverse them - // if we don't find a match directly on this node first. - errorVisitedDirectEntries := n.visitDirectMatchingEntries(node, head, tail, pathStack, visit) - - if errorVisitedDirectEntries != nil || !n.navigationStrategy.FollowAlias(NewNodeContext(node, head, tail, pathStack)) { - return errorVisitedDirectEntries - } - return n.visitAliases(contents, head, tail, pathStack, visit) -} - -func (n *navigator) visitAliases(contents []*yaml.Node, head string, tail []interface{}, pathStack []interface{}, visit mapVisitorFn) error { - // merge aliases are defined first, but we only want to traverse them - // if we don't find a match on this node first. - // traverse them backwards so that the last alias overrides the preceding. - // a node can either be - // an alias to one other node (e.g. <<: *blah) - // or a sequence of aliases (e.g. <<: [*blah, *foo]) - log.Debug("checking for aliases, head: %v, pathstack: %v", head, pathStackToString(pathStack)) - for index := len(contents) - 2; index >= 0; index = index - 2 { - - if contents[index+1].Kind == yaml.AliasNode && contents[index].Value == "<<" { - valueNode := contents[index+1] - log.Debug("found an alias") - DebugNode(contents[index]) - DebugNode(valueNode) - - errorInAlias := n.visitMatchingEntries(valueNode.Alias, head, tail, pathStack, visit) - if errorInAlias != nil { - return errorInAlias - } - } else if contents[index+1].Kind == yaml.SequenceNode { - // could be an array of aliases... - errorVisitingAliasSeq := n.visitAliasSequence(contents[index+1].Content, head, tail, pathStack, visit) - if errorVisitingAliasSeq != nil { - return errorVisitingAliasSeq - } - } - } - return nil -} - -func (n *navigator) visitAliasSequence(possibleAliasArray []*yaml.Node, head string, tail []interface{}, pathStack []interface{}, visit mapVisitorFn) error { - // need to search this backwards too, so that aliases defined last override the preceding. - for aliasIndex := len(possibleAliasArray) - 1; aliasIndex >= 0; aliasIndex = aliasIndex - 1 { - child := possibleAliasArray[aliasIndex] - if child.Kind == yaml.AliasNode { - log.Debug("found an alias") - DebugNode(child) - errorInAlias := n.visitMatchingEntries(child.Alias, head, tail, pathStack, visit) - if errorInAlias != nil { - return errorInAlias - } - } - } - return nil -} - -func (n *navigator) splatArray(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error { - for index, childValue := range value.Content { - log.Debug("processing") - DebugNode(childValue) - childValue = n.getOrReplace(childValue, guessKind(head, tail, childValue.Kind)) - - newPathStack := append(pathStack, index) - if n.navigationStrategy.ShouldTraverse(NewNodeContext(childValue, head, tail, newPathStack), childValue.Value) { - // here we should not deeply traverse the array if we are appending..not sure how to do that. - // need to visit instead... - // easiest way is to pop off the head and pass the rest of the tail in. - var err = n.doTraverse(childValue, head, tail, newPathStack) - if err != nil { - return err - } - } - } - return nil -} - -func (n *navigator) appendArray(value *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) error { - var newNode = yaml.Node{Kind: guessKind(head, tail, 0)} - value.Content = append(value.Content, &newNode) - log.Debug("appending a new node, %v", value.Content) - return n.doTraverse(&newNode, head, tail, append(pathStack, len(value.Content)-1)) -} - -func (n *navigator) recurseArray(value *yaml.Node, index int64, head interface{}, tail []interface{}, pathStack []interface{}) error { - var contentLength = int64(len(value.Content)) - for contentLength <= index { - value.Content = append(value.Content, &yaml.Node{Kind: guessKind(head, tail, 0)}) - contentLength = int64(len(value.Content)) - } - var indexToUse = index - - if indexToUse < 0 { - indexToUse = contentLength + indexToUse - } - - if indexToUse < 0 { - return fmt.Errorf("Index [%v] out of range, array size is %v", index, contentLength) - } - - value.Content[indexToUse] = n.getOrReplace(value.Content[indexToUse], guessKind(head, tail, value.Content[indexToUse].Kind)) - - return n.doTraverse(value.Content[indexToUse], head, tail, append(pathStack, index)) -} diff --git a/pkg/yqlib/data_navigator_test.go b/pkg/yqlib/data_navigator_test.go deleted file mode 100644 index 88c44e97..00000000 --- a/pkg/yqlib/data_navigator_test.go +++ /dev/null @@ -1 +0,0 @@ -package yqlib diff --git a/pkg/yqlib/data_tree_navigator.go b/pkg/yqlib/data_tree_navigator.go new file mode 100644 index 00000000..f8acc73f --- /dev/null +++ b/pkg/yqlib/data_tree_navigator.go @@ -0,0 +1,43 @@ +package yqlib + +import ( + "fmt" + + "container/list" + + logging "gopkg.in/op/go-logging.v1" +) + +type DataTreeNavigator interface { + // given a list of CandidateEntities and a pathNode, + // this will process the list against the given pathNode and return + // a new list of matching candidates + GetMatchingNodes(matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) +} + +type dataTreeNavigator struct { +} + +func NewDataTreeNavigator() DataTreeNavigator { + return &dataTreeNavigator{} +} + +func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + if pathNode == nil { + log.Debugf("getMatchingNodes - nothing to do") + return matchingNodes, nil + } + log.Debugf("Processing Op: %v", pathNode.Operation.toString()) + if log.IsEnabledFor(logging.DEBUG) { + for el := matchingNodes.Front(); el != nil; el = el.Next() { + log.Debug(NodeToString(el.Value.(*CandidateNode))) + } + } + log.Debug(">>") + handler := pathNode.Operation.OperationType.Handler + if handler != nil { + return handler(d, matchingNodes, pathNode) + } + return nil, fmt.Errorf("Unknown operator %v", pathNode.Operation.OperationType) + +} diff --git a/pkg/yqlib/delete_navigation_strategy.go b/pkg/yqlib/delete_navigation_strategy.go deleted file mode 100644 index a0792c38..00000000 --- a/pkg/yqlib/delete_navigation_strategy.go +++ /dev/null @@ -1,73 +0,0 @@ -package yqlib - -import ( - yaml "gopkg.in/yaml.v3" -) - -func DeleteNavigationStrategy(pathElementToDelete interface{}) NavigationStrategy { - parser := NewPathParser() - return &NavigationStrategyImpl{ - visitedNodes: []*NodeContext{}, - pathParser: parser, - followAlias: func(nodeContext NodeContext) bool { - return false - }, - shouldOnlyDeeplyVisitLeaves: func(nodeContext NodeContext) bool { - return false - }, - visit: func(nodeContext NodeContext) error { - node := nodeContext.Node - log.Debug("need to find and delete %v in here (%v)", pathElementToDelete, pathStackToString(nodeContext.PathStack)) - DebugNode(node) - if node.Kind == yaml.SequenceNode { - newContent := deleteFromArray(parser, node.Content, nodeContext.PathStack, pathElementToDelete) - node.Content = newContent - } else if node.Kind == yaml.MappingNode { - node.Content = deleteFromMap(parser, node.Content, nodeContext.PathStack, pathElementToDelete) - } - return nil - }, - } -} -func deleteFromMap(pathParser PathParser, contents []*yaml.Node, pathStack []interface{}, pathElementToDelete interface{}) []*yaml.Node { - newContents := make([]*yaml.Node, 0) - for index := 0; index < len(contents); index = index + 2 { - keyNode := contents[index] - valueNode := contents[index+1] - if !pathParser.MatchesNextPathElement(NewNodeContext(keyNode, pathElementToDelete, make([]interface{}, 0), pathStack), keyNode.Value) { - log.Debug("adding node %v", keyNode.Value) - newContents = append(newContents, keyNode, valueNode) - } else { - log.Debug("skipping node %v", keyNode.Value) - } - } - return newContents -} - -func deleteFromArray(pathParser PathParser, content []*yaml.Node, pathStack []interface{}, pathElementToDelete interface{}) []*yaml.Node { - - switch pathElementToDelete := pathElementToDelete.(type) { - case int64: - return deleteIndexInArray(content, pathElementToDelete) - default: - log.Debug("%v is not a numeric index, finding matching patterns", pathElementToDelete) - var newArray = make([]*yaml.Node, 0) - - for _, childValue := range content { - if !pathParser.MatchesNextPathElement(NewNodeContext(childValue, pathElementToDelete, make([]interface{}, 0), pathStack), childValue.Value) { - newArray = append(newArray, childValue) - } - } - return newArray - } - -} - -func deleteIndexInArray(content []*yaml.Node, index int64) []*yaml.Node { - log.Debug("deleting index %v in array", index) - if index >= int64(len(content)) { - log.Debug("index %v is greater than content length %v", index, len(content)) - return content - } - return append(content[:index], content[index+1:]...) -} diff --git a/pkg/yqlib/doc/.gitignore b/pkg/yqlib/doc/.gitignore new file mode 100644 index 00000000..c4c4ffc6 --- /dev/null +++ b/pkg/yqlib/doc/.gitignore @@ -0,0 +1 @@ +*.zip diff --git a/pkg/yqlib/doc/Add.md b/pkg/yqlib/doc/Add.md new file mode 100644 index 00000000..bb994943 --- /dev/null +++ b/pkg/yqlib/doc/Add.md @@ -0,0 +1,133 @@ +Add behaves differently according to the type of the LHS: +- arrays: concatenate +- number scalars: arithmetic addition (soon) +- string scalars: concatenate (soon) + +Use `+=` as append assign for things like increment. `.a += .x` is equivalent to running `.a |= . + .x`. + +## Concatenate and assign arrays +Given a sample.yml file of: +```yaml +a: + val: thing + b: + - cat + - dog +``` +then +```bash +yq eval '.a.b += ["cow"]' sample.yml +``` +will output +```yaml +a: + val: thing + b: + - cat + - dog + - cow +``` + +## Concatenate arrays +Given a sample.yml file of: +```yaml +a: + - 1 + - 2 +b: + - 3 + - 4 +``` +then +```bash +yq eval '.a + .b' sample.yml +``` +will output +```yaml +- 1 +- 2 +- 3 +- 4 +``` + +## Concatenate null to array +Given a sample.yml file of: +```yaml +a: + - 1 + - 2 +``` +then +```bash +yq eval '.a + null' sample.yml +``` +will output +```yaml +- 1 +- 2 +``` + +## Add object to array +Given a sample.yml file of: +```yaml +a: + - 1 + - 2 +c: + cat: meow +``` +then +```bash +yq eval '.a + .c' sample.yml +``` +will output +```yaml +- 1 +- 2 +- cat: meow +``` + +## Add string to array +Given a sample.yml file of: +```yaml +a: + - 1 + - 2 +``` +then +```bash +yq eval '.a + "hello"' sample.yml +``` +will output +```yaml +- 1 +- 2 +- hello +``` + +## Update array (append) +Given a sample.yml file of: +```yaml +a: + - 1 + - 2 +b: + - 3 + - 4 +``` +then +```bash +yq eval '.a = .a + .b' sample.yml +``` +will output +```yaml +a: + - 1 + - 2 + - 3 + - 4 +b: + - 3 + - 4 +``` + diff --git a/pkg/yqlib/doc/Assign.md b/pkg/yqlib/doc/Assign.md new file mode 100644 index 00000000..ce549eb2 --- /dev/null +++ b/pkg/yqlib/doc/Assign.md @@ -0,0 +1,169 @@ +This operator is used to update node values. It can be used in either the: + +### plain form: `=` +Which will assign the LHS node values to the RHS node values. The RHS expression is run against the matching nodes in the pipeline. + +### relative form: `|=` +This will do a similar thing to the plain form, however, the RHS expression is run against _the LHS nodes_. This is useful for updating values based on old values, e.g. increment. +## Create yaml file +Running +```bash +yq eval --null-input '.a.b = "cat" | .x = "frog"' +``` +will output +```yaml +a: + b: cat +x: frog +``` + +## Update node 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 node 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 +{} +``` +then +```bash +yq eval '.a.b |= "bogs"' sample.yml +``` +will output +```yaml +{a: {b: bogs}} +``` + +## Update empty object and array +Given a sample.yml file of: +```yaml +{} +``` +then +```bash +yq eval '.a.b.[0] |= "bogs"' sample.yml +``` +will output +```yaml +{a: {b: [bogs]}} +``` + diff --git a/pkg/yqlib/doc/Boolean Operators.md b/pkg/yqlib/doc/Boolean Operators.md new file mode 100644 index 00000000..7955937c --- /dev/null +++ b/pkg/yqlib/doc/Boolean Operators.md @@ -0,0 +1,113 @@ +The `or` and `and` operators take two parameters and return a boolean result. `not` flips a boolean from true to false, or vice versa. These are most commonly used with the `select` operator to filter particular nodes. +## OR example +Running +```bash +yq eval --null-input 'true or false' +``` +will output +```yaml +true +``` + +## AND example +Running +```bash +yq eval --null-input 'true and false' +``` +will output +```yaml +false +``` + +## Matching nodes with select, equals and or +Given a sample.yml file of: +```yaml +- a: bird + b: dog +- a: frog + b: bird +- a: cat + b: fly +``` +then +```bash +yq eval '[.[] | select(.a == "cat" or .b == "dog")]' sample.yml +``` +will output +```yaml +- a: bird + b: dog +- a: cat + b: fly +``` + +## 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 +``` + diff --git a/pkg/yqlib/doc/Collect into Array.md b/pkg/yqlib/doc/Collect into Array.md new file mode 100644 index 00000000..8195ee9a --- /dev/null +++ b/pkg/yqlib/doc/Collect into Array.md @@ -0,0 +1,41 @@ +# Collect into Array + +This creates an array using the expression between the square brackets. + + +## 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 +``` + diff --git a/pkg/yqlib/doc/Collect into Object.md b/pkg/yqlib/doc/Collect into Object.md new file mode 100644 index 00000000..ffbc11bf --- /dev/null +++ b/pkg/yqlib/doc/Collect into Object.md @@ -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. +## 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 +``` + diff --git a/pkg/yqlib/doc/Comment Operators.md b/pkg/yqlib/doc/Comment Operators.md new file mode 100644 index 00000000..d6d133ca --- /dev/null +++ b/pkg/yqlib/doc/Comment Operators.md @@ -0,0 +1,119 @@ +Use these comment operators to set or retrieve comments. +## 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 + +``` + diff --git a/pkg/yqlib/doc/Delete.md b/pkg/yqlib/doc/Delete.md new file mode 100644 index 00000000..601bf30a --- /dev/null +++ b/pkg/yqlib/doc/Delete.md @@ -0,0 +1,65 @@ +Deletes matching entries in maps or arrays. +## 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 +``` + diff --git a/pkg/yqlib/doc/Document Index.md b/pkg/yqlib/doc/Document Index.md new file mode 100644 index 00000000..948e50e4 --- /dev/null +++ b/pkg/yqlib/doc/Document Index.md @@ -0,0 +1,54 @@ +Use the `documentIndex` operator to select nodes of a particular document. +## 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 +``` + diff --git a/pkg/yqlib/doc/Equals.md b/pkg/yqlib/doc/Equals.md new file mode 100644 index 00000000..fda10922 --- /dev/null +++ b/pkg/yqlib/doc/Equals.md @@ -0,0 +1,59 @@ +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) +``` + + +## 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 +``` + diff --git a/pkg/yqlib/doc/Explode.md b/pkg/yqlib/doc/Explode.md new file mode 100644 index 00000000..379d44a0 --- /dev/null +++ b/pkg/yqlib/doc/Explode.md @@ -0,0 +1,98 @@ +Explodes (or dereferences) aliases and anchors. +## 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 +``` + diff --git a/pkg/yqlib/doc/File Operators.md b/pkg/yqlib/doc/File Operators.md new file mode 100644 index 00000000..946dfee9 --- /dev/null +++ b/pkg/yqlib/doc/File Operators.md @@ -0,0 +1,35 @@ +File operators are most often used with merge when needing to merge specific files together. Note that when doing this, you will need to use `eval-all` to ensure all yaml documents are loaded into memory before performing the merge (as opposed to `eval` which runs the expression once per document). + +## Merging files +Note the use of eval-all to ensure all documents are loaded into memory. +```bash +yq eval-all 'select(fileIndex == 0) * select(filename == "file2.yaml")' file1.yaml file2.yaml +``` +## Get filename +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval 'filename' sample.yml +``` +will output +```yaml +sample.yaml +``` + +## Get file index +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval 'fileIndex' sample.yml +``` +will output +```yaml +0 +``` + diff --git a/pkg/yqlib/doc/Has.md b/pkg/yqlib/doc/Has.md new file mode 100644 index 00000000..1f25009c --- /dev/null +++ b/pkg/yqlib/doc/Has.md @@ -0,0 +1,44 @@ +This is operation that returns true if the key exists in a map (or index in an array), false otherwise. +## Has map key +Given a sample.yml file of: +```yaml +- a: yes +- a: ~ +- a: +- b: nope +``` +then +```bash +yq eval '.[] | has("a")' sample.yml +``` +will output +```yaml +true +true +true +false +``` + +## Has array index +Given a sample.yml file of: +```yaml +- [] +- [1] +- [1, 2] +- [1, null] +- [1, 2, 3] + +``` +then +```bash +yq eval '.[] | has(1)' sample.yml +``` +will output +```yaml +false +false +true +true +true +``` + diff --git a/pkg/yqlib/doc/Length.md b/pkg/yqlib/doc/Length.md new file mode 100644 index 00000000..dcd76562 --- /dev/null +++ b/pkg/yqlib/doc/Length.md @@ -0,0 +1,54 @@ +Returns the lengths of the nodes. Length is defined according to the type of the node. + +## String length +returns length of string + +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval '.a | length' sample.yml +``` +will output +```yaml +3 +``` + +## Map length +returns number of entries + +Given a sample.yml file of: +```yaml +a: cat +c: dog +``` +then +```bash +yq eval 'length' sample.yml +``` +will output +```yaml +2 +``` + +## Array length +returns number of elements + +Given a sample.yml file of: +```yaml +- 2 +- 4 +- 6 +- 8 +``` +then +```bash +yq eval 'length' sample.yml +``` +will output +```yaml +4 +``` + diff --git a/pkg/yqlib/doc/Multiply.md b/pkg/yqlib/doc/Multiply.md new file mode 100644 index 00000000..3fc98d53 --- /dev/null +++ b/pkg/yqlib/doc/Multiply.md @@ -0,0 +1,238 @@ +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). + +To concatenate when merging objects, use the `*+` form (see examples below). This will recursively merge objects, appending arrays when it encounters them. + +Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below. + +## Merging files +Note the use of eval-all to ensure all documents are loaded into memory. + +```bash +yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' file1.yaml file2.yaml +``` + +## 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, appending arrays +Given a sample.yml file of: +```yaml +a: + array: + - 1 + - 2 + - animal: dog + value: coconut +b: + array: + - 3 + - 4 + - animal: cat + value: banana +``` +then +```bash +yq eval '.a *+ .b' sample.yml +``` +will output +```yaml +array: + - 1 + - 2 + - animal: dog + - 3 + - 4 + - animal: cat +value: banana +``` + +## 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 +``` + diff --git a/pkg/yqlib/doc/Path.md b/pkg/yqlib/doc/Path.md new file mode 100644 index 00000000..9bf1f404 --- /dev/null +++ b/pkg/yqlib/doc/Path.md @@ -0,0 +1,92 @@ +The path operator can be used to get the traversal paths of matching nodes in an expression. The path is returned as an array, which if traversed in order will lead to the matching node. + +You can get the key/index of matching nodes by using the `path` operator to return the path array then piping that through `.[-1]` to get the last element of that array, the key. + +## Map path +Given a sample.yml file of: +```yaml +a: + b: cat +``` +then +```bash +yq eval '.a.b | path' sample.yml +``` +will output +```yaml +- a +- b +``` + +## Get map key +Given a sample.yml file of: +```yaml +a: + b: cat +``` +then +```bash +yq eval '.a.b | path | .[-1]' sample.yml +``` +will output +```yaml +b +``` + +## Array path +Given a sample.yml file of: +```yaml +a: + - cat + - dog +``` +then +```bash +yq eval '.a.[] | select(. == "dog") | path' sample.yml +``` +will output +```yaml +- a +- 1 +``` + +## Get array index +Given a sample.yml file of: +```yaml +a: + - cat + - dog +``` +then +```bash +yq eval '.a.[] | select(. == "dog") | path | .[-1]' sample.yml +``` +will output +```yaml +1 +``` + +## Print path and value +Given a sample.yml file of: +```yaml +a: + - cat + - dog + - frog +``` +then +```bash +yq eval '.a.[] | select(. == "*og") | [{"path":path, "value":.}]' sample.yml +``` +will output +```yaml +- path: + - a + - 1 + value: dog +- path: + - a + - 2 + value: frog +``` + diff --git a/pkg/yqlib/doc/Pipe.md b/pkg/yqlib/doc/Pipe.md new file mode 100644 index 00000000..b1160d29 --- /dev/null +++ b/pkg/yqlib/doc/Pipe.md @@ -0,0 +1,35 @@ +Pipe the results of an expression into another. Like the bash operator. + +## Simple Pipe +Given a sample.yml file of: +```yaml +a: + b: cat +``` +then +```bash +yq eval '.a | .b' sample.yml +``` +will output +```yaml +cat +``` + +## Multiple updates +Given a sample.yml file of: +```yaml +a: cow +b: sheep +c: same +``` +then +```bash +yq eval '.a = "cat" | .b = "dog"' sample.yml +``` +will output +```yaml +a: cat +b: dog +c: same +``` + diff --git a/pkg/yqlib/doc/Recursive Descent.md b/pkg/yqlib/doc/Recursive Descent.md new file mode 100644 index 00000000..3a3390e3 --- /dev/null +++ b/pkg/yqlib/doc/Recursive Descent.md @@ -0,0 +1,63 @@ +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 +``` +## 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 +``` + diff --git a/pkg/yqlib/doc/Select.md b/pkg/yqlib/doc/Select.md new file mode 100644 index 00000000..0dfe2769 --- /dev/null +++ b/pkg/yqlib/doc/Select.md @@ -0,0 +1,38 @@ +Select is used to filter arrays and maps by a boolean expression. +## 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 +``` + diff --git a/pkg/yqlib/doc/Sort Keys.md b/pkg/yqlib/doc/Sort Keys.md new file mode 100644 index 00000000..a1a99306 --- /dev/null +++ b/pkg/yqlib/doc/Sort Keys.md @@ -0,0 +1,68 @@ +The Sort Keys operator sorts maps by their keys (based on their string value). This operator does not do anything to arrays or scalars (so you can easily recursively apply it to all maps). + +Sort is particularly useful for diffing two different yaml documents: + +```bash +yq eval -i 'sortKeys(..)' file1.yml +yq eval -i 'sortKeys(..)' file2.yml +diff file1.yml file2.yml +``` + +## Sort keys of map +Given a sample.yml file of: +```yaml +c: frog +a: blah +b: bing +``` +then +```bash +yq eval 'sortKeys(.)' sample.yml +``` +will output +```yaml +a: blah +b: bing +c: frog +``` + +## Sort keys recursively +Note the array elements are left unsorted, but maps inside arrays are sorted + +Given a sample.yml file of: +```yaml +bParent: + c: dog + array: + - 3 + - 1 + - 2 +aParent: + z: donkey + x: + - c: yum + b: delish + - b: ew + a: apple +``` +then +```bash +yq eval 'sortKeys(..)' sample.yml +``` +will output +```yaml +aParent: + x: + - b: delish + c: yum + - a: apple + b: ew + z: donkey +bParent: + array: + - 3 + - 1 + - 2 + c: dog +``` + diff --git a/pkg/yqlib/doc/Style.md b/pkg/yqlib/doc/Style.md new file mode 100644 index 00000000..d089aa4d --- /dev/null +++ b/pkg/yqlib/doc/Style.md @@ -0,0 +1,165 @@ +The style operator can be used to get or set the style of nodes (e.g. string style, yaml style) +## 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} +``` + +## Pretty print +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 +flow +double +single +``` + diff --git a/pkg/yqlib/doc/Tag.md b/pkg/yqlib/doc/Tag.md new file mode 100644 index 00000000..519142fd --- /dev/null +++ b/pkg/yqlib/doc/Tag.md @@ -0,0 +1,44 @@ +The tag operator can be used to get or set the tag of nodes (e.g. `!!str`, `!!int`, `!!bool`). +## 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 +``` + diff --git a/pkg/yqlib/doc/Traverse.md b/pkg/yqlib/doc/Traverse.md new file mode 100644 index 00000000..20befb25 --- /dev/null +++ b/pkg/yqlib/doc/Traverse.md @@ -0,0 +1,368 @@ +This is the simplest (and perhaps most used) operator, it is used to navigate deeply into yaml structurse. +## 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 +``` + +## Special characters +Use quotes around path elements with special characters + +Given a sample.yml file of: +```yaml +"{}": frog +``` +then +```bash +yq eval '."{}"' sample.yml +``` +will output +```yaml +frog +``` + +## 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 +``` + diff --git a/pkg/yqlib/doc/Union.md b/pkg/yqlib/doc/Union.md new file mode 100644 index 00000000..e01782fe --- /dev/null +++ b/pkg/yqlib/doc/Union.md @@ -0,0 +1,30 @@ +This operator is used to combine different results together. +## 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 +``` + diff --git a/pkg/yqlib/doc/aa.md b/pkg/yqlib/doc/aa.md new file mode 100644 index 00000000..e69de29b diff --git a/pkg/yqlib/doc/headers/Add.md b/pkg/yqlib/doc/headers/Add.md new file mode 100644 index 00000000..96cf7246 --- /dev/null +++ b/pkg/yqlib/doc/headers/Add.md @@ -0,0 +1,6 @@ +Add behaves differently according to the type of the LHS: +- arrays: concatenate +- number scalars: arithmetic addition (soon) +- string scalars: concatenate (soon) + +Use `+=` as append assign for things like increment. `.a += .x` is equivalent to running `.a |= . + .x`. diff --git a/pkg/yqlib/doc/headers/Assign.md b/pkg/yqlib/doc/headers/Assign.md new file mode 100644 index 00000000..5cd669cf --- /dev/null +++ b/pkg/yqlib/doc/headers/Assign.md @@ -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. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Boolean Operators.md b/pkg/yqlib/doc/headers/Boolean Operators.md new file mode 100644 index 00000000..69c46bda --- /dev/null +++ b/pkg/yqlib/doc/headers/Boolean Operators.md @@ -0,0 +1 @@ +The `or` and `and` operators take two parameters and return a boolean result. `not` flips a boolean from true to false, or vice versa. These are most commonly used with the `select` operator to filter particular nodes. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Collect into Array.md b/pkg/yqlib/doc/headers/Collect into Array.md new file mode 100644 index 00000000..defa7ce8 --- /dev/null +++ b/pkg/yqlib/doc/headers/Collect into Array.md @@ -0,0 +1,4 @@ +# Collect into Array + +This creates an array using the expression between the square brackets. + diff --git a/pkg/yqlib/doc/headers/Collect into Object.md b/pkg/yqlib/doc/headers/Collect into Object.md new file mode 100644 index 00000000..e0552575 --- /dev/null +++ b/pkg/yqlib/doc/headers/Collect into Object.md @@ -0,0 +1 @@ +This is used to construct objects (or maps). This can be used against existing yaml, or to create fresh yaml documents. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Comment Operators.md b/pkg/yqlib/doc/headers/Comment Operators.md new file mode 100644 index 00000000..aacd9771 --- /dev/null +++ b/pkg/yqlib/doc/headers/Comment Operators.md @@ -0,0 +1 @@ +Use these comment operators to set or retrieve comments. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Delete.md b/pkg/yqlib/doc/headers/Delete.md new file mode 100644 index 00000000..e3c0d1cc --- /dev/null +++ b/pkg/yqlib/doc/headers/Delete.md @@ -0,0 +1 @@ +Deletes matching entries in maps or arrays. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Document Index.md b/pkg/yqlib/doc/headers/Document Index.md new file mode 100644 index 00000000..4430ab14 --- /dev/null +++ b/pkg/yqlib/doc/headers/Document Index.md @@ -0,0 +1 @@ +Use the `documentIndex` operator to select nodes of a particular document. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Equals.md b/pkg/yqlib/doc/headers/Equals.md new file mode 100644 index 00000000..5e68bc50 --- /dev/null +++ b/pkg/yqlib/doc/headers/Equals.md @@ -0,0 +1,12 @@ +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) +``` + diff --git a/pkg/yqlib/doc/headers/Explode.md b/pkg/yqlib/doc/headers/Explode.md new file mode 100644 index 00000000..5fea8360 --- /dev/null +++ b/pkg/yqlib/doc/headers/Explode.md @@ -0,0 +1 @@ +Explodes (or dereferences) aliases and anchors. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/File Operators.md b/pkg/yqlib/doc/headers/File Operators.md new file mode 100644 index 00000000..394a0bc4 --- /dev/null +++ b/pkg/yqlib/doc/headers/File Operators.md @@ -0,0 +1,7 @@ +File operators are most often used with merge when needing to merge specific files together. Note that when doing this, you will need to use `eval-all` to ensure all yaml documents are loaded into memory before performing the merge (as opposed to `eval` which runs the expression once per document). + +## Merging files +Note the use of eval-all to ensure all documents are loaded into memory. +```bash +yq eval-all 'select(fileIndex == 0) * select(filename == "file2.yaml")' file1.yaml file2.yaml +``` \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Has.md b/pkg/yqlib/doc/headers/Has.md new file mode 100644 index 00000000..df898780 --- /dev/null +++ b/pkg/yqlib/doc/headers/Has.md @@ -0,0 +1 @@ +This is operation that returns true if the key exists in a map (or index in an array), false otherwise. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Length.md b/pkg/yqlib/doc/headers/Length.md new file mode 100644 index 00000000..7415c182 --- /dev/null +++ b/pkg/yqlib/doc/headers/Length.md @@ -0,0 +1 @@ +Returns the lengths of the nodes. Length is defined according to the type of the node. diff --git a/pkg/yqlib/doc/headers/Multiply.md b/pkg/yqlib/doc/headers/Multiply.md new file mode 100644 index 00000000..81cf758b --- /dev/null +++ b/pkg/yqlib/doc/headers/Multiply.md @@ -0,0 +1,14 @@ +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). + +To concatenate when merging objects, use the `*+` form (see examples below). This will recursively merge objects, appending arrays when it encounters them. + +Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below. + +## Merging files +Note the use of eval-all to ensure all documents are loaded into memory. + +```bash +yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' file1.yaml file2.yaml +``` diff --git a/pkg/yqlib/doc/headers/Path.md b/pkg/yqlib/doc/headers/Path.md new file mode 100644 index 00000000..3347edea --- /dev/null +++ b/pkg/yqlib/doc/headers/Path.md @@ -0,0 +1,3 @@ +The path operator can be used to get the traversal paths of matching nodes in an expression. The path is returned as an array, which if traversed in order will lead to the matching node. + +You can get the key/index of matching nodes by using the `path` operator to return the path array then piping that through `.[-1]` to get the last element of that array, the key. diff --git a/pkg/yqlib/doc/headers/Pipe.md b/pkg/yqlib/doc/headers/Pipe.md new file mode 100644 index 00000000..6b3c7e84 --- /dev/null +++ b/pkg/yqlib/doc/headers/Pipe.md @@ -0,0 +1 @@ +Pipe the results of an expression into another. Like the bash operator. diff --git a/pkg/yqlib/doc/headers/Recursive Descent.md b/pkg/yqlib/doc/headers/Recursive Descent.md new file mode 100644 index 00000000..530e64c6 --- /dev/null +++ b/pkg/yqlib/doc/headers/Recursive Descent.md @@ -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 +``` \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Select.md b/pkg/yqlib/doc/headers/Select.md new file mode 100644 index 00000000..3d1546f1 --- /dev/null +++ b/pkg/yqlib/doc/headers/Select.md @@ -0,0 +1 @@ +Select is used to filter arrays and maps by a boolean expression. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Sort Keys.md b/pkg/yqlib/doc/headers/Sort Keys.md new file mode 100644 index 00000000..00eb71a9 --- /dev/null +++ b/pkg/yqlib/doc/headers/Sort Keys.md @@ -0,0 +1,9 @@ +The Sort Keys operator sorts maps by their keys (based on their string value). This operator does not do anything to arrays or scalars (so you can easily recursively apply it to all maps). + +Sort is particularly useful for diffing two different yaml documents: + +```bash +yq eval -i 'sortKeys(..)' file1.yml +yq eval -i 'sortKeys(..)' file2.yml +diff file1.yml file2.yml +``` diff --git a/pkg/yqlib/doc/headers/Style.md b/pkg/yqlib/doc/headers/Style.md new file mode 100644 index 00000000..08fc4d23 --- /dev/null +++ b/pkg/yqlib/doc/headers/Style.md @@ -0,0 +1 @@ +The style operator can be used to get or set the style of nodes (e.g. string style, yaml style) \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Tag.md b/pkg/yqlib/doc/headers/Tag.md new file mode 100644 index 00000000..0d329fc1 --- /dev/null +++ b/pkg/yqlib/doc/headers/Tag.md @@ -0,0 +1 @@ +The tag operator can be used to get or set the tag of nodes (e.g. `!!str`, `!!int`, `!!bool`). \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Traverse.md b/pkg/yqlib/doc/headers/Traverse.md new file mode 100644 index 00000000..d69c3078 --- /dev/null +++ b/pkg/yqlib/doc/headers/Traverse.md @@ -0,0 +1 @@ +This is the simplest (and perhaps most used) operator, it is used to navigate deeply into yaml structurse. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Union.md b/pkg/yqlib/doc/headers/Union.md new file mode 100644 index 00000000..787bda7f --- /dev/null +++ b/pkg/yqlib/doc/headers/Union.md @@ -0,0 +1 @@ +This operator is used to combine different results together. \ No newline at end of file diff --git a/pkg/yqlib/encoder.go b/pkg/yqlib/encoder.go index e9ab9587..11bad1e8 100644 --- a/pkg/yqlib/encoder.go +++ b/pkg/yqlib/encoder.go @@ -74,16 +74,14 @@ func mapKeysToStrings(node *yaml.Node) { } } -func NewJsonEncoder(destination io.Writer, prettyPrint bool, indent int) Encoder { +func NewJsonEncoder(destination io.Writer, indent int) Encoder { var encoder = json.NewEncoder(destination) var indentString = "" for index := 0; index < indent; index++ { indentString = indentString + " " } - if prettyPrint { - encoder.SetIndent("", indentString) - } + encoder.SetIndent("", indentString) return &jsonEncoder{encoder} } diff --git a/pkg/yqlib/encoder_test.go b/pkg/yqlib/encoder_test.go new file mode 100644 index 00000000..d7211734 --- /dev/null +++ b/pkg/yqlib/encoder_test.go @@ -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", 0) + 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()) + +} diff --git a/pkg/yqlib/file_utils.go b/pkg/yqlib/file_utils.go new file mode 100644 index 00000000..cfe8d2c4 --- /dev/null +++ b/pkg/yqlib/file_utils.go @@ -0,0 +1,50 @@ +package yqlib + +import ( + "io" + "os" +) + +func safelyRenameFile(from string, to string) { + if renameError := os.Rename(from, to); renameError != nil { + log.Debugf("Error renaming from %v to %v, attempting to copy contents", from, to) + log.Debug(renameError.Error()) + // can't do this rename when running in docker to a file targeted in a mounted volume, + // so gracefully degrade to copying the entire contents. + if copyError := copyFileContents(from, to); copyError != nil { + log.Errorf("Failed copying from %v to %v", from, to) + log.Error(copyError.Error()) + } else { + removeErr := os.Remove(from) + if removeErr != nil { + log.Errorf("failed removing original file: %s", from) + } + } + } +} + +// 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 { + return err + } + defer safelyCloseFile(in) + out, err := os.Create(dst) + if err != nil { + return err + } + defer safelyCloseFile(out) + if _, err = io.Copy(out, in); err != nil { + return err + } + return out.Sync() +} + +func safelyCloseFile(file *os.File) { + err := file.Close() + if err != nil { + log.Error("Error closing file!") + log.Error(err.Error()) + } +} diff --git a/pkg/yqlib/filter_matching_node_navigation_strategy.go b/pkg/yqlib/filter_matching_node_navigation_strategy.go deleted file mode 100644 index 4c15c4fe..00000000 --- a/pkg/yqlib/filter_matching_node_navigation_strategy.go +++ /dev/null @@ -1,15 +0,0 @@ -package yqlib - -func FilterMatchingNodesNavigationStrategy(value string) NavigationStrategy { - return &NavigationStrategyImpl{ - visitedNodes: []*NodeContext{}, - pathParser: NewPathParser(), - visit: func(nodeContext NodeContext) error { - return nil - }, - shouldVisitExtraFn: func(nodeContext NodeContext) bool { - log.Debug("does %v match %v ? %v", nodeContext.Node.Value, value, nodeContext.Node.Value == value) - return matchesString(value, nodeContext.Node.Value) - }, - } -} diff --git a/pkg/yqlib/lib.go b/pkg/yqlib/lib.go index 3f548f6b..ce6367e9 100644 --- a/pkg/yqlib/lib.go +++ b/pkg/yqlib/lib.go @@ -2,24 +2,160 @@ package yqlib import ( "bytes" + "container/list" "fmt" - "strconv" - "strings" logging "gopkg.in/op/go-logging.v1" yaml "gopkg.in/yaml.v3" ) -var log = logging.MustGetLogger("yq") +var log = logging.MustGetLogger("yq-lib") -type UpdateCommand struct { - Command string - Path string - Value *yaml.Node - Overwrite bool - DontUpdateNodeValue bool - DontUpdateNodeContent bool - CommentsMergeStrategy CommentsMergeStrategy +type OperationType struct { + Type string + NumArgs uint // number of arguments to the op + Precedence uint + Handler OperatorHandler +} + +// operators TODO: +// - cookbook doc for common things +// - mergeEmpty (sets only if the document is empty, do I do that now?) + +var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: OrOperator} +var And = &OperationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: AndOperator} + +var Union = &OperationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: UnionOperator} + +var Pipe = &OperationType{Type: "PIPE", NumArgs: 2, Precedence: 30, Handler: PipeOperator} + +var Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignUpdateOperator} +var AddAssign = &OperationType{Type: "ADD_ASSIGN", NumArgs: 2, Precedence: 40, Handler: AddAssignOperator} + +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: 45, Handler: MultiplyOperator} +var Add = &OperationType{Type: "ADD", NumArgs: 2, Precedence: 45, Handler: AddOperator} + +var Equals = &OperationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: EqualsOperator} +var CreateMap = &OperationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 40, Handler: CreateMapOperator} + +var ShortPipe = &OperationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence: 45, Handler: PipeOperator} + +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} +var GetFilename = &OperationType{Type: "GET_FILENAME", NumArgs: 0, Precedence: 50, Handler: GetFilenameOperator} +var GetFileIndex = &OperationType{Type: "GET_FILE_INDEX", NumArgs: 0, Precedence: 50, Handler: GetFileIndexOperator} +var GetPath = &OperationType{Type: "GET_PATH", NumArgs: 0, Precedence: 50, Handler: GetPathOperator} + +var Explode = &OperationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: ExplodeOperator} +var SortKeys = &OperationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: SortKeysOperator} + +var CollectObject = &OperationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: CollectObjectOperator} +var TraversePath = &OperationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator} + +var DocumentFilter = &OperationType{Type: "DOCUMENT_FILTER", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator} +var SelfReference = &OperationType{Type: "SELF", NumArgs: 0, Precedence: 50, Handler: SelfOperator} +var ValueOp = &OperationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: ValueOperator} +var Not = &OperationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: NotOperator} +var Empty = &OperationType{Type: "EMPTY", NumArgs: 50, Handler: EmptyOperator} + +var RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator} + +var Select = &OperationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: SelectOperator} +var Has = &OperationType{Type: "HAS", NumArgs: 1, Precedence: 50, Handler: HasOperator} +var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: DeleteChildOperator} + +type Operation struct { + OperationType *OperationType + Value interface{} + StringValue string + CandidateNode *CandidateNode // used for Value Path elements + Preferences interface{} +} + +func CreateValueOperation(value interface{}, stringValue string) *Operation { + var node yaml.Node = yaml.Node{Kind: yaml.ScalarNode} + node.Value = stringValue + + switch value.(type) { + case float32, float64: + node.Tag = "!!float" + case int, int64, int32: + node.Tag = "!!int" + case bool: + node.Tag = "!!bool" + case string: + node.Tag = "!!str" + case nil: + node.Tag = "!!null" + } + + return &Operation{ + OperationType: ValueOp, + Value: value, + StringValue: stringValue, + CandidateNode: &CandidateNode{Node: &node}, + } +} + +// debugging purposes only +func (p *Operation) toString() string { + if p.OperationType == TraversePath { + return fmt.Sprintf("%v", p.Value) + } else if p.OperationType == DocumentFilter { + return fmt.Sprintf("d%v", p.Value) + } else if p.OperationType == SelfReference { + return "SELF" + } else if p.OperationType == ValueOp { + return fmt.Sprintf("%v (%T)", p.Value, p.Value) + } else { + return fmt.Sprintf("%v", p.OperationType.Type) + } +} + +//use for debugging only +func NodesToString(collection *list.List) string { + if !log.IsEnabledFor(logging.DEBUG) { + return "" + } + + result := "" + for el := collection.Front(); el != nil; el = el.Next() { + result = result + "\n" + NodeToString(el.Value.(*CandidateNode)) + } + return result +} + +func NodeToString(node *CandidateNode) string { + if !log.IsEnabledFor(logging.DEBUG) { + return "" + } + value := node.Node + if value == nil { + return "-- nil --" + } + buf := new(bytes.Buffer) + encoder := yaml.NewEncoder(buf) + errorEncoding := encoder.Encode(value) + if errorEncoding != nil { + log.Error("Error debugging node, %v", errorEncoding.Error()) + } + encoder.Close() + tag := value.Tag + if value.Kind == yaml.DocumentNode { + tag = "doc" + } else if value.Kind == yaml.AliasNode { + tag = "alias" + } + return fmt.Sprintf(`D%v, P%v, (%v)::%v`, node.Document, node.Path, tag, buf.String()) } func KindString(kind yaml.Kind) string { @@ -38,171 +174,3 @@ func KindString(kind yaml.Kind) string { return "unknown!" } } - -func DebugNode(value *yaml.Node) { - if value == nil { - log.Debug("-- node is nil --") - } else if log.IsEnabledFor(logging.DEBUG) { - buf := new(bytes.Buffer) - encoder := yaml.NewEncoder(buf) - errorEncoding := encoder.Encode(value) - if errorEncoding != nil { - log.Error("Error debugging node, %v", errorEncoding.Error()) - } - encoder.Close() - log.Debug("Tag: %v, Kind: %v, Anchor: %v", value.Tag, KindString(value.Kind), value.Anchor) - log.Debug("Head Comment: %v", value.HeadComment) - log.Debug("Line Comment: %v", value.LineComment) - log.Debug("FootComment Comment: %v", value.FootComment) - log.Debug("\n%v", buf.String()) - } -} - -func pathStackToString(pathStack []interface{}) string { - return mergePathStackToString(pathStack, UpdateArrayMergeStrategy) -} - -func mergePathStackToString(pathStack []interface{}, arrayMergeStrategy ArrayMergeStrategy) string { - var sb strings.Builder - for index, path := range pathStack { - switch path.(type) { - case int, int64: - if arrayMergeStrategy == AppendArrayMergeStrategy { - sb.WriteString("[+]") - } else { - sb.WriteString(fmt.Sprintf("[%v]", path)) - } - - default: - s := fmt.Sprintf("%v", path) - var _, errParsingInt = strconv.ParseInt(s, 10, 64) // nolint - - hasSpecial := strings.Contains(s, ".") || strings.Contains(s, "[") || strings.Contains(s, "]") || strings.Contains(s, "\"") - hasDoubleQuotes := strings.Contains(s, "\"") - wrappingCharacterStart := "\"" - wrappingCharacterEnd := "\"" - if hasDoubleQuotes { - wrappingCharacterStart = "(" - wrappingCharacterEnd = ")" - } - if hasSpecial || errParsingInt == nil { - sb.WriteString(wrappingCharacterStart) - } - sb.WriteString(s) - if hasSpecial || errParsingInt == nil { - sb.WriteString(wrappingCharacterEnd) - } - } - - if index < len(pathStack)-1 { - sb.WriteString(".") - } - } - return sb.String() -} - -func guessKind(head interface{}, tail []interface{}, guess yaml.Kind) yaml.Kind { - log.Debug("guessKind: tail %v", tail) - if len(tail) == 0 && guess == 0 { - log.Debug("end of path, must be a scalar") - return yaml.ScalarNode - } else if len(tail) == 0 { - return guess - } - var next = tail[0] - switch next.(type) { - case int64: - return yaml.SequenceNode - default: - var nextString = fmt.Sprintf("%v", next) - if nextString == "+" { - return yaml.SequenceNode - } - pathParser := NewPathParser() - if pathParser.IsPathExpression(nextString) && (guess == yaml.SequenceNode || guess == yaml.MappingNode) { - return guess - } else if guess == yaml.AliasNode { - log.Debug("guess was an alias, okey doke.") - return guess - } else if head == "**" { - log.Debug("deep wildcard, go with the guess") - return guess - } - log.Debug("forcing a mapping node") - log.Debug("yaml.SequenceNode %v", guess == yaml.SequenceNode) - log.Debug("yaml.ScalarNode %v", guess == yaml.ScalarNode) - return yaml.MappingNode - } -} - -type YqLib interface { - Get(rootNode *yaml.Node, path string) ([]*NodeContext, error) - GetForMerge(rootNode *yaml.Node, path string, arrayMergeStrategy ArrayMergeStrategy) ([]*NodeContext, error) - Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreate bool) error - New(path string) yaml.Node - - PathStackToString(pathStack []interface{}) string - MergePathStackToString(pathStack []interface{}, arrayMergeStrategy ArrayMergeStrategy) string -} - -type lib struct { - parser PathParser -} - -func NewYqLib() YqLib { - return &lib{ - parser: NewPathParser(), - } -} - -func (l *lib) Get(rootNode *yaml.Node, path string) ([]*NodeContext, error) { - var paths = l.parser.ParsePath(path) - navigationStrategy := ReadNavigationStrategy() - navigator := NewDataNavigator(navigationStrategy) - error := navigator.Traverse(rootNode, paths) - return navigationStrategy.GetVisitedNodes(), error -} - -func (l *lib) GetForMerge(rootNode *yaml.Node, path string, arrayMergeStrategy ArrayMergeStrategy) ([]*NodeContext, error) { - var paths = l.parser.ParsePath(path) - navigationStrategy := ReadForMergeNavigationStrategy(arrayMergeStrategy) - navigator := NewDataNavigator(navigationStrategy) - error := navigator.Traverse(rootNode, paths) - return navigationStrategy.GetVisitedNodes(), error -} - -func (l *lib) PathStackToString(pathStack []interface{}) string { - return pathStackToString(pathStack) -} - -func (l *lib) MergePathStackToString(pathStack []interface{}, arrayMergeStrategy ArrayMergeStrategy) string { - return mergePathStackToString(pathStack, arrayMergeStrategy) -} - -func (l *lib) New(path string) yaml.Node { - var paths = l.parser.ParsePath(path) - newNode := yaml.Node{Kind: guessKind("", paths, 0)} - return newNode -} - -func (l *lib) Update(rootNode *yaml.Node, updateCommand UpdateCommand, autoCreate bool) error { - log.Debugf("%v to %v", updateCommand.Command, updateCommand.Path) - switch updateCommand.Command { - case "update": - var paths = l.parser.ParsePath(updateCommand.Path) - navigator := NewDataNavigator(UpdateNavigationStrategy(updateCommand, autoCreate)) - return navigator.Traverse(rootNode, paths) - case "merge": - var paths = l.parser.ParsePath(updateCommand.Path) - navigator := NewDataNavigator(MergeNavigationStrategy(updateCommand, autoCreate)) - return navigator.Traverse(rootNode, paths) - case "delete": - var paths = l.parser.ParsePath(updateCommand.Path) - lastBit, newTail := paths[len(paths)-1], paths[:len(paths)-1] - navigator := NewDataNavigator(DeleteNavigationStrategy(lastBit)) - return navigator.Traverse(rootNode, newTail) - default: - return fmt.Errorf("Unknown command %v", updateCommand.Command) - } - -} diff --git a/pkg/yqlib/lib_test.go b/pkg/yqlib/lib_test.go deleted file mode 100644 index 52e7933c..00000000 --- a/pkg/yqlib/lib_test.go +++ /dev/null @@ -1,176 +0,0 @@ -package yqlib - -import ( - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -func TestLib(t *testing.T) { - - subject := NewYqLib() - - t.Run("PathStackToString_Empty", func(t *testing.T) { - emptyArray := make([]interface{}, 0) - got := subject.PathStackToString(emptyArray) - test.AssertResult(t, ``, got) - }) - - t.Run("PathStackToString", func(t *testing.T) { - array := make([]interface{}, 3) - array[0] = "a" - array[1] = 0 - array[2] = "b" - got := subject.PathStackToString(array) - test.AssertResult(t, `a.[0].b`, got) - }) - - t.Run("MergePathStackToString", func(t *testing.T) { - array := make([]interface{}, 3) - array[0] = "a" - array[1] = 0 - array[2] = "b" - got := subject.MergePathStackToString(array, AppendArrayMergeStrategy) - test.AssertResult(t, `a.[+].b`, got) - }) - - // t.Run("TestReadPath_WithError", func(t *testing.T) { - // var data = test.ParseData(` - // --- - // b: - // - c - // `) - - // _, err := subject.ReadPath(data, "b.[a]") - // if err == nil { - // t.Fatal("Expected error due to invalid path") - // } - // }) - - // t.Run("TestWritePath", func(t *testing.T) { - // var data = test.ParseData(` - // --- - // b: - // 2: c - // `) - - // got := subject.WritePath(data, "b.3", "a") - // test.AssertResult(t, `[{b [{2 c} {3 a}]}]`, fmt.Sprintf("%v", got)) - // }) - - // t.Run("TestPrefixPath", func(t *testing.T) { - // var data = test.ParseData(` - // --- - // b: - // 2: c - // `) - - // got := subject.PrefixPath(data, "a.d") - // test.AssertResult(t, `[{a [{d [{b [{2 c}]}]}]}]`, fmt.Sprintf("%v", got)) - // }) - - // t.Run("TestDeletePath", func(t *testing.T) { - // var data = test.ParseData(` - // --- - // b: - // 2: c - // 3: a - // `) - - // got, _ := subject.DeletePath(data, "b.2") - // test.AssertResult(t, `[{b [{3 a}]}]`, fmt.Sprintf("%v", got)) - // }) - - // t.Run("TestDeletePath_WithError", func(t *testing.T) { - // var data = test.ParseData(` - // --- - // b: - // - c - // `) - - // _, err := subject.DeletePath(data, "b.[a]") - // if err == nil { - // t.Fatal("Expected error due to invalid path") - // } - // }) - - // t.Run("TestMerge", func(t *testing.T) { - // var dst = test.ParseData(` - // --- - // a: b - // c: d - // `) - // var src = test.ParseData(` - // --- - // a: 1 - // b: 2 - // `) - - // var mergedData = make(map[interface{}]interface{}) - // mergedData["root"] = dst - // var mapDataBucket = make(map[interface{}]interface{}) - // mapDataBucket["root"] = src - - // err := subject.Merge(&mergedData, mapDataBucket, false, false) - // if err != nil { - // t.Fatal("Unexpected error") - // } - // test.AssertResult(t, `[{a b} {c d}]`, fmt.Sprintf("%v", mergedData["root"])) - // }) - - // t.Run("TestMerge_WithOverwrite", func(t *testing.T) { - // var dst = test.ParseData(` - // --- - // a: b - // c: d - // `) - // var src = test.ParseData(` - // --- - // a: 1 - // b: 2 - // `) - - // var mergedData = make(map[interface{}]interface{}) - // mergedData["root"] = dst - // var mapDataBucket = make(map[interface{}]interface{}) - // mapDataBucket["root"] = src - - // err := subject.Merge(&mergedData, mapDataBucket, true, false) - // if err != nil { - // t.Fatal("Unexpected error") - // } - // test.AssertResult(t, `[{a 1} {b 2}]`, fmt.Sprintf("%v", mergedData["root"])) - // }) - - // t.Run("TestMerge_WithAppend", func(t *testing.T) { - // var dst = test.ParseData(` - // --- - // a: b - // c: d - // `) - // var src = test.ParseData(` - // --- - // a: 1 - // b: 2 - // `) - - // var mergedData = make(map[interface{}]interface{}) - // mergedData["root"] = dst - // var mapDataBucket = make(map[interface{}]interface{}) - // mapDataBucket["root"] = src - - // err := subject.Merge(&mergedData, mapDataBucket, false, true) - // if err != nil { - // t.Fatal("Unexpected error") - // } - // test.AssertResult(t, `[{a b} {c d} {a 1} {b 2}]`, fmt.Sprintf("%v", mergedData["root"])) - // }) - - // t.Run("TestMerge_WithError", func(t *testing.T) { - // err := subject.Merge(nil, nil, false, false) - // if err == nil { - // t.Fatal("Expected error due to nil") - // } - // }) - -} diff --git a/pkg/yqlib/matchKeyString.go b/pkg/yqlib/matchKeyString.go new file mode 100644 index 00000000..0ccb4f91 --- /dev/null +++ b/pkg/yqlib/matchKeyString.go @@ -0,0 +1,34 @@ +package yqlib + +func Match(name string, pattern string) (matched bool) { + if pattern == "" { + return name == pattern + } + log.Debug("pattern: %v", pattern) + if pattern == "*" { + log.Debug("wild!") + return true + } + return deepMatch([]rune(name), []rune(pattern)) +} + +func deepMatch(str, pattern []rune) bool { + for len(pattern) > 0 { + switch pattern[0] { + default: + if len(str) == 0 || str[0] != pattern[0] { + return false + } + case '?': + if len(str) == 0 { + return false + } + case '*': + return deepMatch(str, pattern[1:]) || + (len(str) > 0 && deepMatch(str[1:], pattern)) + } + str = str[1:] + pattern = pattern[1:] + } + return len(str) == 0 && len(pattern) == 0 +} diff --git a/pkg/yqlib/merge_navigation_strategy.go b/pkg/yqlib/merge_navigation_strategy.go deleted file mode 100644 index 686ba4eb..00000000 --- a/pkg/yqlib/merge_navigation_strategy.go +++ /dev/null @@ -1,103 +0,0 @@ -package yqlib - -import "gopkg.in/yaml.v3" - -type ArrayMergeStrategy uint32 - -const ( - UpdateArrayMergeStrategy ArrayMergeStrategy = 1 << iota - OverwriteArrayMergeStrategy - AppendArrayMergeStrategy -) - -type CommentsMergeStrategy uint32 - -const ( - SetWhenBlankCommentsMergeStrategy CommentsMergeStrategy = 1 << iota - IgnoreCommentsMergeStrategy - OverwriteCommentsMergeStrategy - AppendCommentsMergeStrategy -) - -func MergeNavigationStrategy(updateCommand UpdateCommand, autoCreate bool) NavigationStrategy { - return &NavigationStrategyImpl{ - visitedNodes: []*NodeContext{}, - pathParser: NewPathParser(), - followAlias: func(nodeContext NodeContext) bool { - return false - }, - autoCreateMap: func(nodeContext NodeContext) bool { - return autoCreate - }, - visit: func(nodeContext NodeContext) error { - node := nodeContext.Node - changesToApply := updateCommand.Value - - if node.Kind == yaml.DocumentNode && changesToApply.Kind != yaml.DocumentNode { - // when the path is empty, it matches both the top level pseudo document node - // and the actual top level node (e.g. map/sequence/whatever) - // so when we are updating with no path, make sure we update the right node. - node = node.Content[0] - } - - log.Debug("going to update") - DebugNode(node) - log.Debug("with") - DebugNode(changesToApply) - - if updateCommand.Overwrite || node.Value == "" { - node.Value = changesToApply.Value - node.Tag = changesToApply.Tag - node.Kind = changesToApply.Kind - node.Style = changesToApply.Style - node.Anchor = changesToApply.Anchor - node.Alias = changesToApply.Alias - - if !updateCommand.DontUpdateNodeContent { - node.Content = changesToApply.Content - } - } else { - log.Debug("skipping update as node already has value %v and overwriteFlag is ", node.Value, updateCommand.Overwrite) - } - - switch updateCommand.CommentsMergeStrategy { - case OverwriteCommentsMergeStrategy: - node.HeadComment = changesToApply.HeadComment - node.LineComment = changesToApply.LineComment - node.FootComment = changesToApply.FootComment - case SetWhenBlankCommentsMergeStrategy: - if node.HeadComment == "" { - node.HeadComment = changesToApply.HeadComment - } - if node.LineComment == "" { - node.LineComment = changesToApply.LineComment - } - if node.FootComment == "" { - node.FootComment = changesToApply.FootComment - } - case AppendCommentsMergeStrategy: - if node.HeadComment == "" { - node.HeadComment = changesToApply.HeadComment - } else { - node.HeadComment = node.HeadComment + "\n" + changesToApply.HeadComment - } - if node.LineComment == "" { - node.LineComment = changesToApply.LineComment - } else { - node.LineComment = node.LineComment + " " + changesToApply.LineComment - } - if node.FootComment == "" { - node.FootComment = changesToApply.FootComment - } else { - node.FootComment = node.FootComment + "\n" + changesToApply.FootComment - } - default: - } - - log.Debug("result") - DebugNode(node) - - return nil - }, - } -} diff --git a/pkg/yqlib/navigation_strategy.go b/pkg/yqlib/navigation_strategy.go deleted file mode 100644 index e88641d0..00000000 --- a/pkg/yqlib/navigation_strategy.go +++ /dev/null @@ -1,180 +0,0 @@ -package yqlib - -import ( - "fmt" - - yaml "gopkg.in/yaml.v3" -) - -type NodeContext struct { - Node *yaml.Node - Head interface{} - Tail []interface{} - PathStack []interface{} - // middle nodes are nodes that match along the original path, but not a - // target match of the path. This is only relevant when ShouldOnlyDeeplyVisitLeaves is false. - IsMiddleNode bool -} - -func NewNodeContext(node *yaml.Node, head interface{}, tail []interface{}, pathStack []interface{}) NodeContext { - newTail := make([]interface{}, len(tail)) - copy(newTail, tail) - - newPathStack := make([]interface{}, len(pathStack)) - copy(newPathStack, pathStack) - return NodeContext{ - Node: node, - Head: head, - Tail: newTail, - PathStack: newPathStack, - } -} - -type NavigationStrategy interface { - FollowAlias(nodeContext NodeContext) bool - AutoCreateMap(nodeContext NodeContext) bool - Visit(nodeContext NodeContext) error - // node key is the string value of the last element in the path stack - // we use it to match against the pathExpression in head. - ShouldTraverse(nodeContext NodeContext, nodeKey string) bool - ShouldDeeplyTraverse(nodeContext NodeContext) bool - // when deeply traversing, should we visit all matching nodes, or just leaves? - ShouldOnlyDeeplyVisitLeaves(NodeContext) bool - GetVisitedNodes() []*NodeContext - DebugVisitedNodes() - GetPathParser() PathParser -} - -type NavigationStrategyImpl struct { - followAlias func(nodeContext NodeContext) bool - autoCreateMap func(nodeContext NodeContext) bool - visit func(nodeContext NodeContext) error - shouldVisitExtraFn func(nodeContext NodeContext) bool - shouldDeeplyTraverse func(nodeContext NodeContext) bool - shouldOnlyDeeplyVisitLeaves func(nodeContext NodeContext) bool - visitedNodes []*NodeContext - pathParser PathParser -} - -func (ns *NavigationStrategyImpl) GetPathParser() PathParser { - return ns.pathParser -} - -func (ns *NavigationStrategyImpl) GetVisitedNodes() []*NodeContext { - return ns.visitedNodes -} - -func (ns *NavigationStrategyImpl) FollowAlias(nodeContext NodeContext) bool { - if ns.followAlias != nil { - return ns.followAlias(nodeContext) - } - return true -} - -func (ns *NavigationStrategyImpl) AutoCreateMap(nodeContext NodeContext) bool { - if ns.autoCreateMap != nil { - return ns.autoCreateMap(nodeContext) - } - return false -} - -func (ns *NavigationStrategyImpl) ShouldDeeplyTraverse(nodeContext NodeContext) bool { - if ns.shouldDeeplyTraverse != nil { - return ns.shouldDeeplyTraverse(nodeContext) - } - return true -} - -func (ns *NavigationStrategyImpl) ShouldOnlyDeeplyVisitLeaves(nodeContext NodeContext) bool { - if ns.shouldOnlyDeeplyVisitLeaves != nil { - return ns.shouldOnlyDeeplyVisitLeaves(nodeContext) - } - return true - -} - -func (ns *NavigationStrategyImpl) ShouldTraverse(nodeContext NodeContext, nodeKey string) bool { - // we should traverse aliases (if enabled), but not visit them :/ - if len(nodeContext.PathStack) == 0 { - return true - } - - if ns.alreadyVisited(nodeContext.PathStack) { - return false - } - - return (nodeKey == "<<" && ns.FollowAlias(nodeContext)) || (nodeKey != "<<" && - ns.pathParser.MatchesNextPathElement(nodeContext, nodeKey)) -} - -func (ns *NavigationStrategyImpl) shouldVisit(nodeContext NodeContext) bool { - pathStack := nodeContext.PathStack - if len(pathStack) == 0 { - return true - } - log.Debug("tail len %v", len(nodeContext.Tail)) - - if ns.alreadyVisited(pathStack) || len(nodeContext.Tail) != 0 { - return false - } - - nodeKey := fmt.Sprintf("%v", pathStack[len(pathStack)-1]) - log.Debug("nodeKey: %v, nodeContext.Head: %v", nodeKey, nodeContext.Head) - - // only visit aliases if its an exact match - return ((nodeKey == "<<" && nodeContext.Head == "<<") || (nodeKey != "<<" && - ns.pathParser.MatchesNextPathElement(nodeContext, nodeKey))) && (ns.shouldVisitExtraFn == nil || ns.shouldVisitExtraFn(nodeContext)) -} - -func (ns *NavigationStrategyImpl) Visit(nodeContext NodeContext) error { - log.Debug("Visit?, %v, %v", nodeContext.Head, pathStackToString(nodeContext.PathStack)) - DebugNode(nodeContext.Node) - if ns.shouldVisit(nodeContext) { - log.Debug("yep, visiting") - // pathStack array must be - // copied, as append() may sometimes reuse and modify the array - ns.visitedNodes = append(ns.visitedNodes, &nodeContext) - ns.DebugVisitedNodes() - return ns.visit(nodeContext) - } - log.Debug("nope, skip it") - return nil -} - -func (ns *NavigationStrategyImpl) DebugVisitedNodes() { - log.Debug("Visited Nodes:") - for _, candidate := range ns.visitedNodes { - log.Debug(" - %v", pathStackToString(candidate.PathStack)) - } -} - -func (ns *NavigationStrategyImpl) alreadyVisited(pathStack []interface{}) bool { - log.Debug("checking already visited pathStack: %v", pathStackToString(pathStack)) - for _, candidate := range ns.visitedNodes { - candidatePathStack := candidate.PathStack - if patchStacksMatch(candidatePathStack, pathStack) { - log.Debug("paths match, already seen it") - return true - } - - } - log.Debug("never seen it before!") - return false -} - -func patchStacksMatch(path1 []interface{}, path2 []interface{}) bool { - log.Debug("checking against path: %v", pathStackToString(path1)) - - if len(path1) != len(path2) { - return false - } - for index, p1Value := range path1 { - - p2Value := path2[index] - if p1Value != p2Value { - return false - } - } - return true - -} diff --git a/pkg/yqlib/operator_add.go b/pkg/yqlib/operator_add.go new file mode 100644 index 00000000..4bab2f22 --- /dev/null +++ b/pkg/yqlib/operator_add.go @@ -0,0 +1,83 @@ +package yqlib + +import ( + "fmt" + + "container/list" + + yaml "gopkg.in/yaml.v3" +) + +func createSelfAddOp(rhs *PathTreeNode) *PathTreeNode { + return &PathTreeNode{Operation: &Operation{OperationType: Add}, + Lhs: &PathTreeNode{Operation: &Operation{OperationType: SelfReference}}, + Rhs: rhs} +} + +func AddAssignOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + assignmentOp := &Operation{OperationType: Assign} + assignmentOp.Preferences = &AssignOpPreferences{true} + + assignmentOpNode := &PathTreeNode{Operation: assignmentOp, Lhs: pathNode.Lhs, Rhs: createSelfAddOp(pathNode.Rhs)} + return d.GetMatchingNodes(matchingNodes, assignmentOpNode) +} + +func toNodes(candidates *list.List) []*yaml.Node { + + if candidates.Len() == 0 { + return []*yaml.Node{} + } + candidate := candidates.Front().Value.(*CandidateNode) + + if candidate.Node.Tag == "!!null" { + return []*yaml.Node{} + } + + switch candidate.Node.Kind { + case yaml.SequenceNode: + return candidate.Node.Content + default: + return []*yaml.Node{candidate.Node} + } + +} + +func AddOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("Add operator") + var results = list.New() + lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs) + + if err != nil { + return nil, err + } + + for el := lhs.Front(); el != nil; el = el.Next() { + lhsCandidate := el.Value.(*CandidateNode) + lhsNode := UnwrapDoc(lhsCandidate.Node) + + target := &CandidateNode{ + Path: lhsCandidate.Path, + Document: lhsCandidate.Document, + Filename: lhsCandidate.Filename, + Node: &yaml.Node{}, + } + + switch lhsNode.Kind { + case yaml.MappingNode: + return nil, fmt.Errorf("Maps not yet supported for addition") + case yaml.SequenceNode: + target.Node.Kind = yaml.SequenceNode + target.Node.Style = lhsNode.Style + target.Node.Tag = "!!seq" + target.Node.Content = append(lhsNode.Content, toNodes(rhs)...) + results.PushBack(target) + case yaml.ScalarNode: + return nil, fmt.Errorf("Scalars not yet supported for addition") + } + } + return results, nil +} diff --git a/pkg/yqlib/operator_add_test.go b/pkg/yqlib/operator_add_test.go new file mode 100644 index 00000000..04092635 --- /dev/null +++ b/pkg/yqlib/operator_add_test.go @@ -0,0 +1,63 @@ +package yqlib + +import ( + "testing" +) + +var addOperatorScenarios = []expressionScenario{ + { + description: "Concatenate and assign arrays", + document: `{a: {val: thing, b: [cat,dog]}}`, + expression: ".a.b += [\"cow\"]", + expected: []string{ + "D0, P[], (doc)::{a: {val: thing, b: [cat, dog, cow]}}\n", + }, + }, + { + description: "Concatenate arrays", + document: `{a: [1,2], b: [3,4]}`, + expression: `.a + .b`, + expected: []string{ + "D0, P[a], (!!seq)::[1, 2, 3, 4]\n", + }, + }, + { + description: "Concatenate null to array", + document: `{a: [1,2]}`, + expression: `.a + null`, + expected: []string{ + "D0, P[a], (!!seq)::[1, 2]\n", + }, + }, + { + description: "Add object to array", + document: `{a: [1,2], c: {cat: meow}}`, + expression: `.a + .c`, + expected: []string{ + "D0, P[a], (!!seq)::[1, 2, {cat: meow}]\n", + }, + }, + { + description: "Add string to array", + document: `{a: [1,2]}`, + expression: `.a + "hello"`, + expected: []string{ + "D0, P[a], (!!seq)::[1, 2, hello]\n", + }, + }, + { + description: "Update array (append)", + document: `{a: [1,2], b: [3,4]}`, + expression: `.a = .a + .b`, + expected: []string{ + "D0, P[], (doc)::{a: [1, 2, 3, 4], b: [3, 4]}\n", + }, + }, +} + +func TestAddOperatorScenarios(t *testing.T) { + for _, tt := range addOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Add", addOperatorScenarios) +} diff --git a/pkg/yqlib/operator_assign.go b/pkg/yqlib/operator_assign.go new file mode 100644 index 00000000..82c7db04 --- /dev/null +++ b/pkg/yqlib/operator_assign.go @@ -0,0 +1,70 @@ +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) + + if preferences.UpdateAssign { + rhs, err = d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs) + } + + if err != nil { + return nil, err + } + + // grab the first value + first := rhs.Front() + + if first != nil { + candidate.UpdateFrom(first.Value.(*CandidateNode)) + } + } + // // if there was nothing given, perhaps we are creating a new yaml doc + // if matchingNodes.Len() == 0 { + // log.Debug("started with nothing, returning LHS, %v", lhs.Len()) + // return lhs, nil + // } + return matchingNodes, nil +} + +// does not update content or values +func AssignAttributesOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + 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) + + rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs) + + if err != nil { + return nil, err + } + + // grab the first value + first := rhs.Front() + + if first != nil { + candidate.UpdateAttributesFrom(first.Value.(*CandidateNode)) + } + } + return matchingNodes, nil +} diff --git a/pkg/yqlib/operator_assign_test.go b/pkg/yqlib/operator_assign_test.go new file mode 100644 index 00000000..67eacc74 --- /dev/null +++ b/pkg/yqlib/operator_assign_test.go @@ -0,0 +1,129 @@ +package yqlib + +import ( + "testing" +) + +var assignOperatorScenarios = []expressionScenario{ + { + description: "Create yaml file", + expression: `.a.b = "cat" | .x = "frog"`, + expected: []string{ + "D0, P[], ()::a:\n b: cat\nx: frog\n", + }, + }, + { + description: "Update node to be the child value", + document: `{a: {b: {g: foof}}}`, + expression: `.a |= .b`, + expected: []string{ + "D0, P[], (doc)::{a: {g: foof}}\n", + }, + }, + { + description: "Update node 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", + dontFormatInputForDoc: true, + document: `{}`, + expression: `.a.b |= "bogs"`, + expected: []string{ + "D0, P[], (doc)::{a: {b: bogs}}\n", + }, + }, + { + description: "Update empty object and array", + dontFormatInputForDoc: true, + 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", assignOperatorScenarios) +} diff --git a/pkg/yqlib/operator_booleans.go b/pkg/yqlib/operator_booleans.go new file mode 100644 index 00000000..74c32cea --- /dev/null +++ b/pkg/yqlib/operator_booleans.go @@ -0,0 +1,113 @@ +package yqlib + +import ( + "container/list" + + yaml "gopkg.in/yaml.v3" +) + +func isTruthy(c *CandidateNode) (bool, error) { + node := UnwrapDoc(c.Node) + value := true + + if node.Tag == "!!null" { + return false, nil + } + if node.Kind == yaml.ScalarNode && node.Tag == "!!bool" { + errDecoding := node.Decode(&value) + if errDecoding != nil { + return false, errDecoding + } + + } + return value, nil +} + +type boolOp func(bool, bool) bool + +func performBoolOp(results *list.List, lhs *list.List, rhs *list.List, op boolOp) error { + for lhsChild := lhs.Front(); lhsChild != nil; lhsChild = lhsChild.Next() { + lhsCandidate := lhsChild.Value.(*CandidateNode) + lhsTrue, errDecoding := isTruthy(lhsCandidate) + if errDecoding != nil { + return errDecoding + } + + for rhsChild := rhs.Front(); rhsChild != nil; rhsChild = rhsChild.Next() { + rhsCandidate := rhsChild.Value.(*CandidateNode) + rhsTrue, errDecoding := isTruthy(rhsCandidate) + if errDecoding != nil { + return errDecoding + } + boolResult := createBooleanCandidate(lhsCandidate, op(lhsTrue, rhsTrue)) + results.PushBack(boolResult) + } + } + return nil +} + +func booleanOp(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode, op boolOp) (*list.List, error) { + var results = list.New() + + if matchingNodes.Len() == 0 { + lhs, err := d.GetMatchingNodes(list.New(), pathNode.Lhs) + if err != nil { + return nil, err + } + rhs, err := d.GetMatchingNodes(list.New(), pathNode.Rhs) + if err != nil { + return nil, err + } + return results, performBoolOp(results, lhs, rhs, op) + } + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + lhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Lhs) + if err != nil { + return nil, err + } + rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs) + if err != nil { + return nil, err + } + + err = performBoolOp(results, lhs, rhs, op) + if err != nil { + return nil, err + } + + } + return results, nil +} + +func OrOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- orOp") + return booleanOp(d, matchingNodes, pathNode, func(b1 bool, b2 bool) bool { + return b1 || b2 + }) +} + +func AndOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- AndOp") + return booleanOp(d, matchingNodes, pathNode, func(b1 bool, b2 bool) bool { + return b1 && b2 + }) +} + +func NotOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- notOperation") + var results = list.New() + + for el := matchMap.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + log.Debug("notOperation checking %v", candidate) + truthy, errDecoding := isTruthy(candidate) + if errDecoding != nil { + return nil, errDecoding + } + result := createBooleanCandidate(candidate, !truthy) + results.PushBack(result) + } + return results, nil +} diff --git a/pkg/yqlib/operator_booleans_test.go b/pkg/yqlib/operator_booleans_test.go new file mode 100644 index 00000000..ba2b6aa2 --- /dev/null +++ b/pkg/yqlib/operator_booleans_test.go @@ -0,0 +1,105 @@ +package yqlib + +import ( + "testing" +) + +var booleanOperatorScenarios = []expressionScenario{ + { + description: "OR example", + expression: `true or false`, + expected: []string{ + "D0, P[], (!!bool)::true\n", + }, + }, + { + description: "AND example", + expression: `true and false`, + expected: []string{ + "D0, P[], (!!bool)::false\n", + }, + }, + { + document: "[{a: bird, b: dog}, {a: frog, b: bird}, {a: cat, b: fly}]", + description: "Matching nodes with select, equals and or", + expression: `[.[] | select(.a == "cat" or .b == "dog")]`, + expected: []string{ + "D0, P[], (!!seq)::- {a: bird, b: dog}\n- {a: cat, b: fly}\n", + }, + }, + { + skipDoc: true, + expression: `false or false`, + expected: []string{ + "D0, P[], (!!bool)::false\n", + }, + }, + { + skipDoc: true, + document: `{a: true, b: false}`, + expression: `.[] or (false, true)`, + expected: []string{ + "D0, P[a], (!!bool)::true\n", + "D0, P[a], (!!bool)::true\n", + "D0, P[b], (!!bool)::false\n", + "D0, P[b], (!!bool)::true\n", + }, + }, + { + 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", + }, + }, +} + +func TestBooleanOperatorScenarios(t *testing.T) { + for _, tt := range booleanOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Boolean Operators", booleanOperatorScenarios) +} diff --git a/pkg/yqlib/operator_collect.go b/pkg/yqlib/operator_collect.go new file mode 100644 index 00000000..ffdfda62 --- /dev/null +++ b/pkg/yqlib/operator_collect.go @@ -0,0 +1,39 @@ +package yqlib + +import ( + "container/list" + + yaml "gopkg.in/yaml.v3" +) + +func CollectOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- collectOperation") + + if matchMap.Len() == 0 { + node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq", Value: "[]"} + candidate := &CandidateNode{Node: node} + return nodeToMap(candidate), nil + } + + var results = list.New() + + node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} + + var document uint = 0 + var path []interface{} + + for el := matchMap.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + log.Debugf("Collecting %v", NodeToString(candidate)) + if path == nil && candidate.Path != nil && len(candidate.Path) > 1 { + path = candidate.Path[:len(candidate.Path)-1] + document = candidate.Document + } + node.Content = append(node.Content, candidate.Node) + } + + collectC := &CandidateNode{Node: node, Document: document, Path: path} + results.PushBack(collectC) + + return results, nil +} diff --git a/pkg/yqlib/operator_collect_object.go b/pkg/yqlib/operator_collect_object.go new file mode 100644 index 00000000..147fc633 --- /dev/null +++ b/pkg/yqlib/operator_collect_object.go @@ -0,0 +1,106 @@ +package yqlib + +import ( + "container/list" + + yaml "gopkg.in/yaml.v3" +) + +/* +[Mike: cat, Bob: dog] +[Thing: rabbit, peter: sam] + +==> cross multiply + +{Mike: cat, Thing: rabbit} +{Mike: cat, peter: sam} +... +*/ + +func CollectObjectOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- collectObjectOperation") + + if matchMap.Len() == 0 { + node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Value: "{}"} + candidate := &CandidateNode{Node: node} + return nodeToMap(candidate), nil + } + first := matchMap.Front().Value.(*CandidateNode) + var rotated []*list.List = make([]*list.List, len(first.Node.Content)) + + for i := 0; i < len(first.Node.Content); i++ { + rotated[i] = list.New() + } + + for el := matchMap.Front(); el != nil; el = el.Next() { + candidateNode := el.Value.(*CandidateNode) + for i := 0; i < len(first.Node.Content); i++ { + rotated[i].PushBack(createChildCandidate(candidateNode, i)) + } + } + + newObject := list.New() + for i := 0; i < len(first.Node.Content); i++ { + additions, err := collect(d, list.New(), rotated[i]) + if err != nil { + return nil, err + } + newObject.PushBackList(additions) + } + + return newObject, nil + +} + +func createChildCandidate(candidate *CandidateNode, index int) *CandidateNode { + return &CandidateNode{ + Document: candidate.Document, + Path: append(candidate.Path, index), + Filename: candidate.Filename, + Node: candidate.Node.Content[index], + } +} + +func collect(d *dataTreeNavigator, aggregate *list.List, remainingMatches *list.List) (*list.List, error) { + if remainingMatches.Len() == 0 { + return aggregate, nil + } + + candidate := remainingMatches.Remove(remainingMatches.Front()).(*CandidateNode) + splatted, err := Splat(d, nodeToMap(candidate)) + + for splatEl := splatted.Front(); splatEl != nil; splatEl = splatEl.Next() { + splatEl.Value.(*CandidateNode).Path = nil + } + + if err != nil { + return nil, err + } + + if aggregate.Len() == 0 { + return collect(d, splatted, remainingMatches) + } + + newAgg := list.New() + + for el := aggregate.Front(); el != nil; el = el.Next() { + aggCandidate := el.Value.(*CandidateNode) + for splatEl := splatted.Front(); splatEl != nil; splatEl = splatEl.Next() { + splatCandidate := splatEl.Value.(*CandidateNode) + newCandidate, err := aggCandidate.Copy() + if err != nil { + return nil, err + } + + newCandidate.Path = nil + + newCandidate, err = multiply(&MultiplyPreferences{AppendArrays: false})(d, newCandidate, splatCandidate) + if err != nil { + return nil, err + } + newAgg.PushBack(newCandidate) + } + } + return collect(d, newAgg, remainingMatches) + +} diff --git a/pkg/yqlib/operator_collect_object_test.go b/pkg/yqlib/operator_collect_object_test.go new file mode 100644 index 00000000..165b0c9c --- /dev/null +++ b/pkg/yqlib/operator_collect_object_test.go @@ -0,0 +1,114 @@ +package yqlib + +import ( + "testing" +) + +var collectObjectOperatorScenarios = []expressionScenario{ + { + description: `Collect empty object`, + document: ``, + expression: `{}`, + expected: []string{ + "D0, P[], (!!map)::{}\n", + }, + }, + { + 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{ + "D0, P[], (!!map)::wrap: {name: Mike}\n", + "D0, P[], (!!map)::wrap: {name: Bob}\n", + }, + }, + { + skipDoc: true, + document: `{name: Mike, age: 32}`, + expression: `{.name: .age}`, + expected: []string{ + "D0, P[], (!!map)::Mike: 32\n", + }, + }, + { + 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", + }, + }, + { + 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", + "D0, P[], (!!map)::Rosey: monkey\n", + "D0, P[], (!!map)::Rosey: sheep\n", + }, + }, + { + skipDoc: true, + document: `{name: Mike, pets: [cat, dog], food: [hotdog, burger]}`, + expression: `{.name: .pets.[], "f":.food.[]}`, + expected: []string{ + "D0, P[], (!!map)::Mike: cat\nf: hotdog\n", + "D0, P[], (!!map)::Mike: cat\nf: burger\n", + "D0, P[], (!!map)::Mike: dog\nf: hotdog\n", + "D0, P[], (!!map)::Mike: dog\nf: burger\n", + }, + }, + { + skipDoc: true, + document: `{name: Mike, pets: {cows: [apl, bba]}}`, + expression: `{"a":.name, "b":.pets}`, + expected: []string{ + `D0, P[], (!!map)::a: Mike +b: {cows: [apl, bba]} +`, + }, + }, + { + 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{ + "D0, P[], (!!map)::wrap: {name: Mike}\n", + }, + }, + { + skipDoc: true, + document: `{name: Mike}`, + expression: `{"wrap": {"further": .}} | (.. style= "flow")`, + expected: []string{ + "D0, P[], (!!map)::{wrap: {further: {name: Mike}}}\n", + }, + }, +} + +func TestCollectObjectOperatorScenarios(t *testing.T) { + for _, tt := range collectObjectOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Collect into Object", collectObjectOperatorScenarios) +} diff --git a/pkg/yqlib/operator_collect_test.go b/pkg/yqlib/operator_collect_test.go new file mode 100644 index 00000000..9441ae10 --- /dev/null +++ b/pkg/yqlib/operator_collect_test.go @@ -0,0 +1,78 @@ +package yqlib + +import ( + "testing" +) + +var collectOperatorScenarios = []expressionScenario{ + { + description: "Collect empty", + document: ``, + expression: `[]`, + expected: []string{ + "D0, P[], (!!seq)::[]\n", + }, + }, + { + skipDoc: true, + document: ``, + expression: `[3]`, + expected: []string{ + "D0, P[], (!!seq)::- 3\n", + }, + }, + { + 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", + }, + }, + { + 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", + }, + }, +} + +func TestCollectOperatorScenarios(t *testing.T) { + for _, tt := range collectOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Collect into Array", collectOperatorScenarios) +} diff --git a/pkg/yqlib/operator_comments.go b/pkg/yqlib/operator_comments.go new file mode 100644 index 00000000..58cc17ac --- /dev/null +++ b/pkg/yqlib/operator_comments.go @@ -0,0 +1,76 @@ +package yqlib + +import ( + "container/list" + "strings" + + "gopkg.in/yaml.v3" +) + +type CommentOpPreferences struct { + LineComment bool + HeadComment bool + FootComment bool +} + +func AssignCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + + log.Debugf("AssignComments operator!") + + rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs) + if err != nil { + return nil, err + } + comment := "" + if rhs.Front() != nil { + comment = rhs.Front().Value.(*CandidateNode).Node.Value + } + + lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs) + + if err != nil { + return nil, err + } + + preferences := pathNode.Operation.Preferences.(*CommentOpPreferences) + + for el := lhs.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + log.Debugf("Setting comment of : %v", candidate.GetKey()) + if preferences.LineComment { + candidate.Node.LineComment = comment + } + if preferences.HeadComment { + candidate.Node.HeadComment = comment + } + if preferences.FootComment { + candidate.Node.FootComment = comment + } + + } + return matchingNodes, nil +} + +func GetCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + preferences := pathNode.Operation.Preferences.(*CommentOpPreferences) + log.Debugf("GetComments operator!") + var results = list.New() + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + comment := "" + if preferences.LineComment { + comment = candidate.Node.LineComment + } else if preferences.HeadComment { + comment = candidate.Node.HeadComment + } else if preferences.FootComment { + comment = candidate.Node.FootComment + } + comment = strings.Replace(comment, "# ", "", 1) + + node := &yaml.Node{Kind: yaml.ScalarNode, Value: comment, Tag: "!!str"} + lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} + results.PushBack(lengthCand) + } + return results, nil +} diff --git a/pkg/yqlib/operator_comments_test.go b/pkg/yqlib/operator_comments_test.go new file mode 100644 index 00000000..7405624f --- /dev/null +++ b/pkg/yqlib/operator_comments_test.go @@ -0,0 +1,79 @@ +package yqlib + +import ( + "testing" +) + +var commentOperatorScenarios = []expressionScenario{ + { + description: "Set line comment", + document: `a: cat`, + expression: `.a lineComment="single"`, + expected: []string{ + "D0, P[], (doc)::a: cat # single\n", + }, + }, + { + description: "Set head comment", + document: `a: cat`, + expression: `. headComment="single"`, + expected: []string{ + "D0, P[], (doc)::# single\n\na: cat\n", + }, + }, + { + description: "Set foot comment, using an expression", + document: `a: cat`, + expression: `. footComment=.a`, + expected: []string{ + "D0, P[], (doc)::a: cat\n\n# cat\n", + }, + }, + { + description: "Remove comment", + document: "a: cat # comment\nb: dog # leave this", + expression: `.a lineComment=""`, + expected: []string{ + "D0, P[], (doc)::a: cat\nb: dog # leave this\n", + }, + }, + { + description: "Remove all comments", + document: "# hi\n\na: cat # comment\n\n# great\n", + expression: `.. comments=""`, + expected: []string{ + "D0, P[], (!!map)::a: cat\n", + }, + }, + { + description: "Get line comment", + document: "# welcome!\n\na: cat # meow\n\n# have a great day", + expression: `.a | lineComment`, + expected: []string{ + "D0, P[a], (!!str)::meow\n", + }, + }, + { + description: "Get head comment", + document: "# welcome!\n\na: cat # meow\n\n# have a great day", + expression: `. | headComment`, + expected: []string{ + "D0, P[], (!!str)::welcome!\n", + }, + }, + { + description: "Get foot comment", + document: "# welcome!\n\na: cat # meow\n\n# have a great day", + expression: `. | footComment`, + expected: []string{ + "D0, P[], (!!str)::have a great day\n", + }, + }, +} + +func TestCommentOperatorScenarios(t *testing.T) { + for _, tt := range commentOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Comment Operators", commentOperatorScenarios) +} diff --git a/pkg/yqlib/operator_create_map.go b/pkg/yqlib/operator_create_map.go new file mode 100644 index 00000000..28cbad9d --- /dev/null +++ b/pkg/yqlib/operator_create_map.go @@ -0,0 +1,85 @@ +package yqlib + +import ( + "container/list" + + "gopkg.in/yaml.v3" +) + +func CreateMapOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- createMapOperation") + + //each matchingNodes entry should turn into a sequence of keys to create. + //then collect object should do a cross function of the same index sequence for all matches. + + var path []interface{} + + var document uint = 0 + + sequences := list.New() + + if matchingNodes.Len() > 0 { + + for matchingNodeEl := matchingNodes.Front(); matchingNodeEl != nil; matchingNodeEl = matchingNodeEl.Next() { + matchingNode := matchingNodeEl.Value.(*CandidateNode) + sequenceNode, err := sequenceFor(d, matchingNode, pathNode) + if err != nil { + return nil, err + } + sequences.PushBack(sequenceNode) + } + } else { + sequenceNode, err := sequenceFor(d, nil, pathNode) + if err != nil { + return nil, err + } + sequences.PushBack(sequenceNode) + } + + return nodeToMap(&CandidateNode{Node: listToNodeSeq(sequences), Document: document, Path: path}), nil + +} + +func sequenceFor(d *dataTreeNavigator, matchingNode *CandidateNode, pathNode *PathTreeNode) (*CandidateNode, error) { + var path []interface{} + var document uint = 0 + var matches = list.New() + + if matchingNode != nil { + path = matchingNode.Path + document = matchingNode.Document + matches = nodeToMap(matchingNode) + } + + mapPairs, err := crossFunction(d, matches, pathNode, + func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { + node := yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"} + log.Debugf("LHS:", NodeToString(lhs)) + log.Debugf("RHS:", NodeToString(rhs)) + node.Content = []*yaml.Node{ + UnwrapDoc(lhs.Node), + UnwrapDoc(rhs.Node), + } + + return &CandidateNode{Node: &node, Document: document, Path: path}, nil + }) + + if err != nil { + return nil, err + } + innerList := listToNodeSeq(mapPairs) + innerList.Style = yaml.FlowStyle + return &CandidateNode{Node: innerList, Document: document, Path: path}, nil +} + +//NOTE: here the document index gets dropped so we +// no longer know where the node originates from. +func listToNodeSeq(list *list.List) *yaml.Node { + node := yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} + for entry := list.Front(); entry != nil; entry = entry.Next() { + entryCandidate := entry.Value.(*CandidateNode) + log.Debugf("Collecting %v into sequence", NodeToString(entryCandidate)) + node.Content = append(node.Content, entryCandidate.Node) + } + return &node +} diff --git a/pkg/yqlib/operator_create_map_test.go b/pkg/yqlib/operator_create_map_test.go new file mode 100644 index 00000000..6a972467 --- /dev/null +++ b/pkg/yqlib/operator_create_map_test.go @@ -0,0 +1,81 @@ +package yqlib + +import ( + "testing" +) + +var createMapOperatorScenarios = []expressionScenario{ + { + document: ``, + expression: `"frog": "jumps"`, + expected: []string{ + "D0, P[], (!!seq)::- [{frog: jumps}]\n", + }, + }, + { + document: `{name: Mike, age: 32}`, + expression: `.name: .age`, + expected: []string{ + "D0, P[], (!!seq)::- [{Mike: 32}]\n", + }, + }, + { + document: `{name: Mike, pets: [cat, dog]}`, + expression: `.name: .pets.[]`, + expected: []string{ + "D0, P[], (!!seq)::- [{Mike: cat}, {Mike: dog}]\n", + }, + }, + { + document: `{name: Mike, pets: [cat, dog], food: [hotdog, burger]}`, + expression: `.name: .pets.[], "f":.food.[]`, + expected: []string{ + "D0, P[], (!!seq)::- [{Mike: cat}, {Mike: dog}]\n", + "D0, P[], (!!seq)::- [{f: hotdog}, {f: burger}]\n", + }, + }, + { + document: "{name: Mike, pets: [cat, dog], food: [hotdog, burger]}\n---\n{name: Fred, pets: [mouse], food: [pizza, onion, apple]}", + expression: `.name: .pets.[], "f":.food.[]`, + expected: []string{ + "D0, P[], (!!seq)::- [{Mike: cat}, {Mike: dog}]\n- [{Fred: mouse}]\n", + "D0, P[], (!!seq)::- [{f: hotdog}, {f: burger}]\n- [{f: pizza}, {f: onion}, {f: apple}]\n", + }, + }, + { + document: `{name: Mike, pets: {cows: [apl, bba]}}`, + expression: `"a":.name, "b":.pets`, + expected: []string{ + "D0, P[], (!!seq)::- [{a: Mike}]\n", + "D0, P[], (!!seq)::- [{b: {cows: [apl, bba]}}]\n", + }, + }, + { + document: `{name: Mike}`, + expression: `"wrap": .`, + expected: []string{ + "D0, P[], (!!seq)::- [{wrap: {name: Mike}}]\n", + }, + }, + { + document: "{name: Mike}\n---\n{name: Bob}", + expression: `"wrap": .`, + expected: []string{ + "D0, P[], (!!seq)::- [{wrap: {name: Mike}}]\n- [{wrap: {name: Bob}}]\n", + }, + }, + { + document: "{name: Mike}\n---\n{name: Bob}", + expression: `"wrap": ., .name: "great"`, + expected: []string{ + "D0, P[], (!!seq)::- [{wrap: {name: Mike}}]\n- [{wrap: {name: Bob}}]\n", + "D0, P[], (!!seq)::- [{Mike: great}]\n- [{Bob: great}]\n", + }, + }, +} + +func TestCreateMapOperatorScenarios(t *testing.T) { + for _, tt := range createMapOperatorScenarios { + testScenario(t, &tt) + } +} diff --git a/pkg/yqlib/operator_delete.go b/pkg/yqlib/operator_delete.go new file mode 100644 index 00000000..0e01af50 --- /dev/null +++ b/pkg/yqlib/operator_delete.go @@ -0,0 +1,96 @@ +package yqlib + +import ( + "container/list" + + yaml "gopkg.in/yaml.v3" +) + +func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + // for each lhs, splat the node, + // the intersect it against the rhs expression + // recreate the contents using only the intersection result. + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + elMap := list.New() + elMap.PushBack(candidate) + nodesToDelete, err := d.GetMatchingNodes(elMap, pathNode.Rhs) + log.Debug("nodesToDelete:\n%v", NodesToString(nodesToDelete)) + if err != nil { + return nil, err + } + + realNode := UnwrapDoc(candidate.Node) + + if realNode.Kind == yaml.SequenceNode { + deleteFromArray(candidate, nodesToDelete) + } 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 matchingNodes, nil +} + +func deleteFromMap(candidate *CandidateNode, nodesToDelete *list.List) { + log.Debug("deleteFromMap") + node := UnwrapDoc(candidate.Node) + contents := node.Content + newContents := make([]*yaml.Node, 0) + + for index := 0; index < len(contents); index = index + 2 { + key := contents[index] + value := contents[index+1] + + childCandidate := &CandidateNode{ + Node: value, + Document: candidate.Document, + Path: append(candidate.Path, key.Value), + } + + 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) + + if !shouldDelete { + newContents = append(newContents, key, value) + } + } + node.Content = newContents +} + +func deleteFromArray(candidate *CandidateNode, nodesToDelete *list.List) { + log.Debug("deleteFromArray") + 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), + } + + shouldDelete := false + for el := nodesToDelete.Front(); el != nil && !shouldDelete; el = el.Next() { + if el.Value.(*CandidateNode).GetKey() == childCandidate.GetKey() { + shouldDelete = true + } + } + + if !shouldDelete { + newContents = append(newContents, value) + } + } + node.Content = newContents +} diff --git a/pkg/yqlib/operator_delete_test.go b/pkg/yqlib/operator_delete_test.go new file mode 100644 index 00000000..43e02d15 --- /dev/null +++ b/pkg/yqlib/operator_delete_test.go @@ -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", deleteOperatorScenarios) +} diff --git a/pkg/yqlib/operator_document_index.go b/pkg/yqlib/operator_document_index.go new file mode 100644 index 00000000..52ec7a52 --- /dev/null +++ b/pkg/yqlib/operator_document_index.go @@ -0,0 +1,20 @@ +package yqlib + +import ( + "container/list" + "fmt" + + "gopkg.in/yaml.v3" +) + +func GetDocumentIndexOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + var results = list.New() + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Document), Tag: "!!int"} + scalar := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} + results.PushBack(scalar) + } + return results, nil +} diff --git a/pkg/yqlib/operator_document_index_test.go b/pkg/yqlib/operator_document_index_test.go new file mode 100644 index 00000000..0f0a3088 --- /dev/null +++ b/pkg/yqlib/operator_document_index_test.go @@ -0,0 +1,41 @@ +package yqlib + +import ( + "testing" +) + +var documentIndexScenarios = []expressionScenario{ + { + description: "Retrieve a document index", + document: "a: cat\n---\na: frog\n", + expression: `.a | documentIndex`, + expected: []string{ + "D0, P[a], (!!int)::0\n", + "D1, P[a], (!!int)::1\n", + }, + }, + { + description: "Filter by document index", + document: "a: cat\n---\na: frog\n", + expression: `select(. | documentIndex == 1)`, + expected: []string{ + "D1, P[], (doc)::a: frog\n", + }, + }, + { + description: "Print Document Index with matches", + document: "a: cat\n---\na: frog\n", + expression: `.a | ({"match": ., "doc": (. | documentIndex)})`, + expected: []string{ + "D0, P[], (!!map)::match: cat\ndoc: 0\n", + "D0, P[], (!!map)::match: frog\ndoc: 1\n", + }, + }, +} + +func TestDocumentIndexScenarios(t *testing.T) { + for _, tt := range documentIndexScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Document Index", documentIndexScenarios) +} diff --git a/pkg/yqlib/operator_equals.go b/pkg/yqlib/operator_equals.go new file mode 100644 index 00000000..8f663f37 --- /dev/null +++ b/pkg/yqlib/operator_equals.go @@ -0,0 +1,22 @@ +package yqlib + +import ( + "container/list" +) + +func EqualsOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- equalsOperation") + return crossFunction(d, matchingNodes, pathNode, isEquals) +} + +func isEquals(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { + value := false + + if lhs.Node.Tag == "!!null" { + value = (rhs.Node.Tag == "!!null") + } else { + value = Match(lhs.Node.Value, rhs.Node.Value) + } + log.Debugf("%v == %v ? %v", NodeToString(lhs), NodeToString(rhs), value) + return createBooleanCandidate(lhs, value), nil +} diff --git a/pkg/yqlib/operator_equals_test.go b/pkg/yqlib/operator_equals_test.go new file mode 100644 index 00000000..ce172503 --- /dev/null +++ b/pkg/yqlib/operator_equals_test.go @@ -0,0 +1,58 @@ +package yqlib + +import ( + "testing" +) + +var equalsOperatorScenarios = []expressionScenario{ + { + 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", + }, + }, { + 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{ + "D0, P[a cat b], (!!bool)::true\n", + "D0, P[a pat b], (!!bool)::false\n", + }, + }, + { + skipDoc: true, + document: ``, + expression: `null == null`, + expected: []string{ + "D0, P[], (!!bool)::true\n", + }, + }, + { + description: "Match nulls", + document: ``, + expression: `null == ~`, + expected: []string{ + "D0, P[], (!!bool)::true\n", + }, + }, +} + +func TestEqualOperatorScenarios(t *testing.T) { + for _, tt := range equalsOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Equals", equalsOperatorScenarios) +} diff --git a/pkg/yqlib/operator_explode.go b/pkg/yqlib/operator_explode.go new file mode 100644 index 00000000..b678f61b --- /dev/null +++ b/pkg/yqlib/operator_explode.go @@ -0,0 +1,151 @@ +package yqlib + +import ( + "container/list" + + "gopkg.in/yaml.v3" +) + +func ExplodeOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- ExplodeOperation") + + for el := matchMap.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + + rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs) + + if err != nil { + return nil, err + } + for childEl := rhs.Front(); childEl != nil; childEl = childEl.Next() { + err = explodeNode(childEl.Value.(*CandidateNode).Node) + if err != nil { + return nil, err + } + } + + } + + return matchMap, nil +} + +func explodeNode(node *yaml.Node) error { + node.Anchor = "" + switch node.Kind { + case yaml.SequenceNode, yaml.DocumentNode: + for index, contentNode := range node.Content { + log.Debugf("exploding index %v", index) + errorInContent := explodeNode(contentNode) + if errorInContent != nil { + return errorInContent + } + } + return nil + case yaml.AliasNode: + log.Debugf("its an alias!") + if node.Alias != nil { + node.Kind = node.Alias.Kind + node.Style = node.Alias.Style + node.Tag = node.Alias.Tag + node.Content = node.Alias.Content + node.Value = node.Alias.Value + node.Alias = nil + } + return nil + case yaml.MappingNode: + var newContent = list.New() + for index := 0; index < len(node.Content); index = index + 2 { + keyNode := node.Content[index] + valueNode := node.Content[index+1] + log.Debugf("traversing %v", keyNode.Value) + if keyNode.Value != "<<" { + err := overrideEntry(node, keyNode, valueNode, index, newContent) + if err != nil { + return err + } + } else { + if valueNode.Kind == yaml.SequenceNode { + log.Debugf("an alias merge list!") + for index := 0; index < len(valueNode.Content); index = index + 1 { + aliasNode := valueNode.Content[index] + err := applyAlias(node, aliasNode.Alias, index, newContent) + if err != nil { + return err + } + } + } else { + log.Debugf("an alias merge!") + err := applyAlias(node, valueNode.Alias, index, newContent) + if err != nil { + return err + } + } + } + } + node.Content = make([]*yaml.Node, newContent.Len()) + index := 0 + for newEl := newContent.Front(); newEl != nil; newEl = newEl.Next() { + node.Content[index] = newEl.Value.(*yaml.Node) + index++ + } + + return nil + default: + return nil + } +} + +func applyAlias(node *yaml.Node, alias *yaml.Node, aliasIndex int, newContent *list.List) error { + if alias == nil { + return nil + } + for index := 0; index < len(alias.Content); index = index + 2 { + keyNode := alias.Content[index] + log.Debugf("applying alias key %v", keyNode.Value) + valueNode := alias.Content[index+1] + err := overrideEntry(node, keyNode, valueNode, aliasIndex, newContent) + if err != nil { + return err + } + } + return nil +} + +func overrideEntry(node *yaml.Node, key *yaml.Node, value *yaml.Node, startIndex int, newContent *list.List) error { + + err := explodeNode(value) + + if err != nil { + return err + } + + for newEl := newContent.Front(); newEl != nil; newEl = newEl.Next() { + valueEl := newEl.Next() // move forward twice + keyNode := newEl.Value.(*yaml.Node) + log.Debugf("checking new content %v:%v", keyNode.Value, valueEl.Value.(*yaml.Node).Value) + if keyNode.Value == key.Value && keyNode.Alias == nil && key.Alias == nil { + log.Debugf("overridign new content") + valueEl.Value = value + return nil + } + newEl = valueEl // move forward twice + } + + for index := startIndex + 2; index < len(node.Content); index = index + 2 { + keyNode := node.Content[index] + + if keyNode.Value == key.Value && keyNode.Alias == nil { + log.Debugf("content will be overridden at index %v", index) + return nil + } + } + + err = explodeNode(key) + if err != nil { + return err + } + log.Debugf("adding %v:%v", key.Value, value.Value) + newContent.PushBack(key) + newContent.PushBack(value) + return nil +} diff --git a/pkg/yqlib/operator_explode_test.go b/pkg/yqlib/operator_explode_test.go new file mode 100644 index 00000000..5f3aac99 --- /dev/null +++ b/pkg/yqlib/operator_explode_test.go @@ -0,0 +1,90 @@ +package yqlib + +import ( + "testing" +) + +var explodeTest = []expressionScenario{ + { + 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", + }, + }, + { + 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{ + "D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n", + "D0, P[foobarList], (!!map)::{b: bar_b, a: foo_a, thing: bar_thing, c: foobarList_c}\n", + "D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n", + }, + }, + { + skipDoc: true, + document: mergeDocSample, + expression: `.foo* | explode(explode(.)) | (. style="flow")`, + expected: []string{ + "D0, P[foo], (!!map)::{a: foo_a, thing: foo_thing, c: foo_c}\n", + "D0, P[foobarList], (!!map)::{b: bar_b, a: foo_a, thing: bar_thing, c: foobarList_c}\n", + "D0, P[foobar], (!!map)::{c: foo_c, a: foo_a, thing: foobar_thing}\n", + }, + }, + { + skipDoc: true, + document: `{f : {a: &a cat, b: &b {f: *a}, *a: *b}}`, + expression: `explode(.f)`, + expected: []string{ + "D0, P[], (doc)::{f: {a: cat, b: {f: cat}, cat: {f: cat}}}\n", + }, + }, +} + +func TestExplodeOperatorScenarios(t *testing.T) { + for _, tt := range explodeTest { + testScenario(t, &tt) + } + documentScenarios(t, "Explode", explodeTest) +} diff --git a/pkg/yqlib/operator_file.go b/pkg/yqlib/operator_file.go new file mode 100644 index 00000000..23331951 --- /dev/null +++ b/pkg/yqlib/operator_file.go @@ -0,0 +1,38 @@ +package yqlib + +import ( + "container/list" + "fmt" + + yaml "gopkg.in/yaml.v3" +) + +func GetFilenameOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("GetFilename") + + 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.Filename, Tag: "!!str"} + lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} + results.PushBack(lengthCand) + } + + return results, nil +} + +func GetFileIndexOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("GetFileIndex") + + var results = list.New() + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.FileIndex), Tag: "!!int"} + lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} + results.PushBack(lengthCand) + } + + return results, nil +} diff --git a/pkg/yqlib/operator_file_test.go b/pkg/yqlib/operator_file_test.go new file mode 100644 index 00000000..a4735cef --- /dev/null +++ b/pkg/yqlib/operator_file_test.go @@ -0,0 +1,31 @@ +package yqlib + +import ( + "testing" +) + +var fileOperatorScenarios = []expressionScenario{ + { + description: "Get filename", + document: `{a: cat}`, + expression: `filename`, + expected: []string{ + "D0, P[], (!!str)::sample.yml\n", + }, + }, + { + description: "Get file index", + document: `{a: cat}`, + expression: `fileIndex`, + expected: []string{ + "D0, P[], (!!int)::0\n", + }, + }, +} + +func TestFileOperatorsScenarios(t *testing.T) { + for _, tt := range fileOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "File Operators", fileOperatorScenarios) +} diff --git a/pkg/yqlib/operator_has.go b/pkg/yqlib/operator_has.go new file mode 100644 index 00000000..91e7a8e5 --- /dev/null +++ b/pkg/yqlib/operator_has.go @@ -0,0 +1,53 @@ +package yqlib + +import ( + "container/list" + "strconv" + + yaml "gopkg.in/yaml.v3" +) + +func HasOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + + log.Debugf("-- hasOperation") + var results = list.New() + + rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs) + wanted := rhs.Front().Value.(*CandidateNode).Node + wantedKey := wanted.Value + + if err != nil { + return nil, err + } + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + + // grab the first value + var contents = candidate.Node.Content + switch candidate.Node.Kind { + case yaml.MappingNode: + candidateHasKey := false + for index := 0; index < len(contents) && !candidateHasKey; index = index + 2 { + key := contents[index] + if key.Value == wantedKey { + candidateHasKey = true + } + } + results.PushBack(createBooleanCandidate(candidate, candidateHasKey)) + case yaml.SequenceNode: + candidateHasKey := false + if wanted.Tag == "!!int" { + var number, errParsingInt = strconv.ParseInt(wantedKey, 10, 64) // nolint + if errParsingInt != nil { + return nil, errParsingInt + } + candidateHasKey = int64(len(contents)) > number + } + results.PushBack(createBooleanCandidate(candidate, candidateHasKey)) + default: + results.PushBack(createBooleanCandidate(candidate, false)) + } + } + return results, nil +} diff --git a/pkg/yqlib/operator_has_test.go b/pkg/yqlib/operator_has_test.go new file mode 100644 index 00000000..dc0c3d2d --- /dev/null +++ b/pkg/yqlib/operator_has_test.go @@ -0,0 +1,48 @@ +package yqlib + +import ( + "testing" +) + +var hasOperatorScenarios = []expressionScenario{ + { + description: "Has map key", + document: `- a: "yes" +- a: ~ +- a: +- b: nope +`, + expression: `.[] | has("a")`, + expected: []string{ + "D0, P[0], (!!bool)::true\n", + "D0, P[1], (!!bool)::true\n", + "D0, P[2], (!!bool)::true\n", + "D0, P[3], (!!bool)::false\n", + }, + }, + { + dontFormatInputForDoc: true, + description: "Has array index", + document: `- [] +- [1] +- [1, 2] +- [1, null] +- [1, 2, 3] +`, + expression: `.[] | has(1)`, + expected: []string{ + "D0, P[0], (!!bool)::false\n", + "D0, P[1], (!!bool)::false\n", + "D0, P[2], (!!bool)::true\n", + "D0, P[3], (!!bool)::true\n", + "D0, P[4], (!!bool)::true\n", + }, + }, +} + +func TestHasOperatorScenarios(t *testing.T) { + for _, tt := range hasOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Has", hasOperatorScenarios) +} diff --git a/pkg/yqlib/operator_length.go b/pkg/yqlib/operator_length.go new file mode 100644 index 00000000..38c959a9 --- /dev/null +++ b/pkg/yqlib/operator_length.go @@ -0,0 +1,35 @@ +package yqlib + +import ( + "container/list" + "fmt" + + yaml "gopkg.in/yaml.v3" +) + +func LengthOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- lengthOperation") + var results = list.New() + + for el := matchMap.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + targetNode := UnwrapDoc(candidate.Node) + var length int + switch targetNode.Kind { + case yaml.ScalarNode: + length = len(targetNode.Value) + case yaml.MappingNode: + length = len(targetNode.Content) / 2 + case yaml.SequenceNode: + length = len(targetNode.Content) + default: + length = 0 + } + + node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", length), Tag: "!!int"} + lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} + results.PushBack(lengthCand) + } + + return results, nil +} diff --git a/pkg/yqlib/operator_length_test.go b/pkg/yqlib/operator_length_test.go new file mode 100644 index 00000000..353f2ef8 --- /dev/null +++ b/pkg/yqlib/operator_length_test.go @@ -0,0 +1,42 @@ +package yqlib + +import ( + "testing" +) + +var lengthOperatorScenarios = []expressionScenario{ + { + description: "String length", + subdescription: "returns length of string", + document: `{a: cat}`, + expression: `.a | length`, + expected: []string{ + "D0, P[a], (!!int)::3\n", + }, + }, + { + description: "Map length", + subdescription: "returns number of entries", + document: `{a: cat, c: dog}`, + expression: `length`, + expected: []string{ + "D0, P[], (!!int)::2\n", + }, + }, + { + description: "Array length", + subdescription: "returns number of elements", + document: `[2,4,6,8]`, + expression: `length`, + expected: []string{ + "D0, P[], (!!int)::4\n", + }, + }, +} + +func TestLengthOperatorScenarios(t *testing.T) { + for _, tt := range lengthOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Length", lengthOperatorScenarios) +} diff --git a/pkg/yqlib/operator_multiply.go b/pkg/yqlib/operator_multiply.go new file mode 100644 index 00000000..dc1af6a3 --- /dev/null +++ b/pkg/yqlib/operator_multiply.go @@ -0,0 +1,140 @@ +package yqlib + +import ( + "fmt" + + "container/list" + + yaml "gopkg.in/yaml.v3" +) + +type CrossFunctionCalculation func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) + +func crossFunction(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode, calculation CrossFunctionCalculation) (*list.List, error) { + lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + log.Debugf("crossFunction LHS len: %v", lhs.Len()) + + rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs) + + if err != nil { + return nil, err + } + log.Debugf("crossFunction RHS len: %v", rhs.Len()) + + var results = list.New() + + for el := lhs.Front(); el != nil; el = el.Next() { + lhsCandidate := el.Value.(*CandidateNode) + + for rightEl := rhs.Front(); rightEl != nil; rightEl = rightEl.Next() { + log.Debugf("Applying calc") + rhsCandidate := rightEl.Value.(*CandidateNode) + resultCandidate, err := calculation(d, lhsCandidate, rhsCandidate) + if err != nil { + return nil, err + } + results.PushBack(resultCandidate) + } + + } + return results, nil +} + +type MultiplyPreferences struct { + AppendArrays bool +} + +func MultiplyOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- MultiplyOperator") + return crossFunction(d, matchingNodes, pathNode, multiply(pathNode.Operation.Preferences.(*MultiplyPreferences))) +} + +func multiply(preferences *MultiplyPreferences) func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { + return func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { + lhs.Node = UnwrapDoc(lhs.Node) + rhs.Node = UnwrapDoc(rhs.Node) + log.Debugf("Multipling LHS: %v", lhs.Node.Tag) + log.Debugf("- RHS: %v", rhs.Node.Tag) + + shouldAppendArrays := preferences.AppendArrays + + if lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode || + (lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) { + + var newBlank = &CandidateNode{ + Path: lhs.Path, + Document: lhs.Document, + Filename: lhs.Filename, + Node: &yaml.Node{}, + } + var newThing, err = mergeObjects(d, newBlank, lhs, false) + if err != nil { + return nil, err + } + return mergeObjects(d, newThing, rhs, shouldAppendArrays) + + } + return nil, fmt.Errorf("Cannot multiply %v with %v", lhs.Node.Tag, rhs.Node.Tag) + } +} + +func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode, shouldAppendArrays bool) (*CandidateNode, error) { + var results = list.New() + + // shouldn't recurse arrays if appending + err := recursiveDecent(d, results, nodeToMap(rhs), !shouldAppendArrays) + if err != nil { + return nil, err + } + + var pathIndexToStartFrom int = 0 + if results.Front() != nil { + pathIndexToStartFrom = len(results.Front().Value.(*CandidateNode).Path) + } + + for el := results.Front(); el != nil; el = el.Next() { + err := applyAssignment(d, pathIndexToStartFrom, lhs, el.Value.(*CandidateNode), shouldAppendArrays) + if err != nil { + return nil, err + } + } + return lhs, nil +} + +func createTraversalTree(path []interface{}) *PathTreeNode { + if len(path) == 0 { + return &PathTreeNode{Operation: &Operation{OperationType: SelfReference}} + } else if len(path) == 1 { + return &PathTreeNode{Operation: &Operation{OperationType: TraversePath, Value: path[0], StringValue: fmt.Sprintf("%v", path[0])}} + } + return &PathTreeNode{ + Operation: &Operation{OperationType: ShortPipe}, + Lhs: createTraversalTree(path[0:1]), + Rhs: createTraversalTree(path[1:])} + +} + +func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, shouldAppendArrays bool) error { + + log.Debugf("merge - applyAssignment lhs %v, rhs: %v", NodeToString(lhs), NodeToString(rhs)) + + lhsPath := rhs.Path[pathIndexToStartFrom:] + + assignmentOp := &Operation{OperationType: AssignAttributes} + if rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode { + assignmentOp.OperationType = Assign + assignmentOp.Preferences = &AssignOpPreferences{false} + } else if shouldAppendArrays && rhs.Node.Kind == yaml.SequenceNode { + assignmentOp.OperationType = AddAssign + } + rhsOp := &Operation{OperationType: ValueOp, CandidateNode: rhs} + + assignmentOpNode := &PathTreeNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath), Rhs: &PathTreeNode{Operation: rhsOp}} + + _, err := d.GetMatchingNodes(nodeToMap(lhs), assignmentOpNode) + + return err +} diff --git a/pkg/yqlib/operator_multiply_test.go b/pkg/yqlib/operator_multiply_test.go new file mode 100644 index 00000000..d900c000 --- /dev/null +++ b/pkg/yqlib/operator_multiply_test.go @@ -0,0 +1,157 @@ +package yqlib + +import ( + "testing" +) + +var multiplyOperatorScenarios = []expressionScenario{ + { + skipDoc: true, + document: `{a: {also: [1]}, b: {also: me}}`, + expression: `. * {"a" : .b}`, + expected: []string{ + "D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n", + }, + }, + { + skipDoc: true, + expression: `{} * {"cat":"dog"}`, + expected: []string{ + "D0, P[], (!!map)::cat: dog\n", + }, + }, + { + skipDoc: true, + document: `{a: {also: me}, b: {also: [1]}}`, + expression: `. * {"a":.b}`, + expected: []string{ + "D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n", + }, + }, + { + 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: {field: {g: wizz}, fieldA: cat, fieldB: dog}, b: {field: {g: wizz}, fieldB: dog}}\n", + }, + }, + { + skipDoc: true, + document: `{a: {also: {g: wizz}}, b: {also: me}}`, + expression: `. * {"a":.b}`, + expected: []string{ + "D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n", + }, + }, + { + skipDoc: true, + document: `{a: {also: {g: wizz}}, b: {also: [1]}}`, + expression: `. * {"a":.b}`, + expected: []string{ + "D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n", + }, + }, + { + skipDoc: true, + document: `{a: {also: [1]}, b: {also: {g: wizz}}}`, + expression: `. * {"a":.b}`, + expected: []string{ + "D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n", + }, + }, + { + skipDoc: true, + document: `{a: {things: great}, b: {also: me}}`, + expression: `. * {"a":.b}`, + expected: []string{ + "D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n", + }, + }, + { + description: "Merge keeps style of LHS", + dontFormatInputForDoc: true, + document: `a: {things: great} +b: + also: "me" +`, + expression: `. * {"a":.b}`, + expected: []string{ + `D0, P[], (!!map)::a: {things: great, also: "me"} +b: + also: "me" +`, + }, + }, + { + description: "Merge arrays", + document: `{a: [1,2,3], b: [3,4,5]}`, + expression: `. * {"a":.b}`, + expected: []string{ + "D0, P[], (!!map)::{a: [3, 4, 5], b: [3, 4, 5]}\n", + }, + }, + { + skipDoc: true, + document: `{a: [1], b: [2]}`, + expression: `.a *+ .b`, + expected: []string{ + "D0, P[a], (!!seq)::[1, 2]\n", + }, + }, + { + description: "Merge, appending arrays", + document: `{a: {array: [1, 2, animal: dog], value: coconut}, b: {array: [3, 4, animal: cat], value: banana}}`, + expression: `.a *+ .b`, + expected: []string{ + "D0, P[a], (!!map)::{array: [1, 2, {animal: dog}, 3, 4, {animal: cat}], value: banana}\n", + }, + }, + { + description: "Merge to prefix an element", + document: `{a: cat, b: dog}`, + expression: `. * {"a": {"c": .a}}`, + expected: []string{ + "D0, P[], (!!map)::{a: {c: cat}, b: dog}\n", + }, + }, + { + description: "Merge with simple aliases", + document: `{a: &cat {c: frog}, b: {f: *cat}, c: {g: thongs}}`, + expression: `.c * .b`, + expected: []string{ + "D0, P[c], (!!map)::{g: thongs, f: *cat}\n", + }, + }, + { + description: "Merge does not copy anchor names", + document: `{a: {c: &cat frog}, b: {f: *cat}, c: {g: thongs}}`, + expression: `.c * .a`, + expected: []string{ + "D0, P[c], (!!map)::{g: thongs, c: frog}\n", + }, + }, + { + description: "Merge with merge anchors", + document: mergeDocSample, + expression: `.foobar * .foobarList`, + expected: []string{ + "D0, P[foobar], (!!map)::c: foobarList_c\n<<: [*foo, *bar]\nthing: foobar_thing\nb: foobarList_b\n", + }, + }, +} + +func TestMultiplyOperatorScenarios(t *testing.T) { + for _, tt := range multiplyOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Multiply", multiplyOperatorScenarios) +} diff --git a/pkg/yqlib/operator_path.go b/pkg/yqlib/operator_path.go new file mode 100644 index 00000000..2f27f48e --- /dev/null +++ b/pkg/yqlib/operator_path.go @@ -0,0 +1,39 @@ +package yqlib + +import ( + "container/list" + "fmt" + + yaml "gopkg.in/yaml.v3" +) + +func createPathNodeFor(pathElement interface{}) *yaml.Node { + switch pathElement := pathElement.(type) { + case string: + return &yaml.Node{Kind: yaml.ScalarNode, Value: pathElement, Tag: "!!str"} + default: + return &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", pathElement), Tag: "!!int"} + } +} + +func GetPathOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("GetPath") + + var results = list.New() + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} + + content := make([]*yaml.Node, len(candidate.Path)) + for pathIndex := 0; pathIndex < len(candidate.Path); pathIndex++ { + path := candidate.Path[pathIndex] + content[pathIndex] = createPathNodeFor(path) + } + node.Content = content + lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} + results.PushBack(lengthCand) + } + + return results, nil +} diff --git a/pkg/yqlib/operator_path_test.go b/pkg/yqlib/operator_path_test.go new file mode 100644 index 00000000..d9170878 --- /dev/null +++ b/pkg/yqlib/operator_path_test.go @@ -0,0 +1,61 @@ +package yqlib + +import ( + "testing" +) + +var pathOperatorScenarios = []expressionScenario{ + { + description: "Map path", + document: `{a: {b: cat}}`, + expression: `.a.b | path`, + expected: []string{ + "D0, P[a b], (!!seq)::- a\n- b\n", + }, + }, + { + description: "Get map key", + document: `{a: {b: cat}}`, + expression: `.a.b | path | .[-1]`, + expected: []string{ + "D0, P[a b -1], (!!str)::b\n", + }, + }, + { + description: "Array path", + document: `{a: [cat, dog]}`, + expression: `.a.[] | select(. == "dog") | path`, + expected: []string{ + "D0, P[a 1], (!!seq)::- a\n- 1\n", + }, + }, + { + description: "Get array index", + document: `{a: [cat, dog]}`, + expression: `.a.[] | select(. == "dog") | path | .[-1]`, + expected: []string{ + "D0, P[a 1 -1], (!!int)::1\n", + }, + }, + { + description: "Print path and value", + document: `{a: [cat, dog, frog]}`, + expression: `.a.[] | select(. == "*og") | [{"path":path, "value":.}]`, + expected: []string{`D0, P[], (!!seq)::- path: + - a + - 1 + value: dog +- path: + - a + - 2 + value: frog +`}, + }, +} + +func TestPathOperatorsScenarios(t *testing.T) { + for _, tt := range pathOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Path", pathOperatorScenarios) +} diff --git a/pkg/yqlib/operator_pipe.go b/pkg/yqlib/operator_pipe.go new file mode 100644 index 00000000..189bd2de --- /dev/null +++ b/pkg/yqlib/operator_pipe.go @@ -0,0 +1,11 @@ +package yqlib + +import "container/list" + +func PipeOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + return d.GetMatchingNodes(lhs, pathNode.Rhs) +} diff --git a/pkg/yqlib/operator_pipe_test.go b/pkg/yqlib/operator_pipe_test.go new file mode 100644 index 00000000..c9df9e93 --- /dev/null +++ b/pkg/yqlib/operator_pipe_test.go @@ -0,0 +1,31 @@ +package yqlib + +import ( + "testing" +) + +var pipeOperatorScenarios = []expressionScenario{ + { + description: "Simple Pipe", + document: `{a: {b: cat}}`, + expression: `.a | .b`, + expected: []string{ + "D0, P[a b], (!!str)::cat\n", + }, + }, + { + description: "Multiple updates", + document: `{a: cow, b: sheep, c: same}`, + expression: `.a = "cat" | .b = "dog"`, + expected: []string{ + "D0, P[], (doc)::{a: cat, b: dog, c: same}\n", + }, + }, +} + +func TestPipeOperatorScenarios(t *testing.T) { + for _, tt := range pipeOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Pipe", pipeOperatorScenarios) +} diff --git a/pkg/yqlib/operator_recursive_descent.go b/pkg/yqlib/operator_recursive_descent.go new file mode 100644 index 00000000..0cb8f898 --- /dev/null +++ b/pkg/yqlib/operator_recursive_descent.go @@ -0,0 +1,44 @@ +package yqlib + +import ( + "container/list" + + yaml "gopkg.in/yaml.v3" +) + +func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { + var results = list.New() + + err := recursiveDecent(d, results, matchMap, true) + if err != nil { + return nil, err + } + + return results, nil +} + +func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.List, recurseArray bool) error { + for el := matchMap.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + + candidate.Node = UnwrapDoc(candidate.Node) + + log.Debugf("Recursive Decent, added %v", NodeToString(candidate)) + results.PushBack(candidate) + + if candidate.Node.Kind != yaml.AliasNode && len(candidate.Node.Content) > 0 && + (recurseArray || candidate.Node.Kind != yaml.SequenceNode) { + + children, err := Splat(d, nodeToMap(candidate)) + + if err != nil { + return err + } + err = recursiveDecent(d, results, children, recurseArray) + if err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/yqlib/operator_recursive_descent_test.go b/pkg/yqlib/operator_recursive_descent_test.go new file mode 100644 index 00000000..c5c7254a --- /dev/null +++ b/pkg/yqlib/operator_recursive_descent_test.go @@ -0,0 +1,110 @@ +package yqlib + +import ( + "testing" +) + +var recursiveDescentOperatorScenarios = []expressionScenario{ + { + skipDoc: true, + document: `{}`, + expression: `..`, + expected: []string{ + "D0, P[], (!!map)::{}\n", + }, + }, + { + skipDoc: true, + document: `[]`, + expression: `..`, + expected: []string{ + "D0, P[], (!!seq)::[]\n", + }, + }, + { + skipDoc: true, + document: `cat`, + expression: `..`, + expected: []string{ + "D0, P[], (!!str)::cat\n", + }, + }, + { + skipDoc: true, + document: `{a: frog}`, + expression: `..`, + expected: []string{ + "D0, P[], (!!map)::{a: frog}\n", + "D0, P[a], (!!str)::frog\n", + }, + }, + { + skipDoc: true, + document: `{a: {b: apple}}`, + expression: `..`, + expected: []string{ + "D0, P[], (!!map)::{a: {b: apple}}\n", + "D0, P[a], (!!map)::{b: apple}\n", + "D0, P[a b], (!!str)::apple\n", + }, + }, + { + skipDoc: true, + document: `[1,2,3]`, + expression: `..`, + expected: []string{ + "D0, P[], (!!seq)::[1, 2, 3]\n", + "D0, P[0], (!!int)::1\n", + "D0, P[1], (!!int)::2\n", + "D0, P[2], (!!int)::3\n", + }, + }, + { + skipDoc: true, + document: `[{a: cat},2,true]`, + expression: `..`, + expected: []string{ + "D0, P[], (!!seq)::[{a: cat}, 2, true]\n", + "D0, P[0], (!!map)::{a: cat}\n", + "D0, P[0 a], (!!str)::cat\n", + "D0, P[1], (!!int)::2\n", + "D0, P[2], (!!bool)::true\n", + }, + }, + { + description: "Aliases are not traversed", + document: `{a: &cat {c: frog}, b: *cat}`, + expression: `[..]`, + expected: []string{ + "D0, P[a], (!!seq)::- {a: &cat {c: frog}, b: *cat}\n- &cat {c: frog}\n- frog\n- *cat\n", + }, + }, + { + description: "Merge docs are not traversed", + document: mergeDocSample, + expression: `.foobar | [..]`, + expected: []string{ + "D0, P[foobar], (!!seq)::- c: foobar_c\n !!merge <<: *foo\n thing: foobar_thing\n- foobar_c\n- *foo\n- foobar_thing\n", + }, + }, + { + skipDoc: true, + document: mergeDocSample, + expression: `.foobarList | ..`, + expected: []string{ + "D0, P[foobarList], (!!map)::b: foobarList_b\n!!merge <<: [*foo, *bar]\nc: foobarList_c\n", + "D0, P[foobarList b], (!!str)::foobarList_b\n", + "D0, P[foobarList <<], (!!seq)::[*foo, *bar]\n", + "D0, P[foobarList << 0], (alias)::*foo\n", + "D0, P[foobarList << 1], (alias)::*bar\n", + "D0, P[foobarList c], (!!str)::foobarList_c\n", + }, + }, +} + +func TestRecursiveDescentOperatorScenarios(t *testing.T) { + for _, tt := range recursiveDescentOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Recursive Descent", recursiveDescentOperatorScenarios) +} diff --git a/pkg/yqlib/operator_select.go b/pkg/yqlib/operator_select.go new file mode 100644 index 00000000..72ba06a1 --- /dev/null +++ b/pkg/yqlib/operator_select.go @@ -0,0 +1,37 @@ +package yqlib + +import ( + "container/list" +) + +func SelectOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + + log.Debugf("-- selectOperation") + var results = list.New() + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + + rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs) + + if err != nil { + return nil, err + } + + // grab the first value + first := rhs.Front() + + if first != nil { + result := first.Value.(*CandidateNode) + includeResult, errDecoding := isTruthy(result) + if errDecoding != nil { + return nil, errDecoding + } + + if includeResult { + results.PushBack(candidate) + } + } + } + return results, nil +} diff --git a/pkg/yqlib/operator_select_test.go b/pkg/yqlib/operator_select_test.go new file mode 100644 index 00000000..d98805ae --- /dev/null +++ b/pkg/yqlib/operator_select_test.go @@ -0,0 +1,63 @@ +package yqlib + +import ( + "testing" +) + +var selectOperatorScenarios = []expressionScenario{ + { + 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"}, + }, + { + 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[], (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{ + "D0, P[1], (!!null)::~\n", + }, + }, +} + +func TestSelectOperatorScenarios(t *testing.T) { + for _, tt := range selectOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Select", selectOperatorScenarios) +} diff --git a/pkg/yqlib/operator_self.go b/pkg/yqlib/operator_self.go new file mode 100644 index 00000000..58bf2aa1 --- /dev/null +++ b/pkg/yqlib/operator_self.go @@ -0,0 +1,7 @@ +package yqlib + +import "container/list" + +func SelfOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { + return matchMap, nil +} diff --git a/pkg/yqlib/operator_sort_keys.go b/pkg/yqlib/operator_sort_keys.go new file mode 100644 index 00000000..e8f39381 --- /dev/null +++ b/pkg/yqlib/operator_sort_keys.go @@ -0,0 +1,53 @@ +package yqlib + +import ( + "container/list" + "sort" + + yaml "gopkg.in/yaml.v3" +) + +func SortKeysOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs) + if err != nil { + return nil, err + } + + for childEl := rhs.Front(); childEl != nil; childEl = childEl.Next() { + node := UnwrapDoc(childEl.Value.(*CandidateNode).Node) + if node.Kind == yaml.MappingNode { + sortKeys(node) + } + if err != nil { + return nil, err + } + } + + } + return matchingNodes, nil +} + +func sortKeys(node *yaml.Node) { + keys := make([]string, len(node.Content)/2) + keyBucket := map[string]*yaml.Node{} + valueBucket := map[string]*yaml.Node{} + var contents = node.Content + for index := 0; index < len(contents); index = index + 2 { + key := contents[index] + value := contents[index+1] + keys[index/2] = key.Value + keyBucket[key.Value] = key + valueBucket[key.Value] = value + } + sort.Strings(keys) + sortedContent := make([]*yaml.Node, len(node.Content)) + for index := 0; index < len(keys); index = index + 1 { + keyString := keys[index] + sortedContent[index*2] = keyBucket[keyString] + sortedContent[1+(index*2)] = valueBucket[keyString] + } + node.Content = sortedContent +} diff --git a/pkg/yqlib/operator_sort_keys_test.go b/pkg/yqlib/operator_sort_keys_test.go new file mode 100644 index 00000000..303b5adc --- /dev/null +++ b/pkg/yqlib/operator_sort_keys_test.go @@ -0,0 +1,32 @@ +package yqlib + +import ( + "testing" +) + +var sortKeysOperatorScenarios = []expressionScenario{ + { + description: "Sort keys of map", + document: `{c: frog, a: blah, b: bing}`, + expression: `sortKeys(.)`, + expected: []string{ + "D0, P[], (doc)::{a: blah, b: bing, c: frog}\n", + }, + }, + { + description: "Sort keys recursively", + subdescription: "Note the array elements are left unsorted, but maps inside arrays are sorted", + document: `{bParent: {c: dog, array: [3,1,2]}, aParent: {z: donkey, x: [{c: yum, b: delish}, {b: ew, a: apple}]}}`, + expression: `sortKeys(..)`, + expected: []string{ + "D0, P[], (!!map)::{aParent: {x: [{b: delish, c: yum}, {a: apple, b: ew}], z: donkey}, bParent: {array: [3, 1, 2], c: dog}}\n", + }, + }, +} + +func TestSortKeysOperatorScenarios(t *testing.T) { + for _, tt := range sortKeysOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Sort Keys", sortKeysOperatorScenarios) +} diff --git a/pkg/yqlib/operator_style.go b/pkg/yqlib/operator_style.go new file mode 100644 index 00000000..b2ace2fd --- /dev/null +++ b/pkg/yqlib/operator_style.go @@ -0,0 +1,87 @@ +package yqlib + +import ( + "container/list" + "fmt" + + "gopkg.in/yaml.v3" +) + +func AssignStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + + log.Debugf("AssignStyleOperator: %v") + + rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs) + if err != nil { + return nil, err + } + customStyle := "" + + if rhs.Front() != nil { + customStyle = rhs.Front().Value.(*CandidateNode).Node.Value + } + + var style yaml.Style + if customStyle == "tagged" { + style = yaml.TaggedStyle + } else if customStyle == "double" { + style = yaml.DoubleQuotedStyle + } else if customStyle == "single" { + style = yaml.SingleQuotedStyle + } else if customStyle == "literal" { + style = yaml.LiteralStyle + } else if customStyle == "folded" { + style = yaml.FoldedStyle + } else if customStyle == "flow" { + style = yaml.FlowStyle + } else if customStyle != "" { + return nil, fmt.Errorf("Unknown style %v", customStyle) + } + 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 style of : %v", candidate.GetKey()) + candidate.Node.Style = style + } + + return matchingNodes, nil +} + +func GetStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("GetStyleOperator") + + var results = list.New() + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + var style string + switch candidate.Node.Style { + case yaml.TaggedStyle: + style = "tagged" + case yaml.DoubleQuotedStyle: + style = "double" + case yaml.SingleQuotedStyle: + style = "single" + case yaml.LiteralStyle: + style = "literal" + case yaml.FoldedStyle: + style = "folded" + case yaml.FlowStyle: + style = "flow" + case 0: + style = "" + default: + style = "" + } + node := &yaml.Node{Kind: yaml.ScalarNode, Value: style, Tag: "!!str"} + lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} + results.PushBack(lengthCand) + } + + return results, nil +} diff --git a/pkg/yqlib/operator_style_test.go b/pkg/yqlib/operator_style_test.go new file mode 100644 index 00000000..d98f48a0 --- /dev/null +++ b/pkg/yqlib/operator_style_test.go @@ -0,0 +1,116 @@ +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: "Pretty print", + subdescription: "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'}`, + dontFormatInputForDoc: true, + 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", styleOperatorScenarios) +} diff --git a/pkg/yqlib/operator_tag.go b/pkg/yqlib/operator_tag.go new file mode 100644 index 00000000..8b9c515b --- /dev/null +++ b/pkg/yqlib/operator_tag.go @@ -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 +} diff --git a/pkg/yqlib/operator_tag_test.go b/pkg/yqlib/operator_tag_test.go new file mode 100644 index 00000000..e918c73f --- /dev/null +++ b/pkg/yqlib/operator_tag_test.go @@ -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", tagOperatorScenarios) +} diff --git a/pkg/yqlib/operator_traverse_path.go b/pkg/yqlib/operator_traverse_path.go new file mode 100644 index 00000000..574cfb36 --- /dev/null +++ b/pkg/yqlib/operator_traverse_path.go @@ -0,0 +1,223 @@ +package yqlib + +import ( + "fmt" + + "container/list" + + "github.com/elliotchance/orderedmap" + + "gopkg.in/yaml.v3" +) + +type TraversePreferences struct { + DontFollowAlias bool +} + +func Splat(d *dataTreeNavigator, matches *list.List) (*list.List, error) { + preferences := &TraversePreferences{DontFollowAlias: true} + splatOperation := &Operation{OperationType: TraversePath, Value: "[]", Preferences: preferences} + splatTreeNode := &PathTreeNode{Operation: splatOperation} + return TraversePathOperator(d, matches, splatTreeNode) +} + +func TraversePathOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debugf("-- Traversing") + var matchingNodeMap = list.New() + var newNodes []*CandidateNode + var err error + + for el := matchMap.Front(); el != nil; el = el.Next() { + newNodes, err = traverse(d, el.Value.(*CandidateNode), pathNode.Operation) + if err != nil { + return nil, err + } + for _, n := range newNodes { + matchingNodeMap.PushBack(n) + } + } + + return matchingNodeMap, nil +} + +func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, operation *Operation) ([]*CandidateNode, error) { + log.Debug("Traversing %v", NodeToString(matchingNode)) + value := matchingNode.Node + + if value.Tag == "!!null" && operation.Value != "[]" { + log.Debugf("Guessing kind") + // we must ahve added this automatically, lets guess what it should be now + switch operation.Value.(type) { + case int, int64: + log.Debugf("probably an array") + value.Kind = yaml.SequenceNode + default: + log.Debugf("probably a map") + value.Kind = yaml.MappingNode + } + value.Tag = "" + } + + switch value.Kind { + case yaml.MappingNode: + log.Debug("its a map with %v entries", len(value.Content)/2) + var newMatches = orderedmap.NewOrderedMap() + err := traverseMap(newMatches, matchingNode, operation) + + if err != nil { + return nil, err + } + + if newMatches.Len() == 0 { + //no matches, create one automagically + valueNode := &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"} + node := matchingNode.Node + node.Content = append(node.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: operation.StringValue}, valueNode) + candidateNode := &CandidateNode{ + Node: valueNode, + Path: append(matchingNode.Path, operation.StringValue), + Document: matchingNode.Document, + } + newMatches.Set(candidateNode.GetKey(), candidateNode) + + } + + arrayMatches := make([]*CandidateNode, newMatches.Len()) + i := 0 + for el := newMatches.Front(); el != nil; el = el.Next() { + arrayMatches[i] = el.Value.(*CandidateNode) + i++ + } + return arrayMatches, nil + + case yaml.SequenceNode: + log.Debug("its a sequence of %v things!", len(value.Content)) + return traverseArray(matchingNode, operation) + + case yaml.AliasNode: + log.Debug("its an alias!") + matchingNode.Node = matchingNode.Node.Alias + return traverse(d, matchingNode, operation) + case yaml.DocumentNode: + log.Debug("digging into doc node") + return traverse(d, &CandidateNode{ + Node: matchingNode.Node.Content[0], + Document: matchingNode.Document}, operation) + default: + return nil, nil + } +} + +func keyMatches(key *yaml.Node, pathNode *Operation) bool { + return pathNode.Value == "[]" || Match(key.Value, pathNode.StringValue) +} + +func traverseMap(newMatches *orderedmap.OrderedMap, candidate *CandidateNode, operation *Operation) error { + // value.Content is a concatenated array of key, value, + // so keys are in the even indexes, values in odd. + // merge aliases are defined first, but we only want to traverse them + // if we don't find a match directly on this node first. + //TODO ALIASES, auto creation? + + node := candidate.Node + + followAlias := true + + if operation.Preferences != nil { + followAlias = !operation.Preferences.(*TraversePreferences).DontFollowAlias + } + + var contents = node.Content + for index := 0; index < len(contents); index = index + 2 { + key := contents[index] + value := contents[index+1] + + log.Debug("checking %v (%v)", key.Value, key.Tag) + //skip the 'merge' tag, find a direct match first + if key.Tag == "!!merge" && followAlias { + log.Debug("Merge anchor") + err := traverseMergeAnchor(newMatches, candidate, value, operation) + if err != nil { + return err + } + } else if keyMatches(key, operation) { + log.Debug("MATCHED") + candidateNode := &CandidateNode{ + Node: value, + Path: append(candidate.Path, key.Value), + Document: candidate.Document, + } + newMatches.Set(candidateNode.GetKey(), candidateNode) + } + } + + return nil +} + +func traverseMergeAnchor(newMatches *orderedmap.OrderedMap, originalCandidate *CandidateNode, value *yaml.Node, operation *Operation) error { + switch value.Kind { + case yaml.AliasNode: + candidateNode := &CandidateNode{ + Node: value.Alias, + Path: originalCandidate.Path, + Document: originalCandidate.Document, + } + return traverseMap(newMatches, candidateNode, operation) + case yaml.SequenceNode: + for _, childValue := range value.Content { + err := traverseMergeAnchor(newMatches, originalCandidate, childValue, operation) + if err != nil { + return err + } + } + } + return nil +} + +func traverseArray(candidate *CandidateNode, operation *Operation) ([]*CandidateNode, error) { + log.Debug("operation Value %v", operation.Value) + if operation.Value == "[]" { + + var contents = candidate.Node.Content + var newMatches = make([]*CandidateNode, len(contents)) + var index int64 + for index = 0; index < int64(len(contents)); index = index + 1 { + newMatches[index] = &CandidateNode{ + Document: candidate.Document, + Path: append(candidate.Path, index), + Node: contents[index], + } + } + return newMatches, nil + + } + + switch operation.Value.(type) { + case int64: + index := operation.Value.(int64) + indexToUse := index + contentLength := int64(len(candidate.Node.Content)) + for contentLength <= index { + candidate.Node.Content = append(candidate.Node.Content, &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"}) + contentLength = int64(len(candidate.Node.Content)) + } + + if indexToUse < 0 { + indexToUse = contentLength + indexToUse + } + + if indexToUse < 0 { + return nil, fmt.Errorf("Index [%v] out of range, array size is %v", index, contentLength) + } + + return []*CandidateNode{&CandidateNode{ + Node: candidate.Node.Content[indexToUse], + Document: candidate.Document, + Path: append(candidate.Path, index), + }}, nil + default: + log.Debug("argument not an int (%v), no array matches", operation.Value) + return nil, nil + } + +} diff --git a/pkg/yqlib/operator_traverse_path_test.go b/pkg/yqlib/operator_traverse_path_test.go new file mode 100644 index 00000000..e39e5a62 --- /dev/null +++ b/pkg/yqlib/operator_traverse_path_test.go @@ -0,0 +1,284 @@ +package yqlib + +import ( + "testing" +) + +var mergeDocSample = ` +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 +` + +var traversePathOperatorScenarios = []expressionScenario{ + { + description: "Simple map navigation", + document: `{a: {b: apple}}`, + expression: `.a`, + expected: []string{ + "D0, P[a], (!!map)::{b: apple}\n", + }, + }, + { + 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", + }, + }, + { + description: "Special characters", + subdescription: "Use quotes around path elements with special characters", + document: `{"{}": frog}`, + expression: `."{}"`, + expected: []string{ + "D0, P[{}], (!!str)::frog\n", + }, + }, + { + 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{ + "D0, P[1 a], (!!null)::null\n", + }, + }, + { + skipDoc: true, + document: `{}`, + expression: `.a.[1]`, + expected: []string{ + "D0, P[a 1], (!!null)::null\n", + }, + }, + { + 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{ + "D0, P[a cat b], (!!int)::3\n", + "D0, P[a mad b], (!!int)::4\n", + "D0, P[a fad b], (!!null)::null\n", + }, + }, + { + skipDoc: true, + document: `{a: {cat: apple, mad: things}}`, + expression: `.a | (.cat, .mad)`, + expected: []string{ + "D0, P[a cat], (!!str)::apple\n", + "D0, P[a mad], (!!str)::things\n", + }, + }, + { + skipDoc: true, + document: `{a: {cat: apple, mad: things}}`, + expression: `.a | (.cat, .mad, .fad)`, + expected: []string{ + "D0, P[a cat], (!!str)::apple\n", + "D0, P[a mad], (!!str)::things\n", + "D0, P[a fad], (!!null)::null\n", + }, + }, + { + skipDoc: true, + document: `{a: {cat: apple, mad: things}}`, + expression: `.a | (.cat, .mad, .fad) | select( (. == null) | not)`, + expected: []string{ + "D0, P[a cat], (!!str)::apple\n", + "D0, P[a mad], (!!str)::things\n", + }, + }, + { + description: "Aliases", + document: `{a: &cat {c: frog}, b: *cat}`, + expression: `.b`, + expected: []string{ + "D0, P[b], (alias)::*cat\n", + }, + }, + { + description: "Traversing aliases with splat", + document: `{a: &cat {c: frog}, b: *cat}`, + expression: `.b.[]`, + expected: []string{ + "D0, P[b c], (!!str)::frog\n", + }, + }, + { + 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{}, + }, + { + description: "Traversing arrays by index", + document: `[1,2,3]`, + expression: `.[0]`, + expected: []string{ + "D0, P[0], (!!int)::1\n", + }, + }, + { + 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{ + "D0, P[0], (!!null)::null\n", + }, + }, + { + skipDoc: true, + document: mergeDocSample, + expression: `.foobar`, + expected: []string{ + "D0, P[foobar], (!!map)::c: foobar_c\n!!merge <<: *foo\nthing: foobar_thing\n", + }, + }, + { + description: "Traversing merge anchors", + document: mergeDocSample, + expression: `.foobar.a`, + expected: []string{ + "D0, P[foobar a], (!!str)::foo_a\n", + }, + }, + { + description: "Traversing merge anchors with override", + document: mergeDocSample, + expression: `.foobar.c`, + expected: []string{ + "D0, P[foobar c], (!!str)::foo_c\n", + }, + }, + { + description: "Traversing merge anchors with local override", + document: mergeDocSample, + expression: `.foobar.thing`, + expected: []string{ + "D0, P[foobar thing], (!!str)::foobar_thing\n", + }, + }, + { + 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", + "D0, P[foobar thing], (!!str)::foobar_thing\n", + }, + }, + { + skipDoc: true, + document: mergeDocSample, + expression: `.foobarList`, + expected: []string{ + "D0, P[foobarList], (!!map)::b: foobarList_b\n!!merge <<: [*foo, *bar]\nc: foobarList_c\n", + }, + }, + { + skipDoc: true, + document: mergeDocSample, + expression: `.foobarList.a`, + expected: []string{ + "D0, P[foobarList a], (!!str)::foo_a\n", + }, + }, + { + 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{ + "D0, P[foobarList c], (!!str)::foobarList_c\n", + }, + }, + { + skipDoc: true, + document: mergeDocSample, + expression: `.foobarList.b`, + expected: []string{ + "D0, P[foobarList b], (!!str)::bar_b\n", + }, + }, + { + 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", + "D0, P[foobarList thing], (!!str)::bar_thing\n", + "D0, P[foobarList c], (!!str)::foobarList_c\n", + }, + }, +} + +func TestTraversePathOperatorScenarios(t *testing.T) { + for _, tt := range traversePathOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Traverse", traversePathOperatorScenarios) +} diff --git a/pkg/yqlib/operator_union.go b/pkg/yqlib/operator_union.go new file mode 100644 index 00000000..f436fa0e --- /dev/null +++ b/pkg/yqlib/operator_union.go @@ -0,0 +1,19 @@ +package yqlib + +import "container/list" + +func UnionOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs) + if err != nil { + return nil, err + } + for el := rhs.Front(); el != nil; el = el.Next() { + node := el.Value.(*CandidateNode) + lhs.PushBack(node) + } + return lhs, nil +} diff --git a/pkg/yqlib/operator_union_test.go b/pkg/yqlib/operator_union_test.go new file mode 100644 index 00000000..41719296 --- /dev/null +++ b/pkg/yqlib/operator_union_test.go @@ -0,0 +1,33 @@ +package yqlib + +import ( + "testing" +) + +var unionOperatorScenarios = []expressionScenario{ + { + description: "Combine scalars", + expression: `1, true, "cat"`, + expected: []string{ + "D0, P[], (!!int)::1\n", + "D0, P[], (!!bool)::true\n", + "D0, P[], (!!str)::cat\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", + }, + }, +} + +func TestUnionOperatorScenarios(t *testing.T) { + for _, tt := range unionOperatorScenarios { + testScenario(t, &tt) + } + documentScenarios(t, "Union", unionOperatorScenarios) +} diff --git a/pkg/yqlib/operator_value.go b/pkg/yqlib/operator_value.go new file mode 100644 index 00000000..94650050 --- /dev/null +++ b/pkg/yqlib/operator_value.go @@ -0,0 +1,8 @@ +package yqlib + +import "container/list" + +func ValueOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { + log.Debug("value = %v", pathNode.Operation.CandidateNode.Node.Value) + return nodeToMap(pathNode.Operation.CandidateNode), nil +} diff --git a/pkg/yqlib/operator_value_test.go b/pkg/yqlib/operator_value_test.go new file mode 100644 index 00000000..0642600a --- /dev/null +++ b/pkg/yqlib/operator_value_test.go @@ -0,0 +1,90 @@ +package yqlib + +import ( + "testing" +) + +var valueOperatorScenarios = []expressionScenario{ + { + document: ``, + expression: `1`, + expected: []string{ + "D0, P[], (!!int)::1\n", + }, + }, + { + document: ``, + expression: `-1`, + expected: []string{ + "D0, P[], (!!int)::-1\n", + }, + }, { + document: ``, + expression: `1.2`, + expected: []string{ + "D0, P[], (!!float)::1.2\n", + }, + }, { + document: ``, + expression: `-5.2e11`, + expected: []string{ + "D0, P[], (!!float)::-5.2e11\n", + }, + }, { + document: ``, + expression: `5e-10`, + expected: []string{ + "D0, P[], (!!float)::5e-10\n", + }, + }, { + document: ``, + expression: `"cat"`, + expected: []string{ + "D0, P[], (!!str)::cat\n", + }, + }, { + document: ``, + expression: `"1.3"`, + expected: []string{ + "D0, P[], (!!str)::\"1.3\"\n", + }, + }, { + document: ``, + expression: `"true"`, + expected: []string{ + "D0, P[], (!!str)::\"true\"\n", + }, + }, { + document: ``, + expression: `true`, + expected: []string{ + "D0, P[], (!!bool)::true\n", + }, + }, { + document: ``, + expression: `false`, + expected: []string{ + "D0, P[], (!!bool)::false\n", + }, + }, + { + document: ``, + expression: `Null`, + expected: []string{ + "D0, P[], (!!null)::Null\n", + }, + }, + { + document: ``, + expression: `~`, + expected: []string{ + "D0, P[], (!!null)::~\n", + }, + }, +} + +func TestValueOperatorScenarios(t *testing.T) { + for _, tt := range valueOperatorScenarios { + testScenario(t, &tt) + } +} diff --git a/pkg/yqlib/operators.go b/pkg/yqlib/operators.go new file mode 100644 index 00000000..f9f16122 --- /dev/null +++ b/pkg/yqlib/operators.go @@ -0,0 +1,35 @@ +package yqlib + +import ( + "container/list" + + "gopkg.in/yaml.v3" +) + +type OperatorHandler func(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) + +func UnwrapDoc(node *yaml.Node) *yaml.Node { + if node.Kind == yaml.DocumentNode { + return node.Content[0] + } + return node +} + +func EmptyOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + return list.New(), nil +} + +func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode { + valString := "true" + if !value { + valString = "false" + } + node := &yaml.Node{Kind: yaml.ScalarNode, Value: valString, Tag: "!!bool"} + return &CandidateNode{Node: node, Document: owner.Document, Path: owner.Path} +} + +func nodeToMap(candidate *CandidateNode) *list.List { + elMap := list.New() + elMap.PushBack(candidate) + return elMap +} diff --git a/pkg/yqlib/operators_test.go b/pkg/yqlib/operators_test.go new file mode 100644 index 00000000..610c8873 --- /dev/null +++ b/pkg/yqlib/operators_test.go @@ -0,0 +1,190 @@ +package yqlib + +import ( + "bufio" + "bytes" + "container/list" + "fmt" + "io" + "os" + "strings" + "testing" + + "github.com/mikefarah/yq/v4/test" + yaml "gopkg.in/yaml.v3" +) + +type expressionScenario struct { + 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) { + var results *list.List + var err error + + node, err := treeCreator.ParsePath(s.expression) + if err != nil { + t.Error(fmt.Errorf("Error parsing expression %v of %v: %v", s.expression, s.description, err)) + return + } + inputs := list.New() + + if s.document != "" { + inputs, err = readDocuments(strings.NewReader(s.document), "sample.yml", 0) + if err != nil { + t.Error(err, s.document) + return + } + } else { + candidateNode := &CandidateNode{ + Document: 0, + Filename: "", + Node: &yaml.Node{Tag: "!!null"}, + FileIndex: 0, + } + inputs.PushBack(candidateNode) + + } + + results, err = treeNavigator.GetMatchingNodes(inputs, node) + + if err != nil { + t.Error(err) + return + } + 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 { + panic(err) + } +} + +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) + } + streamEvaluator := NewStreamEvaluator() + err = streamEvaluator.Evaluate("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, "\n") + + for _, s := range scenarios { + if !s.skipDoc { + + writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) + + if s.subdescription != "" { + writeOrPanic(w, s.subdescription) + writeOrPanic(w, "\n\n") + } + formattedDoc := "" + if s.document != "" { + if s.dontFormatInputForDoc { + formattedDoc = s.document + "\n" + } 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, "will output\n") + + var output bytes.Buffer + var err error + printer := NewPrinter(bufio.NewWriter(&output), false, true, false, 2, true) + streamEvaluator := NewStreamEvaluator() + + if s.document != "" { + node, err := treeCreator.ParsePath(s.expression) + if err != nil { + t.Error(err) + } + err = streamEvaluator.Evaluate("sample.yaml", strings.NewReader(formattedDoc), node, printer) + + if err != nil { + t.Error(err) + } + } else { + err = streamEvaluator.EvaluateNew(s.expression, printer) + if err != nil { + t.Error(err) + } + } + + writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", output.String())) + + } + + } + w.Flush() +} diff --git a/pkg/yqlib/path_parse_test.go b/pkg/yqlib/path_parse_test.go new file mode 100644 index 00000000..17673114 --- /dev/null +++ b/pkg/yqlib/path_parse_test.go @@ -0,0 +1,177 @@ +package yqlib + +import ( + "fmt" + "testing" + + "github.com/mikefarah/yq/v4/test" +) + +var pathTests = []struct { + path string + expectedTokens []interface{} + expectedPostFix []interface{} +}{ + { + `[]`, + append(make([]interface{}, 0), "[", "]"), + append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE"), + }, + { + `.a[]`, + append(make([]interface{}, 0), "a", "SHORT_PIPE", "[]"), + append(make([]interface{}, 0), "a", "[]", "SHORT_PIPE"), + }, + { + `.a.[]`, + append(make([]interface{}, 0), "a", "SHORT_PIPE", "[]"), + append(make([]interface{}, 0), "a", "[]", "SHORT_PIPE"), + }, + { + `.a[].c`, + append(make([]interface{}, 0), "a", "SHORT_PIPE", "[]", "SHORT_PIPE", "c"), + append(make([]interface{}, 0), "a", "[]", "SHORT_PIPE", "c", "SHORT_PIPE"), + }, + { + `[3]`, + append(make([]interface{}, 0), "[", "3 (int64)", "]"), + append(make([]interface{}, 0), "3 (int64)", "COLLECT", "SHORT_PIPE"), + }, + { + `d0.a`, + append(make([]interface{}, 0), "d0", "SHORT_PIPE", "a"), + append(make([]interface{}, 0), "d0", "a", "SHORT_PIPE"), + }, + { + `.a | .[].b == "apple"`, + append(make([]interface{}, 0), "a", "PIPE", "[]", "SHORT_PIPE", "b", "EQUALS", "apple (string)"), + append(make([]interface{}, 0), "a", "[]", "b", "SHORT_PIPE", "apple (string)", "EQUALS", "PIPE"), + }, + { + `(.a | .[].b) == "apple"`, + append(make([]interface{}, 0), "(", "a", "PIPE", "[]", "SHORT_PIPE", "b", ")", "EQUALS", "apple (string)"), + append(make([]interface{}, 0), "a", "[]", "b", "SHORT_PIPE", "PIPE", "apple (string)", "EQUALS"), + }, + { + `.[] | select(. == "*at")`, + append(make([]interface{}, 0), "[]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at (string)", ")"), + append(make([]interface{}, 0), "[]", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"), + }, + { + `[true]`, + append(make([]interface{}, 0), "[", "true (bool)", "]"), + append(make([]interface{}, 0), "true (bool)", "COLLECT", "SHORT_PIPE"), + }, + { + `[true, false]`, + append(make([]interface{}, 0), "[", "true (bool)", "UNION", "false (bool)", "]"), + append(make([]interface{}, 0), "true (bool)", "false (bool)", "UNION", "COLLECT", "SHORT_PIPE"), + }, + { + `"mike": .a`, + append(make([]interface{}, 0), "mike (string)", "CREATE_MAP", "a"), + append(make([]interface{}, 0), "mike (string)", "a", "CREATE_MAP"), + }, + { + `.a: "mike"`, + append(make([]interface{}, 0), "a", "CREATE_MAP", "mike (string)"), + append(make([]interface{}, 0), "a", "mike (string)", "CREATE_MAP"), + }, + { + `{"mike": .a}`, + append(make([]interface{}, 0), "{", "mike (string)", "CREATE_MAP", "a", "}"), + append(make([]interface{}, 0), "mike (string)", "a", "CREATE_MAP", "COLLECT_OBJECT", "SHORT_PIPE"), + }, + { + `{.a: "mike"}`, + append(make([]interface{}, 0), "{", "a", "CREATE_MAP", "mike (string)", "}"), + append(make([]interface{}, 0), "a", "mike (string)", "CREATE_MAP", "COLLECT_OBJECT", "SHORT_PIPE"), + }, + { + `{.a: .c, .b.[]: .f.g.[]}`, + append(make([]interface{}, 0), "{", "a", "CREATE_MAP", "c", "UNION", "b", "SHORT_PIPE", "[]", "CREATE_MAP", "f", "SHORT_PIPE", "g", "SHORT_PIPE", "[]", "}"), + append(make([]interface{}, 0), "a", "c", "CREATE_MAP", "b", "[]", "SHORT_PIPE", "f", "g", "SHORT_PIPE", "[]", "SHORT_PIPE", "CREATE_MAP", "UNION", "COLLECT_OBJECT", "SHORT_PIPE"), + }, + { + `explode(.a.b)`, + append(make([]interface{}, 0), "EXPLODE", "(", "a", "SHORT_PIPE", "b", ")"), + append(make([]interface{}, 0), "a", "b", "SHORT_PIPE", "EXPLODE"), + }, + { + `.a.b style="folded"`, + append(make([]interface{}, 0), "a", "SHORT_PIPE", "b", "ASSIGN_STYLE", "folded (string)"), + append(make([]interface{}, 0), "a", "b", "SHORT_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), "a", "SHORT_PIPE", "b", "ASSIGN_TAG", "!!str (string)"), + append(make([]interface{}, 0), "a", "b", "SHORT_PIPE", "!!str (string)", "ASSIGN_TAG"), + }, + { + `""`, + append(make([]interface{}, 0), " (string)"), + append(make([]interface{}, 0), " (string)"), + }, + { + `.foo* | (. style="flow")`, + append(make([]interface{}, 0), "foo*", "PIPE", "(", "SELF", "ASSIGN_STYLE", "flow (string)", ")"), + append(make([]interface{}, 0), "foo*", "SELF", "flow (string)", "ASSIGN_STYLE", "PIPE"), + }, + { + `{}`, + append(make([]interface{}, 0), "{", "}"), + append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE"), + }, +} + +var tokeniser = NewPathTokeniser() +var postFixer = NewPathPostFixer() + +func TestPathParsing(t *testing.T) { + for _, tt := range pathTests { + tokens, err := tokeniser.Tokenise(tt.path) + if err != nil { + t.Error(tt.path, err) + } + var tokenValues []interface{} + for _, token := range tokens { + tokenValues = append(tokenValues, token.toString()) + } + test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path)) + + results, errorP := postFixer.ConvertToPostfix(tokens) + + var readableResults []interface{} + for _, token := range results { + readableResults = append(readableResults, token.toString()) + } + + if errorP != nil { + t.Error(tt.path, err) + } + + test.AssertResultComplexWithContext(t, tt.expectedPostFix, readableResults, fmt.Sprintf("postfix: %v", tt.path)) + + } +} diff --git a/pkg/yqlib/path_parser.go b/pkg/yqlib/path_parser.go deleted file mode 100644 index 4e518990..00000000 --- a/pkg/yqlib/path_parser.go +++ /dev/null @@ -1,153 +0,0 @@ -package yqlib - -import ( - "fmt" - "strconv" - "strings" - - yaml "gopkg.in/yaml.v3" -) - -type PathParser interface { - ParsePath(path string) []interface{} - MatchesNextPathElement(nodeContext NodeContext, nodeKey string) bool - IsPathExpression(pathElement string) bool -} - -type pathParser struct{} - -func NewPathParser() PathParser { - return &pathParser{} -} - -func matchesString(expression string, value string) bool { - var prefixMatch = strings.TrimSuffix(expression, "*") - if prefixMatch != expression { - log.Debug("prefix match, %v", strings.HasPrefix(value, prefixMatch)) - return strings.HasPrefix(value, prefixMatch) - } - return value == expression -} - -func (p *pathParser) IsPathExpression(pathElement string) bool { - return pathElement == "*" || pathElement == "**" || strings.Contains(pathElement, "==") -} - -/** - * node: node that we may traverse/visit - * head: path element expression to match against - * tail: remaining path element expressions - * pathStack: stack of actual paths we've matched to get to node - * nodeKey: actual value of this nodes 'key' or index. - */ -func (p *pathParser) MatchesNextPathElement(nodeContext NodeContext, nodeKey string) bool { - head := nodeContext.Head - if head == "**" || head == "*" { - return true - } - var headString = fmt.Sprintf("%v", head) - - if strings.Contains(headString, "==") && nodeContext.Node.Kind != yaml.ScalarNode { - log.Debug("ooh deep recursion time") - result := strings.SplitN(headString, "==", 2) - path := strings.TrimSpace(result[0]) - value := strings.TrimSpace(result[1]) - log.Debug("path %v", path) - log.Debug("value %v", value) - DebugNode(nodeContext.Node) - navigationStrategy := FilterMatchingNodesNavigationStrategy(value) - - navigator := NewDataNavigator(navigationStrategy) - err := navigator.Traverse(nodeContext.Node, p.ParsePath(path)) - if err != nil { - log.Error("Error deep recursing - ignoring") - log.Error(err.Error()) - } - log.Debug("done deep recursing, found %v matches", len(navigationStrategy.GetVisitedNodes())) - return len(navigationStrategy.GetVisitedNodes()) > 0 - } else if strings.Contains(headString, "==") && nodeContext.Node.Kind == yaml.ScalarNode { - result := strings.SplitN(headString, "==", 2) - path := strings.TrimSpace(result[0]) - value := strings.TrimSpace(result[1]) - if path == "." { - log.Debug("need to match scalar") - return matchesString(value, nodeContext.Node.Value) - } - } - - if head == "+" { - log.Debug("head is +, nodeKey is %v", nodeKey) - var _, err = strconv.ParseInt(nodeKey, 10, 64) // nolint - if err == nil { - return true - } - } - - return matchesString(headString, nodeKey) -} - -func (p *pathParser) ParsePath(path string) []interface{} { - var paths = make([]interface{}, 0) - if path == "" { - return paths - } - return p.parsePathAccum(paths, path) -} - -func (p *pathParser) parsePathAccum(paths []interface{}, remaining string) []interface{} { - head, tail := p.nextYamlPath(remaining) - if tail == "" { - return append(paths, head) - } - return p.parsePathAccum(append(paths, head), tail) -} - -func (p *pathParser) nextYamlPath(path string) (pathElement interface{}, remaining string) { - switch path[0] { - case '[': - // e.g [0].blah.cat -> we need to return "0" and "blah.cat" - var value, remainingBit = p.search(path[1:], []uint8{']'}, true) - var number, errParsingInt = strconv.ParseInt(value, 10, 64) // nolint - if errParsingInt == nil { - return number, remainingBit - } - return value, remainingBit - case '"': - // e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat" - return p.search(path[1:], []uint8{'"'}, true) - case '(': - // e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat" - return p.search(path[1:], []uint8{')'}, true) - default: - // e.g "a.blah.cat" -> return "a" and "blah.cat" - return p.search(path[0:], []uint8{'.', '[', '"', '('}, false) - } -} - -func (p *pathParser) search(path string, matchingChars []uint8, skipNext bool) (pathElement string, remaining string) { - for i := 0; i < len(path); i++ { - var char = path[i] - if p.contains(matchingChars, char) { - var remainingStart = i + 1 - if skipNext { - remainingStart = remainingStart + 1 - } else if !skipNext && char != '.' { - remainingStart = i - } - if remainingStart > len(path) { - remainingStart = len(path) - } - return path[0:i], path[remainingStart:] - } - } - return path, "" -} - -func (p *pathParser) contains(matchingChars []uint8, candidate uint8) bool { - for _, a := range matchingChars { - if a == candidate { - return true - } - } - return false -} diff --git a/pkg/yqlib/path_parser_test.go b/pkg/yqlib/path_parser_test.go deleted file mode 100644 index 7af12667..00000000 --- a/pkg/yqlib/path_parser_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package yqlib - -import ( - "testing" - - "github.com/mikefarah/yq/v3/test" -) - -var parser = NewPathParser() - -var parsePathsTests = []struct { - path string - expectedPaths []interface{} -}{ - {"a.b", append(make([]interface{}, 0), "a", "b")}, - {"a.b.**", append(make([]interface{}, 0), "a", "b", "**")}, - {"a.b.*", append(make([]interface{}, 0), "a", "b", "*")}, - {"a.b[0]", append(make([]interface{}, 0), "a", "b", int64(0))}, - {"a.b.0", append(make([]interface{}, 0), "a", "b", "0")}, - {"a.b.d[+]", append(make([]interface{}, 0), "a", "b", "d", "+")}, - {"a", append(make([]interface{}, 0), "a")}, - {"a.b.c", append(make([]interface{}, 0), "a", "b", "c")}, - {"\"a.b\".c", append(make([]interface{}, 0), "a.b", "c")}, - {"a.\"b.c\".d", append(make([]interface{}, 0), "a", "b.c", "d")}, - {"[1].a.d", append(make([]interface{}, 0), int64(1), "a", "d")}, - {"a[0].c", append(make([]interface{}, 0), "a", int64(0), "c")}, - {"[0]", append(make([]interface{}, 0), int64(0))}, -} - -func TestPathParserParsePath(t *testing.T) { - for _, tt := range parsePathsTests { - test.AssertResultComplex(t, tt.expectedPaths, parser.ParsePath(tt.path)) - } -} - -func TestPathParserMatchesNextPathElementSplat(t *testing.T) { - var node = NodeContext{Head: "*"} - test.AssertResult(t, true, parser.MatchesNextPathElement(node, "")) -} - -func TestPathParserMatchesNextPathElementDeepSplat(t *testing.T) { - var node = NodeContext{Head: "**"} - test.AssertResult(t, true, parser.MatchesNextPathElement(node, "")) -} - -func TestPathParserMatchesNextPathElementAppendArrayValid(t *testing.T) { - var node = NodeContext{Head: "+"} - test.AssertResult(t, true, parser.MatchesNextPathElement(node, "3")) -} - -func TestPathParserMatchesNextPathElementAppendArrayInvalid(t *testing.T) { - var node = NodeContext{Head: "+"} - test.AssertResult(t, false, parser.MatchesNextPathElement(node, "cat")) -} - -func TestPathParserMatchesNextPathElementPrefixMatchesWhole(t *testing.T) { - var node = NodeContext{Head: "cat*"} - test.AssertResult(t, true, parser.MatchesNextPathElement(node, "cat")) -} - -func TestPathParserMatchesNextPathElementPrefixMatchesStart(t *testing.T) { - var node = NodeContext{Head: "cat*"} - test.AssertResult(t, true, parser.MatchesNextPathElement(node, "caterpillar")) -} - -func TestPathParserMatchesNextPathElementPrefixMismatch(t *testing.T) { - var node = NodeContext{Head: "cat*"} - test.AssertResult(t, false, parser.MatchesNextPathElement(node, "dog")) -} - -func TestPathParserMatchesNextPathElementExactMatch(t *testing.T) { - var node = NodeContext{Head: "farahtek"} - test.AssertResult(t, true, parser.MatchesNextPathElement(node, "farahtek")) -} - -func TestPathParserMatchesNextPathElementExactMismatch(t *testing.T) { - var node = NodeContext{Head: "farahtek"} - test.AssertResult(t, false, parser.MatchesNextPathElement(node, "othertek")) -} diff --git a/pkg/yqlib/path_postfix.go b/pkg/yqlib/path_postfix.go new file mode 100644 index 00000000..a26adbcc --- /dev/null +++ b/pkg/yqlib/path_postfix.go @@ -0,0 +1,92 @@ +package yqlib + +import ( + "errors" + + logging "gopkg.in/op/go-logging.v1" +) + +type PathPostFixer interface { + ConvertToPostfix([]*Token) ([]*Operation, error) +} + +type pathPostFixer struct { +} + +func NewPathPostFixer() PathPostFixer { + return &pathPostFixer{} +} + +func popOpToResult(opStack []*Token, result []*Operation) ([]*Token, []*Operation) { + var newOp *Token + opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1] + return opStack, append(result, newOp.Operation) +} + +func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*Operation, error) { + var result []*Operation + // surround the whole thing with quotes + var opStack = []*Token{&Token{TokenType: OpenBracket}} + var tokens = append(infixTokens, &Token{TokenType: CloseBracket}) + + for _, token := range tokens { + log.Debugf("postfix processing token %v, %v", token.toString(), token.Operation) + switch token.TokenType { + case OpenBracket, OpenCollect, OpenCollectObject: + opStack = append(opStack, token) + case CloseCollect, CloseCollectObject: + var opener TokenType = OpenCollect + var collectOperator *OperationType = Collect + if token.TokenType == CloseCollectObject { + opener = OpenCollectObject + collectOperator = CollectObject + } + itemsInMiddle := false + for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener { + opStack, result = popOpToResult(opStack, result) + itemsInMiddle = true + } + if !itemsInMiddle { + // must be an empty collection, add the empty object as a LHS parameter + result = append(result, &Operation{OperationType: Empty}) + } + if len(opStack) == 0 { + return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket") + } + // now we should have [] as the last element on the opStack, get rid of it + opStack = opStack[0 : len(opStack)-1] + //and append a collect to the opStack + opStack = append(opStack, &Token{TokenType: OperationToken, Operation: &Operation{OperationType: ShortPipe}}) + opStack = append(opStack, &Token{TokenType: OperationToken, Operation: &Operation{OperationType: collectOperator}}) + case CloseBracket: + for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != OpenBracket { + opStack, result = popOpToResult(opStack, result) + } + if len(opStack) == 0 { + return nil, errors.New("Bad path expression, got close brackets without matching opening bracket") + } + // now we should have ( as the last element on the opStack, get rid of it + opStack = opStack[0 : len(opStack)-1] + + default: + var currentPrecedence = token.Operation.OperationType.Precedence + // pop off higher precedent operators onto the result + for len(opStack) > 0 && + opStack[len(opStack)-1].TokenType == OperationToken && + opStack[len(opStack)-1].Operation.OperationType.Precedence >= currentPrecedence { + opStack, result = popOpToResult(opStack, result) + } + // add this operator to the opStack + opStack = append(opStack, token) + } + } + + if log.IsEnabledFor(logging.DEBUG) { + log.Debugf("PostFix Result:") + for _, token := range result { + log.Debugf("> %v", token.toString()) + } + } + + return result, nil +} diff --git a/pkg/yqlib/path_tokeniser.go b/pkg/yqlib/path_tokeniser.go new file mode 100644 index 00000000..a3d64e61 --- /dev/null +++ b/pkg/yqlib/path_tokeniser.go @@ -0,0 +1,360 @@ +package yqlib + +import ( + "fmt" + "strconv" + + lex "github.com/timtadh/lexmachine" + "github.com/timtadh/lexmachine/machines" +) + +func skip(*lex.Scanner, *machines.Match) (interface{}, error) { + return nil, nil +} + +type TokenType uint32 + +const ( + OperationToken = 1 << iota + OpenBracket + CloseBracket + OpenCollect + CloseCollect + OpenCollectObject + CloseCollectObject + SplatOrEmptyCollect +) + +type Token struct { + 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 + +} + +func (t *Token) toString() string { + if t.TokenType == OperationToken { + log.Debug("toString, its an op") + return t.Operation.toString() + } else if t.TokenType == OpenBracket { + return "(" + } else if t.TokenType == CloseBracket { + return ")" + } else if t.TokenType == OpenCollect { + return "[" + } else if t.TokenType == CloseCollect { + return "]" + } else if t.TokenType == OpenCollectObject { + return "{" + } else if t.TokenType == CloseCollectObject { + return "}" + } else if t.TokenType == SplatOrEmptyCollect { + return "[]?" + } else { + return "NFI" + } +} + +func pathToken(wrapped bool) lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + value := string(m.Bytes) + value = value[1:] + if wrapped { + value = unwrap(value) + } + log.Debug("PathToken %v", value) + op := &Operation{OperationType: TraversePath, Value: value, StringValue: value} + return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil + } +} + +func documentToken() lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + var numberString = string(m.Bytes) + numberString = numberString[1:] + var number, errParsingInt = strconv.ParseInt(numberString, 10, 64) // nolint + if errParsingInt != nil { + return nil, errParsingInt + } + log.Debug("documentToken %v", string(m.Bytes)) + op := &Operation{OperationType: DocumentFilter, Value: number, StringValue: numberString} + return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil + } +} + +func opToken(op *OperationType) lex.Action { + return opTokenWithPrefs(op, nil, nil) +} + +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) { + log.Debug("opTokenWithPrefs %v", string(m.Bytes)) + value := string(m.Bytes) + op := &Operation{OperationType: op, Value: op.Type, StringValue: value, Preferences: preferences} + 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 + } +} + +func literalToken(pType TokenType, checkForPost bool) lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + return &Token{TokenType: pType, CheckForPostTraverse: checkForPost}, nil + } +} + +func unwrap(value string) string { + return value[1 : len(value)-1] +} + +func arrayIndextoken(precedingDot bool) lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + var numberString = string(m.Bytes) + startIndex := 1 + if precedingDot { + startIndex = 2 + } + numberString = numberString[startIndex : len(numberString)-1] + var number, errParsingInt = strconv.ParseInt(numberString, 10, 64) // nolint + if errParsingInt != nil { + return nil, errParsingInt + } + op := &Operation{OperationType: TraversePath, Value: number, StringValue: numberString} + return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil + } +} + +func numberValue() lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + var numberString = string(m.Bytes) + var number, errParsingInt = strconv.ParseInt(numberString, 10, 64) // nolint + if errParsingInt != nil { + return nil, errParsingInt + } + + return &Token{TokenType: OperationToken, Operation: CreateValueOperation(number, numberString)}, nil + } +} + +func floatValue() lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + var numberString = string(m.Bytes) + var number, errParsingInt = strconv.ParseFloat(numberString, 64) // nolint + if errParsingInt != nil { + return nil, errParsingInt + } + return &Token{TokenType: OperationToken, Operation: CreateValueOperation(number, numberString)}, nil + } +} + +func booleanValue(val bool) lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + return &Token{TokenType: OperationToken, Operation: CreateValueOperation(val, string(m.Bytes))}, nil + } +} + +func stringValue(wrapped bool) lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + value := string(m.Bytes) + if wrapped { + value = unwrap(value) + } + return &Token{TokenType: OperationToken, Operation: CreateValueOperation(value, value)}, nil + } +} + +func nullValue() lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + return &Token{TokenType: OperationToken, Operation: CreateValueOperation(nil, string(m.Bytes))}, nil + } +} + +func selfToken() lex.Action { + return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + op := &Operation{OperationType: SelfReference} + return &Token{TokenType: OperationToken, Operation: op}, nil + } +} + +func initLexer() (*lex.Lexer, error) { + lexer := lex.NewLexer() + lexer.Add([]byte(`\(`), literalToken(OpenBracket, false)) + lexer.Add([]byte(`\)`), literalToken(CloseBracket, true)) + + lexer.Add([]byte(`\.\[\]`), pathToken(false)) + lexer.Add([]byte(`\.\.`), opToken(RecursiveDescent)) + + lexer.Add([]byte(`,`), opToken(Union)) + lexer.Add([]byte(`:\s*`), opToken(CreateMap)) + lexer.Add([]byte(`length`), opToken(Length)) + lexer.Add([]byte(`sortKeys`), opToken(SortKeys)) + lexer.Add([]byte(`select`), opToken(Select)) + lexer.Add([]byte(`has`), opToken(Has)) + lexer.Add([]byte(`explode`), opToken(Explode)) + lexer.Add([]byte(`or`), opToken(Or)) + lexer.Add([]byte(`and`), opToken(And)) + lexer.Add([]byte(`not`), opToken(Not)) + + lexer.Add([]byte(`documentIndex`), opToken(GetDocumentIndex)) + + lexer.Add([]byte(`style`), opAssignableToken(GetStyle, AssignStyle)) + + lexer.Add([]byte(`tag`), opAssignableToken(GetTag, AssignTag)) + lexer.Add([]byte(`filename`), opToken(GetFilename)) + lexer.Add([]byte(`fileIndex`), opToken(GetFileIndex)) + lexer.Add([]byte(`path`), opToken(GetPath)) + + lexer.Add([]byte(`lineComment`), opTokenWithPrefs(GetComment, AssignComment, &CommentOpPreferences{LineComment: true})) + + lexer.Add([]byte(`headComment`), opTokenWithPrefs(GetComment, AssignComment, &CommentOpPreferences{HeadComment: 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(`del`), opToken(DeleteChild)) + + lexer.Add([]byte(`\s*\|=\s*`), opTokenWithPrefs(Assign, nil, &AssignOpPreferences{true})) + + lexer.Add([]byte(`\.\[-?[0-9]+\]`), arrayIndextoken(true)) + + lexer.Add([]byte("( |\t|\n|\r)+"), skip) + + lexer.Add([]byte(`d[0-9]+`), documentToken()) + lexer.Add([]byte(`\."[^ "]+"`), pathToken(true)) + lexer.Add([]byte(`\.[^ \}\{\:\[\],\|\.\[\(\)=]+`), pathToken(false)) + lexer.Add([]byte(`\.`), selfToken()) + + lexer.Add([]byte(`\|`), opToken(Pipe)) + + lexer.Add([]byte(`-?\d+(\.\d+)`), floatValue()) + lexer.Add([]byte(`-?[1-9](\.\d+)?[Ee][-+]?\d+`), floatValue()) + lexer.Add([]byte(`-?\d+`), numberValue()) + + lexer.Add([]byte(`[Tt][Rr][Uu][Ee]`), booleanValue(true)) + lexer.Add([]byte(`[Ff][Aa][Ll][Ss][Ee]`), booleanValue(false)) + + lexer.Add([]byte(`[Nn][Uu][Ll][Ll]`), nullValue()) + lexer.Add([]byte(`~`), nullValue()) + + lexer.Add([]byte(`"[^ "]*"`), stringValue(true)) + + lexer.Add([]byte(`\[\]`), literalToken(SplatOrEmptyCollect, true)) + + lexer.Add([]byte(`\[`), literalToken(OpenCollect, false)) + lexer.Add([]byte(`\]`), literalToken(CloseCollect, true)) + lexer.Add([]byte(`\{`), literalToken(OpenCollectObject, false)) + lexer.Add([]byte(`\}`), literalToken(CloseCollectObject, true)) + lexer.Add([]byte(`\*`), opTokenWithPrefs(Multiply, nil, &MultiplyPreferences{AppendArrays: false})) + lexer.Add([]byte(`\*\+`), opTokenWithPrefs(Multiply, nil, &MultiplyPreferences{AppendArrays: true})) + lexer.Add([]byte(`\+`), opToken(Add)) + lexer.Add([]byte(`\+=`), opToken(AddAssign)) + + err := lexer.Compile() + if err != nil { + return nil, err + } + return lexer, nil +} + +type PathTokeniser interface { + Tokenise(path string) ([]*Token, error) +} + +type pathTokeniser struct { + lexer *lex.Lexer +} + +func NewPathTokeniser() PathTokeniser { + var lexer, err = initLexer() + if err != nil { + panic(err) + } + return &pathTokeniser{lexer} +} + +func (p *pathTokeniser) Tokenise(path string) ([]*Token, error) { + scanner, err := p.lexer.Scanner([]byte(path)) + + if err != nil { + return nil, fmt.Errorf("Parsing expression: %v", err) + } + var tokens []*Token + for tok, err, eof := scanner.Next(); !eof; tok, err, eof = scanner.Next() { + + if tok != nil { + token := tok.(*Token) + log.Debugf("Tokenising %v", token.toString()) + tokens = append(tokens, token) + } + if err != nil { + return nil, fmt.Errorf("Parsing expression: %v", err) + } + } + var postProcessedTokens = make([]*Token, 0) + + skipNextToken := false + + for index := range tokens { + if skipNextToken { + skipNextToken = false + } else { + postProcessedTokens, skipNextToken = p.handleToken(tokens, index, postProcessedTokens) + } + } + + return postProcessedTokens, nil +} + +func (p *pathTokeniser) handleToken(tokens []*Token, index int, postProcessedTokens []*Token) (tokensAccum []*Token, skipNextToken bool) { + skipNextToken = false + token := tokens[index] + if token.TokenType == SplatOrEmptyCollect { + if index > 0 && tokens[index-1].TokenType == OperationToken && + tokens[index-1].Operation.OperationType == TraversePath { + // must be a splat without a preceding dot , e.g. .a[] + // lets put a pipe in front of it, and convert it to a traverse "[]" token + pipeOp := &Operation{OperationType: ShortPipe, Value: "PIPE"} + + postProcessedTokens = append(postProcessedTokens, &Token{TokenType: OperationToken, Operation: pipeOp}) + + traverseOp := &Operation{OperationType: TraversePath, Value: "[]", StringValue: "[]"} + token = &Token{TokenType: OperationToken, Operation: traverseOp, CheckForPostTraverse: true} + + } else { + // gotta be a collect empty array, we need to split this into two tokens + // one OpenCollect, the other CloseCollect + postProcessedTokens = append(postProcessedTokens, &Token{TokenType: OpenCollect}) + token = &Token{TokenType: CloseCollect, CheckForPostTraverse: true} + } + } + + 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 + } + + 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: ShortPipe, Value: "PIPE"} + postProcessedTokens = append(postProcessedTokens, &Token{TokenType: OperationToken, Operation: op}) + } + return postProcessedTokens, skipNextToken +} diff --git a/pkg/yqlib/path_tree.go b/pkg/yqlib/path_tree.go new file mode 100644 index 00000000..4ea6f42f --- /dev/null +++ b/pkg/yqlib/path_tree.go @@ -0,0 +1,77 @@ +package yqlib + +import ( + "fmt" + "strings" +) + +var myPathTokeniser = NewPathTokeniser() +var myPathPostfixer = NewPathPostFixer() + +type PathTreeNode struct { + Operation *Operation + Lhs *PathTreeNode + Rhs *PathTreeNode +} + +type PathTreeCreator interface { + ParsePath(path string) (*PathTreeNode, error) + CreatePathTree(postFixPath []*Operation) (*PathTreeNode, error) +} + +type pathTreeCreator struct { +} + +func NewPathTreeCreator() PathTreeCreator { + return &pathTreeCreator{} +} + +func (p *pathTreeCreator) ParsePath(path string) (*PathTreeNode, error) { + tokens, err := myPathTokeniser.Tokenise(path) + if err != nil { + return nil, err + } + var Operations []*Operation + Operations, err = myPathPostfixer.ConvertToPostfix(tokens) + if err != nil { + return nil, err + } + return p.CreatePathTree(Operations) +} + +func (p *pathTreeCreator) CreatePathTree(postFixPath []*Operation) (*PathTreeNode, error) { + var stack = make([]*PathTreeNode, 0) + + if len(postFixPath) == 0 { + return nil, nil + } + + for _, Operation := range postFixPath { + var newNode = PathTreeNode{Operation: Operation} + log.Debugf("pathTree %v ", Operation.toString()) + if Operation.OperationType.NumArgs > 0 { + numArgs := Operation.OperationType.NumArgs + if numArgs == 1 { + if len(stack) < 1 { + return nil, fmt.Errorf("'%v' expects 1 arg but received none", strings.TrimSpace(Operation.StringValue)) + } + remaining, rhs := stack[:len(stack)-1], stack[len(stack)-1] + newNode.Rhs = rhs + stack = remaining + } else if numArgs == 2 { + if len(stack) < 2 { + return nil, fmt.Errorf("'%v' expects 2 args but there is %v", strings.TrimSpace(Operation.StringValue), len(stack)) + } + remaining, lhs, rhs := stack[:len(stack)-2], stack[len(stack)-2], stack[len(stack)-1] + newNode.Lhs = lhs + newNode.Rhs = rhs + stack = remaining + } + } + stack = append(stack, &newNode) + } + if len(stack) != 1 { + return nil, fmt.Errorf("expected end of expression but found '%v', please check expression syntax", strings.TrimSpace(stack[1].Operation.StringValue)) + } + return stack[0], nil +} diff --git a/pkg/yqlib/path_tree_test.go b/pkg/yqlib/path_tree_test.go new file mode 100644 index 00000000..6b2ab713 --- /dev/null +++ b/pkg/yqlib/path_tree_test.go @@ -0,0 +1,42 @@ +package yqlib + +import ( + "testing" + + "github.com/mikefarah/yq/v4/test" +) + +func TestPathTreeNoArgsForTwoArgOp(t *testing.T) { + _, err := treeCreator.ParsePath("=") + test.AssertResultComplex(t, "'=' expects 2 args but there is 0", err.Error()) +} + +func TestPathTreeOneLhsArgsForTwoArgOp(t *testing.T) { + _, err := treeCreator.ParsePath(".a =") + test.AssertResultComplex(t, "'=' expects 2 args but there is 1", err.Error()) +} + +func TestPathTreeOneRhsArgsForTwoArgOp(t *testing.T) { + _, err := treeCreator.ParsePath("= .a") + test.AssertResultComplex(t, "'=' expects 2 args but there is 1", err.Error()) +} + +func TestPathTreeTwoArgsForTwoArgOp(t *testing.T) { + _, err := treeCreator.ParsePath(".a = .b") + test.AssertResultComplex(t, nil, err) +} + +func TestPathTreeNoArgsForOneArgOp(t *testing.T) { + _, err := treeCreator.ParsePath("explode") + test.AssertResultComplex(t, "'explode' expects 1 arg but received none", err.Error()) +} + +func TestPathTreeOneArgForOneArgOp(t *testing.T) { + _, err := treeCreator.ParsePath("explode(.)") + test.AssertResultComplex(t, nil, err) +} + +func TestPathTreeExtraArgs(t *testing.T) { + _, err := treeCreator.ParsePath("sortKeys(.) explode(.)") + test.AssertResultComplex(t, "expected end of expression but found 'explode', please check expression syntax", err.Error()) +} diff --git a/pkg/yqlib/printer.go b/pkg/yqlib/printer.go new file mode 100644 index 00000000..340abef6 --- /dev/null +++ b/pkg/yqlib/printer.go @@ -0,0 +1,117 @@ +package yqlib + +import ( + "bufio" + "container/list" + "io" + + yaml "gopkg.in/yaml.v3" +) + +type Printer interface { + PrintResults(matchingNodes *list.List) error + PrintedAnything() bool +} + +type resultsPrinter struct { + outputToJSON bool + unwrapScalar bool + colorsEnabled bool + indent int + printDocSeparators bool + writer io.Writer + firstTimePrinting bool + previousDocIndex uint + previousFileIndex int + printedMatches bool +} + +func NewPrinter(writer io.Writer, outputToJSON bool, unwrapScalar bool, colorsEnabled bool, indent int, printDocSeparators bool) Printer { + return &resultsPrinter{ + writer: writer, + outputToJSON: outputToJSON, + unwrapScalar: unwrapScalar, + colorsEnabled: colorsEnabled, + indent: indent, + printDocSeparators: printDocSeparators, + firstTimePrinting: true, + } +} + +func (p *resultsPrinter) PrintedAnything() bool { + return p.printedMatches +} + +func (p *resultsPrinter) printNode(node *yaml.Node, writer io.Writer) error { + p.printedMatches = p.printedMatches || (node.Tag != "!!null" && + (node.Tag != "!!bool" || node.Value != "false")) + + var encoder Encoder + if node.Kind == yaml.ScalarNode && p.unwrapScalar && !p.outputToJSON { + return p.writeString(writer, node.Value+"\n") + } + if p.outputToJSON { + encoder = NewJsonEncoder(writer, p.indent) + } else { + encoder = NewYamlEncoder(writer, p.indent, p.colorsEnabled) + } + return encoder.Encode(node) +} + +func (p *resultsPrinter) writeString(writer io.Writer, txt string) error { + _, errorWriting := writer.Write([]byte(txt)) + return errorWriting +} + +func (p *resultsPrinter) safelyFlush(writer *bufio.Writer) { + if err := writer.Flush(); err != nil { + log.Error("Error flushing writer!") + log.Error(err.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} + explodeNode := PathTreeNode{Operation: &explodeOp} + matchingNodes, err = treeNavigator.GetMatchingNodes(matchingNodes, &explodeNode) + if err != nil { + return err + } + } + + bufferedWriter := bufio.NewWriter(p.writer) + defer p.safelyFlush(bufferedWriter) + + if matchingNodes.Len() == 0 { + log.Debug("no matching results, nothing to print") + return nil + } + if p.firstTimePrinting { + node := matchingNodes.Front().Value.(*CandidateNode) + p.previousDocIndex = node.Document + p.previousFileIndex = node.FileIndex + p.firstTimePrinting = false + } + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + mappedDoc := el.Value.(*CandidateNode) + 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.previousFileIndex != mappedDoc.FileIndex) && p.printDocSeparators { + log.Debug("-- writing doc sep") + if err := p.writeString(bufferedWriter, "---\n"); err != nil { + return err + } + } + + if err := p.printNode(mappedDoc.Node, bufferedWriter); err != nil { + return err + } + + p.previousDocIndex = mappedDoc.Document + } + + return nil +} diff --git a/pkg/yqlib/printer_test.go b/pkg/yqlib/printer_test.go new file mode 100644 index 00000000..aaafb448 --- /dev/null +++ b/pkg/yqlib/printer_test.go @@ -0,0 +1,145 @@ +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", 0) + 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 TestPrinterMultipleFilesInSequence(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", 0) + if err != nil { + panic(err) + } + + el := inputs.Front() + elNode := el.Value.(*CandidateNode) + elNode.Document = 0 + elNode.FileIndex = 0 + sample1 := nodeToMap(elNode) + + el = el.Next() + elNode = el.Value.(*CandidateNode) + elNode.Document = 0 + elNode.FileIndex = 1 + sample2 := nodeToMap(elNode) + + el = el.Next() + elNode = el.Value.(*CandidateNode) + elNode.Document = 0 + elNode.FileIndex = 2 + sample3 := nodeToMap(elNode) + + 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", 0) + 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", 0) + 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()) +} diff --git a/pkg/yqlib/read_for_merge_navigation_strategy.go b/pkg/yqlib/read_for_merge_navigation_strategy.go deleted file mode 100644 index 73061338..00000000 --- a/pkg/yqlib/read_for_merge_navigation_strategy.go +++ /dev/null @@ -1,37 +0,0 @@ -package yqlib - -import "gopkg.in/yaml.v3" - -func ReadForMergeNavigationStrategy(arrayMergeStrategy ArrayMergeStrategy) NavigationStrategy { - return &NavigationStrategyImpl{ - visitedNodes: []*NodeContext{}, - pathParser: NewPathParser(), - followAlias: func(nodeContext NodeContext) bool { - return false - }, - shouldOnlyDeeplyVisitLeaves: func(nodeContext NodeContext) bool { - return false - }, - visit: func(nodeContext NodeContext) error { - return nil - }, - shouldDeeplyTraverse: func(nodeContext NodeContext) bool { - if nodeContext.Node.Kind == yaml.SequenceNode && arrayMergeStrategy == OverwriteArrayMergeStrategy { - nodeContext.IsMiddleNode = false - return false - } - - var isInArray = false - if len(nodeContext.PathStack) > 0 { - var lastElement = nodeContext.PathStack[len(nodeContext.PathStack)-1] - switch lastElement.(type) { - case int: - isInArray = true - default: - isInArray = false - } - } - return arrayMergeStrategy == UpdateArrayMergeStrategy || !isInArray - }, - } -} diff --git a/pkg/yqlib/read_navigation_strategy.go b/pkg/yqlib/read_navigation_strategy.go deleted file mode 100644 index ba29e94e..00000000 --- a/pkg/yqlib/read_navigation_strategy.go +++ /dev/null @@ -1,11 +0,0 @@ -package yqlib - -func ReadNavigationStrategy() NavigationStrategy { - return &NavigationStrategyImpl{ - visitedNodes: []*NodeContext{}, - pathParser: NewPathParser(), - visit: func(nodeContext NodeContext) error { - return nil - }, - } -} diff --git a/pkg/yqlib/stream_evaluator.go b/pkg/yqlib/stream_evaluator.go new file mode 100644 index 00000000..b2fdb9e1 --- /dev/null +++ b/pkg/yqlib/stream_evaluator.go @@ -0,0 +1,107 @@ +package yqlib + +import ( + "container/list" + "io" + "os" + + yaml "gopkg.in/yaml.v3" +) + +type StreamEvaluator interface { + Evaluate(filename string, reader io.Reader, node *PathTreeNode, printer Printer) error + EvaluateFiles(expression string, filenames []string, printer Printer) error + EvaluateNew(expression string, printer Printer) error +} + +type streamEvaluator struct { + treeNavigator DataTreeNavigator + treeCreator PathTreeCreator + fileIndex int +} + +func NewStreamEvaluator() StreamEvaluator { + return &streamEvaluator{treeNavigator: NewDataTreeNavigator(), treeCreator: NewPathTreeCreator()} +} + +func (s *streamEvaluator) EvaluateNew(expression string, printer Printer) error { + node, err := treeCreator.ParsePath(expression) + if err != nil { + return err + } + candidateNode := &CandidateNode{ + Document: 0, + Filename: "", + Node: &yaml.Node{Tag: "!!null"}, + FileIndex: 0, + } + inputList := list.New() + inputList.PushBack(candidateNode) + + matches, errorParsing := treeNavigator.GetMatchingNodes(inputList, node) + if errorParsing != nil { + return errorParsing + } + return printer.PrintResults(matches) +} + +func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer) error { + + node, err := treeCreator.ParsePath(expression) + if err != nil { + return err + } + + for _, filename := range filenames { + reader, err := readStream(filename) + if err != nil { + return err + } + err = s.Evaluate(filename, reader, node, printer) + if err != nil { + return err + } + + switch reader := reader.(type) { + case *os.File: + safelyCloseFile(reader) + } + } + return nil +} + +func (s *streamEvaluator) Evaluate(filename string, reader io.Reader, node *PathTreeNode, printer Printer) error { + + var currentIndex uint + + decoder := yaml.NewDecoder(reader) + for { + var dataBucket yaml.Node + errorReading := decoder.Decode(&dataBucket) + + if errorReading == io.EOF { + s.fileIndex = s.fileIndex + 1 + return nil + } else if errorReading != nil { + return errorReading + } + candidateNode := &CandidateNode{ + Document: currentIndex, + Filename: filename, + Node: &dataBucket, + FileIndex: s.fileIndex, + } + inputList := list.New() + inputList.PushBack(candidateNode) + + matches, errorParsing := treeNavigator.GetMatchingNodes(inputList, node) + if errorParsing != nil { + return errorParsing + } + err := printer.PrintResults(matches) + if err != nil { + return err + } + currentIndex = currentIndex + 1 + } +} diff --git a/pkg/yqlib/update_navigation_strategy.go b/pkg/yqlib/update_navigation_strategy.go deleted file mode 100644 index b5b0e750..00000000 --- a/pkg/yqlib/update_navigation_strategy.go +++ /dev/null @@ -1,43 +0,0 @@ -package yqlib - -func UpdateNavigationStrategy(updateCommand UpdateCommand, autoCreate bool) NavigationStrategy { - return &NavigationStrategyImpl{ - visitedNodes: []*NodeContext{}, - pathParser: NewPathParser(), - followAlias: func(nodeContext NodeContext) bool { - return false - }, - autoCreateMap: func(nodeContext NodeContext) bool { - return autoCreate - }, - visit: func(nodeContext NodeContext) error { - node := nodeContext.Node - changesToApply := updateCommand.Value - if updateCommand.Overwrite || node.Value == "" { - log.Debug("going to update") - DebugNode(node) - log.Debug("with") - DebugNode(changesToApply) - if !updateCommand.DontUpdateNodeValue { - node.Value = changesToApply.Value - } - node.Tag = changesToApply.Tag - node.Kind = changesToApply.Kind - node.Style = changesToApply.Style - if !updateCommand.DontUpdateNodeContent { - node.Content = changesToApply.Content - } - node.Anchor = changesToApply.Anchor - node.Alias = changesToApply.Alias - if updateCommand.CommentsMergeStrategy != IgnoreCommentsMergeStrategy { - node.HeadComment = changesToApply.HeadComment - node.LineComment = changesToApply.LineComment - node.FootComment = changesToApply.FootComment - } - } else { - log.Debug("skipping update as node already has value %v and overwriteFlag is ", node.Value, updateCommand.Overwrite) - } - return nil - }, - } -} diff --git a/pkg/yqlib/utils.go b/pkg/yqlib/utils.go new file mode 100644 index 00000000..092df9d2 --- /dev/null +++ b/pkg/yqlib/utils.go @@ -0,0 +1,52 @@ +package yqlib + +import ( + "bufio" + "container/list" + "io" + "os" + + yaml "gopkg.in/yaml.v3" +) + +var treeNavigator = NewDataTreeNavigator() +var treeCreator = NewPathTreeCreator() + +func readStream(filename string) (io.Reader, error) { + if filename == "-" { + return bufio.NewReader(os.Stdin), nil + } else { + return os.Open(filename) // nolint gosec + } +} + +func readDocuments(reader io.Reader, filename string, fileIndex int) (*list.List, error) { + decoder := yaml.NewDecoder(reader) + inputList := list.New() + var currentIndex uint = 0 + + for { + var dataBucket yaml.Node + errorReading := decoder.Decode(&dataBucket) + + if errorReading == io.EOF { + switch reader := reader.(type) { + case *os.File: + safelyCloseFile(reader) + } + return inputList, nil + } else if errorReading != nil { + return nil, errorReading + } + candidateNode := &CandidateNode{ + Document: currentIndex, + Filename: filename, + Node: &dataBucket, + FileIndex: fileIndex, + } + + inputList.PushBack(candidateNode) + + currentIndex = currentIndex + 1 + } +} diff --git a/pkg/yqlib/value_parser.go b/pkg/yqlib/value_parser.go deleted file mode 100644 index 6dee7400..00000000 --- a/pkg/yqlib/value_parser.go +++ /dev/null @@ -1,46 +0,0 @@ -package yqlib - -import ( - yaml "gopkg.in/yaml.v3" -) - -type ValueParser interface { - Parse(argument string, customTag string, customStyle string, anchorName string, createAlias bool) *yaml.Node -} - -type valueParser struct { -} - -func NewValueParser() ValueParser { - return &valueParser{} -} - -func (v *valueParser) Parse(argument string, customTag string, customStyle string, anchorName string, createAlias bool) *yaml.Node { - var style yaml.Style - if customStyle == "tagged" { - style = yaml.TaggedStyle - } else if customStyle == "double" { - style = yaml.DoubleQuotedStyle - } else if customStyle == "single" { - style = yaml.SingleQuotedStyle - } else if customStyle == "literal" { - style = yaml.LiteralStyle - } else if customStyle == "folded" { - style = yaml.FoldedStyle - } else if customStyle == "flow" { - style = yaml.FlowStyle - } else if customStyle != "" { - log.Error("Unknown style %v, ignoring", customStyle) - } - if argument == "[]" { - return &yaml.Node{Tag: "!!seq", Kind: yaml.SequenceNode, Style: style} - } - - kind := yaml.ScalarNode - - if createAlias { - kind = yaml.AliasNode - } - - return &yaml.Node{Value: argument, Tag: customTag, Kind: kind, Style: style, Anchor: anchorName} -} diff --git a/pkg/yqlib/value_parser_test.go b/pkg/yqlib/value_parser_test.go deleted file mode 100644 index a21a6ec1..00000000 --- a/pkg/yqlib/value_parser_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package yqlib - -import ( - "testing" - - "github.com/mikefarah/yq/v3/test" - yaml "gopkg.in/yaml.v3" -) - -var parseStyleTests = []struct { - customStyle string - expectedStyle yaml.Style -}{ - {"", 0}, - {"tagged", yaml.TaggedStyle}, - {"double", yaml.DoubleQuotedStyle}, - {"single", yaml.SingleQuotedStyle}, - {"folded", yaml.FoldedStyle}, - {"flow", yaml.FlowStyle}, - {"literal", yaml.LiteralStyle}, -} - -func TestValueParserStyleTag(t *testing.T) { - for _, tt := range parseStyleTests { - actual := NewValueParser().Parse("cat", "", tt.customStyle, "", false) - test.AssertResultWithContext(t, tt.expectedStyle, actual.Style, tt.customStyle) - } -} - -var parseValueTests = []struct { - argument string - customTag string - expectedTag string - testDescription string -}{ - {"true", "!!str", "!!str", "boolean forced as string"}, - {"3", "!!int", "!!int", "int"}, - {"cat", "", "", "default"}, -} - -func TestValueParserParse(t *testing.T) { - for _, tt := range parseValueTests { - actual := NewValueParser().Parse(tt.argument, tt.customTag, "", "", false) - test.AssertResultWithContext(t, tt.argument, actual.Value, tt.testDescription) - test.AssertResultWithContext(t, tt.expectedTag, actual.Tag, tt.testDescription) - test.AssertResult(t, yaml.ScalarNode, actual.Kind) - } -} - -func TestValueParserParseEmptyArray(t *testing.T) { - actual := NewValueParser().Parse("[]", "", "", "", false) - test.AssertResult(t, "!!seq", actual.Tag) - test.AssertResult(t, yaml.SequenceNode, actual.Kind) -} - -func TestValueParserParseAlias(t *testing.T) { - actual := NewValueParser().Parse("bob", "", "", "", true) - test.AssertResult(t, "bob", actual.Value) - test.AssertResult(t, yaml.AliasNode, actual.Kind) -} - -func TestValueParserAnchorname(t *testing.T) { - actual := NewValueParser().Parse("caterpillar", "", "", "foo", false) - test.AssertResult(t, "foo", actual.Anchor) -} diff --git a/pkg/yqlib/write_in_place_handler.go b/pkg/yqlib/write_in_place_handler.go new file mode 100644 index 00000000..a4fbb013 --- /dev/null +++ b/pkg/yqlib/write_in_place_handler.go @@ -0,0 +1,59 @@ +package yqlib + +import ( + "io/ioutil" + "os" +) + +type WriteInPlaceHandler interface { + CreateTempFile() (*os.File, error) + FinishWriteInPlace(evaluatedSuccessfully bool) +} + +type writeInPlaceHandler struct { + inputFilename string + tempFile *os.File +} + +func NewWriteInPlaceHandler(inputFile string) WriteInPlaceHandler { + + return &writeInPlaceHandler{inputFile, nil} +} + +func (w *writeInPlaceHandler) CreateTempFile() (*os.File, error) { + info, err := os.Stat(w.inputFilename) + if err != nil { + return nil, err + } + _, err = os.Stat(os.TempDir()) + if os.IsNotExist(err) { + err = os.Mkdir(os.TempDir(), 0700) + if err != nil { + return nil, err + } + } else if err != nil { + return nil, err + } + + file, err := ioutil.TempFile("", "temp") + if err != nil { + return nil, err + } + + err = os.Chmod(file.Name(), info.Mode()) + log.Debug("writing to tempfile: %v", file.Name()) + w.tempFile = file + return file, err +} + +func (w *writeInPlaceHandler) FinishWriteInPlace(evaluatedSuccessfully bool) { + log.Debug("Going to write-inplace, evaluatedSuccessfully=%v, target=%v", evaluatedSuccessfully, w.inputFilename) + safelyCloseFile(w.tempFile) + if evaluatedSuccessfully { + log.Debug("moved temp file to target") + safelyRenameFile(w.tempFile.Name(), w.inputFilename) + } else { + log.Debug("removed temp file") + os.Remove(w.tempFile.Name()) + } +} diff --git a/release_instructions.txt b/release_instructions.txt index ef6c88c3..ff478013 100644 --- a/release_instructions.txt +++ b/release_instructions.txt @@ -9,11 +9,11 @@ - 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 - - see https://build.snapcraft.io/user/mikefarah/yq sudo snap remove yq sudo snap install --edge yq diff --git a/scripts/acceptance.sh b/scripts/acceptance.sh index 309d588b..b543a8b3 100755 --- a/scripts/acceptance.sh +++ b/scripts/acceptance.sh @@ -3,10 +3,62 @@ set -e # acceptance test -X=$(./yq w ./examples/sample.yaml b.c 3 | ./yq r - b.c) -if [[ $X != 3 ]]; then - echo "Failed acceptance test: expected 3 but was $X" + + +echo "test eval-sequence" +random=$((1 + $RANDOM % 10)) +./yq e -n ".a = $random" > test.yml +X=$(./yq e '.a' test.yml) + +if [[ $X != $random ]]; then + echo "Failed create: expected $random but was $X" exit 1 fi + +echo "--success" + +echo "test update-in-place" + +update=$(($random + 1)) +./yq e -i ".a = $update" test.yml + +X=$(./yq e '.a' test.yml) +if [[ $X != $update ]]; then + echo "Failed to update inplace test: expected $update but was $X" + exit 1 +fi + +echo "--success" + +echo "test eval-all" +./yq ea -n ".a = $random" > test-eval-all.yml +Y=$(./yq ea '.a' test-eval-all.yml) + +if [[ $Y != $random ]]; then + echo "Failed create with eval all: expected $random but was $X" + exit 1 +fi +echo "--success" + +echo "test no exit status" +./yq e '.z' test.yml +echo "--success" + +echo "test exit status" +set +e + +./yq e -e '.z' test.yml + +if [[ $? != 1 ]]; then + echo "Expected error code 1 but was $?" + exit 1 +fi + +echo "--success" + +set -e + +rm test.yml +rm test-eval-all.yml echo "acceptance tests passed" diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 00000000..c169aa2d --- /dev/null +++ b/scripts/release.sh @@ -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 + diff --git a/scripts/publish.sh b/scripts/upload.sh similarity index 82% rename from scripts/publish.sh rename to scripts/upload.sh index 40b290f6..7498c2f4 100755 --- a/scripts/publish.sh +++ b/scripts/upload.sh @@ -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 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 2b1ae803..e8c60978 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: yq -version: '3.4.1' +version: '4.0.0' 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. diff --git a/test/utils.go b/test/utils.go index 25093e2f..3cd61385 100644 --- a/test/utils.go +++ b/test/utils.go @@ -50,7 +50,15 @@ func AssertResult(t *testing.T, expectedValue interface{}, actualValue interface func AssertResultComplex(t *testing.T, expectedValue interface{}, actualValue interface{}) { t.Helper() if !reflect.DeepEqual(expectedValue, actualValue) { - t.Error("Expected <", expectedValue, "> but got <", actualValue, ">", fmt.Sprintf("%T", actualValue)) + t.Error("\nExpected <", expectedValue, ">\nbut got <", actualValue, ">", fmt.Sprintf("%T", actualValue)) + } +} + +func AssertResultComplexWithContext(t *testing.T, expectedValue interface{}, actualValue interface{}, context interface{}) { + t.Helper() + if !reflect.DeepEqual(expectedValue, actualValue) { + t.Error(context) + t.Error("\nExpected <", expectedValue, ">\nbut got <", actualValue, ">", fmt.Sprintf("%T", actualValue)) } } diff --git a/yq.go b/yq.go index b409fab5..f95d4dea 100644 --- a/yq.go +++ b/yq.go @@ -3,7 +3,7 @@ package main import ( "os" - command "github.com/mikefarah/yq/v3/cmd" + command "github.com/mikefarah/yq/v4/cmd" ) func main() {