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

View File

@ -1,4 +1,4 @@
package yqlib
package properties
import (
"bufio"
@ -8,13 +8,14 @@ import (
"strings"
"github.com/magiconair/properties"
"github.com/mikefarah/yq/v4/pkg/yqlib"
)
type propertiesEncoder struct {
prefs PropertiesPreferences
}
func NewPropertiesEncoder(prefs PropertiesPreferences) Encoder {
func NewPropertiesEncoder(prefs PropertiesPreferences) yqlib.Encoder {
return &propertiesEncoder{
prefs: prefs,
}
@ -61,9 +62,9 @@ func (pe *propertiesEncoder) PrintLeadingContent(writer io.Writer, content strin
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")
}
@ -79,7 +80,7 @@ func (pe *propertiesEncoder) Encode(writer io.Writer, node *CandidateNode) error
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 := ""
if keyNode != nil {
@ -91,7 +92,7 @@ func (pe *propertiesEncoder) doEncode(p *properties.Properties, node *CandidateN
p.SetComments(path, strings.Split(commentsWithSpaces, "\n"))
switch node.Kind {
case ScalarNode:
case yqlib.ScalarNode:
var nodeValue string
if pe.prefs.UnwrapScalar || !strings.Contains(node.Value, " ") {
nodeValue = node.Value
@ -100,11 +101,11 @@ func (pe *propertiesEncoder) doEncode(p *properties.Properties, node *CandidateN
}
_, _, err := p.Set(path, nodeValue)
return err
case SequenceNode:
case yqlib.SequenceNode:
return pe.encodeArray(p, node.Content, path)
case MappingNode:
case yqlib.MappingNode:
return pe.encodeMap(p, node.Content, path)
case AliasNode:
case yqlib.AliasNode:
return pe.doEncode(p, node.Alias, path, nil)
default:
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)
}
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 {
err := pe.doEncode(p, child, pe.appendPath(path, index), nil)
if err != nil {
@ -135,7 +136,7 @@ func (pe *propertiesEncoder) encodeArray(p *properties.Properties, kids []*Candi
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 {
key := kids[index]
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
package yqlib
package xml
import (
"encoding/xml"
@ -11,6 +11,7 @@ import (
"strings"
"unicode"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"golang.org/x/net/html/charset"
)
@ -21,7 +22,7 @@ type xmlDecoder struct {
prefs XmlPreferences
}
func NewXMLDecoder(prefs XmlPreferences) Decoder {
func NewXMLDecoder(prefs XmlPreferences) yqlib.Decoder {
return &xmlDecoder{
finished: false,
prefs: prefs,
@ -35,8 +36,8 @@ func (dec *xmlDecoder) Init(reader io.Reader) error {
return nil
}
func (dec *xmlDecoder) createSequence(nodes []*xmlNode) (*CandidateNode, error) {
yamlNode := &CandidateNode{Kind: SequenceNode, Tag: "!!seq"}
func (dec *xmlDecoder) createSequence(nodes []*xmlNode) (*yqlib..CandidateNode, error) {
yamlNode := &yqlib..CandidateNode{Kind: SequenceNode, Tag: "!!seq"}
for _, child := range nodes {
yamlChild, err := dec.convertToYamlNode(child)
if err != nil {
@ -63,9 +64,9 @@ func (dec *xmlDecoder) processComment(c string) string {
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)
yamlNode := &CandidateNode{Kind: MappingNode, Tag: "!!map"}
yamlNode := &yqlib..CandidateNode{Kind: MappingNode, Tag: "!!map"}
if len(n.Data) > 0 {
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
children := keyValuePair.V
labelNode := createScalarNode(label, label)
var valueNode *CandidateNode
var valueNode *yqlib..CandidateNode
var err error
if i == 0 {
@ -125,18 +126,18 @@ func (dec *xmlDecoder) createMap(n *xmlNode) (*CandidateNode, error) {
return yamlNode, nil
}
func (dec *xmlDecoder) createValueNodeFromData(values []string) *CandidateNode {
func (dec *xmlDecoder) createValueNodeFromData(values []string) *yqlib..CandidateNode {
switch len(values) {
case 0:
return createScalarNode(nil, "")
case 1:
return createScalarNode(values[0], values[0])
default:
content := make([]*CandidateNode, 0)
content := make([]*yqlib..CandidateNode, 0)
for _, value := range values {
content = append(content, createScalarNode(value, value))
}
return &CandidateNode{
return &yqlib..CandidateNode{
Kind: SequenceNode,
Tag: "!!seq",
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 {
return dec.createMap(n)
}
@ -164,7 +165,7 @@ func (dec *xmlDecoder) convertToYamlNode(n *xmlNode) (*CandidateNode, error) {
return scalar, nil
}
func (dec *xmlDecoder) Decode() (*CandidateNode, error) {
func (dec *xmlDecoder) Decode() (*yqlib..CandidateNode, error) {
if dec.finished {
return nil, io.EOF
}

View File

@ -1,6 +1,6 @@
//go:build !yq_noxml
package yqlib
package xml
import (
"encoding/xml"
@ -8,6 +8,8 @@ import (
"io"
"regexp"
"strings"
"github.com/mikefarah/yq/v4/pkg/yqlib"
)
type xmlEncoder struct {
@ -17,7 +19,7 @@ type xmlEncoder struct {
leadingContent string
}
func NewXMLEncoder(prefs XmlPreferences) Encoder {
func NewXMLEncoder(prefs XmlPreferences) yqlib.Encoder {
var indentString = ""
for index := 0; index < prefs.Indent; index++ {
@ -39,7 +41,7 @@ func (e *xmlEncoder) PrintLeadingContent(_ io.Writer, content string) error {
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)
// hack so we can manually add newlines to procInst and directives
e.writer = writer
@ -79,12 +81,12 @@ func (e *xmlEncoder) Encode(writer io.Writer, node *CandidateNode) error {
}
switch node.Kind {
case MappingNode:
case yqlib.MappingNode:
err := e.encodeTopLevelMap(encoder, node)
if err != nil {
return err
}
case ScalarNode:
case yqlib.ScalarNode:
var charData xml.CharData = []byte(node.Value)
err := encoder.EncodeToken(charData)
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))
if err != nil {
return err
@ -158,7 +160,7 @@ func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *CandidateNode
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)
if err != nil {
return err
@ -166,7 +168,7 @@ func (e *xmlEncoder) encodeStart(encoder *xml.Encoder, node *CandidateNode, star
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())
if err != nil {
return err
@ -174,7 +176,7 @@ func (e *xmlEncoder) encodeEnd(encoder *xml.Encoder, node *CandidateNode, start
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 {
case MappingNode:
return e.encodeMap(encoder, node, start)
@ -238,7 +240,7 @@ func (e *xmlEncoder) encodeComment(encoder *xml.Encoder, commentStr string) erro
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 {
return err
@ -260,7 +262,7 @@ func (e *xmlEncoder) isAttribute(name string) bool {
!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")
//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 {
Indent int
@ -44,3 +46,22 @@ func (p *XmlPreferences) Copy() XmlPreferences {
}
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
package yqlib
package xml
import (
"bufio"
"fmt"
"testing"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/mikefarah/yq/v4/test"
)
@ -262,7 +263,7 @@ const expectedXmlWithProcInstAndDirectives = `<?xml version="1.0"?>
</apple>
`
var xmlScenarios = []formatScenario{
var xmlScenarios = []yqlib.FormatScenario{
{
skipDoc: true,
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 {
case "", "decode":
yamlPrefs := ConfiguredYamlPreferences.Copy()
yamlPrefs := yqlib.ConfiguredYamlPreferences.Copy()
yamlPrefs.Indent = 4
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewXMLDecoder(ConfiguredXMLPreferences), NewYamlEncoder(yamlPrefs)), s.description)
case "encode":
@ -661,7 +662,7 @@ func testXMLScenario(t *testing.T, s formatScenario) {
}
func documentXMLScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario)
s := i.(FormatScenario)
if s.skipDoc {
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))
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))))
}
func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s formatScenario) {
func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
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))))
}
func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s formatScenario) {
func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
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))))
}
func documentXMLEncodeScenario(w *bufio.Writer, s formatScenario) {
func documentXMLEncodeScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
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))))
}
func documentXMLRoundTripScenario(w *bufio.Writer, s formatScenario) {
func documentXMLRoundTripScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
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))))
}
func documentXMLSkipDirectivesScenario(w *bufio.Writer, s formatScenario) {
func documentXMLSkipDirectivesScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" {

View File

@ -243,7 +243,7 @@ func (n *CandidateNode) AddChildren(children []*CandidateNode) {
func (n *CandidateNode) GetValueRep() (interface{}, error) {
log.Debugf("GetValueRep for %v value: %v", n.GetNicePath(), n.Value)
realTag := n.guessTagFromCustomType()
realTag := n.GuessTagFromCustomType()
switch realTag {
case "!!int":
@ -261,17 +261,17 @@ func (n *CandidateNode) GetValueRep() (interface{}, error) {
return n.Value, nil
}
func (n *CandidateNode) guessTagFromCustomType() string {
func (n *CandidateNode) GuessTagFromCustomType() string {
if strings.HasPrefix(n.Tag, "!!") {
return n.Tag
} 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
}
dataBucket, errorReading := parseSnippet(n.Value)
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
}
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 (n.Kind != ScalarNode && len(n.Content) == 0) ||
// if the tag has changed (e.g. from str to bool)
(n.guessTagFromCustomType() != other.guessTagFromCustomType()) {
(n.GuessTagFromCustomType() != other.GuessTagFromCustomType()) {
n.Style = other.Style
}

View File

@ -94,7 +94,7 @@ const tsvTestExpectedSimpleCsv = `i like csv
because excel is cool
`
var csvScenarios = []formatScenario{
var csvScenarios = []FormatScenario{
{
description: "Encode CSV simple",
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 {
case "encode-csv":
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))
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))
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))
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))
if s.subdescription != "" {
@ -348,7 +348,7 @@ func documentCSVRoundTripScenario(w *bufio.Writer, s formatScenario, formatType
}
func documentCSVScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario)
s := i.(FormatScenario)
if s.skipDoc {
return
}

View File

@ -7,7 +7,7 @@ import (
"strings"
)
type formatScenario struct {
type FormatScenario struct {
input string
indent int
expression string
@ -19,7 +19,7 @@ type formatScenario struct {
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
writer := bufio.NewWriter(&output)
@ -64,7 +64,7 @@ func processFormatScenario(s formatScenario, decoder Decoder, encoder Encoder) (
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)
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 {
if node.guessTagFromCustomType() != "!!str" {
if node.GuessTagFromCustomType() != "!!str" {
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))))

View File

@ -30,7 +30,7 @@ func (e *shEncoder) PrintLeadingContent(_ io.Writer, _ string) 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)
}

View File

@ -26,7 +26,7 @@ func (e *uriEncoder) PrintLeadingContent(_ io.Writer, _ string) 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)
}
_, err := writer.Write([]byte(url.QueryEscape(node.Value)))

View File

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

View File

@ -25,11 +25,6 @@ var JSONFormat = &Format{"json", []string{"j"},
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"},
func() Encoder { return NewCsvEncoder(ConfiguredCsvPreferences) },
func() Decoder { return NewCSVObjectDecoder(ConfiguredCsvPreferences) },
@ -40,11 +35,6 @@ var TSVFormat = &Format{"tsv", []string{"t"},
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{},
func() Encoder { return NewBase64Encoder() },
func() Decoder { return NewBase64Decoder() },
@ -78,10 +68,8 @@ var LuaFormat = &Format{"lua", []string{"l"},
var Formats = []*Format{
YamlFormat,
JSONFormat,
PropertiesFormat,
CSVFormat,
TSVFormat,
XMLFormat,
Base64Format,
UriFormat,
ShFormat,
@ -90,6 +78,10 @@ var Formats = []*Format{
LuaFormat,
}
func RegisterFormat(f *Format) {
Formats = append(Formats, f)
}
func (f *Format) MatchesName(name string) bool {
if f.FormalName == name {
return true

View File

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

View File

@ -6,7 +6,7 @@ import (
"github.com/mikefarah/yq/v4/test"
)
var goccyYamlFormatScenarios = []formatScenario{
var goccyYamlFormatScenarios = []FormatScenario{
{
description: "basic - 3",
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)
}

View File

@ -79,7 +79,7 @@ const roundTripMultiLineJson = `{
}
`
var jsonScenarios = []formatScenario{
var jsonScenarios = []FormatScenario{
{
description: "array empty",
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))
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))))
}
func documentDecodeNdJsonScenario(w *bufio.Writer, s formatScenario) {
func documentDecodeNdJsonScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" {
@ -385,7 +385,7 @@ func decodeJSON(t *testing.T, jsonString string) *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.Indent = s.indent
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))
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{}) {
s := i.(formatScenario)
s := i.(FormatScenario)
if s.skipDoc {
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))
if s.subdescription != "" {

View File

@ -1,4 +1,4 @@
package yqlib
package exp_parser
import (
"strconv"
@ -7,7 +7,7 @@ import (
"github.com/alecthomas/participle/v2/lexer"
)
var participleYqRules = []*participleYqRule{
var participleYqRules = []*ParticipleYqRule{
{"LINE_COMMENT", `line_?comment|lineComment`, opTokenWithPrefs(getCommentOpType, assignCommentOpType, commentOpPreferences{LineComment: 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},
@ -58,7 +58,7 @@ var participleYqRules = []*participleYqRule{
{"ArrayToMap", "array_?to_?map", expressionOpToken(`(.[] | select(. != null) ) as $i ireduce({}; .[$i | key] = $i)`), 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},
{"YamlDecode", `from_?yaml|@yamld|from_?json|@jsond`, decodeOp(YamlFormat), 0},
@ -67,13 +67,6 @@ var participleYqRules = []*participleYqRule{
{"JSONEncode", `to_?json`, encodeWithIndent(JSONFormat, 2), 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},
{"CSVEncode", `to_?csv|@csv`, encodeWithIndent(CSVFormat, 0), 0},
@ -87,12 +80,8 @@ var participleYqRules = []*participleYqRule{
{"Uri", `@uri`, encodeWithIndent(UriFormat, 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},
{"LoadProperties", `load_?props`, loadOp(NewPropertiesDecoder(), false), 0},
{"LoadString", `load_?str|str_?load`, loadOp(nil, true), 0},
{"LoadYaml", `load`, loadOp(NewYamlDecoder(LoadYamlPreferences), false), 0},
@ -229,7 +218,7 @@ var participleYqRules = []*participleYqRule{
type yqAction func(lexer.Token) (*token, error)
type participleYqRule struct {
type ParticipleYqRule struct {
Name string
Pattern string
CreateYqToken yqAction
@ -240,12 +229,16 @@ type participleLexer struct {
lexerDefinition lexer.StringDefinition
}
func simpleOp(name string, opType *operationType) *participleYqRule {
return &participleYqRule{strings.ToUpper(string(name[1])) + name[1:], name, opToken(opType), 0}
func simpleOp(name string, opType *operationType) *ParticipleYqRule {
return &ParticipleYqRule{strings.ToUpper(string(name[1])) + name[1:], name, opToken(opType), 0}
}
func assignableOp(name string, opType *operationType, assignOpType *operationType) *participleYqRule {
return &participleYqRule{strings.ToUpper(string(name[1])) + name[1:], name, opTokenWithPrefs(opType, assignOpType, nil), 0}
func assignableOp(name string, opType *operationType, assignOpType *operationType) *ParticipleYqRule {
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 {
@ -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 {
if yqRule.ParticipleTokenType == rawToken.Type {
return yqRule
}
}
return &participleYqRule{}
return &ParticipleYqRule{}
}
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.
//dont worry about matching tags of maps or arrays.
lhsTag := lhs.guessTagFromCustomType()
rhsTag := rhs.guessTagFromCustomType()
lhsTag := lhs.GuessTagFromCustomType()
rhsTag := rhs.GuessTagFromCustomType()
if lhsTag != rhsTag {
return false

View File

@ -10,7 +10,7 @@ import (
"github.com/mikefarah/yq/v4/test"
)
var luaScenarios = []formatScenario{
var luaScenarios = []FormatScenario{
{
description: "Basic input example",
input: `return {
@ -250,7 +250,7 @@ numbers:
},
}
func testLuaScenario(t *testing.T, s formatScenario) {
func testLuaScenario(t *testing.T, s FormatScenario) {
switch s.scenarioType {
case "", "decode":
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{}) {
s := i.(formatScenario)
s := i.(FormatScenario)
if s.skipDoc {
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))
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))))
}
func documentLuaEncodeScenario(w *bufio.Writer, s formatScenario) {
func documentLuaEncodeScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
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))))
}
func documentLuaRoundTripScenario(w *bufio.Writer, s formatScenario) {
func documentLuaRoundTripScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
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 {
lhsTag := lhs.Tag
rhsTag := rhs.guessTagFromCustomType()
rhsTag := rhs.GuessTagFromCustomType()
lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType()
lhsTag = lhs.GuessTagFromCustomType()
lhsIsCustom = true
}

View File

@ -6,6 +6,10 @@ type assignPreferences struct {
ClobberCustomTags bool
}
func PerformAssignment(context Context, path []interface{}) {
}
func assignUpdateFunc(prefs assignPreferences) crossFunctionCalculation {
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
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) {
lhsTag := lhs.guessTagFromCustomType()
rhsTag := rhs.guessTagFromCustomType()
lhsTag := lhs.GuessTagFromCustomType()
rhsTag := rhs.GuessTagFromCustomType()
isDateTime := lhs.Tag == "!!timestamp"
// 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() {
candidate := el.Value.(*CandidateNode)
actualTag := candidate.guessTagFromCustomType()
actualTag := candidate.GuessTagFromCustomType()
if actualTag != "!!int" && actualTag != "!!float" {
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 {
lhsTag := lhs.Tag
rhsTag := rhs.guessTagFromCustomType()
rhsTag := rhs.GuessTagFromCustomType()
lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType()
lhsTag = lhs.GuessTagFromCustomType()
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 {
lhsTag := lhs.Tag
rhsTag := rhs.guessTagFromCustomType()
rhsTag := rhs.GuessTagFromCustomType()
lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType()
lhsTag = lhs.GuessTagFromCustomType()
lhsIsCustom = true
}

View File

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

View File

@ -92,12 +92,12 @@ func (a sortableNodeArray) compare(lhs *CandidateNode, rhs *CandidateNode, dateT
if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType()
lhsTag = lhs.GuessTagFromCustomType()
}
if !strings.HasPrefix(rhsTag, "!!") {
// custom tag - we have to have a guess
rhsTag = rhs.guessTagFromCustomType()
rhsTag = rhs.GuessTagFromCustomType()
}
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() {
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)
}
@ -175,7 +175,7 @@ func changeCaseOperator(_ *dataTreeNavigator, context Context, expressionNode *E
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
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)
}
@ -246,7 +246,7 @@ func substituteStringOperator(d *dataTreeNavigator, context Context, expressionN
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
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)
}
@ -425,7 +425,7 @@ func matchOperator(d *dataTreeNavigator, context Context, expressionNode *Expres
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
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)
}
@ -445,7 +445,7 @@ func captureOperator(d *dataTreeNavigator, context Context, expressionNode *Expr
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
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)
}
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() {
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)
}
matches := regEx.FindStringSubmatch(node.Value)
@ -535,7 +535,7 @@ func splitStringOperator(d *dataTreeNavigator, context Context, expressionNode *
continue
}
if node.guessTagFromCustomType() != "!!str" {
if node.GuessTagFromCustomType() != "!!str" {
return Context{}, fmt.Errorf("cannot split %v, can only split strings", node.Tag)
}
kind, tag, content := split(node.Value, splitStr)

View File

@ -77,13 +77,13 @@ func subtractScalars(context Context, target *CandidateNode, lhs *CandidateNode,
lhsIsCustom := false
if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess
lhsTag = lhs.guessTagFromCustomType()
lhsTag = lhs.GuessTagFromCustomType()
lhsIsCustom = true
}
if !strings.HasPrefix(rhsTag, "!!") {
// custom tag - we have to have a guess
rhsTag = rhs.guessTagFromCustomType()
rhsTag = rhs.GuessTagFromCustomType()
}
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 =
`
var propertyScenarios = []formatScenario{
var propertyScenarios = []FormatScenario{
{
description: "Encode properties",
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))
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))))
}
func documentWrappedEncodePropertyScenario(w *bufio.Writer, s formatScenario) {
func documentWrappedEncodePropertyScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
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))))
}
func documentDecodePropertyScenario(w *bufio.Writer, s formatScenario) {
func documentDecodePropertyScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
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))))
}
func documentRoundTripPropertyScenario(w *bufio.Writer, s formatScenario) {
func documentRoundTripPropertyScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" {
@ -408,7 +408,7 @@ func documentRoundTripPropertyScenario(w *bufio.Writer, s formatScenario) {
}
func documentPropertyScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario)
s := i.(FormatScenario)
if s.skipDoc {
return
}

View File

@ -8,7 +8,7 @@ import (
"github.com/mikefarah/yq/v4/test"
)
var shellVariablesScenarios = []formatScenario{
var shellVariablesScenarios = []FormatScenario{
{
description: "Encode shell variables",
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{}) {
s := i.(formatScenario)
s := i.(FormatScenario)
if s.skipDoc {
return
}

View File

@ -79,7 +79,7 @@ var expectedSampleWithHeader = `servers:
ip: 10.0.0.1
`
var tomlScenarios = []formatScenario{
var tomlScenarios = []FormatScenario{
{
skipDoc: true,
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 {
case "", "decode":
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))
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))))
}
func documentTomlRoundtripScenario(w *bufio.Writer, s formatScenario) {
func documentTomlRoundtripScenario(w *bufio.Writer, s FormatScenario) {
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
if s.subdescription != "" {
@ -301,7 +301,7 @@ func documentTomlRoundtripScenario(w *bufio.Writer, s formatScenario) {
}
func documentTomlScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
s := i.(formatScenario)
s := i.(FormatScenario)
if s.skipDoc {
return

View File

@ -6,7 +6,7 @@ import (
"github.com/mikefarah/yq/v4/test"
)
var yamlFormatScenarios = []formatScenario{
var yamlFormatScenarios = []FormatScenario{
{
description: "scalar with doc separator",
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)
}

View File

@ -1,5 +1,5 @@
4.43.1: (not released yet)
- Added tostring #72
- Added to_string #72
- Added string interpolation #1149
- Can specify parent(n) levels #1970
- Fixed CSV line break issue #1974
@ -151,7 +151,7 @@
- XML Decoder: Fixed issue where content surrounding tags are lost #1447
- XML Decoder: Fixed xml decode bug when there is content after a comment
- 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!
4.30.4:

3
yq.go
View File

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