diff --git a/.gitignore b/.gitignore index 8aa98112..43bb095c 100644 --- a/.gitignore +++ b/.gitignore @@ -56,4 +56,7 @@ _build debian/files # intellij -/.idea \ No newline at end of file +/.idea + +# vscode +.vscode \ No newline at end of file diff --git a/pkg/yqlib/stream_evaluator.go b/pkg/yqlib/stream_evaluator.go index cda18624..55eb6623 100644 --- a/pkg/yqlib/stream_evaluator.go +++ b/pkg/yqlib/stream_evaluator.go @@ -110,7 +110,7 @@ func (s *streamEvaluator) Evaluate(filename string, reader io.Reader, node *Expr Node: &dataBucket, FileIndex: s.fileIndex, } - //move document comments into candidate node + // move document comments into candidate node // otherwise unwrap drops them. candidateNode.TrailingContent = dataBucket.FootComment dataBucket.FootComment = "" diff --git a/pkg/yqlib/string_evaluator.go b/pkg/yqlib/string_evaluator.go new file mode 100644 index 00000000..62bc253d --- /dev/null +++ b/pkg/yqlib/string_evaluator.go @@ -0,0 +1,85 @@ +package yqlib + +import ( + "bytes" + "container/list" + "errors" + "fmt" + "io" + + yaml "gopkg.in/yaml.v3" +) + +type StringEvaluator interface { + Evaluate(expression string, input string, encoder Encoder, leadingContentPreProcessing bool, decoder Decoder) (string, error) +} + +type stringEvaluator struct { + treeNavigator DataTreeNavigator + fileIndex int +} + +func NewStringEvaluator() StringEvaluator { + return &stringEvaluator{ + treeNavigator: NewDataTreeNavigator(), + } +} + +func (s *stringEvaluator) Evaluate(expression string, input string, encoder Encoder, leadingContentPreProcessing bool, decoder Decoder) (string, error) { + + // Use bytes.Buffer for output of string + out := new(bytes.Buffer) + printer := NewPrinter(encoder, NewSinglePrinterWriter(out)) + + InitExpressionParser() + node, err := ExpressionParser.ParseExpression(expression) + if err != nil { + return "", err + } + + reader, leadingContent, err := readString(input, leadingContentPreProcessing) + if err != nil { + return "", err + } + + var currentIndex uint + decoder.Init(reader) + for { + var dataBucket yaml.Node + errorReading := decoder.Decode(&dataBucket) + + if errors.Is(errorReading, io.EOF) { + s.fileIndex = s.fileIndex + 1 + return out.String(), nil + } else if errorReading != nil { + return "", fmt.Errorf("bad input '%v': %w", input, errorReading) + } + + candidateNode := &CandidateNode{ + Document: currentIndex, + Node: &dataBucket, + FileIndex: s.fileIndex, + } + // move document comments into candidate node + // otherwise unwrap drops them. + candidateNode.TrailingContent = dataBucket.FootComment + dataBucket.FootComment = "" + + if currentIndex == 0 { + candidateNode.LeadingContent = leadingContent + } + inputList := list.New() + inputList.PushBack(candidateNode) + + result, errorParsing := s.treeNavigator.GetMatchingNodes(Context{MatchingNodes: inputList}, node) + if errorParsing != nil { + return "", errorParsing + } + err = printer.PrintResults(result.MatchingNodes) + + if err != nil { + return "", err + } + currentIndex = currentIndex + 1 + } +} diff --git a/pkg/yqlib/string_evaluator_test.go b/pkg/yqlib/string_evaluator_test.go new file mode 100644 index 00000000..a359b3c3 --- /dev/null +++ b/pkg/yqlib/string_evaluator_test.go @@ -0,0 +1,30 @@ +package yqlib + +import ( + "testing" + + "github.com/mikefarah/yq/v4/test" +) + +func TestStringEvaluator_Evaluate_Nominal(t *testing.T) { + expected_output := `` + + `yq` + "\n" + + `---` + "\n" + + `jq` + "\n" + expression := ".[].name" + input := `` + + ` - name: yq` + "\n" + + ` description: yq is a portable command-line YAML, JSON and XML processor` + "\n" + + `---` + "\n" + + ` - name: jq` + "\n" + + ` description: Command-line JSON processor` + "\n" + encoder := NewYamlEncoder(2, true, true, true) + decoder := NewYamlDecoder() + + result, err := NewStringEvaluator().Evaluate(expression, input, encoder, true, decoder) + if err != nil { + t.Error(err) + } + + test.AssertResult(t, expected_output, result) +} diff --git a/pkg/yqlib/utils.go b/pkg/yqlib/utils.go index ecb00ef4..27964d58 100644 --- a/pkg/yqlib/utils.go +++ b/pkg/yqlib/utils.go @@ -33,6 +33,14 @@ func readStream(filename string, leadingContentPreProcessing bool) (io.Reader, s return processReadStream(reader) } +func readString(input string, leadingContentPreProcessing bool) (io.Reader, string, error) { + reader := bufio.NewReader(strings.NewReader(input)) + if !leadingContentPreProcessing { + return reader, "", nil + } + return processReadStream(reader) +} + func writeString(writer io.Writer, txt string) error { _, errorWriting := writer.Write([]byte(txt)) return errorWriting