This commit is contained in:
Mike Farah 2024-03-12 15:45:08 +11:00
parent 2b2c285415
commit fc5595d0e9
39 changed files with 237 additions and 199 deletions

View File

@ -1,4 +1,4 @@
package yqlib package properties
import ( import (
"bytes" "bytes"
@ -8,16 +8,17 @@ import (
"strings" "strings"
"github.com/magiconair/properties" "github.com/magiconair/properties"
"github.com/mikefarah/yq/v4/pkg/yqlib"
) )
type propertiesDecoder struct { type propertiesDecoder struct {
reader io.Reader reader io.Reader
finished bool finished bool
d DataTreeNavigator d yqlib.DataTreeNavigator
} }
func NewPropertiesDecoder() Decoder { func NewPropertiesDecoder() yqlib.Decoder {
return &propertiesDecoder{d: NewDataTreeNavigator(), finished: false} return &propertiesDecoder{d: yqlib.NewDataTreeNavigator(), finished: false}
} }
func (dec *propertiesDecoder) Init(reader io.Reader) error { func (dec *propertiesDecoder) Init(reader io.Reader) error {
@ -47,31 +48,31 @@ func (dec *propertiesDecoder) processComment(c string) string {
return "# " + c return "# " + c
} }
func (dec *propertiesDecoder) applyPropertyComments(context Context, path []interface{}, comments []string) error { func (dec *propertiesDecoder) applyPropertyComments(context yqlib.Context, path []interface{}, comments []string) error {
assignmentOp := &Operation{OperationType: assignOpType, Preferences: assignPreferences{}} assignmentOp := &yqlib.Operation{OperationType: assignOpType, Preferences: assignPreferences{}}
rhsCandidateNode := &CandidateNode{ rhsCandidateNode := &yqlib.CandidateNode{
Tag: "!!str", Tag: "!!str",
Value: fmt.Sprintf("%v", path[len(path)-1]), Value: fmt.Sprintf("%v", path[len(path)-1]),
HeadComment: dec.processComment(strings.Join(comments, "\n")), HeadComment: dec.processComment(strings.Join(comments, "\n")),
Kind: ScalarNode, Kind: yqlib.ScalarNode,
} }
rhsCandidateNode.Tag = rhsCandidateNode.guessTagFromCustomType() rhsCandidateNode.Tag = rhsCandidateNode.GuessTagFromCustomType()
rhsOp := &Operation{OperationType: referenceOpType, CandidateNode: rhsCandidateNode} rhsOp := &yqlib.Operation{OperationType: referenceOpType, CandidateNode: rhsCandidateNode}
assignmentOpNode := &ExpressionNode{ assignmentOpNode := &yqlib.ExpressionNode{
Operation: assignmentOp, Operation: assignmentOp,
LHS: createTraversalTree(path, traversePreferences{}, true), LHS: createTraversalTree(path, traversePreferences{}, true),
RHS: &ExpressionNode{Operation: rhsOp}, RHS: &yqlib.ExpressionNode{Operation: rhsOp},
} }
_, err := dec.d.GetMatchingNodes(context, assignmentOpNode) _, err := dec.d.GetMatchingNodes(context, assignmentOpNode)
return err return err
} }
func (dec *propertiesDecoder) applyProperty(context Context, properties *properties.Properties, key string) error { func (dec *propertiesDecoder) applyProperty(context yqlib.Context, properties *properties.Properties, key string) error {
value, _ := properties.Get(key) value, _ := properties.Get(key)
path := parsePropKey(key) path := parsePropKey(key)
@ -84,12 +85,12 @@ func (dec *propertiesDecoder) applyProperty(context Context, properties *propert
} }
rhsNode := createStringScalarNode(value) rhsNode := createStringScalarNode(value)
rhsNode.Tag = rhsNode.guessTagFromCustomType() rhsNode.Tag = rhsNode.GuessTagFromCustomType()
return dec.d.DeeplyAssign(context, path, rhsNode) return dec.d.DeeplyAssign(context, path, rhsNode)
} }
func (dec *propertiesDecoder) Decode() (*CandidateNode, error) { func (dec *propertiesDecoder) Decode() (*yqlib.CandidateNode, error) {
if dec.finished { if dec.finished {
return nil, io.EOF return nil, io.EOF
} }
@ -108,12 +109,12 @@ func (dec *propertiesDecoder) Decode() (*CandidateNode, error) {
} }
properties.DisableExpansion = true properties.DisableExpansion = true
rootMap := &CandidateNode{ rootMap := &yqlib.CandidateNode{
Kind: MappingNode, Kind: yqlib.MappingNode,
Tag: "!!map", Tag: "!!map",
} }
context := Context{} context := yqlib.Context{}
context = context.SingleChildContext(rootMap) context = context.SingleChildContext(rootMap)
for _, key := range properties.Keys() { for _, key := range properties.Keys() {

View File

@ -1,4 +1,4 @@
package yqlib package properties
import ( import (
"bufio" "bufio"
@ -8,13 +8,14 @@ import (
"strings" "strings"
"github.com/magiconair/properties" "github.com/magiconair/properties"
"github.com/mikefarah/yq/v4/pkg/yqlib"
) )
type propertiesEncoder struct { type propertiesEncoder struct {
prefs PropertiesPreferences prefs PropertiesPreferences
} }
func NewPropertiesEncoder(prefs PropertiesPreferences) Encoder { func NewPropertiesEncoder(prefs PropertiesPreferences) yqlib.Encoder {
return &propertiesEncoder{ return &propertiesEncoder{
prefs: prefs, prefs: prefs,
} }
@ -61,9 +62,9 @@ func (pe *propertiesEncoder) PrintLeadingContent(writer io.Writer, content strin
return nil return nil
} }
func (pe *propertiesEncoder) Encode(writer io.Writer, node *CandidateNode) error { func (pe *propertiesEncoder) Encode(writer io.Writer, node *yqlib.CandidateNode) error {
if node.Kind == ScalarNode { if node.Kind == yqlib.ScalarNode {
return writeString(writer, node.Value+"\n") return writeString(writer, node.Value+"\n")
} }
@ -79,7 +80,7 @@ func (pe *propertiesEncoder) Encode(writer io.Writer, node *CandidateNode) error
return err return err
} }
func (pe *propertiesEncoder) doEncode(p *properties.Properties, node *CandidateNode, path string, keyNode *CandidateNode) error { func (pe *propertiesEncoder) doEncode(p *properties.Properties, node *yqlib.CandidateNode, path string, keyNode *CandidateNode) error {
comments := "" comments := ""
if keyNode != nil { if keyNode != nil {
@ -91,7 +92,7 @@ func (pe *propertiesEncoder) doEncode(p *properties.Properties, node *CandidateN
p.SetComments(path, strings.Split(commentsWithSpaces, "\n")) p.SetComments(path, strings.Split(commentsWithSpaces, "\n"))
switch node.Kind { switch node.Kind {
case ScalarNode: case yqlib.ScalarNode:
var nodeValue string var nodeValue string
if pe.prefs.UnwrapScalar || !strings.Contains(node.Value, " ") { if pe.prefs.UnwrapScalar || !strings.Contains(node.Value, " ") {
nodeValue = node.Value nodeValue = node.Value
@ -100,11 +101,11 @@ func (pe *propertiesEncoder) doEncode(p *properties.Properties, node *CandidateN
} }
_, _, err := p.Set(path, nodeValue) _, _, err := p.Set(path, nodeValue)
return err return err
case SequenceNode: case yqlib.SequenceNode:
return pe.encodeArray(p, node.Content, path) return pe.encodeArray(p, node.Content, path)
case MappingNode: case yqlib.MappingNode:
return pe.encodeMap(p, node.Content, path) return pe.encodeMap(p, node.Content, path)
case AliasNode: case yqlib.AliasNode:
return pe.doEncode(p, node.Alias, path, nil) return pe.doEncode(p, node.Alias, path, nil)
default: default:
return fmt.Errorf("Unsupported node %v", node.Tag) return fmt.Errorf("Unsupported node %v", node.Tag)
@ -125,7 +126,7 @@ func (pe *propertiesEncoder) appendPath(path string, key interface{}) string {
return fmt.Sprintf("%v.%v", path, key) return fmt.Sprintf("%v.%v", path, key)
} }
func (pe *propertiesEncoder) encodeArray(p *properties.Properties, kids []*CandidateNode, path string) error { func (pe *propertiesEncoder) encodeArray(p *properties.Properties, kids []*yqlib.CandidateNode, path string) error {
for index, child := range kids { for index, child := range kids {
err := pe.doEncode(p, child, pe.appendPath(path, index), nil) err := pe.doEncode(p, child, pe.appendPath(path, index), nil)
if err != nil { if err != nil {
@ -135,7 +136,7 @@ func (pe *propertiesEncoder) encodeArray(p *properties.Properties, kids []*Candi
return nil return nil
} }
func (pe *propertiesEncoder) encodeMap(p *properties.Properties, kids []*CandidateNode, path string) error { func (pe *propertiesEncoder) encodeMap(p *properties.Properties, kids []*yqlib.CandidateNode, path string) error {
for index := 0; index < len(kids); index = index + 2 { for index := 0; index < len(kids); index = index + 2 {
key := kids[index] key := kids[index]
value := kids[index+1] value := kids[index+1]

View File

@ -0,0 +1,44 @@
package properties
import "github.com/mikefarah/yq/v4/pkg/yqlib"
type PropertiesPreferences struct {
UnwrapScalar bool
KeyValueSeparator string
UseArrayBrackets bool
}
func NewDefaultPropertiesPreferences() PropertiesPreferences {
return PropertiesPreferences{
UnwrapScalar: true,
KeyValueSeparator: " = ",
UseArrayBrackets: false,
}
}
func (p *PropertiesPreferences) Copy() PropertiesPreferences {
return PropertiesPreferences{
UnwrapScalar: p.UnwrapScalar,
KeyValueSeparator: p.KeyValueSeparator,
UseArrayBrackets: p.UseArrayBrackets,
}
}
var PropertiesFormat = &yqlib.Format{"props", []string{"p", "properties"},
func() yqlib.Encoder { return NewPropertiesEncoder(ConfiguredPropertiesPreferences) },
func() yqlib.Decoder { return NewPropertiesDecoder() },
}
var propertyYqRules = []*yqlib.ParticipleYqRule{
{"PropertiesDecode", `from_?props|@propsd`, decodeOp(PropertiesFormat), 0},
{"PropsEncode", `to_?props|@props`, encodeWithIndent(PropertiesFormat, 2), 0},
{"LoadProperties", `load_?props`, loadOp(NewPropertiesDecoder(), false), 0},
}
func RegisterPropertiesFormat() {
yqlib.RegisterFormat(PropertiesFormat)
yqlib.RegisterRules(propertyYqRules)
}
var ConfiguredPropertiesPreferences = NewDefaultPropertiesPreferences()

View File

@ -1,6 +1,6 @@
//go:build !yq_noxml //go:build !yq_noxml
package yqlib package xml
import ( import (
"encoding/xml" "encoding/xml"
@ -11,6 +11,7 @@ import (
"strings" "strings"
"unicode" "unicode"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"golang.org/x/net/html/charset" "golang.org/x/net/html/charset"
) )
@ -21,7 +22,7 @@ type xmlDecoder struct {
prefs XmlPreferences prefs XmlPreferences
} }
func NewXMLDecoder(prefs XmlPreferences) Decoder { func NewXMLDecoder(prefs XmlPreferences) yqlib.Decoder {
return &xmlDecoder{ return &xmlDecoder{
finished: false, finished: false,
prefs: prefs, prefs: prefs,
@ -35,8 +36,8 @@ func (dec *xmlDecoder) Init(reader io.Reader) error {
return nil return nil
} }
func (dec *xmlDecoder) createSequence(nodes []*xmlNode) (*CandidateNode, error) { func (dec *xmlDecoder) createSequence(nodes []*xmlNode) (*yqlib..CandidateNode, error) {
yamlNode := &CandidateNode{Kind: SequenceNode, Tag: "!!seq"} yamlNode := &yqlib..CandidateNode{Kind: SequenceNode, Tag: "!!seq"}
for _, child := range nodes { for _, child := range nodes {
yamlChild, err := dec.convertToYamlNode(child) yamlChild, err := dec.convertToYamlNode(child)
if err != nil { if err != nil {
@ -63,9 +64,9 @@ func (dec *xmlDecoder) processComment(c string) string {
return replacement return replacement
} }
func (dec *xmlDecoder) createMap(n *xmlNode) (*CandidateNode, error) { func (dec *xmlDecoder) createMap(n *xmlNode) (*yqlib..CandidateNode, error) {
log.Debug("createMap: headC: %v, lineC: %v, footC: %v", n.HeadComment, n.LineComment, n.FootComment) log.Debug("createMap: headC: %v, lineC: %v, footC: %v", n.HeadComment, n.LineComment, n.FootComment)
yamlNode := &CandidateNode{Kind: MappingNode, Tag: "!!map"} yamlNode := &yqlib..CandidateNode{Kind: MappingNode, Tag: "!!map"}
if len(n.Data) > 0 { if len(n.Data) > 0 {
log.Debugf("creating content node for map: %v", dec.prefs.ContentName) log.Debugf("creating content node for map: %v", dec.prefs.ContentName)
@ -81,7 +82,7 @@ func (dec *xmlDecoder) createMap(n *xmlNode) (*CandidateNode, error) {
label := keyValuePair.K label := keyValuePair.K
children := keyValuePair.V children := keyValuePair.V
labelNode := createScalarNode(label, label) labelNode := createScalarNode(label, label)
var valueNode *CandidateNode var valueNode *yqlib..CandidateNode
var err error var err error
if i == 0 { if i == 0 {
@ -125,18 +126,18 @@ func (dec *xmlDecoder) createMap(n *xmlNode) (*CandidateNode, error) {
return yamlNode, nil return yamlNode, nil
} }
func (dec *xmlDecoder) createValueNodeFromData(values []string) *CandidateNode { func (dec *xmlDecoder) createValueNodeFromData(values []string) *yqlib..CandidateNode {
switch len(values) { switch len(values) {
case 0: case 0:
return createScalarNode(nil, "") return createScalarNode(nil, "")
case 1: case 1:
return createScalarNode(values[0], values[0]) return createScalarNode(values[0], values[0])
default: default:
content := make([]*CandidateNode, 0) content := make([]*yqlib..CandidateNode, 0)
for _, value := range values { for _, value := range values {
content = append(content, createScalarNode(value, value)) content = append(content, createScalarNode(value, value))
} }
return &CandidateNode{ return &yqlib..CandidateNode{
Kind: SequenceNode, Kind: SequenceNode,
Tag: "!!seq", Tag: "!!seq",
Content: content, Content: content,
@ -144,7 +145,7 @@ func (dec *xmlDecoder) createValueNodeFromData(values []string) *CandidateNode {
} }
} }
func (dec *xmlDecoder) convertToYamlNode(n *xmlNode) (*CandidateNode, error) { func (dec *xmlDecoder) convertToYamlNode(n *xmlNode) (*yqlib..CandidateNode, error) {
if len(n.Children) > 0 { if len(n.Children) > 0 {
return dec.createMap(n) return dec.createMap(n)
} }
@ -164,7 +165,7 @@ func (dec *xmlDecoder) convertToYamlNode(n *xmlNode) (*CandidateNode, error) {
return scalar, nil return scalar, nil
} }
func (dec *xmlDecoder) Decode() (*CandidateNode, error) { func (dec *xmlDecoder) Decode() (*yqlib..CandidateNode, error) {
if dec.finished { if dec.finished {
return nil, io.EOF return nil, io.EOF
} }

View File

@ -1,6 +1,6 @@
//go:build !yq_noxml //go:build !yq_noxml
package yqlib package xml
import ( import (
"encoding/xml" "encoding/xml"
@ -8,6 +8,8 @@ import (
"io" "io"
"regexp" "regexp"
"strings" "strings"
"github.com/mikefarah/yq/v4/pkg/yqlib"
) )
type xmlEncoder struct { type xmlEncoder struct {
@ -17,7 +19,7 @@ type xmlEncoder struct {
leadingContent string leadingContent string
} }
func NewXMLEncoder(prefs XmlPreferences) Encoder { func NewXMLEncoder(prefs XmlPreferences) yqlib.Encoder {
var indentString = "" var indentString = ""
for index := 0; index < prefs.Indent; index++ { for index := 0; index < prefs.Indent; index++ {
@ -39,7 +41,7 @@ func (e *xmlEncoder) PrintLeadingContent(_ io.Writer, content string) error {
return nil return nil
} }
func (e *xmlEncoder) Encode(writer io.Writer, node *CandidateNode) error { func (e *xmlEncoder) Encode(writer io.Writer, node *yqlib.CandidateNode) error {
encoder := xml.NewEncoder(writer) encoder := xml.NewEncoder(writer)
// hack so we can manually add newlines to procInst and directives // hack so we can manually add newlines to procInst and directives
e.writer = writer e.writer = writer
@ -79,12 +81,12 @@ func (e *xmlEncoder) Encode(writer io.Writer, node *CandidateNode) error {
} }
switch node.Kind { switch node.Kind {
case MappingNode: case yqlib.MappingNode:
err := e.encodeTopLevelMap(encoder, node) err := e.encodeTopLevelMap(encoder, node)
if err != nil { if err != nil {
return err return err
} }
case ScalarNode: case yqlib.ScalarNode:
var charData xml.CharData = []byte(node.Value) var charData xml.CharData = []byte(node.Value)
err := encoder.EncodeToken(charData) err := encoder.EncodeToken(charData)
if err != nil { if err != nil {
@ -99,7 +101,7 @@ func (e *xmlEncoder) Encode(writer io.Writer, node *CandidateNode) error {
} }
func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *CandidateNode) error { func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *yqlib.CandidateNode) error {
err := e.encodeComment(encoder, headAndLineComment(node)) err := e.encodeComment(encoder, headAndLineComment(node))
if err != nil { if err != nil {
return err return err
@ -158,7 +160,7 @@ func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *CandidateNode
return e.encodeComment(encoder, footComment(node)) return e.encodeComment(encoder, footComment(node))
} }
func (e *xmlEncoder) encodeStart(encoder *xml.Encoder, node *CandidateNode, start xml.StartElement) error { func (e *xmlEncoder) encodeStart(encoder *xml.Encoder, node *yqlib.CandidateNode, start xml.StartElement) error {
err := encoder.EncodeToken(start) err := encoder.EncodeToken(start)
if err != nil { if err != nil {
return err return err
@ -166,7 +168,7 @@ func (e *xmlEncoder) encodeStart(encoder *xml.Encoder, node *CandidateNode, star
return e.encodeComment(encoder, headComment(node)) return e.encodeComment(encoder, headComment(node))
} }
func (e *xmlEncoder) encodeEnd(encoder *xml.Encoder, node *CandidateNode, start xml.StartElement) error { func (e *xmlEncoder) encodeEnd(encoder *xml.Encoder, node *yqlib.CandidateNode, start xml.StartElement) error {
err := encoder.EncodeToken(start.End()) err := encoder.EncodeToken(start.End())
if err != nil { if err != nil {
return err return err
@ -174,7 +176,7 @@ func (e *xmlEncoder) encodeEnd(encoder *xml.Encoder, node *CandidateNode, start
return e.encodeComment(encoder, footComment(node)) return e.encodeComment(encoder, footComment(node))
} }
func (e *xmlEncoder) doEncode(encoder *xml.Encoder, node *CandidateNode, start xml.StartElement) error { func (e *xmlEncoder) doEncode(encoder *xml.Encoder, node *yqlib.CandidateNode, start xml.StartElement) error {
switch node.Kind { switch node.Kind {
case MappingNode: case MappingNode:
return e.encodeMap(encoder, node, start) return e.encodeMap(encoder, node, start)
@ -238,7 +240,7 @@ func (e *xmlEncoder) encodeComment(encoder *xml.Encoder, commentStr string) erro
return nil return nil
} }
func (e *xmlEncoder) encodeArray(encoder *xml.Encoder, node *CandidateNode, start xml.StartElement) error { func (e *xmlEncoder) encodeArray(encoder *xml.Encoder, node *yqlib.CandidateNode, start xml.StartElement) error {
if err := e.encodeComment(encoder, headAndLineComment(node)); err != nil { if err := e.encodeComment(encoder, headAndLineComment(node)); err != nil {
return err return err
@ -260,7 +262,7 @@ func (e *xmlEncoder) isAttribute(name string) bool {
!strings.HasPrefix(name, e.prefs.ProcInstPrefix) !strings.HasPrefix(name, e.prefs.ProcInstPrefix)
} }
func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *CandidateNode, start xml.StartElement) error { func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yqlib.CandidateNode, start xml.StartElement) error {
log.Debug("its a map") log.Debug("its a map")
//first find all the attributes and put them on the start token //first find all the attributes and put them on the start token

View File

@ -1,4 +1,6 @@
package yqlib package xml
import "github.com/mikefarah/yq/v4/pkg/yqlib"
type XmlPreferences struct { type XmlPreferences struct {
Indent int Indent int
@ -44,3 +46,22 @@ func (p *XmlPreferences) Copy() XmlPreferences {
} }
var ConfiguredXMLPreferences = NewDefaultXmlPreferences() var ConfiguredXMLPreferences = NewDefaultXmlPreferences()
var XMLFormat = &yqlib.Format{"xml", []string{"x"},
func() yqlib.Encoder { return NewXMLEncoder(ConfiguredXMLPreferences) },
func() yqlib.Decoder { return NewXMLDecoder(ConfiguredXMLPreferences) },
}
var xmlYqRules = []*yqlib.ParticipleYqRule{
{"XMLEncodeWithIndent", `to_?xml\([0-9]+\)`, encodeParseIndent(XMLFormat), 0},
{"XmlDecode", `from_?xml|@xmld`, decodeOp(XMLFormat), 0},
{"XMLEncode", `to_?xml`, encodeWithIndent(XMLFormat, 2), 0},
{"XMLEncodeNoIndent", `@xml`, encodeWithIndent(XMLFormat, 0), 0},
{"LoadXML", `load_?xml|xml_?load`, loadOp(NewXMLDecoder(ConfiguredXMLPreferences), false), 0},
}
func RegisterXmlFormat() {
yqlib.RegisterFormat(XMLFormat)
yqlib.RegisterRules(xmlYqRules)
}

View File

@ -1,12 +1,13 @@
//go:build !yq_noxml //go:build !yq_noxml
package yqlib package xml
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"testing" "testing"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/mikefarah/yq/v4/test" "github.com/mikefarah/yq/v4/test"
) )
@ -262,7 +263,7 @@ const expectedXmlWithProcInstAndDirectives = `<?xml version="1.0"?>
</apple> </apple>
` `
var xmlScenarios = []formatScenario{ var xmlScenarios = []yqlib.FormatScenario{
{ {
skipDoc: true, skipDoc: true,
description: "bad xml", description: "bad xml",
@ -614,10 +615,10 @@ var xmlScenarios = []formatScenario{
}, },
} }
func testXMLScenario(t *testing.T, s formatScenario) { func testXMLScenario(t *testing.T, s yqlib.FormatScenario) {
switch s.scenarioType { switch s.scenarioType {
case "", "decode": case "", "decode":
yamlPrefs := ConfiguredYamlPreferences.Copy() yamlPrefs := yqlib.ConfiguredYamlPreferences.Copy()
yamlPrefs.Indent = 4 yamlPrefs.Indent = 4
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewXMLDecoder(ConfiguredXMLPreferences), NewYamlEncoder(yamlPrefs)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewXMLDecoder(ConfiguredXMLPreferences), NewYamlEncoder(yamlPrefs)), s.description)
case "encode": case "encode":
@ -661,7 +662,7 @@ func testXMLScenario(t *testing.T, s formatScenario) {
} }
func documentXMLScenario(_ *testing.T, w *bufio.Writer, i interface{}) { func documentXMLScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario) s := i.(FormatScenario)
if s.skipDoc { if s.skipDoc {
return return
@ -685,7 +686,7 @@ func documentXMLScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
} }
} }
func documentXMLDecodeScenario(w *bufio.Writer, s formatScenario) { func documentXMLDecodeScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -707,7 +708,7 @@ func documentXMLDecodeScenario(w *bufio.Writer, s formatScenario) {
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewXMLDecoder(ConfiguredXMLPreferences), NewYamlEncoder(ConfiguredYamlPreferences)))) writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewXMLDecoder(ConfiguredXMLPreferences), NewYamlEncoder(ConfiguredYamlPreferences))))
} }
func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s formatScenario) { func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -731,7 +732,7 @@ func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s formatScenario) {
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", mustProcessFormatScenario(s, NewXMLDecoder(prefsWithout), NewXMLEncoder(prefsWithout)))) writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", mustProcessFormatScenario(s, NewXMLDecoder(prefsWithout), NewXMLEncoder(prefsWithout))))
} }
func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s formatScenario) { func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -758,7 +759,7 @@ func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s formatScenario)
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", mustProcessFormatScenario(s, NewXMLDecoder(prefsWithout), NewXMLEncoder(prefsWithout)))) writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", mustProcessFormatScenario(s, NewXMLDecoder(prefsWithout), NewXMLEncoder(prefsWithout))))
} }
func documentXMLEncodeScenario(w *bufio.Writer, s formatScenario) { func documentXMLEncodeScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -776,7 +777,7 @@ func documentXMLEncodeScenario(w *bufio.Writer, s formatScenario) {
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewXMLEncoder(ConfiguredXMLPreferences)))) writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewXMLEncoder(ConfiguredXMLPreferences))))
} }
func documentXMLRoundTripScenario(w *bufio.Writer, s formatScenario) { func documentXMLRoundTripScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -794,7 +795,7 @@ func documentXMLRoundTripScenario(w *bufio.Writer, s formatScenario) {
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", mustProcessFormatScenario(s, NewXMLDecoder(ConfiguredXMLPreferences), NewXMLEncoder(ConfiguredXMLPreferences)))) writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", mustProcessFormatScenario(s, NewXMLDecoder(ConfiguredXMLPreferences), NewXMLEncoder(ConfiguredXMLPreferences))))
} }
func documentXMLSkipDirectivesScenario(w *bufio.Writer, s formatScenario) { func documentXMLSkipDirectivesScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {

View File

@ -243,7 +243,7 @@ func (n *CandidateNode) AddChildren(children []*CandidateNode) {
func (n *CandidateNode) GetValueRep() (interface{}, error) { func (n *CandidateNode) GetValueRep() (interface{}, error) {
log.Debugf("GetValueRep for %v value: %v", n.GetNicePath(), n.Value) log.Debugf("GetValueRep for %v value: %v", n.GetNicePath(), n.Value)
realTag := n.guessTagFromCustomType() realTag := n.GuessTagFromCustomType()
switch realTag { switch realTag {
case "!!int": case "!!int":
@ -261,17 +261,17 @@ func (n *CandidateNode) GetValueRep() (interface{}, error) {
return n.Value, nil return n.Value, nil
} }
func (n *CandidateNode) guessTagFromCustomType() string { func (n *CandidateNode) GuessTagFromCustomType() string {
if strings.HasPrefix(n.Tag, "!!") { if strings.HasPrefix(n.Tag, "!!") {
return n.Tag return n.Tag
} else if n.Value == "" { } else if n.Value == "" {
log.Debug("guessTagFromCustomType: node has no value to guess the type with") log.Debug("GuessTagFromCustomType: node has no value to guess the type with")
return n.Tag return n.Tag
} }
dataBucket, errorReading := parseSnippet(n.Value) dataBucket, errorReading := parseSnippet(n.Value)
if errorReading != nil { if errorReading != nil {
log.Debug("guessTagFromCustomType: could not guess underlying tag type %v", errorReading) log.Debug("GuessTagFromCustomType: could not guess underlying tag type %v", errorReading)
return n.Tag return n.Tag
} }
guessedTag := dataBucket.Tag guessedTag := dataBucket.Tag
@ -376,7 +376,7 @@ func (n *CandidateNode) UpdateFrom(other *CandidateNode, prefs assignPreferences
// if this is an empty map or empty array, use the style of other node. // if this is an empty map or empty array, use the style of other node.
if (n.Kind != ScalarNode && len(n.Content) == 0) || if (n.Kind != ScalarNode && len(n.Content) == 0) ||
// if the tag has changed (e.g. from str to bool) // if the tag has changed (e.g. from str to bool)
(n.guessTagFromCustomType() != other.guessTagFromCustomType()) { (n.GuessTagFromCustomType() != other.GuessTagFromCustomType()) {
n.Style = other.Style n.Style = other.Style
} }

View File

@ -94,7 +94,7 @@ const tsvTestExpectedSimpleCsv = `i like csv
because excel is cool because excel is cool
` `
var csvScenarios = []formatScenario{ var csvScenarios = []FormatScenario{
{ {
description: "Encode CSV simple", description: "Encode CSV simple",
input: csvTestSimpleYaml, input: csvTestSimpleYaml,
@ -210,7 +210,7 @@ var csvScenarios = []formatScenario{
}, },
} }
func testCSVScenario(t *testing.T, s formatScenario) { func testCSVScenario(t *testing.T, s FormatScenario) {
switch s.scenarioType { switch s.scenarioType {
case "encode-csv": case "encode-csv":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(ConfiguredCsvPreferences)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(ConfiguredCsvPreferences)), s.description)
@ -229,7 +229,7 @@ func testCSVScenario(t *testing.T, s formatScenario) {
} }
} }
func documentCSVDecodeObjectScenario(w *bufio.Writer, s formatScenario, formatType string) { func documentCSVDecodeObjectScenario(w *bufio.Writer, s FormatScenario, formatType string) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -254,7 +254,7 @@ func documentCSVDecodeObjectScenario(w *bufio.Writer, s formatScenario, formatTy
) )
} }
func documentCSVDecodeObjectNoAutoScenario(w *bufio.Writer, s formatScenario, formatType string) { func documentCSVDecodeObjectNoAutoScenario(w *bufio.Writer, s FormatScenario, formatType string) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -279,7 +279,7 @@ func documentCSVDecodeObjectNoAutoScenario(w *bufio.Writer, s formatScenario, fo
) )
} }
func documentCSVEncodeScenario(w *bufio.Writer, s formatScenario, formatType string) { func documentCSVEncodeScenario(w *bufio.Writer, s FormatScenario, formatType string) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -312,7 +312,7 @@ func documentCSVEncodeScenario(w *bufio.Writer, s formatScenario, formatType str
) )
} }
func documentCSVRoundTripScenario(w *bufio.Writer, s formatScenario, formatType string) { func documentCSVRoundTripScenario(w *bufio.Writer, s FormatScenario, formatType string) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -348,7 +348,7 @@ func documentCSVRoundTripScenario(w *bufio.Writer, s formatScenario, formatType
} }
func documentCSVScenario(_ *testing.T, w *bufio.Writer, i interface{}) { func documentCSVScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario) s := i.(FormatScenario)
if s.skipDoc { if s.skipDoc {
return return
} }

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
) )
type formatScenario struct { type FormatScenario struct {
input string input string
indent int indent int
expression string expression string
@ -19,7 +19,7 @@ type formatScenario struct {
expectedError string expectedError string
} }
func processFormatScenario(s formatScenario, decoder Decoder, encoder Encoder) (string, error) { func processFormatScenario(s FormatScenario, decoder Decoder, encoder Encoder) (string, error) {
var output bytes.Buffer var output bytes.Buffer
writer := bufio.NewWriter(&output) writer := bufio.NewWriter(&output)
@ -64,7 +64,7 @@ func processFormatScenario(s formatScenario, decoder Decoder, encoder Encoder) (
return output.String(), nil return output.String(), nil
} }
func mustProcessFormatScenario(s formatScenario, decoder Decoder, encoder Encoder) string { func mustProcessFormatScenario(s FormatScenario, decoder Decoder, encoder Encoder) string {
result, err := processFormatScenario(s, decoder, encoder) result, err := processFormatScenario(s, decoder, encoder)
if err != nil { if err != nil {

View File

@ -27,7 +27,7 @@ func (e *base64Encoder) PrintLeadingContent(_ io.Writer, _ string) error {
} }
func (e *base64Encoder) Encode(writer io.Writer, node *CandidateNode) error { func (e *base64Encoder) Encode(writer io.Writer, node *CandidateNode) error {
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return fmt.Errorf("cannot encode %v as base64, can only operate on strings", node.Tag) return fmt.Errorf("cannot encode %v as base64, can only operate on strings", node.Tag)
} }
_, err := writer.Write([]byte(e.encoding.EncodeToString([]byte(node.Value)))) _, err := writer.Write([]byte(e.encoding.EncodeToString([]byte(node.Value))))

View File

@ -30,7 +30,7 @@ func (e *shEncoder) PrintLeadingContent(_ io.Writer, _ string) error {
} }
func (e *shEncoder) Encode(writer io.Writer, node *CandidateNode) error { func (e *shEncoder) Encode(writer io.Writer, node *CandidateNode) error {
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return fmt.Errorf("cannot encode %v as URI, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string", node.Tag) return fmt.Errorf("cannot encode %v as URI, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string", node.Tag)
} }

View File

@ -26,7 +26,7 @@ func (e *uriEncoder) PrintLeadingContent(_ io.Writer, _ string) error {
} }
func (e *uriEncoder) Encode(writer io.Writer, node *CandidateNode) error { func (e *uriEncoder) Encode(writer io.Writer, node *CandidateNode) error {
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return fmt.Errorf("cannot encode %v as URI, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string", node.Tag) return fmt.Errorf("cannot encode %v as URI, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string", node.Tag)
} }
_, err := writer.Write([]byte(url.QueryEscape(node.Value))) _, err := writer.Write([]byte(url.QueryEscape(node.Value)))

View File

@ -1,4 +1,4 @@
package yqlib package exp_parser
import ( import (
"errors" "errors"

View File

@ -25,11 +25,6 @@ var JSONFormat = &Format{"json", []string{"j"},
func() Decoder { return NewJSONDecoder() }, func() Decoder { return NewJSONDecoder() },
} }
var PropertiesFormat = &Format{"props", []string{"p", "properties"},
func() Encoder { return NewPropertiesEncoder(ConfiguredPropertiesPreferences) },
func() Decoder { return NewPropertiesDecoder() },
}
var CSVFormat = &Format{"csv", []string{"c"}, var CSVFormat = &Format{"csv", []string{"c"},
func() Encoder { return NewCsvEncoder(ConfiguredCsvPreferences) }, func() Encoder { return NewCsvEncoder(ConfiguredCsvPreferences) },
func() Decoder { return NewCSVObjectDecoder(ConfiguredCsvPreferences) }, func() Decoder { return NewCSVObjectDecoder(ConfiguredCsvPreferences) },
@ -40,11 +35,6 @@ var TSVFormat = &Format{"tsv", []string{"t"},
func() Decoder { return NewCSVObjectDecoder(ConfiguredTsvPreferences) }, func() Decoder { return NewCSVObjectDecoder(ConfiguredTsvPreferences) },
} }
var XMLFormat = &Format{"xml", []string{"x"},
func() Encoder { return NewXMLEncoder(ConfiguredXMLPreferences) },
func() Decoder { return NewXMLDecoder(ConfiguredXMLPreferences) },
}
var Base64Format = &Format{"base64", []string{}, var Base64Format = &Format{"base64", []string{},
func() Encoder { return NewBase64Encoder() }, func() Encoder { return NewBase64Encoder() },
func() Decoder { return NewBase64Decoder() }, func() Decoder { return NewBase64Decoder() },
@ -78,10 +68,8 @@ var LuaFormat = &Format{"lua", []string{"l"},
var Formats = []*Format{ var Formats = []*Format{
YamlFormat, YamlFormat,
JSONFormat, JSONFormat,
PropertiesFormat,
CSVFormat, CSVFormat,
TSVFormat, TSVFormat,
XMLFormat,
Base64Format, Base64Format,
UriFormat, UriFormat,
ShFormat, ShFormat,
@ -90,6 +78,10 @@ var Formats = []*Format{
LuaFormat, LuaFormat,
} }
func RegisterFormat(f *Format) {
Formats = append(Formats, f)
}
func (f *Format) MatchesName(name string) bool { func (f *Format) MatchesName(name string) bool {
if f.FormalName == name { if f.FormalName == name {
return true return true

View File

@ -9,7 +9,7 @@ import (
"github.com/mikefarah/yq/v4/test" "github.com/mikefarah/yq/v4/test"
) )
var formattingExpressionScenarios = []formatScenario{ var formattingExpressionScenarios = []FormatScenario{
{ {
description: "Using expression files and comments", description: "Using expression files and comments",
skipDoc: true, skipDoc: true,
@ -43,7 +43,7 @@ var formattingExpressionScenarios = []formatScenario{
} }
func documentExpressionScenario(_ *testing.T, w *bufio.Writer, i interface{}) { func documentExpressionScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario) s := i.(FormatScenario)
if s.skipDoc { if s.skipDoc {
return return

View File

@ -6,7 +6,7 @@ import (
"github.com/mikefarah/yq/v4/test" "github.com/mikefarah/yq/v4/test"
) )
var goccyYamlFormatScenarios = []formatScenario{ var goccyYamlFormatScenarios = []FormatScenario{
{ {
description: "basic - 3", description: "basic - 3",
skipDoc: true, skipDoc: true,
@ -159,7 +159,7 @@ var goccyYamlFormatScenarios = []formatScenario{
// }, // },
} }
func testGoccyYamlScenario(t *testing.T, s formatScenario) { func testGoccyYamlScenario(t *testing.T, s FormatScenario) {
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewGoccyYAMLDecoder(), NewYamlEncoder(ConfiguredYamlPreferences)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewGoccyYAMLDecoder(), NewYamlEncoder(ConfiguredYamlPreferences)), s.description)
} }

View File

@ -79,7 +79,7 @@ const roundTripMultiLineJson = `{
} }
` `
var jsonScenarios = []formatScenario{ var jsonScenarios = []FormatScenario{
{ {
description: "array empty", description: "array empty",
skipDoc: true, skipDoc: true,
@ -307,7 +307,7 @@ var jsonScenarios = []formatScenario{
}, },
} }
func documentRoundtripNdJsonScenario(w *bufio.Writer, s formatScenario, indent int) { func documentRoundtripNdJsonScenario(w *bufio.Writer, s FormatScenario, indent int) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -335,7 +335,7 @@ func documentRoundtripNdJsonScenario(w *bufio.Writer, s formatScenario, indent i
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewJSONDecoder(), NewJSONEncoder(prefs)))) writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewJSONDecoder(), NewJSONEncoder(prefs))))
} }
func documentDecodeNdJsonScenario(w *bufio.Writer, s formatScenario) { func documentDecodeNdJsonScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -385,7 +385,7 @@ func decodeJSON(t *testing.T, jsonString string) *CandidateNode {
return context.MatchingNodes.Front().Value.(*CandidateNode) return context.MatchingNodes.Front().Value.(*CandidateNode)
} }
func testJSONScenario(t *testing.T, s formatScenario) { func testJSONScenario(t *testing.T, s FormatScenario) {
prefs := ConfiguredJSONPreferences.Copy() prefs := ConfiguredJSONPreferences.Copy()
prefs.Indent = s.indent prefs.Indent = s.indent
prefs.UnwrapScalar = false prefs.UnwrapScalar = false
@ -413,7 +413,7 @@ func testJSONScenario(t *testing.T, s formatScenario) {
} }
} }
func documentJSONDecodeScenario(t *testing.T, w *bufio.Writer, s formatScenario) { func documentJSONDecodeScenario(t *testing.T, w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -443,7 +443,7 @@ func documentJSONDecodeScenario(t *testing.T, w *bufio.Writer, s formatScenario)
} }
func documentJSONScenario(t *testing.T, w *bufio.Writer, i interface{}) { func documentJSONScenario(t *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario) s := i.(FormatScenario)
if s.skipDoc { if s.skipDoc {
return return
} }
@ -464,7 +464,7 @@ func documentJSONScenario(t *testing.T, w *bufio.Writer, i interface{}) {
} }
} }
func documentJSONEncodeScenario(w *bufio.Writer, s formatScenario) { func documentJSONEncodeScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {

View File

@ -1,4 +1,4 @@
package yqlib package exp_parser
import ( import (
"strconv" "strconv"
@ -7,7 +7,7 @@ import (
"github.com/alecthomas/participle/v2/lexer" "github.com/alecthomas/participle/v2/lexer"
) )
var participleYqRules = []*participleYqRule{ var participleYqRules = []*ParticipleYqRule{
{"LINE_COMMENT", `line_?comment|lineComment`, opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{LineComment: true}), 0}, {"LINE_COMMENT", `line_?comment|lineComment`, opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{LineComment: true}), 0},
{"HEAD_COMMENT", `head_?comment|headComment`, opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{HeadComment: true}), 0}, {"HEAD_COMMENT", `head_?comment|headComment`, opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{HeadComment: true}), 0},
{"FOOT_COMMENT", `foot_?comment|footComment`, opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{FootComment: true}), 0}, {"FOOT_COMMENT", `foot_?comment|footComment`, opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{FootComment: true}), 0},
@ -58,7 +58,7 @@ var participleYqRules = []*participleYqRule{
{"ArrayToMap", "array_?to_?map", expressionOpToken(`(.[] | select(. != null) ) as $i ireduce({}; .[$i | key] = $i)`), 0}, {"ArrayToMap", "array_?to_?map", expressionOpToken(`(.[] | select(. != null) ) as $i ireduce({}; .[$i | key] = $i)`), 0},
{"YamlEncodeWithIndent", `to_?yaml\([0-9]+\)`, encodeParseIndent(YamlFormat), 0}, {"YamlEncodeWithIndent", `to_?yaml\([0-9]+\)`, encodeParseIndent(YamlFormat), 0},
{"XMLEncodeWithIndent", `to_?xml\([0-9]+\)`, encodeParseIndent(XMLFormat), 0},
{"JSONEncodeWithIndent", `to_?json\([0-9]+\)`, encodeParseIndent(JSONFormat), 0}, {"JSONEncodeWithIndent", `to_?json\([0-9]+\)`, encodeParseIndent(JSONFormat), 0},
{"YamlDecode", `from_?yaml|@yamld|from_?json|@jsond`, decodeOp(YamlFormat), 0}, {"YamlDecode", `from_?yaml|@yamld|from_?json|@jsond`, decodeOp(YamlFormat), 0},
@ -67,13 +67,6 @@ var participleYqRules = []*participleYqRule{
{"JSONEncode", `to_?json`, encodeWithIndent(JSONFormat, 2), 0}, {"JSONEncode", `to_?json`, encodeWithIndent(JSONFormat, 2), 0},
{"JSONEncodeNoIndent", `@json`, encodeWithIndent(JSONFormat, 0), 0}, {"JSONEncodeNoIndent", `@json`, encodeWithIndent(JSONFormat, 0), 0},
{"PropertiesDecode", `from_?props|@propsd`, decodeOp(PropertiesFormat), 0},
{"PropsEncode", `to_?props|@props`, encodeWithIndent(PropertiesFormat, 2), 0},
{"XmlDecode", `from_?xml|@xmld`, decodeOp(XMLFormat), 0},
{"XMLEncode", `to_?xml`, encodeWithIndent(XMLFormat, 2), 0},
{"XMLEncodeNoIndent", `@xml`, encodeWithIndent(XMLFormat, 0), 0},
{"CSVDecode", `from_?csv|@csvd`, decodeOp(CSVFormat), 0}, {"CSVDecode", `from_?csv|@csvd`, decodeOp(CSVFormat), 0},
{"CSVEncode", `to_?csv|@csv`, encodeWithIndent(CSVFormat, 0), 0}, {"CSVEncode", `to_?csv|@csv`, encodeWithIndent(CSVFormat, 0), 0},
@ -87,12 +80,8 @@ var participleYqRules = []*participleYqRule{
{"Uri", `@uri`, encodeWithIndent(UriFormat, 0), 0}, {"Uri", `@uri`, encodeWithIndent(UriFormat, 0), 0},
{"SH", `@sh`, encodeWithIndent(ShFormat, 0), 0}, {"SH", `@sh`, encodeWithIndent(ShFormat, 0), 0},
{"LoadXML", `load_?xml|xml_?load`, loadOp(NewXMLDecoder(ConfiguredXMLPreferences), false), 0},
{"LoadBase64", `load_?base64`, loadOp(NewBase64Decoder(), false), 0}, {"LoadBase64", `load_?base64`, loadOp(NewBase64Decoder(), false), 0},
{"LoadProperties", `load_?props`, loadOp(NewPropertiesDecoder(), false), 0},
{"LoadString", `load_?str|str_?load`, loadOp(nil, true), 0}, {"LoadString", `load_?str|str_?load`, loadOp(nil, true), 0},
{"LoadYaml", `load`, loadOp(NewYamlDecoder(LoadYamlPreferences), false), 0}, {"LoadYaml", `load`, loadOp(NewYamlDecoder(LoadYamlPreferences), false), 0},
@ -229,7 +218,7 @@ var participleYqRules = []*participleYqRule{
type yqAction func(lexer.Token) (*token, error) type yqAction func(lexer.Token) (*token, error)
type participleYqRule struct { type ParticipleYqRule struct {
Name string Name string
Pattern string Pattern string
CreateYqToken yqAction CreateYqToken yqAction
@ -240,12 +229,16 @@ type participleLexer struct {
lexerDefinition lexer.StringDefinition lexerDefinition lexer.StringDefinition
} }
func simpleOp(name string, opType *operationType) *participleYqRule { func simpleOp(name string, opType *operationType) *ParticipleYqRule {
return &participleYqRule{strings.ToUpper(string(name[1])) + name[1:], name, opToken(opType), 0} return &ParticipleYqRule{strings.ToUpper(string(name[1])) + name[1:], name, opToken(opType), 0}
} }
func assignableOp(name string, opType *operationType, assignOpType *operationType) *participleYqRule { func assignableOp(name string, opType *operationType, assignOpType *operationType) *ParticipleYqRule {
return &participleYqRule{strings.ToUpper(string(name[1])) + name[1:], name, opTokenWithPrefs(opType, assignOpType, nil), 0} return &ParticipleYqRule{strings.ToUpper(string(name[1])) + name[1:], name, opTokenWithPrefs(opType, assignOpType, nil), 0}
}
func RegisterRules(rules []*ParticipleYqRule) {
participleYqRules = append(participleYqRules, rules...)
} }
func newParticipleLexer() expressionTokeniser { func newParticipleLexer() expressionTokeniser {
@ -564,13 +557,13 @@ func literalToken(tt tokenType, checkForPost bool) yqAction {
} }
} }
func (p *participleLexer) getYqDefinition(rawToken lexer.Token) *participleYqRule { func (p *participleLexer) getYqDefinition(rawToken lexer.Token) *ParticipleYqRule {
for _, yqRule := range participleYqRules { for _, yqRule := range participleYqRules {
if yqRule.ParticipleTokenType == rawToken.Type { if yqRule.ParticipleTokenType == rawToken.Type {
return yqRule return yqRule
} }
} }
return &participleYqRule{} return &ParticipleYqRule{}
} }
func (p *participleLexer) Tokenise(expression string) ([]*token, error) { func (p *participleLexer) Tokenise(expression string) ([]*token, error) {

View File

@ -114,8 +114,8 @@ func recursiveNodeEqual(lhs *CandidateNode, rhs *CandidateNode) bool {
//process custom tags of scalar nodes. //process custom tags of scalar nodes.
//dont worry about matching tags of maps or arrays. //dont worry about matching tags of maps or arrays.
lhsTag := lhs.guessTagFromCustomType() lhsTag := lhs.GuessTagFromCustomType()
rhsTag := rhs.guessTagFromCustomType() rhsTag := rhs.GuessTagFromCustomType()
if lhsTag != rhsTag { if lhsTag != rhsTag {
return false return false

View File

@ -10,7 +10,7 @@ import (
"github.com/mikefarah/yq/v4/test" "github.com/mikefarah/yq/v4/test"
) )
var luaScenarios = []formatScenario{ var luaScenarios = []FormatScenario{
{ {
description: "Basic input example", description: "Basic input example",
input: `return { input: `return {
@ -250,7 +250,7 @@ numbers:
}, },
} }
func testLuaScenario(t *testing.T, s formatScenario) { func testLuaScenario(t *testing.T, s FormatScenario) {
switch s.scenarioType { switch s.scenarioType {
case "", "decode": case "", "decode":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewLuaDecoder(ConfiguredLuaPreferences), NewYamlEncoder(ConfiguredYamlPreferences)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewLuaDecoder(ConfiguredLuaPreferences), NewYamlEncoder(ConfiguredYamlPreferences)), s.description)
@ -278,7 +278,7 @@ func testLuaScenario(t *testing.T, s formatScenario) {
} }
func documentLuaScenario(_ *testing.T, w *bufio.Writer, i interface{}) { func documentLuaScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario) s := i.(FormatScenario)
if s.skipDoc { if s.skipDoc {
return return
@ -295,7 +295,7 @@ func documentLuaScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
} }
} }
func documentLuaDecodeScenario(w *bufio.Writer, s formatScenario) { func documentLuaDecodeScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -317,7 +317,7 @@ func documentLuaDecodeScenario(w *bufio.Writer, s formatScenario) {
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewLuaDecoder(ConfiguredLuaPreferences), NewYamlEncoder(ConfiguredYamlPreferences)))) writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewLuaDecoder(ConfiguredLuaPreferences), NewYamlEncoder(ConfiguredYamlPreferences))))
} }
func documentLuaEncodeScenario(w *bufio.Writer, s formatScenario) { func documentLuaEncodeScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -359,7 +359,7 @@ func documentLuaEncodeScenario(w *bufio.Writer, s formatScenario) {
writeOrPanic(w, fmt.Sprintf("```lua\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewLuaEncoder(prefs)))) writeOrPanic(w, fmt.Sprintf("```lua\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewLuaEncoder(prefs))))
} }
func documentLuaRoundTripScenario(w *bufio.Writer, s formatScenario) { func documentLuaRoundTripScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {

View File

@ -74,11 +74,11 @@ func add(_ *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *Candida
func addScalars(context Context, target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error { func addScalars(context Context, target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {
lhsTag := lhs.Tag lhsTag := lhs.Tag
rhsTag := rhs.guessTagFromCustomType() rhsTag := rhs.GuessTagFromCustomType()
lhsIsCustom := false lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") { if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType() lhsTag = lhs.GuessTagFromCustomType()
lhsIsCustom = true lhsIsCustom = true
} }

View File

@ -6,6 +6,10 @@ type assignPreferences struct {
ClobberCustomTags bool ClobberCustomTags bool
} }
func PerformAssignment(context Context, path []interface{}) {
}
func assignUpdateFunc(prefs assignPreferences) crossFunctionCalculation { func assignUpdateFunc(prefs assignPreferences) crossFunctionCalculation {
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
if !prefs.OnlyWriteNull || lhs.Tag == "!!null" { if !prefs.OnlyWriteNull || lhs.Tag == "!!null" {

View File

@ -69,8 +69,8 @@ func compareDateTime(layout string, prefs compareTypePref, lhs *CandidateNode, r
} }
func compareScalars(context Context, prefs compareTypePref, lhs *CandidateNode, rhs *CandidateNode) (bool, error) { func compareScalars(context Context, prefs compareTypePref, lhs *CandidateNode, rhs *CandidateNode) (bool, error) {
lhsTag := lhs.guessTagFromCustomType() lhsTag := lhs.GuessTagFromCustomType()
rhsTag := rhs.guessTagFromCustomType() rhsTag := rhs.GuessTagFromCustomType()
isDateTime := lhs.Tag == "!!timestamp" isDateTime := lhs.Tag == "!!timestamp"
// if the lhs is a string, it might be a timestamp in a custom format. // if the lhs is a string, it might be a timestamp in a custom format.

View File

@ -141,7 +141,7 @@ func fromUnixOp(_ *dataTreeNavigator, context Context, _ *ExpressionNode) (Conte
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
actualTag := candidate.guessTagFromCustomType() actualTag := candidate.GuessTagFromCustomType()
if actualTag != "!!int" && actualTag != "!!float" { if actualTag != "!!int" && actualTag != "!!float" {
return Context{}, fmt.Errorf("from_unix only works on numbers, found %v instead", candidate.Tag) return Context{}, fmt.Errorf("from_unix only works on numbers, found %v instead", candidate.Tag)

View File

@ -32,11 +32,11 @@ func divide(_ *dataTreeNavigator, _ Context, lhs *CandidateNode, rhs *CandidateN
func divideScalars(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error { func divideScalars(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {
lhsTag := lhs.Tag lhsTag := lhs.Tag
rhsTag := rhs.guessTagFromCustomType() rhsTag := rhs.GuessTagFromCustomType()
lhsIsCustom := false lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") { if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType() lhsTag = lhs.GuessTagFromCustomType()
lhsIsCustom = true lhsIsCustom = true
} }

View File

@ -33,11 +33,11 @@ func modulo(_ *dataTreeNavigator, _ Context, lhs *CandidateNode, rhs *CandidateN
func moduloScalars(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error { func moduloScalars(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {
lhsTag := lhs.Tag lhsTag := lhs.Tag
rhsTag := rhs.guessTagFromCustomType() rhsTag := rhs.GuessTagFromCustomType()
lhsIsCustom := false lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") { if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType() lhsTag = lhs.GuessTagFromCustomType()
lhsIsCustom = true lhsIsCustom = true
} }

View File

@ -79,11 +79,11 @@ func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, contex
func multiplyScalars(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func multiplyScalars(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
lhsTag := lhs.Tag lhsTag := lhs.Tag
rhsTag := rhs.guessTagFromCustomType() rhsTag := rhs.GuessTagFromCustomType()
lhsIsCustom := false lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") { if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType() lhsTag = lhs.GuessTagFromCustomType()
lhsIsCustom = true lhsIsCustom = true
} }

View File

@ -92,12 +92,12 @@ func (a sortableNodeArray) compare(lhs *CandidateNode, rhs *CandidateNode, dateT
if !strings.HasPrefix(lhsTag, "!!") { if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType() lhsTag = lhs.GuessTagFromCustomType()
} }
if !strings.HasPrefix(rhsTag, "!!") { if !strings.HasPrefix(rhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
rhsTag = rhs.guessTagFromCustomType() rhsTag = rhs.GuessTagFromCustomType()
} }
isDateTime := lhsTag == "!!timestamp" && rhsTag == "!!timestamp" isDateTime := lhsTag == "!!timestamp" && rhsTag == "!!timestamp"

View File

@ -130,7 +130,7 @@ func trimSpaceOperator(_ *dataTreeNavigator, context Context, _ *ExpressionNode)
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode) node := el.Value.(*CandidateNode)
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return Context{}, fmt.Errorf("cannot trim %v, can only operate on strings. ", node.Tag) return Context{}, fmt.Errorf("cannot trim %v, can only operate on strings. ", node.Tag)
} }
@ -175,7 +175,7 @@ func changeCaseOperator(_ *dataTreeNavigator, context Context, expressionNode *E
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode) node := el.Value.(*CandidateNode)
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return Context{}, fmt.Errorf("cannot change case with %v, can only operate on strings. ", node.Tag) return Context{}, fmt.Errorf("cannot change case with %v, can only operate on strings. ", node.Tag)
} }
@ -246,7 +246,7 @@ func substituteStringOperator(d *dataTreeNavigator, context Context, expressionN
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode) node := el.Value.(*CandidateNode)
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return Context{}, fmt.Errorf("cannot substitute with %v, can only substitute strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag) return Context{}, fmt.Errorf("cannot substitute with %v, can only substitute strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag)
} }
@ -425,7 +425,7 @@ func matchOperator(d *dataTreeNavigator, context Context, expressionNode *Expres
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode) node := el.Value.(*CandidateNode)
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return Context{}, fmt.Errorf("cannot match with %v, can only match strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag) return Context{}, fmt.Errorf("cannot match with %v, can only match strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag)
} }
@ -445,7 +445,7 @@ func captureOperator(d *dataTreeNavigator, context Context, expressionNode *Expr
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode) node := el.Value.(*CandidateNode)
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return Context{}, fmt.Errorf("cannot match with %v, can only match strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag) return Context{}, fmt.Errorf("cannot match with %v, can only match strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag)
} }
capture(matchPrefs, regEx, node, node.Value, results) capture(matchPrefs, regEx, node, node.Value, results)
@ -465,7 +465,7 @@ func testOperator(d *dataTreeNavigator, context Context, expressionNode *Express
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode) node := el.Value.(*CandidateNode)
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return Context{}, fmt.Errorf("cannot match with %v, can only match strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag) return Context{}, fmt.Errorf("cannot match with %v, can only match strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag)
} }
matches := regEx.FindStringSubmatch(node.Value) matches := regEx.FindStringSubmatch(node.Value)
@ -535,7 +535,7 @@ func splitStringOperator(d *dataTreeNavigator, context Context, expressionNode *
continue continue
} }
if node.guessTagFromCustomType() != "!!str" { if node.GuessTagFromCustomType() != "!!str" {
return Context{}, fmt.Errorf("cannot split %v, can only split strings", node.Tag) return Context{}, fmt.Errorf("cannot split %v, can only split strings", node.Tag)
} }
kind, tag, content := split(node.Value, splitStr) kind, tag, content := split(node.Value, splitStr)

View File

@ -77,13 +77,13 @@ func subtractScalars(context Context, target *CandidateNode, lhs *CandidateNode,
lhsIsCustom := false lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") { if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType() lhsTag = lhs.GuessTagFromCustomType()
lhsIsCustom = true lhsIsCustom = true
} }
if !strings.HasPrefix(rhsTag, "!!") { if !strings.HasPrefix(rhsTag, "!!") {
// custom tag - we have to have a guess // custom tag - we have to have a guess
rhsTag = rhs.guessTagFromCustomType() rhsTag = rhs.GuessTagFromCustomType()
} }
isDateTime := lhsTag == "!!timestamp" isDateTime := lhsTag == "!!timestamp"

View File

@ -1,25 +0,0 @@
package yqlib
type PropertiesPreferences struct {
UnwrapScalar bool
KeyValueSeparator string
UseArrayBrackets bool
}
func NewDefaultPropertiesPreferences() PropertiesPreferences {
return PropertiesPreferences{
UnwrapScalar: true,
KeyValueSeparator: " = ",
UseArrayBrackets: false,
}
}
func (p *PropertiesPreferences) Copy() PropertiesPreferences {
return PropertiesPreferences{
UnwrapScalar: p.UnwrapScalar,
KeyValueSeparator: p.KeyValueSeparator,
UseArrayBrackets: p.UseArrayBrackets,
}
}
var ConfiguredPropertiesPreferences = NewDefaultPropertiesPreferences()

View File

@ -155,7 +155,7 @@ emptyArray =
emptyMap = emptyMap =
` `
var propertyScenarios = []formatScenario{ var propertyScenarios = []FormatScenario{
{ {
description: "Encode properties", description: "Encode properties",
subdescription: "Note that empty arrays and maps are not encoded by default.", subdescription: "Note that empty arrays and maps are not encoded by default.",
@ -295,7 +295,7 @@ var propertyScenarios = []formatScenario{
}, },
} }
func documentUnwrappedEncodePropertyScenario(w *bufio.Writer, s formatScenario) { func documentUnwrappedEncodePropertyScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -331,7 +331,7 @@ func documentUnwrappedEncodePropertyScenario(w *bufio.Writer, s formatScenario)
writeOrPanic(w, fmt.Sprintf("```properties\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewPropertiesEncoder(prefs)))) writeOrPanic(w, fmt.Sprintf("```properties\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewPropertiesEncoder(prefs))))
} }
func documentWrappedEncodePropertyScenario(w *bufio.Writer, s formatScenario) { func documentWrappedEncodePropertyScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -357,7 +357,7 @@ func documentWrappedEncodePropertyScenario(w *bufio.Writer, s formatScenario) {
writeOrPanic(w, fmt.Sprintf("```properties\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewPropertiesEncoder(prefs)))) writeOrPanic(w, fmt.Sprintf("```properties\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewPropertiesEncoder(prefs))))
} }
func documentDecodePropertyScenario(w *bufio.Writer, s formatScenario) { func documentDecodePropertyScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -382,7 +382,7 @@ func documentDecodePropertyScenario(w *bufio.Writer, s formatScenario) {
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewPropertiesDecoder(), NewYamlEncoder(ConfiguredYamlPreferences)))) writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewPropertiesDecoder(), NewYamlEncoder(ConfiguredYamlPreferences))))
} }
func documentRoundTripPropertyScenario(w *bufio.Writer, s formatScenario) { func documentRoundTripPropertyScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -408,7 +408,7 @@ func documentRoundTripPropertyScenario(w *bufio.Writer, s formatScenario) {
} }
func documentPropertyScenario(_ *testing.T, w *bufio.Writer, i interface{}) { func documentPropertyScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario) s := i.(FormatScenario)
if s.skipDoc { if s.skipDoc {
return return
} }

View File

@ -8,7 +8,7 @@ import (
"github.com/mikefarah/yq/v4/test" "github.com/mikefarah/yq/v4/test"
) )
var shellVariablesScenarios = []formatScenario{ var shellVariablesScenarios = []FormatScenario{
{ {
description: "Encode shell variables", description: "Encode shell variables",
subdescription: "Note that comments are dropped and values will be enclosed in single quotes as needed.", subdescription: "Note that comments are dropped and values will be enclosed in single quotes as needed.",
@ -69,7 +69,7 @@ func TestShellVariableScenarios(t *testing.T) {
} }
func documentShellVariableScenario(_ *testing.T, w *bufio.Writer, i interface{}) { func documentShellVariableScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario) s := i.(FormatScenario)
if s.skipDoc { if s.skipDoc {
return return
} }

View File

@ -79,7 +79,7 @@ var expectedSampleWithHeader = `servers:
ip: 10.0.0.1 ip: 10.0.0.1
` `
var tomlScenarios = []formatScenario{ var tomlScenarios = []FormatScenario{
{ {
skipDoc: true, skipDoc: true,
description: "blank", description: "blank",
@ -240,7 +240,7 @@ var tomlScenarios = []formatScenario{
}, },
} }
func testTomlScenario(t *testing.T, s formatScenario) { func testTomlScenario(t *testing.T, s FormatScenario) {
switch s.scenarioType { switch s.scenarioType {
case "", "decode": case "", "decode":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewTomlDecoder(), NewYamlEncoder(ConfiguredYamlPreferences)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewTomlDecoder(), NewYamlEncoder(ConfiguredYamlPreferences)), s.description)
@ -256,7 +256,7 @@ func testTomlScenario(t *testing.T, s formatScenario) {
} }
} }
func documentTomlDecodeScenario(w *bufio.Writer, s formatScenario) { func documentTomlDecodeScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -278,7 +278,7 @@ func documentTomlDecodeScenario(w *bufio.Writer, s formatScenario) {
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewTomlDecoder(), NewYamlEncoder(ConfiguredYamlPreferences)))) writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewTomlDecoder(), NewYamlEncoder(ConfiguredYamlPreferences))))
} }
func documentTomlRoundtripScenario(w *bufio.Writer, s formatScenario) { func documentTomlRoundtripScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" { if s.subdescription != "" {
@ -301,7 +301,7 @@ func documentTomlRoundtripScenario(w *bufio.Writer, s formatScenario) {
} }
func documentTomlScenario(_ *testing.T, w *bufio.Writer, i interface{}) { func documentTomlScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario) s := i.(FormatScenario)
if s.skipDoc { if s.skipDoc {
return return

View File

@ -6,7 +6,7 @@ import (
"github.com/mikefarah/yq/v4/test" "github.com/mikefarah/yq/v4/test"
) )
var yamlFormatScenarios = []formatScenario{ var yamlFormatScenarios = []FormatScenario{
{ {
description: "scalar with doc separator", description: "scalar with doc separator",
skipDoc: true, skipDoc: true,
@ -111,7 +111,7 @@ var yamlParseScenarios = []expressionScenario{
}, },
} }
func testYamlScenario(t *testing.T, s formatScenario) { func testYamlScenario(t *testing.T, s FormatScenario) {
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewYamlEncoder(ConfiguredYamlPreferences)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewYamlEncoder(ConfiguredYamlPreferences)), s.description)
} }

View File

@ -1,5 +1,5 @@
4.43.1: (not released yet) 4.43.1: (not released yet)
- Added tostring #72 - Added to_string #72
- Added string interpolation #1149 - Added string interpolation #1149
- Can specify parent(n) levels #1970 - Can specify parent(n) levels #1970
- Fixed CSV line break issue #1974 - Fixed CSV line break issue #1974
@ -151,7 +151,7 @@
- XML Decoder: Fixed issue where content surrounding tags are lost #1447 - XML Decoder: Fixed issue where content surrounding tags are lost #1447
- XML Decoder: Fixed xml decode bug when there is content after a comment - XML Decoder: Fixed xml decode bug when there is content after a comment
- Fixed loading yaml with header issue #1445 - Fixed loading yaml with header issue #1445
- guessTagFromCustomType warning log is now a debug. - GuessTagFromCustomType warning log is now a debug.
- Special thanks to @Kopfbremse for reporting XML issues! - Special thanks to @Kopfbremse for reporting XML issues!
4.30.4: 4.30.4:

3
yq.go
View File

@ -4,9 +4,12 @@ import (
"os" "os"
command "github.com/mikefarah/yq/v4/cmd" command "github.com/mikefarah/yq/v4/cmd"
"github.com/mikefarah/yq/v4/pkg/yqlib"
) )
func main() { func main() {
yqlib.RegisterPropertiesFormat()
cmd := command.New() cmd := command.New()
args := os.Args[1:] args := os.Args[1:]