Refactoring

This commit is contained in:
Mike Farah 2019-12-16 16:46:20 +11:00
parent a3cebec2fd
commit d7392f7b58
5 changed files with 118 additions and 149 deletions

View File

@ -94,6 +94,24 @@ func TestReadCmd(t *testing.T) {
test.AssertResult(t, "2\n", result.Output)
}
// func TestReadRawCmd(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, "21\n", result.Output)
// }
// func TestReadRawMultiCmd(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, "21\n", result.Output)
// }
func TestReadInvalidDocumentIndexCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "read -df examples/sample.yaml b.c")

6
go.mod
View File

@ -1,11 +1,11 @@
module github.com/mikefarah/yq/v3
require (
github.com/mikefarah/yaml/v2 v2.4.0
github.com/mikefarah/yaml/v2 v2.4.0 // indirect
github.com/pkg/errors v0.8.1
github.com/spf13/cobra v0.0.5
golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0 // indirect
gopkg.in/imdario/mergo.v0 v0.3.7
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 // indirect
gopkg.in/imdario/mergo.v0 v0.3.7 // indirect
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2
)

7
go.sum
View File

@ -35,14 +35,21 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0 h1:s5lp4ug7qHzUccgyFdjsX7OZDzHXRaePrF3B3vmUiuM=
golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 h1:kJQZhwFzSwJS2BxboKjdZzWczQOZx8VuH7Y8hhuGUtM=
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/imdario/mergo.v0 v0.3.7 h1:QDotlIZtaO/p+Um0ok18HRTpq5i5/SAk/qprsor+9c8=

View File

@ -2,34 +2,50 @@ package yqlib
import (
"strconv"
logging "gopkg.in/op/go-logging.v1"
yaml "gopkg.in/yaml.v3"
)
type ValueParser interface {
ParseValue(argument string) interface{}
Parse(argument string, customTag string) *yaml.Node
}
type valueParser struct{}
func NewValueParser() ValueParser {
return &valueParser{}
type valueParser struct {
log *logging.Logger
}
func (v *valueParser) ParseValue(argument string) interface{} {
var value, err interface{}
func NewValueParser(l *logging.Logger) ValueParser {
return &valueParser{log: l}
}
func (v *valueParser) Parse(argument string, customTag string) *yaml.Node {
var err interface{}
var tag = customTag
var inQuotes = len(argument) > 0 && argument[0] == '"'
if !inQuotes {
value, err = strconv.ParseFloat(argument, 64)
if tag == "" && !inQuotes {
_, err = strconv.ParseBool(argument)
if err == nil {
return value
tag = "!!bool"
}
value, err = strconv.ParseBool(argument)
_, err = strconv.ParseFloat(argument, 64)
if err == nil {
return value
tag = "!!float"
}
_, err = strconv.ParseInt(argument, 10, 64)
if err == nil {
tag = "!!int"
}
if argument == "null" {
tag = "!!null"
}
if argument == "[]" {
return make([]interface{}, 0)
return &yaml.Node{Tag: "!!seq", Kind: yaml.SequenceNode}
}
return argument
}
return argument[1 : len(argument)-1]
v.log.Debugf("parsed value '%v', tag: '%v'", argument, tag)
return &yaml.Node{Value: argument, Tag: tag, Kind: yaml.ScalarNode}
}

190
yq.go
View File

@ -29,7 +29,7 @@ var version = false
var docIndex = "0"
var log = logging.MustGetLogger("yq")
var lib = yqlib.NewYqLib(log)
var valueParser = yqlib.NewValueParser()
var valueParser = yqlib.NewValueParser(log)
func main() {
cmd := newCommandCLI()
@ -263,28 +263,14 @@ func readProperty(cmd *cobra.Command, args []string) error {
for {
var dataBucket yaml.Node
errorReading := decoder.Decode(&dataBucket)
log.Debugf("decoded node for doc %v", currentIndex)
lib.DebugNode(&dataBucket)
if errorReading == io.EOF {
log.Debugf("done %v / %v", currentIndex, docIndexInt)
if !updateAll && currentIndex <= docIndexInt {
return fmt.Errorf("asked to process document index %v but there are only %v document(s)", docIndex, currentIndex)
}
return nil
return handleEOF(updateAll, docIndexInt, currentIndex)
}
log.Debugf("processing document %v - requested index %v", currentIndex, docIndexInt)
if updateAll || currentIndex == docIndexInt {
log.Debugf("reading %v in document %v", path, currentIndex)
mappedDoc, errorParsing := lib.Get(&dataBucket, path)
lib.DebugNode(mappedDoc)
log.Debugf("carry on")
if errorParsing != nil {
return errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex)
} else if mappedDoc != nil {
mappedDocs = append(mappedDocs, mappedDoc)
}
var errorParsing error
mappedDocs, errorParsing = appendDocument(mappedDocs, dataBucket, path, updateAll, docIndexInt, currentIndex)
if errorParsing != nil {
return errorParsing
}
currentIndex = currentIndex + 1
}
@ -292,7 +278,38 @@ func readProperty(cmd *cobra.Command, args []string) error {
if errorReadingStream != nil {
return errorReadingStream
} else if len(mappedDocs) == 0 {
}
return printResults(mappedDocs, cmd)
}
func handleEOF(updateAll bool, docIndexInt int, currentIndex int) error {
log.Debugf("done %v / %v", currentIndex, docIndexInt)
if !updateAll && currentIndex <= docIndexInt {
return fmt.Errorf("asked to process document index %v but there are only %v document(s)", docIndex, currentIndex)
}
return nil
}
func appendDocument(mappedDocs []*yaml.Node, dataBucket yaml.Node, path string, updateAll bool, docIndexInt int, currentIndex int) ([]*yaml.Node, error) {
log.Debugf("processing document %v - requested index %v", currentIndex, docIndexInt)
lib.DebugNode(&dataBucket)
if !updateAll && currentIndex != docIndexInt {
return mappedDocs, nil
}
log.Debugf("reading %v in document %v", path, currentIndex)
mappedDoc, errorParsing := lib.Get(&dataBucket, path)
lib.DebugNode(mappedDoc)
if errorParsing != nil {
return nil, errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex)
} else if mappedDoc != nil {
return append(mappedDocs, mappedDoc), nil
}
return mappedDocs, nil
}
func printResults(mappedDocs []*yaml.Node, cmd *cobra.Command) error {
if len(mappedDocs) == 0 {
log.Debug("no matching results, nothing to print")
return nil
}
@ -307,7 +324,7 @@ func readProperty(cmd *cobra.Command, args []string) error {
cmd.Println(mappedDoc.Value)
}
}
} else if !updateAll {
} else if len(mappedDocs) == 1 {
err = encoder.Encode(mappedDocs[0])
} else {
err = encoder.Encode(&yaml.Node{Kind: yaml.SequenceNode, Content: mappedDocs})
@ -319,43 +336,6 @@ func readProperty(cmd *cobra.Command, args []string) error {
return nil
}
// func newProperty(cmd *cobra.Command, args []string) error {
// updatedData, err := newYaml(args)
// if err != nil {
// return err
// }
// dataStr, err := toString(updatedData)
// if err != nil {
// return err
// }
// cmd.Println(dataStr)
// return nil
// }
// func newYaml(args []string) (interface{}, error) {
// var writeCommands, writeCommandsError = readUpdateCommands(args, 2, "Must provide <path_to_update> <value>")
// if writeCommandsError != nil {
// return nil, writeCommandsError
// }
// var dataBucket interface{}
// var isArray = strings.HasPrefix(writeCommands[0].Key.(string), "[")
// if isArray {
// dataBucket = make([]interface{}, 0)
// } else {
// dataBucket = make(yaml.MapSlice, 0)
// }
// for _, entry := range writeCommands {
// path := entry.Key.(string)
// value := entry.Value
// log.Debugf("setting %v to %v", path, value)
// dataBucket = lib.WritePath(dataBucket, path, value)
// }
// return dataBucket, nil
// }
func parseDocumentIndex() (bool, int, error) {
if docIndex == "*" {
return true, -1, nil
@ -454,25 +434,27 @@ func prefixProperty(cmd *cobra.Command, args []string) error {
}
var updateData = func(dataBucket *yaml.Node, currentIndex int) error {
if updateAll || currentIndex == docIndexInt {
log.Debugf("Prefixing document %v", currentIndex)
lib.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)
if errorUpdating != nil {
return errorUpdating
}
}
return nil
return prefixDocument(updateAll, docIndexInt, currentIndex, dataBucket, updateCommand)
}
return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
}
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)
lib.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)
if errorUpdating != nil {
return errorUpdating
}
}
return nil
}
func deleteProperty(cmd *cobra.Command, args []string) error {
@ -506,29 +488,6 @@ func updateDoc(inputFile string, updateCommands []yqlib.UpdateCommand, writer io
return readAndUpdate(writer, inputFile, updateData)
}
// func prefixProperty(cmd *cobra.Command, args []string) error {
// if len(args) != 2 {
// return errors.New("Must provide <filename> <prefixed_path>")
// }
// prefixPath := args[1]
// var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
// if errorParsingDocIndex != nil {
// return errorParsingDocIndex
// }
// var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
// if updateAll || currentIndex == docIndexInt {
// log.Debugf("Prefixing %v to doc %v", prefixPath, currentIndex)
// var mapDataBucket = lib.PrefixPath(dataBucket, prefixPath)
// return mapDataBucket, nil
// }
// return dataBucket, nil
// }
// return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
// }
func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) error {
var destination io.Writer
var destinationName string
@ -633,42 +592,11 @@ func readUpdateCommands(args []string, expectedArgs int, badArgsMessage string)
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])}
updateCommands[0] = yqlib.UpdateCommand{Command: "update", Path: args[expectedArgs-2], Value: valueParser.Parse(args[expectedArgs-1], customTag)}
}
return updateCommands, nil
}
func parseValue(argument string) *yaml.Node {
var err interface{}
var tag = customTag
var inQuotes = len(argument) > 0 && argument[0] == '"'
if tag == "" && !inQuotes {
_, err = strconv.ParseBool(argument)
if err == nil {
tag = "!!bool"
}
_, err = strconv.ParseFloat(argument, 64)
if err == nil {
tag = "!!float"
}
_, err = strconv.ParseInt(argument, 10, 64)
if err == nil {
tag = "!!int"
}
if argument == "null" {
tag = "!!null"
}
if argument == "[]" {
return &yaml.Node{Tag: "!!seq", Kind: yaml.SequenceNode}
}
}
log.Debugf("Updating node to value '%v', tag: '%v'", argument, tag)
return &yaml.Node{Value: argument, Tag: tag, Kind: yaml.ScalarNode}
}
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)