Refuse unsafe documents¶
Some YAML cannot be round-tripped safely by the underlying parser. yamldoc
detects those constructs and reports them. It does not decide what to do
about them — that is yours.
f, err := yamldoc.Parse(src)
if err != nil {
return err
}
if u := f.Unsupported(); len(u) > 0 {
return fmt.Errorf("cannot safely edit %s: %s", path, u[0])
}
Each report names the reason and the line:
Tell the two reasons apart¶
There are two, and they call for different responses. Branch on the Reason
constant rather than the message text:
for _, u := range f.Unsupported() {
switch u.Reason {
case yamldoc.ReasonFlowComment:
// the document is fine, its layout is not: offer to rewrite it
case yamldoc.ReasonUndecodable:
// the document is broken: tell whoever wrote it
}
}
ReasonFlowComment is a formatting problem with a mechanical fix, described
below. ReasonUndecodable means the file parses but does not mean anything — in
practice an *alias pointing at an anchor that does not exist:
No rewrite makes that document safe. The full catalogue, including what is deliberately not flagged, is in Unsupported constructs.
You cannot forget to check¶
Emitting a file that contains an unsupported construct returns ErrUnsupported
rather than bytes:
out, err := f.Bytes()
if errors.Is(err, yamldoc.ErrUnsupported) {
// nothing was written, and nothing was damaged
}
Checking up front is better — the caller has already made their edits by the
time Bytes is reached, and failing then looks arbitrary — but the guard is
there either way.
Why a multi-line flow collection with comments is refused¶
A flow collection spanning multiple lines with comments inside it:
Re-emitting that swallows the closing brace into the comment, producing YAML
that does not parse. Rather than emit a broken document, yamldoc refuses.
Safe flow style is not affected. Single-line flow mappings and sequences, comments above or beside a flow node, and flow collections without interior comments all round-trip normally:
voice: &voice {id: abc, stability: 0.6} # fine
tiers: [core, ingress, 'tier-1'] # fine
# describes the bounds
bounds: {min: 1, max: 10} # fine
Block scalars are not flow collections. Braces and # inside a | or >
block scalar are literal content, not structure, so a JSON- or template-bearing
block scalar round-trips normally and is never flagged:
Working around it¶
Rewrite the collection in block style, which has no such problem: