Skip to content

Commit cf8cfbd

Browse files
authored
Allow build without json and xml support (#1556)
* Refactor ordered_map into separate files Separate json and xml, from the regular yaml. Makes it possible to compile, without those... * Refactor encoder and decoder creation Use more consistent parameters vs globals Return errors instead of calling panic() * Allow build without json and xml support
1 parent 62d167c commit cf8cfbd

22 files changed

+338
-213
lines changed

cmd/evaluate_all_command.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ func evaluateAll(cmd *cobra.Command, args []string) (cmdError error) {
8484
if err != nil {
8585
return err
8686
}
87-
encoder := configureEncoder(format)
87+
encoder, err := configureEncoder()
88+
if err != nil {
89+
return err
90+
}
8891

8992
printer := yqlib.NewPrinter(encoder, printerWriter)
9093

cmd/evalute_sequence_command.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,10 @@ func evaluateSequence(cmd *cobra.Command, args []string) (cmdError error) {
9393
if err != nil {
9494
return err
9595
}
96-
encoder := configureEncoder(format)
96+
encoder, err := configureEncoder()
97+
if err != nil {
98+
return err
99+
}
97100

98101
printer := yqlib.NewPrinter(encoder, printerWriter)
99102

cmd/utils.go

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,15 @@ func configureDecoder(evaluateTogether bool) (yqlib.Decoder, error) {
6161
if err != nil {
6262
return nil, err
6363
}
64-
switch yqlibInputFormat {
64+
yqlibDecoder, err := createDecoder(yqlibInputFormat, evaluateTogether)
65+
if yqlibDecoder == nil {
66+
return nil, fmt.Errorf("no support for %s input format", inputFormat)
67+
}
68+
return yqlibDecoder, err
69+
}
70+
71+
func createDecoder(format yqlib.InputFormat, evaluateTogether bool) (yqlib.Decoder, error) {
72+
switch format {
6573
case yqlib.XMLInputFormat:
6674
return yqlib.NewXMLDecoder(yqlib.ConfiguredXMLPreferences), nil
6775
case yqlib.PropertiesInputFormat:
@@ -72,10 +80,12 @@ func configureDecoder(evaluateTogether bool) (yqlib.Decoder, error) {
7280
return yqlib.NewCSVObjectDecoder(','), nil
7381
case yqlib.TSVObjectInputFormat:
7482
return yqlib.NewCSVObjectDecoder('\t'), nil
83+
case yqlib.YamlInputFormat:
84+
prefs := yqlib.ConfiguredYamlPreferences
85+
prefs.EvaluateTogether = evaluateTogether
86+
return yqlib.NewYamlDecoder(prefs), nil
7587
}
76-
prefs := yqlib.ConfiguredYamlPreferences
77-
prefs.EvaluateTogether = evaluateTogether
78-
return yqlib.NewYamlDecoder(prefs), nil
88+
return nil, fmt.Errorf("invalid decoder: %v", format)
7989
}
8090

8191
func configurePrinterWriter(format yqlib.PrinterOutputFormat, out io.Writer) (yqlib.PrinterWriter, error) {
@@ -95,22 +105,34 @@ func configurePrinterWriter(format yqlib.PrinterOutputFormat, out io.Writer) (yq
95105
return printerWriter, nil
96106
}
97107

98-
func configureEncoder(format yqlib.PrinterOutputFormat) yqlib.Encoder {
108+
func configureEncoder() (yqlib.Encoder, error) {
109+
yqlibOutputFormat, err := yqlib.OutputFormatFromString(outputFormat)
110+
if err != nil {
111+
return nil, err
112+
}
113+
yqlibEncoder, err := createEncoder(yqlibOutputFormat)
114+
if yqlibEncoder == nil {
115+
return nil, fmt.Errorf("no support for %s output format", outputFormat)
116+
}
117+
return yqlibEncoder, err
118+
}
119+
120+
func createEncoder(format yqlib.PrinterOutputFormat) (yqlib.Encoder, error) {
99121
switch format {
100122
case yqlib.JSONOutputFormat:
101-
return yqlib.NewJSONEncoder(indent, colorsEnabled, unwrapScalar)
123+
return yqlib.NewJSONEncoder(indent, colorsEnabled, unwrapScalar), nil
102124
case yqlib.PropsOutputFormat:
103-
return yqlib.NewPropertiesEncoder(unwrapScalar)
125+
return yqlib.NewPropertiesEncoder(unwrapScalar), nil
104126
case yqlib.CSVOutputFormat:
105-
return yqlib.NewCsvEncoder(',')
127+
return yqlib.NewCsvEncoder(','), nil
106128
case yqlib.TSVOutputFormat:
107-
return yqlib.NewCsvEncoder('\t')
129+
return yqlib.NewCsvEncoder('\t'), nil
108130
case yqlib.YamlOutputFormat:
109-
return yqlib.NewYamlEncoder(indent, colorsEnabled, yqlib.ConfiguredYamlPreferences)
131+
return yqlib.NewYamlEncoder(indent, colorsEnabled, yqlib.ConfiguredYamlPreferences), nil
110132
case yqlib.XMLOutputFormat:
111-
return yqlib.NewXMLEncoder(indent, yqlib.ConfiguredXMLPreferences)
133+
return yqlib.NewXMLEncoder(indent, yqlib.ConfiguredXMLPreferences), nil
112134
}
113-
panic("invalid encoder")
135+
return nil, fmt.Errorf("invalid encoder: %v", format)
114136
}
115137

116138
// this is a hack to enable backwards compatibility with githubactions (which pipe /dev/null into everything)

pkg/yqlib/decoder_json.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !yq_nojson
2+
13
package yqlib
24

35
import (

pkg/yqlib/decoder_xml.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !yq_noxml
2+
13
package yqlib
24

35
import (

pkg/yqlib/encoder.go

Lines changed: 7 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package yqlib
22

33
import (
4-
"bytes"
5-
"encoding/json"
6-
"errors"
7-
"fmt"
84
"io"
95

106
yaml "gopkg.in/yaml.v3"
@@ -17,162 +13,17 @@ type Encoder interface {
1713
CanHandleAliases() bool
1814
}
1915

20-
// orderedMap allows to marshal and unmarshal JSON and YAML values keeping the
21-
// order of keys and values in a map or an object.
22-
type orderedMap struct {
23-
// if this is an object, kv != nil. If this is not an object, kv == nil.
24-
kv []orderedMapKV
25-
altVal interface{}
26-
}
27-
28-
type orderedMapKV struct {
29-
K string
30-
V orderedMap
31-
}
32-
33-
func (o *orderedMap) UnmarshalJSON(data []byte) error {
34-
switch data[0] {
35-
case '{':
36-
// initialise so that even if the object is empty it is not nil
37-
o.kv = []orderedMapKV{}
38-
39-
// create decoder
40-
dec := json.NewDecoder(bytes.NewReader(data))
41-
_, err := dec.Token() // open object
42-
if err != nil {
43-
return err
44-
}
45-
46-
// cycle through k/v
47-
var tok json.Token
48-
for tok, err = dec.Token(); err == nil; tok, err = dec.Token() {
49-
// we can expect two types: string or Delim. Delim automatically means
50-
// that it is the closing bracket of the object, whereas string means
51-
// that there is another key.
52-
if _, ok := tok.(json.Delim); ok {
53-
break
54-
}
55-
kv := orderedMapKV{
56-
K: tok.(string),
57-
}
58-
if err := dec.Decode(&kv.V); err != nil {
59-
return err
60-
}
61-
o.kv = append(o.kv, kv)
62-
}
63-
// unexpected error
64-
if err != nil && !errors.Is(err, io.EOF) {
65-
return err
66-
}
67-
return nil
68-
case '[':
69-
var res []*orderedMap
70-
if err := json.Unmarshal(data, &res); err != nil {
71-
return err
72-
}
73-
o.altVal = res
74-
o.kv = nil
75-
return nil
76-
}
77-
78-
return json.Unmarshal(data, &o.altVal)
79-
}
16+
func mapKeysToStrings(node *yaml.Node) {
8017

81-
func (o orderedMap) MarshalJSON() ([]byte, error) {
82-
buf := new(bytes.Buffer)
83-
enc := json.NewEncoder(buf)
84-
enc.SetEscapeHTML(false) // do not escape html chars e.g. &, <, >
85-
if o.kv == nil {
86-
if err := enc.Encode(o.altVal); err != nil {
87-
return nil, err
88-
}
89-
return buf.Bytes(), nil
90-
}
91-
buf.WriteByte('{')
92-
for idx, el := range o.kv {
93-
if err := enc.Encode(el.K); err != nil {
94-
return nil, err
95-
}
96-
buf.WriteByte(':')
97-
if err := enc.Encode(el.V); err != nil {
98-
return nil, err
99-
}
100-
if idx != len(o.kv)-1 {
101-
buf.WriteByte(',')
102-
}
103-
}
104-
buf.WriteByte('}')
105-
return buf.Bytes(), nil
106-
}
107-
108-
func (o *orderedMap) UnmarshalYAML(node *yaml.Node) error {
109-
switch node.Kind {
110-
case yaml.DocumentNode:
111-
if len(node.Content) == 0 {
112-
return nil
113-
}
114-
return o.UnmarshalYAML(node.Content[0])
115-
case yaml.AliasNode:
116-
return o.UnmarshalYAML(node.Alias)
117-
case yaml.ScalarNode:
118-
return node.Decode(&o.altVal)
119-
case yaml.MappingNode:
120-
// set kv to non-nil
121-
o.kv = []orderedMapKV{}
122-
for i := 0; i < len(node.Content); i += 2 {
123-
var key string
124-
var val orderedMap
125-
if err := node.Content[i].Decode(&key); err != nil {
126-
return err
18+
if node.Kind == yaml.MappingNode {
19+
for index, child := range node.Content {
20+
if index%2 == 0 { // its a map key
21+
child.Tag = "!!str"
12722
}
128-
if err := node.Content[i+1].Decode(&val); err != nil {
129-
return err
130-
}
131-
o.kv = append(o.kv, orderedMapKV{
132-
K: key,
133-
V: val,
134-
})
135-
}
136-
return nil
137-
case yaml.SequenceNode:
138-
// note that this has to be a pointer, so that nulls can be represented.
139-
var res []*orderedMap
140-
if err := node.Decode(&res); err != nil {
141-
return err
14223
}
143-
o.altVal = res
144-
o.kv = nil
145-
return nil
146-
case 0:
147-
// null
148-
o.kv = nil
149-
o.altVal = nil
150-
return nil
151-
default:
152-
return fmt.Errorf("orderedMap: invalid yaml node")
15324
}
154-
}
15525

156-
func (o *orderedMap) MarshalYAML() (interface{}, error) {
157-
// fast path: kv is nil, use altVal
158-
if o.kv == nil {
159-
return o.altVal, nil
160-
}
161-
content := make([]*yaml.Node, 0, len(o.kv)*2)
162-
for _, val := range o.kv {
163-
n := new(yaml.Node)
164-
if err := n.Encode(val.V); err != nil {
165-
return nil, err
166-
}
167-
content = append(content, &yaml.Node{
168-
Kind: yaml.ScalarNode,
169-
Tag: "!!str",
170-
Value: val.K,
171-
}, n)
26+
for _, child := range node.Content {
27+
mapKeysToStrings(child)
17228
}
173-
return &yaml.Node{
174-
Kind: yaml.MappingNode,
175-
Tag: "!!map",
176-
Content: content,
177-
}, nil
17829
}

pkg/yqlib/encoder_json.go

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !yq_nojson
2+
13
package yqlib
24

35
import (
@@ -14,21 +16,6 @@ type jsonEncoder struct {
1416
UnwrapScalar bool
1517
}
1618

17-
func mapKeysToStrings(node *yaml.Node) {
18-
19-
if node.Kind == yaml.MappingNode {
20-
for index, child := range node.Content {
21-
if index%2 == 0 { // its a map key
22-
child.Tag = "!!str"
23-
}
24-
}
25-
}
26-
27-
for _, child := range node.Content {
28-
mapKeysToStrings(child)
29-
}
30-
}
31-
3219
func NewJSONEncoder(indent int, colorise bool, unwrapScalar bool) Encoder {
3320
var indentString = ""
3421

pkg/yqlib/encoder_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !yq_nojson
2+
13
package yqlib
24

35
import (

pkg/yqlib/encoder_xml.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !yq_noxml
2+
13
package yqlib
24

35
import (

pkg/yqlib/json_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !yq_nojson
2+
13
package yqlib
24

35
import (

0 commit comments

Comments
 (0)