@@ -60,7 +60,12 @@ import (
60
60
// is halfway between YAML and JSON, but is a strict subset of YAML, so it
61
61
// should should be readable by any YAML parser. It is designed to be explicit
62
62
// and unambiguous, and eschews significant whitespace.
63
- type Encoder struct {}
63
+ type Encoder struct {
64
+ // Compact tells the encoder to use compact formatting. This puts all the
65
+ // data on one line, with no extra newlines, no comments, and no multi-line
66
+ // formatting.
67
+ Compact bool
68
+ }
64
69
65
70
// FromYAML renders a KYAML (multi-)document from YAML bytes (JSON is YAML),
66
71
// including the KYAML header. The result always has a trailing newline.
@@ -88,7 +93,7 @@ func (ky *Encoder) FromYAML(in io.Reader, out io.Writer) error {
88
93
return err
89
94
}
90
95
91
- if err := ky .renderDocument (& doc , 0 , flagsNone , out ); err != nil {
96
+ if err := ky .renderDocument (& doc , 0 , ky . flags () , out ); err != nil {
92
97
return err
93
98
}
94
99
}
@@ -138,7 +143,7 @@ func (ky *Encoder) fromObjectYAML(in io.Reader, out io.Writer) error {
138
143
return fmt .Errorf ("kyaml internal error: line %d: expected document node, got %s" , doc .Line , ky .nodeKindString (doc .Kind ))
139
144
}
140
145
141
- if err := ky .renderNode (& doc , 0 , flagsNone , out ); err != nil {
146
+ if err := ky .renderNode (& doc , 0 , ky . flags () , out ); err != nil {
142
147
return fmt .Errorf ("error rendering document: %v" , err )
143
148
}
144
149
@@ -167,10 +172,46 @@ const (
167
172
flagCompact flagMask = 0x02
168
173
)
169
174
175
+ // flags returns a flagMask representing the current encoding options. It can
176
+ // be used directly or OR'ed with another mask.
177
+ func (ky * Encoder ) flags () flagMask {
178
+ flags := flagsNone
179
+ if ky .Compact {
180
+ flags |= flagCompact
181
+ }
182
+ return flags
183
+ }
184
+
185
+ // renderNode processes a YAML node, calling the appropriate render function
186
+ // for its type. Each render function should assume that the output "cursor"
187
+ // is positioned at the start of the node and should not emit a final newline.
188
+ // If a render function needs to linewrap or indent (e.g. a struct), it should
189
+ // assume the indent level is currently correct for the node type itself, and
190
+ // may need to indent more.
191
+ func (ky * Encoder ) renderNode (node * yaml.Node , indent int , flags flagMask , out io.Writer ) error {
192
+ if node == nil {
193
+ return nil
194
+ }
195
+
196
+ switch node .Kind {
197
+ case yaml .DocumentNode :
198
+ return ky .renderDocument (node , indent , flags , out )
199
+ case yaml .ScalarNode :
200
+ return ky .renderScalar (node , indent , flags , out )
201
+ case yaml .SequenceNode :
202
+ return ky .renderSequence (node , indent , flags , out )
203
+ case yaml .MappingNode :
204
+ return ky .renderMapping (node , indent , flags , out )
205
+ case yaml .AliasNode :
206
+ return ky .renderAlias (node , indent , flags , out )
207
+ }
208
+ return nil
209
+ }
210
+
170
211
// renderDocument processes a YAML document node, rendering it to the output.
171
212
// This function assumes that the output "cursor" is positioned at the start of
172
213
// the document and should always emit a final newline.
173
- func (ky * Encoder ) renderDocument (doc * yaml.Node , indent int , _ flagMask , out io.Writer ) error {
214
+ func (ky * Encoder ) renderDocument (doc * yaml.Node , indent int , flags flagMask , out io.Writer ) error {
174
215
if len (doc .Content ) == 0 {
175
216
return fmt .Errorf ("kyaml internal error: line %d: document has no content node (%d)" , doc .Line , len (doc .Content ))
176
217
}
@@ -181,34 +222,40 @@ func (ky *Encoder) renderDocument(doc *yaml.Node, indent int, _ flagMask, out io
181
222
return fmt .Errorf ("kyaml internal error: line %d: document non-zero indent (%d)" , doc .Line , indent )
182
223
}
183
224
225
+ compact := flags & flagCompact != 0
226
+
184
227
// For document nodes, the cursor is assumed to be ready to render.
185
- if len (doc .HeadComment ) > 0 {
186
- ky .renderComments (doc .HeadComment , indent , out )
187
- fmt .Fprint (out , "\n " )
188
- }
189
228
child := doc .Content [0 ]
190
- if len (child .HeadComment ) > 0 {
191
- ky .renderComments (child .HeadComment , indent , out )
192
- fmt .Fprint (out , "\n " )
229
+ if ! compact {
230
+ if len (doc .HeadComment ) > 0 {
231
+ ky .renderComments (doc .HeadComment , indent , out )
232
+ fmt .Fprint (out , "\n " )
233
+ }
234
+ if len (child .HeadComment ) > 0 {
235
+ ky .renderComments (child .HeadComment , indent , out )
236
+ fmt .Fprint (out , "\n " )
237
+ }
193
238
}
194
- if err := ky .renderNode (child , indent , flagsNone , out ); err != nil {
239
+ if err := ky .renderNode (child , indent , flags , out ); err != nil {
195
240
return err
196
241
}
197
- if len (child .LineComment ) > 0 {
198
- ky .renderComments (" " + child .LineComment , 0 , out )
199
- }
200
- fmt .Fprint (out , "\n " )
201
- if len (child .FootComment ) > 0 {
202
- ky .renderComments (child .FootComment , indent , out )
203
- fmt .Fprint (out , "\n " )
204
- }
205
- if len (doc .LineComment ) > 0 {
206
- ky .renderComments (" " + doc .LineComment , 0 , out )
207
- fmt .Fprint (out , "\n " )
208
- }
209
- if len (doc .FootComment ) > 0 {
210
- ky .renderComments (doc .FootComment , indent , out )
242
+ if ! compact {
243
+ if len (child .LineComment ) > 0 {
244
+ ky .renderComments (" " + child .LineComment , 0 , out )
245
+ }
211
246
fmt .Fprint (out , "\n " )
247
+ if len (child .FootComment ) > 0 {
248
+ ky .renderComments (child .FootComment , indent , out )
249
+ fmt .Fprint (out , "\n " )
250
+ }
251
+ if len (doc .LineComment ) > 0 {
252
+ ky .renderComments (" " + doc .LineComment , 0 , out )
253
+ fmt .Fprint (out , "\n " )
254
+ }
255
+ if len (doc .FootComment ) > 0 {
256
+ ky .renderComments (doc .FootComment , indent , out )
257
+ fmt .Fprint (out , "\n " )
258
+ }
212
259
}
213
260
return nil
214
261
}
@@ -220,10 +267,8 @@ func (ky *Encoder) renderScalar(node *yaml.Node, indent int, flags flagMask, out
220
267
switch node .Tag {
221
268
case intTag , floatTag , boolTag , nullTag :
222
269
fmt .Fprint (out , node .Value )
223
- case strTag :
270
+ case strTag , timestampTag :
224
271
return ky .renderString (node .Value , indent + 1 , flags , out )
225
- case timestampTag :
226
- return ky .renderString (node .Value , indent + 1 , flagsNone , out )
227
272
default :
228
273
return fmt .Errorf ("kyaml internal error: line %d: unknown tag %q on scalar node %q" , node .Line , node .Tag , node .Value )
229
274
}
@@ -521,11 +566,14 @@ func (ky *Encoder) appendEscapedRune(r rune, indent int, newline string, buf *by
521
566
// renderSequence processes a YAML sequence node, rendering it to the output. This
522
567
// DOES NOT render a trailing newline or head/line/foot comments of the sequence
523
568
// itself, but DOES render comments of the child nodes.
524
- func (ky * Encoder ) renderSequence (node * yaml.Node , indent int , _ flagMask , out io.Writer ) error {
569
+ func (ky * Encoder ) renderSequence (node * yaml.Node , indent int , flags flagMask , out io.Writer ) error {
525
570
if len (node .Content ) == 0 {
526
571
fmt .Fprint (out , "[]" )
527
572
return nil
528
573
}
574
+ if flags & flagCompact != 0 {
575
+ return ky .renderCompactSequence (node , flags , out )
576
+ }
529
577
530
578
// See if this list can use cuddled formatting.
531
579
cuddle := true
@@ -541,31 +589,45 @@ func (ky *Encoder) renderSequence(node *yaml.Node, indent int, _ flagMask, out i
541
589
}
542
590
543
591
if cuddle {
544
- return ky .renderCuddledSequence (node , indent , out )
592
+ return ky .renderCuddledSequence (node , indent , flags , out )
593
+ }
594
+ return ky .renderUncuddledSequence (node , indent , flags , out )
595
+ }
596
+
597
+ // renderCompactSequence renders a YAML sequence node in compact form.
598
+ func (ky * Encoder ) renderCompactSequence (node * yaml.Node , flags flagMask , out io.Writer ) error {
599
+ fmt .Fprint (out , "[" )
600
+ for i , child := range node .Content {
601
+ if i > 0 {
602
+ fmt .Fprint (out , ", " )
603
+ }
604
+ if err := ky .renderNode (child , 0 , flags , out ); err != nil {
605
+ return err
606
+ }
545
607
}
546
- return ky .renderUncuddledSequence (node , indent , out )
608
+ fmt .Fprint (out , "]" )
609
+ return nil
547
610
}
548
611
549
612
// renderCuddledSequence processes a YAML sequence node which has already been
550
613
// determined to be cuddled. We only cuddle sequences of structs or lists
551
614
// which have no comments.
552
- func (ky * Encoder ) renderCuddledSequence (node * yaml.Node , indent int , out io.Writer ) error {
615
+ func (ky * Encoder ) renderCuddledSequence (node * yaml.Node , indent int , flags flagMask , out io.Writer ) error {
553
616
fmt .Fprint (out , "[" )
554
617
for i , child := range node .Content {
555
618
// Each iteration should leave us cuddled for the next item.
556
619
if i > 0 {
557
620
fmt .Fprint (out , ", " )
558
621
}
559
- if err := ky .renderNode (child , indent , flagsNone , out ); err != nil {
622
+ if err := ky .renderNode (child , indent , flags , out ); err != nil {
560
623
return err
561
624
}
562
625
}
563
626
fmt .Fprint (out , "]" )
564
-
565
627
return nil
566
628
}
567
629
568
- func (ky * Encoder ) renderUncuddledSequence (node * yaml.Node , indent int , out io.Writer ) error {
630
+ func (ky * Encoder ) renderUncuddledSequence (node * yaml.Node , indent int , flags flagMask , out io.Writer ) error {
569
631
// Get into the right state for the first item.
570
632
fmt .Fprint (out , "[\n " )
571
633
ky .writeIndent (indent , out )
@@ -580,7 +642,7 @@ func (ky *Encoder) renderUncuddledSequence(node *yaml.Node, indent int, out io.W
580
642
ky .writeIndent (indent + 1 , out )
581
643
}
582
644
583
- if err := ky .renderNode (child , indent + 1 , flagsNone , out ); err != nil {
645
+ if err := ky .renderNode (child , indent + 1 , flags , out ); err != nil {
584
646
return err
585
647
}
586
648
@@ -635,12 +697,16 @@ func isCuddledKind(node *yaml.Node) bool {
635
697
// renderMapping processes a YAML mapping node, rendering it to the output. This
636
698
// DOES NOT render a trailing newline or head/line/foot comments of the mapping
637
699
// itself, but DOES render comments of the child nodes.
638
- func (ky * Encoder ) renderMapping (node * yaml.Node , indent int , _ flagMask , out io.Writer ) error {
700
+ func (ky * Encoder ) renderMapping (node * yaml.Node , indent int , flags flagMask , out io.Writer ) error {
639
701
if len (node .Content ) == 0 {
640
702
fmt .Fprint (out , "{}" )
641
703
return nil
642
704
}
643
705
706
+ if flags & flagCompact != 0 {
707
+ return ky .renderCompactMapping (node , flags , out )
708
+ }
709
+
644
710
joinComments := func (a , b string ) string {
645
711
if len (a ) > 0 && len (b ) > 0 {
646
712
return a + "\n " + b
@@ -668,7 +734,7 @@ func (ky *Encoder) renderMapping(node *yaml.Node, indent int, _ flagMask, out io
668
734
return err
669
735
}
670
736
fmt .Fprint (out , ": " )
671
- if err := ky .renderNode (val , indent + 1 , flagsNone , out ); err != nil {
737
+ if err := ky .renderNode (val , indent + 1 , flags , out ); err != nil {
672
738
return err
673
739
}
674
740
fmt .Fprint (out , "," )
@@ -693,6 +759,30 @@ func (ky *Encoder) renderMapping(node *yaml.Node, indent int, _ flagMask, out io
693
759
return nil
694
760
}
695
761
762
+ // renderCompactMapping renders a YAML mapping node in compact form.
763
+ func (ky * Encoder ) renderCompactMapping (node * yaml.Node , flags flagMask , out io.Writer ) error {
764
+ fmt .Fprint (out , "{" )
765
+ for i := 0 ; i < len (node .Content ); i += 2 {
766
+ key := node .Content [i ]
767
+ val := node .Content [i + 1 ]
768
+
769
+ if i > 0 {
770
+ fmt .Fprint (out , ", " )
771
+ }
772
+ // Mapping keys are always strings in KYAML, even if the YAML node says
773
+ // otherwise.
774
+ if err := ky .renderString (key .Value , 0 , flags | flagLazyQuote | flagCompact , out ); err != nil {
775
+ return err
776
+ }
777
+ fmt .Fprint (out , ": " )
778
+ if err := ky .renderNode (val , 0 , flags , out ); err != nil {
779
+ return err
780
+ }
781
+ }
782
+ fmt .Fprint (out , "}" )
783
+ return nil
784
+ }
785
+
696
786
func (ky * Encoder ) writeIndent (level int , out io.Writer ) {
697
787
const indentString = " "
698
788
for range level {
@@ -723,29 +813,3 @@ func (ky *Encoder) renderAlias(node *yaml.Node, indent int, flags flagMask, out
723
813
}
724
814
return nil
725
815
}
726
-
727
- // renderNode processes a YAML node, calling the appropriate render function
728
- // for its type. Each render function should assume that the output "cursor"
729
- // is positioned at the start of the node and should not emit a final newline.
730
- // If a render function needs to linewrap or indent (e.g. a struct), it should
731
- // assume the indent level is currently correct for the node type itself, and
732
- // may need to indent more.
733
- func (ky * Encoder ) renderNode (node * yaml.Node , indent int , flags flagMask , out io.Writer ) error {
734
- if node == nil {
735
- return nil
736
- }
737
-
738
- switch node .Kind {
739
- case yaml .DocumentNode :
740
- return ky .renderDocument (node , indent , flags , out )
741
- case yaml .ScalarNode :
742
- return ky .renderScalar (node , indent , flags , out )
743
- case yaml .SequenceNode :
744
- return ky .renderSequence (node , indent , flags , out )
745
- case yaml .MappingNode :
746
- return ky .renderMapping (node , indent , flags , out )
747
- case yaml .AliasNode :
748
- return ky .renderAlias (node , indent , flags , out )
749
- }
750
- return nil
751
- }
0 commit comments