From b81fd638d76f1943ae382848fdb2a7773ed4df98 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Sun, 15 Dec 2019 19:34:05 +1100 Subject: [PATCH] wip - new node --- pkg/yqlib/data_navigator.go | 16 ++++---- pkg/yqlib/lib.go | 11 ++++++ yq.go | 75 ++++++++++++++++++++++++++----------- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/pkg/yqlib/data_navigator.go b/pkg/yqlib/data_navigator.go index 6aa0ad0c..66d4c710 100644 --- a/pkg/yqlib/data_navigator.go +++ b/pkg/yqlib/data_navigator.go @@ -14,6 +14,7 @@ type DataNavigator interface { Get(rootNode *yaml.Node, path []string) (*yaml.Node, error) Update(rootNode *yaml.Node, path []string, changesToApply yaml.Node) error Delete(rootNode *yaml.Node, path []string) error + GuessKind(tail []string, guess yaml.Kind) yaml.Kind } type navigator struct { @@ -68,6 +69,7 @@ func (n *navigator) Update(rootNode *yaml.Node, path []string, changesToApply ya return errorVisiting } +// TODO: refactor delete.. func (n *navigator) Delete(rootNode *yaml.Node, path []string) error { lastBit, newTail := path[len(path)-1], path[:len(path)-1] @@ -129,7 +131,7 @@ func (n *navigator) Visit(value *yaml.Node, path []string, visitor VisitorFn) er return visitor(realValue) } -func (n *navigator) guessKind(tail []string, guess yaml.Kind) yaml.Kind { +func (n *navigator) GuessKind(tail []string, guess yaml.Kind) yaml.Kind { n.log.Debug("tail %v", tail) if len(tail) == 0 && guess == 0 { n.log.Debug("end of path, must be a scalar") @@ -195,7 +197,7 @@ func (n *navigator) splatMap(value *yaml.Node, tail []string, visitor VisitorFn) if index%2 == 0 { continue } - content = n.getOrReplace(content, n.guessKind(tail, content.Kind)) + content = n.getOrReplace(content, n.GuessKind(tail, content.Kind)) var err = n.Visit(content, tail, visitor) if err != nil { return err @@ -206,7 +208,7 @@ func (n *navigator) splatMap(value *yaml.Node, tail []string, visitor VisitorFn) func (n *navigator) recurseMap(value *yaml.Node, head string, tail []string, visitor VisitorFn) error { visited, errorVisiting := n.visitMatchingEntries(value.Content, head, func(indexInMap int) error { - value.Content[indexInMap+1] = n.getOrReplace(value.Content[indexInMap+1], n.guessKind(tail, value.Content[indexInMap+1].Kind)) + value.Content[indexInMap+1] = n.getOrReplace(value.Content[indexInMap+1], n.GuessKind(tail, value.Content[indexInMap+1].Kind)) return n.Visit(value.Content[indexInMap+1], tail, visitor) }) @@ -220,7 +222,7 @@ func (n *navigator) recurseMap(value *yaml.Node, head string, tail []string, vis //didn't find it, lets add it. value.Content = append(value.Content, &yaml.Node{Value: head, Kind: yaml.ScalarNode}) - mapEntryValue := yaml.Node{Kind: n.guessKind(tail, 0)} + mapEntryValue := yaml.Node{Kind: n.GuessKind(tail, 0)} value.Content = append(value.Content, &mapEntryValue) n.log.Debug("adding new node %v", value.Content) return n.Visit(&mapEntryValue, tail, visitor) @@ -259,7 +261,7 @@ func (n *navigator) splatArray(value *yaml.Node, tail []string, visitor VisitorF for _, childValue := range value.Content { n.log.Debug("processing") n.DebugNode(childValue) - childValue = n.getOrReplace(childValue, n.guessKind(tail, childValue.Kind)) + childValue = n.getOrReplace(childValue, n.GuessKind(tail, childValue.Kind)) var err = n.Visit(childValue, tail, visitor) if err != nil { return err @@ -269,7 +271,7 @@ func (n *navigator) splatArray(value *yaml.Node, tail []string, visitor VisitorF } func (n *navigator) appendArray(value *yaml.Node, tail []string, visitor VisitorFn) error { - var newNode = yaml.Node{Kind: n.guessKind(tail, 0)} + var newNode = yaml.Node{Kind: n.GuessKind(tail, 0)} value.Content = append(value.Content, &newNode) n.log.Debug("appending a new node, %v", value.Content) return n.Visit(&newNode, tail, visitor) @@ -283,7 +285,7 @@ func (n *navigator) recurseArray(value *yaml.Node, head string, tail []string, v if index >= int64(len(value.Content)) { return nil } - value.Content[index] = n.getOrReplace(value.Content[index], n.guessKind(tail, value.Content[index].Kind)) + value.Content[index] = n.getOrReplace(value.Content[index], n.GuessKind(tail, value.Content[index].Kind)) return n.Visit(value.Content[index], tail, visitor) } diff --git a/pkg/yqlib/lib.go b/pkg/yqlib/lib.go index 344aa248..9fb1591d 100644 --- a/pkg/yqlib/lib.go +++ b/pkg/yqlib/lib.go @@ -17,6 +17,7 @@ type YqLib interface { DebugNode(node *yaml.Node) Get(rootNode *yaml.Node, path string) (*yaml.Node, error) Update(rootNode *yaml.Node, updateCommand UpdateCommand) error + New(updateCommand UpdateCommand) (yaml.Node, error) } type lib struct { @@ -42,6 +43,16 @@ func (l *lib) Get(rootNode *yaml.Node, path string) (*yaml.Node, error) { return l.navigator.Get(rootNode, paths) } +func (l *lib) New(updateCommand UpdateCommand) (yaml.Node, error) { + var paths = l.parser.ParsePath(updateCommand.Path) + newNode := yaml.Node{Kind: l.navigator.GuessKind(paths, 0)} + errorUpdating := l.navigator.Update(&newNode, paths, updateCommand.Value) + if errorUpdating != nil { + return newNode, errorUpdating + } + return newNode, nil +} + func (l *lib) Update(rootNode *yaml.Node, updateCommand UpdateCommand) error { // later - support other command types l.log.Debugf("%v to %v", updateCommand.Command, updateCommand.Path) diff --git a/yq.go b/yq.go index fc3cb256..3e5cbe9f 100644 --- a/yq.go +++ b/yq.go @@ -78,7 +78,7 @@ func newCommandCLI() *cobra.Command { createWriteCmd(), // createPrefixCmd(), createDeleteCmd(), - // createNewCmd(), + createNewCmd(), // createMergeCmd(), ) rootCmd.SetOutput(os.Stdout) @@ -188,28 +188,28 @@ Outputs to STDOUT unless the inplace flag is used, in which case the file is upd return cmdDelete } -// 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 cat -// yq n -- --key-starting-with-dash cat -// yq n --script create_script.yaml -// `, -// Long: `Creates a new yaml w.r.t the given path and value. -// Outputs to STDOUT +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 cat +yq n -- --key-starting-with-dash cat +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 updating yaml") -// return cmdNew -// } +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 updating yaml") + return cmdNew +} // func createMergeCmd() *cobra.Command { // var cmdMerge = &cobra.Command{ @@ -417,6 +417,34 @@ func writeProperty(cmd *cobra.Command, args []string) error { return updateDoc(args[0], updateCommands, cmd.OutOrStdout()) } +func newProperty(cmd *cobra.Command, args []string) error { + var updateCommands, updateCommandsError = readUpdateCommands(args, 2, "Must provide ") + if updateCommandsError != nil { + return updateCommandsError + } + firstCommand, restOfCommands := updateCommands[0], updateCommands[1:] + newNode, errorCreating := lib.New(firstCommand) + if errorCreating != nil { + return errorCreating + } + + for _, updateCommand := range restOfCommands { + + errorUpdating := lib.Update(&newNode, updateCommand) + + if errorUpdating != nil { + return errorUpdating + } + } + + var encoder = yaml.NewEncoder(cmd.OutOrStdout()) + encoder.SetIndent(2) + encoder.Encode(&newNode) + encoder.Close() + return nil + +} + func deleteProperty(cmd *cobra.Command, args []string) error { if len(args) < 2 { return errors.New("Must provide ") @@ -558,6 +586,9 @@ func readUpdateCommands(args []string, expectedArgs int, badArgsMessage string) return nil, errors.New(badArgsMessage) } else { updateCommands = make([]yqlib.UpdateCommand, 1) + log.Debug("args %v", args) + log.Debug("path %v", args[expectedArgs-2]) + log.Debug("Value %v", args[expectedArgs-1]) updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: parseValue(args[expectedArgs-1])} } return updateCommands, nil