@@ -60,7 +60,12 @@ import (
6060// is halfway between YAML and JSON, but is a strict subset of YAML, so it
6161// should should be readable by any YAML parser. It is designed to be explicit
6262// 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+ }
6469
6570// FromYAML renders a KYAML (multi-)document from YAML bytes (JSON is YAML),
6671// 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 {
8893 return err
8994 }
9095
91- if err := ky .renderDocument (& doc , 0 , flagsNone , out ); err != nil {
96+ if err := ky .renderDocument (& doc , 0 , ky . flags () , out ); err != nil {
9297 return err
9398 }
9499 }
@@ -138,7 +143,7 @@ func (ky *Encoder) fromObjectYAML(in io.Reader, out io.Writer) error {
138143 return fmt .Errorf ("kyaml internal error: line %d: expected document node, got %s" , doc .Line , ky .nodeKindString (doc .Kind ))
139144 }
140145
141- if err := ky .renderNode (& doc , 0 , flagsNone , out ); err != nil {
146+ if err := ky .renderNode (& doc , 0 , ky . flags () , out ); err != nil {
142147 return fmt .Errorf ("error rendering document: %v" , err )
143148 }
144149
@@ -167,10 +172,46 @@ const (
167172 flagCompact flagMask = 0x02
168173)
169174
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+
170211// renderDocument processes a YAML document node, rendering it to the output.
171212// This function assumes that the output "cursor" is positioned at the start of
172213// 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 {
174215 if len (doc .Content ) == 0 {
175216 return fmt .Errorf ("kyaml internal error: line %d: document has no content node (%d)" , doc .Line , len (doc .Content ))
176217 }
@@ -181,34 +222,40 @@ func (ky *Encoder) renderDocument(doc *yaml.Node, indent int, _ flagMask, out io
181222 return fmt .Errorf ("kyaml internal error: line %d: document non-zero indent (%d)" , doc .Line , indent )
182223 }
183224
225+ compact := flags & flagCompact != 0
226+
184227 // 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- }
189228 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+ }
193238 }
194- if err := ky .renderNode (child , indent , flagsNone , out ); err != nil {
239+ if err := ky .renderNode (child , indent , flags , out ); err != nil {
195240 return err
196241 }
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+ }
211246 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+ }
212259 }
213260 return nil
214261}
@@ -220,10 +267,8 @@ func (ky *Encoder) renderScalar(node *yaml.Node, indent int, flags flagMask, out
220267 switch node .Tag {
221268 case intTag , floatTag , boolTag , nullTag :
222269 fmt .Fprint (out , node .Value )
223- case strTag :
270+ case strTag , timestampTag :
224271 return ky .renderString (node .Value , indent + 1 , flags , out )
225- case timestampTag :
226- return ky .renderString (node .Value , indent + 1 , flagsNone , out )
227272 default :
228273 return fmt .Errorf ("kyaml internal error: line %d: unknown tag %q on scalar node %q" , node .Line , node .Tag , node .Value )
229274 }
@@ -521,11 +566,14 @@ func (ky *Encoder) appendEscapedRune(r rune, indent int, newline string, buf *by
521566// renderSequence processes a YAML sequence node, rendering it to the output. This
522567// DOES NOT render a trailing newline or head/line/foot comments of the sequence
523568// 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 {
525570 if len (node .Content ) == 0 {
526571 fmt .Fprint (out , "[]" )
527572 return nil
528573 }
574+ if flags & flagCompact != 0 {
575+ return ky .renderCompactSequence (node , flags , out )
576+ }
529577
530578 // See if this list can use cuddled formatting.
531579 cuddle := true
@@ -541,31 +589,45 @@ func (ky *Encoder) renderSequence(node *yaml.Node, indent int, _ flagMask, out i
541589 }
542590
543591 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+ }
545607 }
546- return ky .renderUncuddledSequence (node , indent , out )
608+ fmt .Fprint (out , "]" )
609+ return nil
547610}
548611
549612// renderCuddledSequence processes a YAML sequence node which has already been
550613// determined to be cuddled. We only cuddle sequences of structs or lists
551614// 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 {
553616 fmt .Fprint (out , "[" )
554617 for i , child := range node .Content {
555618 // Each iteration should leave us cuddled for the next item.
556619 if i > 0 {
557620 fmt .Fprint (out , ", " )
558621 }
559- if err := ky .renderNode (child , indent , flagsNone , out ); err != nil {
622+ if err := ky .renderNode (child , indent , flags , out ); err != nil {
560623 return err
561624 }
562625 }
563626 fmt .Fprint (out , "]" )
564-
565627 return nil
566628}
567629
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 {
569631 // Get into the right state for the first item.
570632 fmt .Fprint (out , "[\n " )
571633 ky .writeIndent (indent , out )
@@ -580,7 +642,7 @@ func (ky *Encoder) renderUncuddledSequence(node *yaml.Node, indent int, out io.W
580642 ky .writeIndent (indent + 1 , out )
581643 }
582644
583- if err := ky .renderNode (child , indent + 1 , flagsNone , out ); err != nil {
645+ if err := ky .renderNode (child , indent + 1 , flags , out ); err != nil {
584646 return err
585647 }
586648
@@ -635,12 +697,16 @@ func isCuddledKind(node *yaml.Node) bool {
635697// renderMapping processes a YAML mapping node, rendering it to the output. This
636698// DOES NOT render a trailing newline or head/line/foot comments of the mapping
637699// 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 {
639701 if len (node .Content ) == 0 {
640702 fmt .Fprint (out , "{}" )
641703 return nil
642704 }
643705
706+ if flags & flagCompact != 0 {
707+ return ky .renderCompactMapping (node , flags , out )
708+ }
709+
644710 joinComments := func (a , b string ) string {
645711 if len (a ) > 0 && len (b ) > 0 {
646712 return a + "\n " + b
@@ -668,7 +734,7 @@ func (ky *Encoder) renderMapping(node *yaml.Node, indent int, _ flagMask, out io
668734 return err
669735 }
670736 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 {
672738 return err
673739 }
674740 fmt .Fprint (out , "," )
@@ -693,6 +759,30 @@ func (ky *Encoder) renderMapping(node *yaml.Node, indent int, _ flagMask, out io
693759 return nil
694760}
695761
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+
696786func (ky * Encoder ) writeIndent (level int , out io.Writer ) {
697787 const indentString = " "
698788 for range level {
@@ -723,29 +813,3 @@ func (ky *Encoder) renderAlias(node *yaml.Node, indent int, flags flagMask, out
723813 }
724814 return nil
725815}
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