mirror of
https://github.com/mikefarah/yq.git
synced 2026-03-10 15:54:26 +00:00
Added "debug-node-info" flag for inspecting yq AST
This commit is contained in:
parent
d0c897f5e6
commit
8e731ac13c
@ -2,6 +2,8 @@ package cmd
|
||||
|
||||
var unwrapScalarFlag = newUnwrapFlag()
|
||||
|
||||
var printNodeInfo = false
|
||||
|
||||
var unwrapScalar = false
|
||||
|
||||
var writeInplace = false
|
||||
|
||||
@ -105,6 +105,11 @@ func evaluateSequence(cmd *cobra.Command, args []string) (cmdError error) {
|
||||
}
|
||||
|
||||
printer := yqlib.NewPrinter(encoder, printerWriter)
|
||||
|
||||
if printNodeInfo {
|
||||
printer = yqlib.NewNodeInfoPrinter(printerWriter)
|
||||
}
|
||||
|
||||
if nulSepOutput {
|
||||
printer.SetNulSepOutput(true)
|
||||
}
|
||||
|
||||
@ -99,6 +99,7 @@ yq -P -oy sample.json
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose mode")
|
||||
rootCmd.PersistentFlags().BoolVarP(&printNodeInfo, "debug-node-info", "", false, "debug node info")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "(deprecated) output as json. Set indent to 0 to print json in one line.")
|
||||
err := rootCmd.PersistentFlags().MarkDeprecated("tojson", "please use -o=json instead")
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
Foo: 3
|
||||
apple: 1
|
||||
bar: 2
|
||||
# 001
|
||||
---
|
||||
abc: # 001
|
||||
- 1 # one
|
||||
- 2 # two
|
||||
|
||||
---
|
||||
def # 002
|
||||
@ -53,6 +53,20 @@ func createScalarNode(value interface{}, stringValue string) *CandidateNode {
|
||||
return node
|
||||
}
|
||||
|
||||
type NodeInfo struct {
|
||||
Kind string `yaml:"kind"`
|
||||
Style string `yaml:"style,omitempty"`
|
||||
Anchor string `yaml:"anchor,omitempty"`
|
||||
Tag string `yaml:"tag,omitempty"`
|
||||
HeadComment string `yaml:"headComment,omitempty"`
|
||||
LineComment string `yaml:"lineComment,omitempty"`
|
||||
FootComment string `yaml:"footComment,omitempty"`
|
||||
Value string `yaml:"value,omitempty"`
|
||||
Line int `yaml:"line,omitempty"`
|
||||
Column int `yaml:"column,omitempty"`
|
||||
Content []*NodeInfo `yaml:"content,omitempty"`
|
||||
}
|
||||
|
||||
type CandidateNode struct {
|
||||
Kind Kind
|
||||
Style Style
|
||||
@ -451,3 +465,64 @@ func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode, prefs assignP
|
||||
n.LineComment = other.LineComment
|
||||
}
|
||||
}
|
||||
|
||||
func (n *CandidateNode) ConvertToNodeInfo() *NodeInfo {
|
||||
info := &NodeInfo{
|
||||
Kind: kindToString(n.Kind),
|
||||
Style: styleToString(n.Style),
|
||||
Anchor: n.Anchor,
|
||||
Tag: n.Tag,
|
||||
HeadComment: n.HeadComment,
|
||||
LineComment: n.LineComment,
|
||||
FootComment: n.FootComment,
|
||||
Value: n.Value,
|
||||
Line: n.Line,
|
||||
Column: n.Column,
|
||||
}
|
||||
if len(n.Content) > 0 {
|
||||
info.Content = make([]*NodeInfo, len(n.Content))
|
||||
for i, child := range n.Content {
|
||||
info.Content[i] = child.ConvertToNodeInfo()
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// Helper functions to convert Kind and Style to string for NodeInfo
|
||||
func kindToString(k Kind) string {
|
||||
switch k {
|
||||
case SequenceNode:
|
||||
return "SequenceNode"
|
||||
case MappingNode:
|
||||
return "MappingNode"
|
||||
case ScalarNode:
|
||||
return "ScalarNode"
|
||||
case AliasNode:
|
||||
return "AliasNode"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func styleToString(s Style) string {
|
||||
var styles []string
|
||||
if s&TaggedStyle != 0 {
|
||||
styles = append(styles, "TaggedStyle")
|
||||
}
|
||||
if s&DoubleQuotedStyle != 0 {
|
||||
styles = append(styles, "DoubleQuotedStyle")
|
||||
}
|
||||
if s&SingleQuotedStyle != 0 {
|
||||
styles = append(styles, "SingleQuotedStyle")
|
||||
}
|
||||
if s&LiteralStyle != 0 {
|
||||
styles = append(styles, "LiteralStyle")
|
||||
}
|
||||
if s&FoldedStyle != 0 {
|
||||
styles = append(styles, "FoldedStyle")
|
||||
}
|
||||
if s&FlowStyle != 0 {
|
||||
styles = append(styles, "FlowStyle")
|
||||
}
|
||||
return strings.Join(styles, ",")
|
||||
}
|
||||
|
||||
@ -160,3 +160,47 @@ func TestCandidateNodeAddKeyValueChild(t *testing.T) {
|
||||
test.AssertResult(t, key.IsMapKey, true)
|
||||
|
||||
}
|
||||
|
||||
func TestConvertToNodeInfo(t *testing.T) {
|
||||
child := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Style: DoubleQuotedStyle,
|
||||
Tag: "!!str",
|
||||
Value: "childValue",
|
||||
Line: 2,
|
||||
Column: 3,
|
||||
}
|
||||
parent := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Style: TaggedStyle,
|
||||
Tag: "!!map",
|
||||
Value: "",
|
||||
Line: 1,
|
||||
Column: 1,
|
||||
Content: []*CandidateNode{child},
|
||||
HeadComment: "head",
|
||||
LineComment: "line",
|
||||
FootComment: "foot",
|
||||
Anchor: "anchor",
|
||||
}
|
||||
info := parent.ConvertToNodeInfo()
|
||||
test.AssertResult(t, "MappingNode", info.Kind)
|
||||
test.AssertResult(t, "TaggedStyle", info.Style)
|
||||
test.AssertResult(t, "!!map", info.Tag)
|
||||
test.AssertResult(t, "head", info.HeadComment)
|
||||
test.AssertResult(t, "line", info.LineComment)
|
||||
test.AssertResult(t, "foot", info.FootComment)
|
||||
test.AssertResult(t, "anchor", info.Anchor)
|
||||
test.AssertResult(t, 1, info.Line)
|
||||
test.AssertResult(t, 1, info.Column)
|
||||
if len(info.Content) != 1 {
|
||||
t.Fatalf("Expected 1 child, got %d", len(info.Content))
|
||||
}
|
||||
childInfo := info.Content[0]
|
||||
test.AssertResult(t, "ScalarNode", childInfo.Kind)
|
||||
test.AssertResult(t, "DoubleQuotedStyle", childInfo.Style)
|
||||
test.AssertResult(t, "!!str", childInfo.Tag)
|
||||
test.AssertResult(t, "childValue", childInfo.Value)
|
||||
test.AssertResult(t, 2, childInfo.Line)
|
||||
test.AssertResult(t, 3, childInfo.Column)
|
||||
}
|
||||
|
||||
76
pkg/yqlib/printer_node_info.go
Normal file
76
pkg/yqlib/printer_node_info.go
Normal file
@ -0,0 +1,76 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/list"
|
||||
"io"
|
||||
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
type nodeInfoPrinter struct {
|
||||
printerWriter PrinterWriter
|
||||
appendixReader io.Reader
|
||||
printedMatches bool
|
||||
}
|
||||
|
||||
func NewNodeInfoPrinter(printerWriter PrinterWriter) Printer {
|
||||
return &nodeInfoPrinter{
|
||||
printerWriter: printerWriter,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *nodeInfoPrinter) SetNulSepOutput(_ bool) {
|
||||
}
|
||||
|
||||
func (p *nodeInfoPrinter) SetAppendix(reader io.Reader) {
|
||||
p.appendixReader = reader
|
||||
}
|
||||
|
||||
func (p *nodeInfoPrinter) PrintedAnything() bool {
|
||||
return p.printedMatches
|
||||
}
|
||||
|
||||
func (p *nodeInfoPrinter) PrintResults(matchingNodes *list.List) error {
|
||||
|
||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
||||
mappedDoc := el.Value.(*CandidateNode)
|
||||
writer, errorWriting := p.printerWriter.GetWriter(mappedDoc)
|
||||
if errorWriting != nil {
|
||||
return errorWriting
|
||||
}
|
||||
bytes, err := yaml.Marshal(mappedDoc.ConvertToNodeInfo())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := writer.Write(bytes); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := writer.Write([]byte("\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
p.printedMatches = true
|
||||
if err := writer.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if p.appendixReader != nil {
|
||||
writer, err := p.printerWriter.GetWriter(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("Piping appendix reader...")
|
||||
betterReader := bufio.NewReader(p.appendixReader)
|
||||
_, err = io.Copy(writer, betterReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writer.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
51
pkg/yqlib/printer_node_info_test.go
Normal file
51
pkg/yqlib/printer_node_info_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"container/list"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mikefarah/yq/v4/test"
|
||||
)
|
||||
|
||||
func TestNodeInfoPrinter_PrintResults(t *testing.T) {
|
||||
// Create a simple CandidateNode
|
||||
node := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Style: DoubleQuotedStyle,
|
||||
Tag: "!!str",
|
||||
Value: "hello world",
|
||||
Line: 5,
|
||||
Column: 7,
|
||||
HeadComment: "head",
|
||||
LineComment: "line",
|
||||
FootComment: "foot",
|
||||
Anchor: "anchor",
|
||||
}
|
||||
listNodes := list.New()
|
||||
listNodes.PushBack(node)
|
||||
|
||||
var output bytes.Buffer
|
||||
writer := bufio.NewWriter(&output)
|
||||
printer := NewNodeInfoPrinter(NewSinglePrinterWriter(writer))
|
||||
err := printer.PrintResults(listNodes)
|
||||
writer.Flush()
|
||||
if err != nil {
|
||||
t.Fatalf("PrintResults error: %v", err)
|
||||
}
|
||||
|
||||
outStr := output.String()
|
||||
// Check for key NodeInfo fields in YAML output using substring checks
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "kind: ScalarNode"))
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "style: DoubleQuotedStyle"))
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "tag: '!!str'"))
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "value: hello world"))
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "line: 5"))
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "column: 7"))
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "headComment: head"))
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "lineComment: line"))
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "footComment: foot"))
|
||||
test.AssertResult(t, true, strings.Contains(outStr, "anchor: anchor"))
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user