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 unwrapScalarFlag = newUnwrapFlag()
|
||||||
|
|
||||||
|
var printNodeInfo = false
|
||||||
|
|
||||||
var unwrapScalar = false
|
var unwrapScalar = false
|
||||||
|
|
||||||
var writeInplace = false
|
var writeInplace = false
|
||||||
|
|||||||
@ -105,6 +105,11 @@ func evaluateSequence(cmd *cobra.Command, args []string) (cmdError error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
printer := yqlib.NewPrinter(encoder, printerWriter)
|
printer := yqlib.NewPrinter(encoder, printerWriter)
|
||||||
|
|
||||||
|
if printNodeInfo {
|
||||||
|
printer = yqlib.NewNodeInfoPrinter(printerWriter)
|
||||||
|
}
|
||||||
|
|
||||||
if nulSepOutput {
|
if nulSepOutput {
|
||||||
printer.SetNulSepOutput(true)
|
printer.SetNulSepOutput(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,6 +99,7 @@ yq -P -oy sample.json
|
|||||||
}
|
}
|
||||||
|
|
||||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose mode")
|
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.")
|
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")
|
err := rootCmd.PersistentFlags().MarkDeprecated("tojson", "please use -o=json instead")
|
||||||
|
|||||||
@ -1,3 +1,8 @@
|
|||||||
Foo: 3
|
# 001
|
||||||
apple: 1
|
---
|
||||||
bar: 2
|
abc: # 001
|
||||||
|
- 1 # one
|
||||||
|
- 2 # two
|
||||||
|
|
||||||
|
---
|
||||||
|
def # 002
|
||||||
@ -53,6 +53,20 @@ func createScalarNode(value interface{}, stringValue string) *CandidateNode {
|
|||||||
return node
|
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 {
|
type CandidateNode struct {
|
||||||
Kind Kind
|
Kind Kind
|
||||||
Style Style
|
Style Style
|
||||||
@ -451,3 +465,64 @@ func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode, prefs assignP
|
|||||||
n.LineComment = other.LineComment
|
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)
|
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