diff --git a/pkg/yqlib/candidate_node.go b/pkg/yqlib/candidate_node.go index a3c3dda3..ce321f12 100644 --- a/pkg/yqlib/candidate_node.go +++ b/pkg/yqlib/candidate_node.go @@ -174,6 +174,7 @@ func (n *CandidateNode) AsList() *list.List { func (n *CandidateNode) GetValueRep() (interface{}, error) { // TODO: handle booleans, ints, etc + log.Debugf("GetValueRep for %v value: %v", n.GetNicePath(), n.Value) realTag := n.guessTagFromCustomType() switch realTag { diff --git a/pkg/yqlib/candidate_node_yaml.go b/pkg/yqlib/candidate_node_yaml.go index 5237ad8f..b022ac09 100644 --- a/pkg/yqlib/candidate_node_yaml.go +++ b/pkg/yqlib/candidate_node_yaml.go @@ -78,59 +78,76 @@ func (o *CandidateNode) copyToYamlNode(node *yaml.Node) { node.Column = o.Column } +func (o *CandidateNode) decodeIntoChild(childNode *yaml.Node) (*CandidateNode, error) { + newChild := o.CreateChild() + + // null yaml.Nodes to not end up calling UnmarshalYAML + // so we call it explicitly + if childNode.Tag == "!!null" { + newChild.Kind = ScalarNode + newChild.copyFromYamlNode(childNode) + return newChild, nil + } + + err := childNode.Decode(newChild) + return newChild, err +} + func (o *CandidateNode) UnmarshalYAML(node *yaml.Node) error { - log.Debugf("unmarshalling %v", node.Tag) + log.Debugf("UnmarshalYAML %v", node.Tag) switch node.Kind { case yaml.DocumentNode: + log.Debugf("UnmarshalYAML - a document") o.Kind = DocumentNode o.copyFromYamlNode(node) if len(node.Content) == 0 { return nil } - singleChild := &CandidateNode{ - Parent: o, - } - err := node.Content[0].Decode(singleChild) + singleChild, err := o.decodeIntoChild(node.Content[0]) + if err != nil { return err } o.Content = []*CandidateNode{singleChild} return nil case yaml.AliasNode: - log.Debug("decoding alias from yaml: %v", o.Tag) + log.Debug("UnmarshalYAML - alias from yaml: %v", o.Tag) o.Kind = AliasNode o.copyFromYamlNode(node) return nil case yaml.ScalarNode: - log.Debugf("its a scalar") + log.Debugf("UnmarshalYAML - a scalar") o.Kind = ScalarNode o.copyFromYamlNode(node) return nil case yaml.MappingNode: + log.Debugf("UnmarshalYAML - a mapping node") o.Kind = MappingNode o.copyFromYamlNode(node) o.Content = make([]*CandidateNode, len(node.Content)) for i := 0; i < len(node.Content); i += 2 { - keyNode := o.CreateChild() - keyNode.IsMapKey = true - err := node.Content[i].Decode(keyNode) + + keyNode, err := o.decodeIntoChild(node.Content[i]) if err != nil { return err } - valueNode := o.CreateChild() - valueNode.Key = keyNode - err = node.Content[i+1].Decode(valueNode) + keyNode.IsMapKey = true + + valueNode, err := o.decodeIntoChild(node.Content[i+1]) if err != nil { return err } + valueNode.Key = keyNode + o.Content[i] = keyNode o.Content[i+1] = valueNode } return nil case yaml.SequenceNode: + log.Debugf("UnmarshalYAML - a sequence: %v", len(node.Content)) o.Kind = SequenceNode o.copyFromYamlNode(node) o.Content = make([]*CandidateNode, len(node.Content)) @@ -141,16 +158,17 @@ func (o *CandidateNode) UnmarshalYAML(node *yaml.Node) error { keyNode.Kind = ScalarNode keyNode.Value = fmt.Sprintf("%v", i) - valueNode := o.CreateChild() - valueNode.Key = keyNode - err := node.Content[i].Decode(valueNode) + valueNode, err := o.decodeIntoChild(node.Content[i]) if err != nil { return err } + + valueNode.Key = keyNode o.Content[i] = valueNode } return nil case 0: + log.Debugf("UnmarshalYAML - errr.. %v", node.Tag) // not sure when this happens o.copyFromYamlNode(node) return nil diff --git a/pkg/yqlib/candidiate_node_json.go b/pkg/yqlib/candidiate_node_json.go index c8c310b5..d9583ddb 100644 --- a/pkg/yqlib/candidiate_node_json.go +++ b/pkg/yqlib/candidiate_node_json.go @@ -7,7 +7,7 @@ import ( ) func (o *CandidateNode) MarshalJSON() ([]byte, error) { - log.Debugf("going to encode %v - %v", o.GetNicePath(), o.Tag) + log.Debugf("MarshalJSON %v", NodeToString(o)) buf := new(bytes.Buffer) enc := json.NewEncoder(buf) enc.SetIndent("", " ") @@ -15,12 +15,15 @@ func (o *CandidateNode) MarshalJSON() ([]byte, error) { switch o.Kind { case DocumentNode: + log.Debugf("MarshalJSON DocumentNode") err := enc.Encode(o.Content[0]) return buf.Bytes(), err case AliasNode: + log.Debugf("MarshalJSON AliasNode") err := enc.Encode(o.Alias) return buf.Bytes(), err case ScalarNode: + log.Debugf("MarshalJSON ScalarNode") value, err := o.GetValueRep() if err != nil { return buf.Bytes(), err @@ -28,14 +31,13 @@ func (o *CandidateNode) MarshalJSON() ([]byte, error) { err = enc.Encode(value) return buf.Bytes(), err case MappingNode: + log.Debugf("MarshalJSON MappingNode") buf.WriteByte('{') for i := 0; i < len(o.Content); i += 2 { - log.Debugf("writing key %v", NodeToString(o.Content[i])) if err := enc.Encode(o.Content[i].Value); err != nil { return nil, err } buf.WriteByte(':') - log.Debugf("writing value %v", NodeToString(o.Content[i+1])) if err := enc.Encode(o.Content[i+1]); err != nil { return nil, err } @@ -45,8 +47,10 @@ func (o *CandidateNode) MarshalJSON() ([]byte, error) { } buf.WriteByte('}') case SequenceNode: + log.Debugf("MarshalJSON SequenceNode") err := enc.Encode(o.Content) return buf.Bytes(), err } + log.Debug("none of those things?") return buf.Bytes(), nil } diff --git a/pkg/yqlib/data_tree_navigator.go b/pkg/yqlib/data_tree_navigator.go index 9b1fc43a..9a246027 100644 --- a/pkg/yqlib/data_tree_navigator.go +++ b/pkg/yqlib/data_tree_navigator.go @@ -49,7 +49,6 @@ func (d *dataTreeNavigator) GetMatchingNodes(context Context, expressionNode *Ex log.Debug(NodeToString(el.Value.(*CandidateNode))) } } - log.Debug("carr on>>") handler := expressionNode.Operation.OperationType.Handler if handler != nil { return handler(d, context, expressionNode) diff --git a/pkg/yqlib/decoder_yaml.go b/pkg/yqlib/decoder_yaml.go index 3b9a1802..a7ba89ab 100644 --- a/pkg/yqlib/decoder_yaml.go +++ b/pkg/yqlib/decoder_yaml.go @@ -99,7 +99,7 @@ func (dec *yamlDecoder) Init(reader io.Reader) error { func (dec *yamlDecoder) Decode() (*CandidateNode, error) { var candidateNode CandidateNode err := dec.decoder.Decode(&candidateNode) - log.Debugf("decoded the yaml") + if errors.Is(err, io.EOF) && dec.leadingContent != "" && !dec.readAnything { // force returning an empty node with a comment. dec.readAnything = true diff --git a/pkg/yqlib/encoder_test.go b/pkg/yqlib/encoder_test.go index 31d5730c..b55b8a9e 100644 --- a/pkg/yqlib/encoder_test.go +++ b/pkg/yqlib/encoder_test.go @@ -21,6 +21,9 @@ func yamlToJSON(sampleYaml string, indent int) string { panic(err) } node := inputs.Front().Value.(*CandidateNode) + log.Debugf("%v", NodeToString(node)) + log.Debugf("Content[0] %v", NodeToString(node.Content[0])) + err = jsonEncoder.Encode(writer, node) if err != nil { panic(err) diff --git a/pkg/yqlib/yaml_test.go b/pkg/yqlib/yaml_test.go new file mode 100644 index 00000000..b056e4a1 --- /dev/null +++ b/pkg/yqlib/yaml_test.go @@ -0,0 +1,60 @@ +package yqlib + +import ( + "testing" + + "github.com/mikefarah/yq/v4/test" +) + +var yamlScenarios = []formatScenario{ + // { + // description: "basic - null", + // skipDoc: true, + // input: "null", + // expected: "null\n", + // }, + { + description: "basic - ~", + skipDoc: true, + input: "~", + expected: "~\n", + }, + // { + // description: "basic - [null]", + // skipDoc: true, + // input: "[null]", + // expected: "[null]\n", + // }, + // { + // description: "basic - [~]", + // skipDoc: true, + // input: "[~]", + // expected: "[~]\n", + // }, + // { + // description: "basic - null map value", + // skipDoc: true, + // input: "a: null", + // expected: "a: null\n", + // }, +} + +func testYamlScenario(t *testing.T, s formatScenario) { + // switch s.scenarioType { + // case "decode": + test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewYamlEncoder(2, false, ConfiguredYamlPreferences)), s.description) + // default: + // panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType)) + // } +} + +func TestYamlScenarios(t *testing.T) { + for _, tt := range yamlScenarios { + testYamlScenario(t, tt) + } + // genericScenarios := make([]interface{}, len(yamlScenarios)) + // for i, s := range yamlScenarios { + // genericScenarios[i] = s + // } + // documentScenarios(t, "usage", "convert", genericScenarios, documentJSONScenario) +}