Skip to content

Commit 59c2c43

Browse files
committed
Add compact mode so KYAML can be used in more places
1 parent abc1add commit 59c2c43

File tree

2 files changed

+369
-82
lines changed

2 files changed

+369
-82
lines changed

kyaml/kyaml.go

Lines changed: 129 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
696786
func (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

Comments
 (0)