pax_global_header 0000666 0000000 0000000 00000000064 14244106522 0014512 g ustar 00root root 0000000 0000000 52 comment=f6f7691b1fdeb513f56608cd2c32c51f8194bf51
yaml-3.0.1/ 0000775 0000000 0000000 00000000000 14244106522 0012455 5 ustar 00root root 0000000 0000000 yaml-3.0.1/.github/ 0000775 0000000 0000000 00000000000 14244106522 0014015 5 ustar 00root root 0000000 0000000 yaml-3.0.1/.github/workflows/ 0000775 0000000 0000000 00000000000 14244106522 0016052 5 ustar 00root root 0000000 0000000 yaml-3.0.1/.github/workflows/go.yaml 0000664 0000000 0000000 00000003153 14244106522 0017345 0 ustar 00root root 0000000 0000000 ---
name: Go
on: [push, pull_request]
jobs:
test:
name: Test
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
go:
- "1.5"
- "1.6"
- "1.7"
- "1.8"
- "1.9"
- "1.10"
- "1.11"
- "1.12"
- "1.13"
- "1.14"
- "1.15"
- "1.16.0-beta1"
- "tip"
env:
GOPATH: ${{ github.workspace }}/go
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
with:
path: ${{ github.workspace }}/go/src/gopkg.in/yaml.v3
- name: Set up Go ${{ matrix.go }}
if: matrix.go != 'tip'
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}
stable: false
- name: Set up Go ${{ matrix.go }}
if: matrix.go == 'tip'
run: |
export GOROOT_BOOTSTRAP=`go env GOROOT`
export GOROOT=$HOME/gotip
mkdir $HOME/gotip
cd $HOME/gotip
curl -s 'https://go.googlesource.com/go/+/refs/heads/master?format=JSON' | awk '/"commit"/{print substr($2,2,40);exit}' >HEAD
awk '{printf("gotip-%s",substr($0,0,7))}'
VERSION
curl -s -o go.tar.gz https://go.googlesource.com/go/+archive/`cat HEAD`.tar.gz
tar xfz go.tar.gz
cd src
bash make.bash
echo "GOROOT=$GOROOT" >> $GITHUB_ENV
echo "$GOROOT/bin" >> $GITHUB_PATH
- run: go version
- run: go get -t ./...
working-directory: ${{ github.workspace }}/go/src/gopkg.in/yaml.v3
- run: go test .
working-directory: ${{ github.workspace }}/go/src/gopkg.in/yaml.v3
yaml-3.0.1/LICENSE 0000664 0000000 0000000 00000004147 14244106522 0013470 0 ustar 00root root 0000000 0000000
This project is covered by two different licenses: MIT and Apache.
#### MIT License ####
The following files were ported to Go from C files of libyaml, and thus
are still covered by their original MIT license, with the additional
copyright staring in 2011 when the project was ported over:
apic.go emitterc.go parserc.go readerc.go scannerc.go
writerc.go yamlh.go yamlprivateh.go
Copyright (c) 2006-2010 Kirill Simonov
Copyright (c) 2006-2011 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
### Apache License ###
All the remaining project files are covered by the Apache license:
Copyright (c) 2011-2019 Canonical Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
yaml-3.0.1/NOTICE 0000664 0000000 0000000 00000001060 14244106522 0013356 0 ustar 00root root 0000000 0000000 Copyright 2011-2016 Canonical Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
yaml-3.0.1/README.md 0000664 0000000 0000000 00000006575 14244106522 0013751 0 ustar 00root root 0000000 0000000 # YAML support for the Go language
Introduction
------------
The yaml package enables Go programs to comfortably encode and decode YAML
values. It was developed within [Canonical](https://www.canonical.com) as
part of the [juju](https://juju.ubuntu.com) project, and is based on a
pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML)
C library to parse and generate YAML data quickly and reliably.
Compatibility
-------------
The yaml package supports most of YAML 1.2, but preserves some behavior
from 1.1 for backwards compatibility.
Specifically, as of v3 of the yaml package:
- YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being
decoded into a typed bool value. Otherwise they behave as a string. Booleans
in YAML 1.2 are _true/false_ only.
- Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_
as specified in YAML 1.2, because most parsers still use the old format.
Octals in the _0o777_ format are supported though, so new files work.
- Does not support base-60 floats. These are gone from YAML 1.2, and were
actually never supported by this package as it's clearly a poor choice.
and offers backwards
compatibility with YAML 1.1 in some cases.
1.2, including support for
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
implemented, and base-60 floats from YAML 1.1 are purposefully not
supported since they're a poor design and are gone in YAML 1.2.
Installation and usage
----------------------
The import path for the package is *gopkg.in/yaml.v3*.
To install it, run:
go get gopkg.in/yaml.v3
API documentation
-----------------
If opened in a browser, the import path itself leads to the API documentation:
- [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3)
API stability
-------------
The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in).
License
-------
The yaml package is licensed under the MIT and Apache License 2.0 licenses.
Please see the LICENSE file for details.
Example
-------
```Go
package main
import (
"fmt"
"log"
"gopkg.in/yaml.v3"
)
var data = `
a: Easy!
b:
c: 2
d: [3, 4]
`
// Note: struct fields must be public in order for unmarshal to
// correctly populate the data.
type T struct {
A string
B struct {
RenamedC int `yaml:"c"`
D []int `yaml:",flow"`
}
}
func main() {
t := T{}
err := yaml.Unmarshal([]byte(data), &t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t:\n%v\n\n", t)
d, err := yaml.Marshal(&t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t dump:\n%s\n\n", string(d))
m := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(data), &m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m:\n%v\n\n", m)
d, err = yaml.Marshal(&m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m dump:\n%s\n\n", string(d))
}
```
This example will generate the following output:
```
--- t:
{Easy! {2 [3 4]}}
--- t dump:
a: Easy!
b:
c: 2
d: [3, 4]
--- m:
map[a:Easy! b:map[c:2 d:[3 4]]]
--- m dump:
a: Easy!
b:
c: 2
d:
- 3
- 4
```
yaml-3.0.1/apic.go 0000664 0000000 0000000 00000052757 14244106522 0013740 0 ustar 00root root 0000000 0000000 //
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
"io"
)
func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) {
//fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens))
// Check if we can move the queue at the beginning of the buffer.
if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) {
if parser.tokens_head != len(parser.tokens) {
copy(parser.tokens, parser.tokens[parser.tokens_head:])
}
parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head]
parser.tokens_head = 0
}
parser.tokens = append(parser.tokens, *token)
if pos < 0 {
return
}
copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:])
parser.tokens[parser.tokens_head+pos] = *token
}
// Create a new parser object.
func yaml_parser_initialize(parser *yaml_parser_t) bool {
*parser = yaml_parser_t{
raw_buffer: make([]byte, 0, input_raw_buffer_size),
buffer: make([]byte, 0, input_buffer_size),
}
return true
}
// Destroy a parser object.
func yaml_parser_delete(parser *yaml_parser_t) {
*parser = yaml_parser_t{}
}
// String read handler.
func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
if parser.input_pos == len(parser.input) {
return 0, io.EOF
}
n = copy(buffer, parser.input[parser.input_pos:])
parser.input_pos += n
return n, nil
}
// Reader read handler.
func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
return parser.input_reader.Read(buffer)
}
// Set a string input.
func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) {
if parser.read_handler != nil {
panic("must set the input source only once")
}
parser.read_handler = yaml_string_read_handler
parser.input = input
parser.input_pos = 0
}
// Set a file input.
func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) {
if parser.read_handler != nil {
panic("must set the input source only once")
}
parser.read_handler = yaml_reader_read_handler
parser.input_reader = r
}
// Set the source encoding.
func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
if parser.encoding != yaml_ANY_ENCODING {
panic("must set the encoding only once")
}
parser.encoding = encoding
}
// Create a new emitter object.
func yaml_emitter_initialize(emitter *yaml_emitter_t) {
*emitter = yaml_emitter_t{
buffer: make([]byte, output_buffer_size),
raw_buffer: make([]byte, 0, output_raw_buffer_size),
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
events: make([]yaml_event_t, 0, initial_queue_size),
best_width: -1,
}
}
// Destroy an emitter object.
func yaml_emitter_delete(emitter *yaml_emitter_t) {
*emitter = yaml_emitter_t{}
}
// String write handler.
func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
*emitter.output_buffer = append(*emitter.output_buffer, buffer...)
return nil
}
// yaml_writer_write_handler uses emitter.output_writer to write the
// emitted text.
func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
_, err := emitter.output_writer.Write(buffer)
return err
}
// Set a string output.
func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) {
if emitter.write_handler != nil {
panic("must set the output target only once")
}
emitter.write_handler = yaml_string_write_handler
emitter.output_buffer = output_buffer
}
// Set a file output.
func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) {
if emitter.write_handler != nil {
panic("must set the output target only once")
}
emitter.write_handler = yaml_writer_write_handler
emitter.output_writer = w
}
// Set the output encoding.
func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) {
if emitter.encoding != yaml_ANY_ENCODING {
panic("must set the output encoding only once")
}
emitter.encoding = encoding
}
// Set the canonical output style.
func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
emitter.canonical = canonical
}
// Set the indentation increment.
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
if indent < 2 || indent > 9 {
indent = 2
}
emitter.best_indent = indent
}
// Set the preferred line width.
func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) {
if width < 0 {
width = -1
}
emitter.best_width = width
}
// Set if unescaped non-ASCII characters are allowed.
func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) {
emitter.unicode = unicode
}
// Set the preferred line break character.
func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) {
emitter.line_break = line_break
}
///*
// * Destroy a token object.
// */
//
//YAML_DECLARE(void)
//yaml_token_delete(yaml_token_t *token)
//{
// assert(token); // Non-NULL token object expected.
//
// switch (token.type)
// {
// case YAML_TAG_DIRECTIVE_TOKEN:
// yaml_free(token.data.tag_directive.handle);
// yaml_free(token.data.tag_directive.prefix);
// break;
//
// case YAML_ALIAS_TOKEN:
// yaml_free(token.data.alias.value);
// break;
//
// case YAML_ANCHOR_TOKEN:
// yaml_free(token.data.anchor.value);
// break;
//
// case YAML_TAG_TOKEN:
// yaml_free(token.data.tag.handle);
// yaml_free(token.data.tag.suffix);
// break;
//
// case YAML_SCALAR_TOKEN:
// yaml_free(token.data.scalar.value);
// break;
//
// default:
// break;
// }
//
// memset(token, 0, sizeof(yaml_token_t));
//}
//
///*
// * Check if a string is a valid UTF-8 sequence.
// *
// * Check 'reader.c' for more details on UTF-8 encoding.
// */
//
//static int
//yaml_check_utf8(yaml_char_t *start, size_t length)
//{
// yaml_char_t *end = start+length;
// yaml_char_t *pointer = start;
//
// while (pointer < end) {
// unsigned char octet;
// unsigned int width;
// unsigned int value;
// size_t k;
//
// octet = pointer[0];
// width = (octet & 0x80) == 0x00 ? 1 :
// (octet & 0xE0) == 0xC0 ? 2 :
// (octet & 0xF0) == 0xE0 ? 3 :
// (octet & 0xF8) == 0xF0 ? 4 : 0;
// value = (octet & 0x80) == 0x00 ? octet & 0x7F :
// (octet & 0xE0) == 0xC0 ? octet & 0x1F :
// (octet & 0xF0) == 0xE0 ? octet & 0x0F :
// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
// if (!width) return 0;
// if (pointer+width > end) return 0;
// for (k = 1; k < width; k ++) {
// octet = pointer[k];
// if ((octet & 0xC0) != 0x80) return 0;
// value = (value << 6) + (octet & 0x3F);
// }
// if (!((width == 1) ||
// (width == 2 && value >= 0x80) ||
// (width == 3 && value >= 0x800) ||
// (width == 4 && value >= 0x10000))) return 0;
//
// pointer += width;
// }
//
// return 1;
//}
//
// Create STREAM-START.
func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) {
*event = yaml_event_t{
typ: yaml_STREAM_START_EVENT,
encoding: encoding,
}
}
// Create STREAM-END.
func yaml_stream_end_event_initialize(event *yaml_event_t) {
*event = yaml_event_t{
typ: yaml_STREAM_END_EVENT,
}
}
// Create DOCUMENT-START.
func yaml_document_start_event_initialize(
event *yaml_event_t,
version_directive *yaml_version_directive_t,
tag_directives []yaml_tag_directive_t,
implicit bool,
) {
*event = yaml_event_t{
typ: yaml_DOCUMENT_START_EVENT,
version_directive: version_directive,
tag_directives: tag_directives,
implicit: implicit,
}
}
// Create DOCUMENT-END.
func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) {
*event = yaml_event_t{
typ: yaml_DOCUMENT_END_EVENT,
implicit: implicit,
}
}
// Create ALIAS.
func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool {
*event = yaml_event_t{
typ: yaml_ALIAS_EVENT,
anchor: anchor,
}
return true
}
// Create SCALAR.
func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {
*event = yaml_event_t{
typ: yaml_SCALAR_EVENT,
anchor: anchor,
tag: tag,
value: value,
implicit: plain_implicit,
quoted_implicit: quoted_implicit,
style: yaml_style_t(style),
}
return true
}
// Create SEQUENCE-START.
func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool {
*event = yaml_event_t{
typ: yaml_SEQUENCE_START_EVENT,
anchor: anchor,
tag: tag,
implicit: implicit,
style: yaml_style_t(style),
}
return true
}
// Create SEQUENCE-END.
func yaml_sequence_end_event_initialize(event *yaml_event_t) bool {
*event = yaml_event_t{
typ: yaml_SEQUENCE_END_EVENT,
}
return true
}
// Create MAPPING-START.
func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) {
*event = yaml_event_t{
typ: yaml_MAPPING_START_EVENT,
anchor: anchor,
tag: tag,
implicit: implicit,
style: yaml_style_t(style),
}
}
// Create MAPPING-END.
func yaml_mapping_end_event_initialize(event *yaml_event_t) {
*event = yaml_event_t{
typ: yaml_MAPPING_END_EVENT,
}
}
// Destroy an event object.
func yaml_event_delete(event *yaml_event_t) {
*event = yaml_event_t{}
}
///*
// * Create a document object.
// */
//
//YAML_DECLARE(int)
//yaml_document_initialize(document *yaml_document_t,
// version_directive *yaml_version_directive_t,
// tag_directives_start *yaml_tag_directive_t,
// tag_directives_end *yaml_tag_directive_t,
// start_implicit int, end_implicit int)
//{
// struct {
// error yaml_error_type_t
// } context
// struct {
// start *yaml_node_t
// end *yaml_node_t
// top *yaml_node_t
// } nodes = { NULL, NULL, NULL }
// version_directive_copy *yaml_version_directive_t = NULL
// struct {
// start *yaml_tag_directive_t
// end *yaml_tag_directive_t
// top *yaml_tag_directive_t
// } tag_directives_copy = { NULL, NULL, NULL }
// value yaml_tag_directive_t = { NULL, NULL }
// mark yaml_mark_t = { 0, 0, 0 }
//
// assert(document) // Non-NULL document object is expected.
// assert((tag_directives_start && tag_directives_end) ||
// (tag_directives_start == tag_directives_end))
// // Valid tag directives are expected.
//
// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error
//
// if (version_directive) {
// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t))
// if (!version_directive_copy) goto error
// version_directive_copy.major = version_directive.major
// version_directive_copy.minor = version_directive.minor
// }
//
// if (tag_directives_start != tag_directives_end) {
// tag_directive *yaml_tag_directive_t
// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
// goto error
// for (tag_directive = tag_directives_start
// tag_directive != tag_directives_end; tag_directive ++) {
// assert(tag_directive.handle)
// assert(tag_directive.prefix)
// if (!yaml_check_utf8(tag_directive.handle,
// strlen((char *)tag_directive.handle)))
// goto error
// if (!yaml_check_utf8(tag_directive.prefix,
// strlen((char *)tag_directive.prefix)))
// goto error
// value.handle = yaml_strdup(tag_directive.handle)
// value.prefix = yaml_strdup(tag_directive.prefix)
// if (!value.handle || !value.prefix) goto error
// if (!PUSH(&context, tag_directives_copy, value))
// goto error
// value.handle = NULL
// value.prefix = NULL
// }
// }
//
// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
// tag_directives_copy.start, tag_directives_copy.top,
// start_implicit, end_implicit, mark, mark)
//
// return 1
//
//error:
// STACK_DEL(&context, nodes)
// yaml_free(version_directive_copy)
// while (!STACK_EMPTY(&context, tag_directives_copy)) {
// value yaml_tag_directive_t = POP(&context, tag_directives_copy)
// yaml_free(value.handle)
// yaml_free(value.prefix)
// }
// STACK_DEL(&context, tag_directives_copy)
// yaml_free(value.handle)
// yaml_free(value.prefix)
//
// return 0
//}
//
///*
// * Destroy a document object.
// */
//
//YAML_DECLARE(void)
//yaml_document_delete(document *yaml_document_t)
//{
// struct {
// error yaml_error_type_t
// } context
// tag_directive *yaml_tag_directive_t
//
// context.error = YAML_NO_ERROR // Eliminate a compiler warning.
//
// assert(document) // Non-NULL document object is expected.
//
// while (!STACK_EMPTY(&context, document.nodes)) {
// node yaml_node_t = POP(&context, document.nodes)
// yaml_free(node.tag)
// switch (node.type) {
// case YAML_SCALAR_NODE:
// yaml_free(node.data.scalar.value)
// break
// case YAML_SEQUENCE_NODE:
// STACK_DEL(&context, node.data.sequence.items)
// break
// case YAML_MAPPING_NODE:
// STACK_DEL(&context, node.data.mapping.pairs)
// break
// default:
// assert(0) // Should not happen.
// }
// }
// STACK_DEL(&context, document.nodes)
//
// yaml_free(document.version_directive)
// for (tag_directive = document.tag_directives.start
// tag_directive != document.tag_directives.end
// tag_directive++) {
// yaml_free(tag_directive.handle)
// yaml_free(tag_directive.prefix)
// }
// yaml_free(document.tag_directives.start)
//
// memset(document, 0, sizeof(yaml_document_t))
//}
//
///**
// * Get a document node.
// */
//
//YAML_DECLARE(yaml_node_t *)
//yaml_document_get_node(document *yaml_document_t, index int)
//{
// assert(document) // Non-NULL document object is expected.
//
// if (index > 0 && document.nodes.start + index <= document.nodes.top) {
// return document.nodes.start + index - 1
// }
// return NULL
//}
//
///**
// * Get the root object.
// */
//
//YAML_DECLARE(yaml_node_t *)
//yaml_document_get_root_node(document *yaml_document_t)
//{
// assert(document) // Non-NULL document object is expected.
//
// if (document.nodes.top != document.nodes.start) {
// return document.nodes.start
// }
// return NULL
//}
//
///*
// * Add a scalar node to a document.
// */
//
//YAML_DECLARE(int)
//yaml_document_add_scalar(document *yaml_document_t,
// tag *yaml_char_t, value *yaml_char_t, length int,
// style yaml_scalar_style_t)
//{
// struct {
// error yaml_error_type_t
// } context
// mark yaml_mark_t = { 0, 0, 0 }
// tag_copy *yaml_char_t = NULL
// value_copy *yaml_char_t = NULL
// node yaml_node_t
//
// assert(document) // Non-NULL document object is expected.
// assert(value) // Non-NULL value is expected.
//
// if (!tag) {
// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG
// }
//
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
// tag_copy = yaml_strdup(tag)
// if (!tag_copy) goto error
//
// if (length < 0) {
// length = strlen((char *)value)
// }
//
// if (!yaml_check_utf8(value, length)) goto error
// value_copy = yaml_malloc(length+1)
// if (!value_copy) goto error
// memcpy(value_copy, value, length)
// value_copy[length] = '\0'
//
// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark)
// if (!PUSH(&context, document.nodes, node)) goto error
//
// return document.nodes.top - document.nodes.start
//
//error:
// yaml_free(tag_copy)
// yaml_free(value_copy)
//
// return 0
//}
//
///*
// * Add a sequence node to a document.
// */
//
//YAML_DECLARE(int)
//yaml_document_add_sequence(document *yaml_document_t,
// tag *yaml_char_t, style yaml_sequence_style_t)
//{
// struct {
// error yaml_error_type_t
// } context
// mark yaml_mark_t = { 0, 0, 0 }
// tag_copy *yaml_char_t = NULL
// struct {
// start *yaml_node_item_t
// end *yaml_node_item_t
// top *yaml_node_item_t
// } items = { NULL, NULL, NULL }
// node yaml_node_t
//
// assert(document) // Non-NULL document object is expected.
//
// if (!tag) {
// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG
// }
//
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
// tag_copy = yaml_strdup(tag)
// if (!tag_copy) goto error
//
// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error
//
// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
// style, mark, mark)
// if (!PUSH(&context, document.nodes, node)) goto error
//
// return document.nodes.top - document.nodes.start
//
//error:
// STACK_DEL(&context, items)
// yaml_free(tag_copy)
//
// return 0
//}
//
///*
// * Add a mapping node to a document.
// */
//
//YAML_DECLARE(int)
//yaml_document_add_mapping(document *yaml_document_t,
// tag *yaml_char_t, style yaml_mapping_style_t)
//{
// struct {
// error yaml_error_type_t
// } context
// mark yaml_mark_t = { 0, 0, 0 }
// tag_copy *yaml_char_t = NULL
// struct {
// start *yaml_node_pair_t
// end *yaml_node_pair_t
// top *yaml_node_pair_t
// } pairs = { NULL, NULL, NULL }
// node yaml_node_t
//
// assert(document) // Non-NULL document object is expected.
//
// if (!tag) {
// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG
// }
//
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
// tag_copy = yaml_strdup(tag)
// if (!tag_copy) goto error
//
// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error
//
// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
// style, mark, mark)
// if (!PUSH(&context, document.nodes, node)) goto error
//
// return document.nodes.top - document.nodes.start
//
//error:
// STACK_DEL(&context, pairs)
// yaml_free(tag_copy)
//
// return 0
//}
//
///*
// * Append an item to a sequence node.
// */
//
//YAML_DECLARE(int)
//yaml_document_append_sequence_item(document *yaml_document_t,
// sequence int, item int)
//{
// struct {
// error yaml_error_type_t
// } context
//
// assert(document) // Non-NULL document is required.
// assert(sequence > 0
// && document.nodes.start + sequence <= document.nodes.top)
// // Valid sequence id is required.
// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE)
// // A sequence node is required.
// assert(item > 0 && document.nodes.start + item <= document.nodes.top)
// // Valid item id is required.
//
// if (!PUSH(&context,
// document.nodes.start[sequence-1].data.sequence.items, item))
// return 0
//
// return 1
//}
//
///*
// * Append a pair of a key and a value to a mapping node.
// */
//
//YAML_DECLARE(int)
//yaml_document_append_mapping_pair(document *yaml_document_t,
// mapping int, key int, value int)
//{
// struct {
// error yaml_error_type_t
// } context
//
// pair yaml_node_pair_t
//
// assert(document) // Non-NULL document is required.
// assert(mapping > 0
// && document.nodes.start + mapping <= document.nodes.top)
// // Valid mapping id is required.
// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE)
// // A mapping node is required.
// assert(key > 0 && document.nodes.start + key <= document.nodes.top)
// // Valid key id is required.
// assert(value > 0 && document.nodes.start + value <= document.nodes.top)
// // Valid value id is required.
//
// pair.key = key
// pair.value = value
//
// if (!PUSH(&context,
// document.nodes.start[mapping-1].data.mapping.pairs, pair))
// return 0
//
// return 1
//}
//
//
yaml-3.0.1/decode.go 0000664 0000000 0000000 00000060571 14244106522 0014240 0 ustar 00root root 0000000 0000000 //
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
"encoding"
"encoding/base64"
"fmt"
"io"
"math"
"reflect"
"strconv"
"time"
)
// ----------------------------------------------------------------------------
// Parser, produces a node tree out of a libyaml event stream.
type parser struct {
parser yaml_parser_t
event yaml_event_t
doc *Node
anchors map[string]*Node
doneInit bool
textless bool
}
func newParser(b []byte) *parser {
p := parser{}
if !yaml_parser_initialize(&p.parser) {
panic("failed to initialize YAML emitter")
}
if len(b) == 0 {
b = []byte{'\n'}
}
yaml_parser_set_input_string(&p.parser, b)
return &p
}
func newParserFromReader(r io.Reader) *parser {
p := parser{}
if !yaml_parser_initialize(&p.parser) {
panic("failed to initialize YAML emitter")
}
yaml_parser_set_input_reader(&p.parser, r)
return &p
}
func (p *parser) init() {
if p.doneInit {
return
}
p.anchors = make(map[string]*Node)
p.expect(yaml_STREAM_START_EVENT)
p.doneInit = true
}
func (p *parser) destroy() {
if p.event.typ != yaml_NO_EVENT {
yaml_event_delete(&p.event)
}
yaml_parser_delete(&p.parser)
}
// expect consumes an event from the event stream and
// checks that it's of the expected type.
func (p *parser) expect(e yaml_event_type_t) {
if p.event.typ == yaml_NO_EVENT {
if !yaml_parser_parse(&p.parser, &p.event) {
p.fail()
}
}
if p.event.typ == yaml_STREAM_END_EVENT {
failf("attempted to go past the end of stream; corrupted value?")
}
if p.event.typ != e {
p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ)
p.fail()
}
yaml_event_delete(&p.event)
p.event.typ = yaml_NO_EVENT
}
// peek peeks at the next event in the event stream,
// puts the results into p.event and returns the event type.
func (p *parser) peek() yaml_event_type_t {
if p.event.typ != yaml_NO_EVENT {
return p.event.typ
}
// It's curious choice from the underlying API to generally return a
// positive result on success, but on this case return true in an error
// scenario. This was the source of bugs in the past (issue #666).
if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR {
p.fail()
}
return p.event.typ
}
func (p *parser) fail() {
var where string
var line int
if p.parser.context_mark.line != 0 {
line = p.parser.context_mark.line
// Scanner errors don't iterate line before returning error
if p.parser.error == yaml_SCANNER_ERROR {
line++
}
} else if p.parser.problem_mark.line != 0 {
line = p.parser.problem_mark.line
// Scanner errors don't iterate line before returning error
if p.parser.error == yaml_SCANNER_ERROR {
line++
}
}
if line != 0 {
where = "line " + strconv.Itoa(line) + ": "
}
var msg string
if len(p.parser.problem) > 0 {
msg = p.parser.problem
} else {
msg = "unknown problem parsing YAML content"
}
failf("%s%s", where, msg)
}
func (p *parser) anchor(n *Node, anchor []byte) {
if anchor != nil {
n.Anchor = string(anchor)
p.anchors[n.Anchor] = n
}
}
func (p *parser) parse() *Node {
p.init()
switch p.peek() {
case yaml_SCALAR_EVENT:
return p.scalar()
case yaml_ALIAS_EVENT:
return p.alias()
case yaml_MAPPING_START_EVENT:
return p.mapping()
case yaml_SEQUENCE_START_EVENT:
return p.sequence()
case yaml_DOCUMENT_START_EVENT:
return p.document()
case yaml_STREAM_END_EVENT:
// Happens when attempting to decode an empty buffer.
return nil
case yaml_TAIL_COMMENT_EVENT:
panic("internal error: unexpected tail comment event (please report)")
default:
panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String())
}
}
func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node {
var style Style
if tag != "" && tag != "!" {
tag = shortTag(tag)
style = TaggedStyle
} else if defaultTag != "" {
tag = defaultTag
} else if kind == ScalarNode {
tag, _ = resolve("", value)
}
n := &Node{
Kind: kind,
Tag: tag,
Value: value,
Style: style,
}
if !p.textless {
n.Line = p.event.start_mark.line + 1
n.Column = p.event.start_mark.column + 1
n.HeadComment = string(p.event.head_comment)
n.LineComment = string(p.event.line_comment)
n.FootComment = string(p.event.foot_comment)
}
return n
}
func (p *parser) parseChild(parent *Node) *Node {
child := p.parse()
parent.Content = append(parent.Content, child)
return child
}
func (p *parser) document() *Node {
n := p.node(DocumentNode, "", "", "")
p.doc = n
p.expect(yaml_DOCUMENT_START_EVENT)
p.parseChild(n)
if p.peek() == yaml_DOCUMENT_END_EVENT {
n.FootComment = string(p.event.foot_comment)
}
p.expect(yaml_DOCUMENT_END_EVENT)
return n
}
func (p *parser) alias() *Node {
n := p.node(AliasNode, "", "", string(p.event.anchor))
n.Alias = p.anchors[n.Value]
if n.Alias == nil {
failf("unknown anchor '%s' referenced", n.Value)
}
p.expect(yaml_ALIAS_EVENT)
return n
}
func (p *parser) scalar() *Node {
var parsedStyle = p.event.scalar_style()
var nodeStyle Style
switch {
case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0:
nodeStyle = DoubleQuotedStyle
case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0:
nodeStyle = SingleQuotedStyle
case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0:
nodeStyle = LiteralStyle
case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0:
nodeStyle = FoldedStyle
}
var nodeValue = string(p.event.value)
var nodeTag = string(p.event.tag)
var defaultTag string
if nodeStyle == 0 {
if nodeValue == "<<" {
defaultTag = mergeTag
}
} else {
defaultTag = strTag
}
n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue)
n.Style |= nodeStyle
p.anchor(n, p.event.anchor)
p.expect(yaml_SCALAR_EVENT)
return n
}
func (p *parser) sequence() *Node {
n := p.node(SequenceNode, seqTag, string(p.event.tag), "")
if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 {
n.Style |= FlowStyle
}
p.anchor(n, p.event.anchor)
p.expect(yaml_SEQUENCE_START_EVENT)
for p.peek() != yaml_SEQUENCE_END_EVENT {
p.parseChild(n)
}
n.LineComment = string(p.event.line_comment)
n.FootComment = string(p.event.foot_comment)
p.expect(yaml_SEQUENCE_END_EVENT)
return n
}
func (p *parser) mapping() *Node {
n := p.node(MappingNode, mapTag, string(p.event.tag), "")
block := true
if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 {
block = false
n.Style |= FlowStyle
}
p.anchor(n, p.event.anchor)
p.expect(yaml_MAPPING_START_EVENT)
for p.peek() != yaml_MAPPING_END_EVENT {
k := p.parseChild(n)
if block && k.FootComment != "" {
// Must be a foot comment for the prior value when being dedented.
if len(n.Content) > 2 {
n.Content[len(n.Content)-3].FootComment = k.FootComment
k.FootComment = ""
}
}
v := p.parseChild(n)
if k.FootComment == "" && v.FootComment != "" {
k.FootComment = v.FootComment
v.FootComment = ""
}
if p.peek() == yaml_TAIL_COMMENT_EVENT {
if k.FootComment == "" {
k.FootComment = string(p.event.foot_comment)
}
p.expect(yaml_TAIL_COMMENT_EVENT)
}
}
n.LineComment = string(p.event.line_comment)
n.FootComment = string(p.event.foot_comment)
if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 {
n.Content[len(n.Content)-2].FootComment = n.FootComment
n.FootComment = ""
}
p.expect(yaml_MAPPING_END_EVENT)
return n
}
// ----------------------------------------------------------------------------
// Decoder, unmarshals a node into a provided value.
type decoder struct {
doc *Node
aliases map[*Node]bool
terrors []string
stringMapType reflect.Type
generalMapType reflect.Type
knownFields bool
uniqueKeys bool
decodeCount int
aliasCount int
aliasDepth int
mergedFields map[interface{}]bool
}
var (
nodeType = reflect.TypeOf(Node{})
durationType = reflect.TypeOf(time.Duration(0))
stringMapType = reflect.TypeOf(map[string]interface{}{})
generalMapType = reflect.TypeOf(map[interface{}]interface{}{})
ifaceType = generalMapType.Elem()
timeType = reflect.TypeOf(time.Time{})
ptrTimeType = reflect.TypeOf(&time.Time{})
)
func newDecoder() *decoder {
d := &decoder{
stringMapType: stringMapType,
generalMapType: generalMapType,
uniqueKeys: true,
}
d.aliases = make(map[*Node]bool)
return d
}
func (d *decoder) terror(n *Node, tag string, out reflect.Value) {
if n.Tag != "" {
tag = n.Tag
}
value := n.Value
if tag != seqTag && tag != mapTag {
if len(value) > 10 {
value = " `" + value[:7] + "...`"
} else {
value = " `" + value + "`"
}
}
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type()))
}
func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) {
err := u.UnmarshalYAML(n)
if e, ok := err.(*TypeError); ok {
d.terrors = append(d.terrors, e.Errors...)
return false
}
if err != nil {
fail(err)
}
return true
}
func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) {
terrlen := len(d.terrors)
err := u.UnmarshalYAML(func(v interface{}) (err error) {
defer handleErr(&err)
d.unmarshal(n, reflect.ValueOf(v))
if len(d.terrors) > terrlen {
issues := d.terrors[terrlen:]
d.terrors = d.terrors[:terrlen]
return &TypeError{issues}
}
return nil
})
if e, ok := err.(*TypeError); ok {
d.terrors = append(d.terrors, e.Errors...)
return false
}
if err != nil {
fail(err)
}
return true
}
// d.prepare initializes and dereferences pointers and calls UnmarshalYAML
// if a value is found to implement it.
// It returns the initialized and dereferenced out value, whether
// unmarshalling was already done by UnmarshalYAML, and if so whether
// its types unmarshalled appropriately.
//
// If n holds a null value, prepare returns before doing anything.
func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
if n.ShortTag() == nullTag {
return out, false, false
}
again := true
for again {
again = false
if out.Kind() == reflect.Ptr {
if out.IsNil() {
out.Set(reflect.New(out.Type().Elem()))
}
out = out.Elem()
again = true
}
if out.CanAddr() {
outi := out.Addr().Interface()
if u, ok := outi.(Unmarshaler); ok {
good = d.callUnmarshaler(n, u)
return out, true, good
}
if u, ok := outi.(obsoleteUnmarshaler); ok {
good = d.callObsoleteUnmarshaler(n, u)
return out, true, good
}
}
}
return out, false, false
}
func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) {
if n.ShortTag() == nullTag {
return reflect.Value{}
}
for _, num := range index {
for {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
continue
}
break
}
v = v.Field(num)
}
return v
}
const (
// 400,000 decode operations is ~500kb of dense object declarations, or
// ~5kb of dense object declarations with 10000% alias expansion
alias_ratio_range_low = 400000
// 4,000,000 decode operations is ~5MB of dense object declarations, or
// ~4.5MB of dense object declarations with 10% alias expansion
alias_ratio_range_high = 4000000
// alias_ratio_range is the range over which we scale allowed alias ratios
alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low)
)
func allowedAliasRatio(decodeCount int) float64 {
switch {
case decodeCount <= alias_ratio_range_low:
// allow 99% to come from alias expansion for small-to-medium documents
return 0.99
case decodeCount >= alias_ratio_range_high:
// allow 10% to come from alias expansion for very large documents
return 0.10
default:
// scale smoothly from 99% down to 10% over the range.
// this maps to 396,000 - 400,000 allowed alias-driven decodes over the range.
// 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps).
return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range)
}
}
func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) {
d.decodeCount++
if d.aliasDepth > 0 {
d.aliasCount++
}
if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) {
failf("document contains excessive aliasing")
}
if out.Type() == nodeType {
out.Set(reflect.ValueOf(n).Elem())
return true
}
switch n.Kind {
case DocumentNode:
return d.document(n, out)
case AliasNode:
return d.alias(n, out)
}
out, unmarshaled, good := d.prepare(n, out)
if unmarshaled {
return good
}
switch n.Kind {
case ScalarNode:
good = d.scalar(n, out)
case MappingNode:
good = d.mapping(n, out)
case SequenceNode:
good = d.sequence(n, out)
case 0:
if n.IsZero() {
return d.null(out)
}
fallthrough
default:
failf("cannot decode node with unknown kind %d", n.Kind)
}
return good
}
func (d *decoder) document(n *Node, out reflect.Value) (good bool) {
if len(n.Content) == 1 {
d.doc = n
d.unmarshal(n.Content[0], out)
return true
}
return false
}
func (d *decoder) alias(n *Node, out reflect.Value) (good bool) {
if d.aliases[n] {
// TODO this could actually be allowed in some circumstances.
failf("anchor '%s' value contains itself", n.Value)
}
d.aliases[n] = true
d.aliasDepth++
good = d.unmarshal(n.Alias, out)
d.aliasDepth--
delete(d.aliases, n)
return good
}
var zeroValue reflect.Value
func resetMap(out reflect.Value) {
for _, k := range out.MapKeys() {
out.SetMapIndex(k, zeroValue)
}
}
func (d *decoder) null(out reflect.Value) bool {
if out.CanAddr() {
switch out.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
out.Set(reflect.Zero(out.Type()))
return true
}
}
return false
}
func (d *decoder) scalar(n *Node, out reflect.Value) bool {
var tag string
var resolved interface{}
if n.indicatedString() {
tag = strTag
resolved = n.Value
} else {
tag, resolved = resolve(n.Tag, n.Value)
if tag == binaryTag {
data, err := base64.StdEncoding.DecodeString(resolved.(string))
if err != nil {
failf("!!binary value contains invalid base64 data")
}
resolved = string(data)
}
}
if resolved == nil {
return d.null(out)
}
if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() {
// We've resolved to exactly the type we want, so use that.
out.Set(resolvedv)
return true
}
// Perhaps we can use the value as a TextUnmarshaler to
// set its value.
if out.CanAddr() {
u, ok := out.Addr().Interface().(encoding.TextUnmarshaler)
if ok {
var text []byte
if tag == binaryTag {
text = []byte(resolved.(string))
} else {
// We let any value be unmarshaled into TextUnmarshaler.
// That might be more lax than we'd like, but the
// TextUnmarshaler itself should bowl out any dubious values.
text = []byte(n.Value)
}
err := u.UnmarshalText(text)
if err != nil {
fail(err)
}
return true
}
}
switch out.Kind() {
case reflect.String:
if tag == binaryTag {
out.SetString(resolved.(string))
return true
}
out.SetString(n.Value)
return true
case reflect.Interface:
out.Set(reflect.ValueOf(resolved))
return true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
// This used to work in v2, but it's very unfriendly.
isDuration := out.Type() == durationType
switch resolved := resolved.(type) {
case int:
if !isDuration && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
return true
}
case int64:
if !isDuration && !out.OverflowInt(resolved) {
out.SetInt(resolved)
return true
}
case uint64:
if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
return true
}
case float64:
if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
return true
}
case string:
if out.Type() == durationType {
d, err := time.ParseDuration(resolved)
if err == nil {
out.SetInt(int64(d))
return true
}
}
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
switch resolved := resolved.(type) {
case int:
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
out.SetUint(uint64(resolved))
return true
}
case int64:
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
out.SetUint(uint64(resolved))
return true
}
case uint64:
if !out.OverflowUint(uint64(resolved)) {
out.SetUint(uint64(resolved))
return true
}
case float64:
if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) {
out.SetUint(uint64(resolved))
return true
}
}
case reflect.Bool:
switch resolved := resolved.(type) {
case bool:
out.SetBool(resolved)
return true
case string:
// This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html).
// It only works if explicitly attempting to unmarshal into a typed bool value.
switch resolved {
case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON":
out.SetBool(true)
return true
case "n", "N", "no", "No", "NO", "off", "Off", "OFF":
out.SetBool(false)
return true
}
}
case reflect.Float32, reflect.Float64:
switch resolved := resolved.(type) {
case int:
out.SetFloat(float64(resolved))
return true
case int64:
out.SetFloat(float64(resolved))
return true
case uint64:
out.SetFloat(float64(resolved))
return true
case float64:
out.SetFloat(resolved)
return true
}
case reflect.Struct:
if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() {
out.Set(resolvedv)
return true
}
case reflect.Ptr:
panic("yaml internal error: please report the issue")
}
d.terror(n, tag, out)
return false
}
func settableValueOf(i interface{}) reflect.Value {
v := reflect.ValueOf(i)
sv := reflect.New(v.Type()).Elem()
sv.Set(v)
return sv
}
func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
l := len(n.Content)
var iface reflect.Value
switch out.Kind() {
case reflect.Slice:
out.Set(reflect.MakeSlice(out.Type(), l, l))
case reflect.Array:
if l != out.Len() {
failf("invalid array: want %d elements but got %d", out.Len(), l)
}
case reflect.Interface:
// No type hints. Will have to use a generic sequence.
iface = out
out = settableValueOf(make([]interface{}, l))
default:
d.terror(n, seqTag, out)
return false
}
et := out.Type().Elem()
j := 0
for i := 0; i < l; i++ {
e := reflect.New(et).Elem()
if ok := d.unmarshal(n.Content[i], e); ok {
out.Index(j).Set(e)
j++
}
}
if out.Kind() != reflect.Array {
out.Set(out.Slice(0, j))
}
if iface.IsValid() {
iface.Set(out)
}
return true
}
func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
l := len(n.Content)
if d.uniqueKeys {
nerrs := len(d.terrors)
for i := 0; i < l; i += 2 {
ni := n.Content[i]
for j := i + 2; j < l; j += 2 {
nj := n.Content[j]
if ni.Kind == nj.Kind && ni.Value == nj.Value {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line))
}
}
}
if len(d.terrors) > nerrs {
return false
}
}
switch out.Kind() {
case reflect.Struct:
return d.mappingStruct(n, out)
case reflect.Map:
// okay
case reflect.Interface:
iface := out
if isStringMap(n) {
out = reflect.MakeMap(d.stringMapType)
} else {
out = reflect.MakeMap(d.generalMapType)
}
iface.Set(out)
default:
d.terror(n, mapTag, out)
return false
}
outt := out.Type()
kt := outt.Key()
et := outt.Elem()
stringMapType := d.stringMapType
generalMapType := d.generalMapType
if outt.Elem() == ifaceType {
if outt.Key().Kind() == reflect.String {
d.stringMapType = outt
} else if outt.Key() == ifaceType {
d.generalMapType = outt
}
}
mergedFields := d.mergedFields
d.mergedFields = nil
var mergeNode *Node
mapIsNew := false
if out.IsNil() {
out.Set(reflect.MakeMap(outt))
mapIsNew = true
}
for i := 0; i < l; i += 2 {
if isMerge(n.Content[i]) {
mergeNode = n.Content[i+1]
continue
}
k := reflect.New(kt).Elem()
if d.unmarshal(n.Content[i], k) {
if mergedFields != nil {
ki := k.Interface()
if mergedFields[ki] {
continue
}
mergedFields[ki] = true
}
kkind := k.Kind()
if kkind == reflect.Interface {
kkind = k.Elem().Kind()
}
if kkind == reflect.Map || kkind == reflect.Slice {
failf("invalid map key: %#v", k.Interface())
}
e := reflect.New(et).Elem()
if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) {
out.SetMapIndex(k, e)
}
}
}
d.mergedFields = mergedFields
if mergeNode != nil {
d.merge(n, mergeNode, out)
}
d.stringMapType = stringMapType
d.generalMapType = generalMapType
return true
}
func isStringMap(n *Node) bool {
if n.Kind != MappingNode {
return false
}
l := len(n.Content)
for i := 0; i < l; i += 2 {
shortTag := n.Content[i].ShortTag()
if shortTag != strTag && shortTag != mergeTag {
return false
}
}
return true
}
func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
sinfo, err := getStructInfo(out.Type())
if err != nil {
panic(err)
}
var inlineMap reflect.Value
var elemType reflect.Type
if sinfo.InlineMap != -1 {
inlineMap = out.Field(sinfo.InlineMap)
elemType = inlineMap.Type().Elem()
}
for _, index := range sinfo.InlineUnmarshalers {
field := d.fieldByIndex(n, out, index)
d.prepare(n, field)
}
mergedFields := d.mergedFields
d.mergedFields = nil
var mergeNode *Node
var doneFields []bool
if d.uniqueKeys {
doneFields = make([]bool, len(sinfo.FieldsList))
}
name := settableValueOf("")
l := len(n.Content)
for i := 0; i < l; i += 2 {
ni := n.Content[i]
if isMerge(ni) {
mergeNode = n.Content[i+1]
continue
}
if !d.unmarshal(ni, name) {
continue
}
sname := name.String()
if mergedFields != nil {
if mergedFields[sname] {
continue
}
mergedFields[sname] = true
}
if info, ok := sinfo.FieldsMap[sname]; ok {
if d.uniqueKeys {
if doneFields[info.Id] {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
continue
}
doneFields[info.Id] = true
}
var field reflect.Value
if info.Inline == nil {
field = out.Field(info.Num)
} else {
field = d.fieldByIndex(n, out, info.Inline)
}
d.unmarshal(n.Content[i+1], field)
} else if sinfo.InlineMap != -1 {
if inlineMap.IsNil() {
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
}
value := reflect.New(elemType).Elem()
d.unmarshal(n.Content[i+1], value)
inlineMap.SetMapIndex(name, value)
} else if d.knownFields {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
}
}
d.mergedFields = mergedFields
if mergeNode != nil {
d.merge(n, mergeNode, out)
}
return true
}
func failWantMap() {
failf("map merge requires map or sequence of maps as the value")
}
func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) {
mergedFields := d.mergedFields
if mergedFields == nil {
d.mergedFields = make(map[interface{}]bool)
for i := 0; i < len(parent.Content); i += 2 {
k := reflect.New(ifaceType).Elem()
if d.unmarshal(parent.Content[i], k) {
d.mergedFields[k.Interface()] = true
}
}
}
switch merge.Kind {
case MappingNode:
d.unmarshal(merge, out)
case AliasNode:
if merge.Alias != nil && merge.Alias.Kind != MappingNode {
failWantMap()
}
d.unmarshal(merge, out)
case SequenceNode:
for i := 0; i < len(merge.Content); i++ {
ni := merge.Content[i]
if ni.Kind == AliasNode {
if ni.Alias != nil && ni.Alias.Kind != MappingNode {
failWantMap()
}
} else if ni.Kind != MappingNode {
failWantMap()
}
d.unmarshal(ni, out)
}
default:
failWantMap()
}
d.mergedFields = mergedFields
}
func isMerge(n *Node) bool {
return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag)
}
yaml-3.0.1/decode_test.go 0000664 0000000 0000000 00000122520 14244106522 0015270 0 ustar 00root root 0000000 0000000 //
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml_test
import (
"bytes"
"errors"
"fmt"
"io"
"math"
"reflect"
"strings"
"time"
. "gopkg.in/check.v1"
"gopkg.in/yaml.v3"
)
var unmarshalIntTest = 123
var unmarshalTests = []struct {
data string
value interface{}
}{
{
"",
(*struct{})(nil),
},
{
"{}", &struct{}{},
}, {
"v: hi",
map[string]string{"v": "hi"},
}, {
"v: hi", map[string]interface{}{"v": "hi"},
}, {
"v: true",
map[string]string{"v": "true"},
}, {
"v: true",
map[string]interface{}{"v": true},
}, {
"v: 10",
map[string]interface{}{"v": 10},
}, {
"v: 0b10",
map[string]interface{}{"v": 2},
}, {
"v: 0xA",
map[string]interface{}{"v": 10},
}, {
"v: 4294967296",
map[string]int64{"v": 4294967296},
}, {
"v: 0.1",
map[string]interface{}{"v": 0.1},
}, {
"v: .1",
map[string]interface{}{"v": 0.1},
}, {
"v: .Inf",
map[string]interface{}{"v": math.Inf(+1)},
}, {
"v: -.Inf",
map[string]interface{}{"v": math.Inf(-1)},
}, {
"v: -10",
map[string]interface{}{"v": -10},
}, {
"v: -.1",
map[string]interface{}{"v": -0.1},
},
// Simple values.
{
"123",
&unmarshalIntTest,
},
// Floats from spec
{
"canonical: 6.8523e+5",
map[string]interface{}{"canonical": 6.8523e+5},
}, {
"expo: 685.230_15e+03",
map[string]interface{}{"expo": 685.23015e+03},
}, {
"fixed: 685_230.15",
map[string]interface{}{"fixed": 685230.15},
}, {
"neginf: -.inf",
map[string]interface{}{"neginf": math.Inf(-1)},
}, {
"fixed: 685_230.15",
map[string]float64{"fixed": 685230.15},
},
//{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported
//{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails.
// Bools are per 1.2 spec.
{
"canonical: true",
map[string]interface{}{"canonical": true},
}, {
"canonical: false",
map[string]interface{}{"canonical": false},
}, {
"bool: True",
map[string]interface{}{"bool": true},
}, {
"bool: False",
map[string]interface{}{"bool": false},
}, {
"bool: TRUE",
map[string]interface{}{"bool": true},
}, {
"bool: FALSE",
map[string]interface{}{"bool": false},
},
// For backwards compatibility with 1.1, decoding old strings into typed values still works.
{
"option: on",
map[string]bool{"option": true},
}, {
"option: y",
map[string]bool{"option": true},
}, {
"option: Off",
map[string]bool{"option": false},
}, {
"option: No",
map[string]bool{"option": false},
}, {
"option: other",
map[string]bool{},
},
// Ints from spec
{
"canonical: 685230",
map[string]interface{}{"canonical": 685230},
}, {
"decimal: +685_230",
map[string]interface{}{"decimal": 685230},
}, {
"octal: 02472256",
map[string]interface{}{"octal": 685230},
}, {
"octal: -02472256",
map[string]interface{}{"octal": -685230},
}, {
"octal: 0o2472256",
map[string]interface{}{"octal": 685230},
}, {
"octal: -0o2472256",
map[string]interface{}{"octal": -685230},
}, {
"hexa: 0x_0A_74_AE",
map[string]interface{}{"hexa": 685230},
}, {
"bin: 0b1010_0111_0100_1010_1110",
map[string]interface{}{"bin": 685230},
}, {
"bin: -0b101010",
map[string]interface{}{"bin": -42},
}, {
"bin: -0b1000000000000000000000000000000000000000000000000000000000000000",
map[string]interface{}{"bin": -9223372036854775808},
}, {
"decimal: +685_230",
map[string]int{"decimal": 685230},
},
//{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported
// Nulls from spec
{
"empty:",
map[string]interface{}{"empty": nil},
}, {
"canonical: ~",
map[string]interface{}{"canonical": nil},
}, {
"english: null",
map[string]interface{}{"english": nil},
}, {
"~: null key",
map[interface{}]string{nil: "null key"},
}, {
"empty:",
map[string]*bool{"empty": nil},
},
// Flow sequence
{
"seq: [A,B]",
map[string]interface{}{"seq": []interface{}{"A", "B"}},
}, {
"seq: [A,B,C,]",
map[string][]string{"seq": []string{"A", "B", "C"}},
}, {
"seq: [A,1,C]",
map[string][]string{"seq": []string{"A", "1", "C"}},
}, {
"seq: [A,1,C]",
map[string][]int{"seq": []int{1}},
}, {
"seq: [A,1,C]",
map[string]interface{}{"seq": []interface{}{"A", 1, "C"}},
},
// Block sequence
{
"seq:\n - A\n - B",
map[string]interface{}{"seq": []interface{}{"A", "B"}},
}, {
"seq:\n - A\n - B\n - C",
map[string][]string{"seq": []string{"A", "B", "C"}},
}, {
"seq:\n - A\n - 1\n - C",
map[string][]string{"seq": []string{"A", "1", "C"}},
}, {
"seq:\n - A\n - 1\n - C",
map[string][]int{"seq": []int{1}},
}, {
"seq:\n - A\n - 1\n - C",
map[string]interface{}{"seq": []interface{}{"A", 1, "C"}},
},
// Literal block scalar
{
"scalar: | # Comment\n\n literal\n\n \ttext\n\n",
map[string]string{"scalar": "\nliteral\n\n\ttext\n"},
},
// Folded block scalar
{
"scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n",
map[string]string{"scalar": "\nfolded line\nnext line\n * one\n * two\n\nlast line\n"},
},
// Map inside interface with no type hints.
{
"a: {b: c}",
map[interface{}]interface{}{"a": map[string]interface{}{"b": "c"}},
},
// Non-string map inside interface with no type hints.
{
"a: {b: c, 1: d}",
map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c", 1: "d"}},
},
// Structs and type conversions.
{
"hello: world",
&struct{ Hello string }{"world"},
}, {
"a: {b: c}",
&struct{ A struct{ B string } }{struct{ B string }{"c"}},
}, {
"a: {b: c}",
&struct{ A *struct{ B string } }{&struct{ B string }{"c"}},
}, {
"a: 'null'",
&struct{ A *unmarshalerType }{&unmarshalerType{"null"}},
}, {
"a: {b: c}",
&struct{ A map[string]string }{map[string]string{"b": "c"}},
}, {
"a: {b: c}",
&struct{ A *map[string]string }{&map[string]string{"b": "c"}},
}, {
"a:",
&struct{ A map[string]string }{},
}, {
"a: 1",
&struct{ A int }{1},
}, {
"a: 1",
&struct{ A float64 }{1},
}, {
"a: 1.0",
&struct{ A int }{1},
}, {
"a: 1.0",
&struct{ A uint }{1},
}, {
"a: [1, 2]",
&struct{ A []int }{[]int{1, 2}},
}, {
"a: [1, 2]",
&struct{ A [2]int }{[2]int{1, 2}},
}, {
"a: 1",
&struct{ B int }{0},
}, {
"a: 1",
&struct {
B int "a"
}{1},
}, {
// Some limited backwards compatibility with the 1.1 spec.
"a: YES",
&struct{ A bool }{true},
},
// Some cross type conversions
{
"v: 42",
map[string]uint{"v": 42},
}, {
"v: -42",
map[string]uint{},
}, {
"v: 4294967296",
map[string]uint64{"v": 4294967296},
}, {
"v: -4294967296",
map[string]uint64{},
},
// int
{
"int_max: 2147483647",
map[string]int{"int_max": math.MaxInt32},
},
{
"int_min: -2147483648",
map[string]int{"int_min": math.MinInt32},
},
{
"int_overflow: 9223372036854775808", // math.MaxInt64 + 1
map[string]int{},
},
// int64
{
"int64_max: 9223372036854775807",
map[string]int64{"int64_max": math.MaxInt64},
},
{
"int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111",
map[string]int64{"int64_max_base2": math.MaxInt64},
},
{
"int64_min: -9223372036854775808",
map[string]int64{"int64_min": math.MinInt64},
},
{
"int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111",
map[string]int64{"int64_neg_base2": -math.MaxInt64},
},
{
"int64_overflow: 9223372036854775808", // math.MaxInt64 + 1
map[string]int64{},
},
// uint
{
"uint_min: 0",
map[string]uint{"uint_min": 0},
},
{
"uint_max: 4294967295",
map[string]uint{"uint_max": math.MaxUint32},
},
{
"uint_underflow: -1",
map[string]uint{},
},
// uint64
{
"uint64_min: 0",
map[string]uint{"uint64_min": 0},
},
{
"uint64_max: 18446744073709551615",
map[string]uint64{"uint64_max": math.MaxUint64},
},
{
"uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111",
map[string]uint64{"uint64_max_base2": math.MaxUint64},
},
{
"uint64_maxint64: 9223372036854775807",
map[string]uint64{"uint64_maxint64": math.MaxInt64},
},
{
"uint64_underflow: -1",
map[string]uint64{},
},
// float32
{
"float32_max: 3.40282346638528859811704183484516925440e+38",
map[string]float32{"float32_max": math.MaxFloat32},
},
{
"float32_nonzero: 1.401298464324817070923729583289916131280e-45",
map[string]float32{"float32_nonzero": math.SmallestNonzeroFloat32},
},
{
"float32_maxuint64: 18446744073709551615",
map[string]float32{"float32_maxuint64": float32(math.MaxUint64)},
},
{
"float32_maxuint64+1: 18446744073709551616",
map[string]float32{"float32_maxuint64+1": float32(math.MaxUint64 + 1)},
},
// float64
{
"float64_max: 1.797693134862315708145274237317043567981e+308",
map[string]float64{"float64_max": math.MaxFloat64},
},
{
"float64_nonzero: 4.940656458412465441765687928682213723651e-324",
map[string]float64{"float64_nonzero": math.SmallestNonzeroFloat64},
},
{
"float64_maxuint64: 18446744073709551615",
map[string]float64{"float64_maxuint64": float64(math.MaxUint64)},
},
{
"float64_maxuint64+1: 18446744073709551616",
map[string]float64{"float64_maxuint64+1": float64(math.MaxUint64 + 1)},
},
// Overflow cases.
{
"v: 4294967297",
map[string]int32{},
}, {
"v: 128",
map[string]int8{},
},
// Quoted values.
{
"'1': '\"2\"'",
map[interface{}]interface{}{"1": "\"2\""},
}, {
"v:\n- A\n- 'B\n\n C'\n",
map[string][]string{"v": []string{"A", "B\nC"}},
},
// Explicit tags.
{
"v: !!float '1.1'",
map[string]interface{}{"v": 1.1},
}, {
"v: !!float 0",
map[string]interface{}{"v": float64(0)},
}, {
"v: !!float -1",
map[string]interface{}{"v": float64(-1)},
}, {
"v: !!null ''",
map[string]interface{}{"v": nil},
}, {
"%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'",
map[string]interface{}{"v": 1},
},
// Non-specific tag (Issue #75)
{
"v: ! test",
map[string]interface{}{"v": "test"},
},
// Anchors and aliases.
{
"a: &x 1\nb: &y 2\nc: *x\nd: *y\n",
&struct{ A, B, C, D int }{1, 2, 1, 2},
}, {
"a: &a {c: 1}\nb: *a",
&struct {
A, B struct {
C int
}
}{struct{ C int }{1}, struct{ C int }{1}},
}, {
"a: &a [1, 2]\nb: *a",
&struct{ B []int }{[]int{1, 2}},
},
// Bug #1133337
{
"foo: ''",
map[string]*string{"foo": new(string)},
}, {
"foo: null",
map[string]*string{"foo": nil},
}, {
"foo: null",
map[string]string{"foo": ""},
}, {
"foo: null",
map[string]interface{}{"foo": nil},
},
// Support for ~
{
"foo: ~",
map[string]*string{"foo": nil},
}, {
"foo: ~",
map[string]string{"foo": ""},
}, {
"foo: ~",
map[string]interface{}{"foo": nil},
},
// Ignored field
{
"a: 1\nb: 2\n",
&struct {
A int
B int "-"
}{1, 0},
},
// Bug #1191981
{
"" +
"%YAML 1.1\n" +
"--- !!str\n" +
`"Generic line break (no glyph)\n\` + "\n" +
` Generic line break (glyphed)\n\` + "\n" +
` Line separator\u2028\` + "\n" +
` Paragraph separator\u2029"` + "\n",
"" +
"Generic line break (no glyph)\n" +
"Generic line break (glyphed)\n" +
"Line separator\u2028Paragraph separator\u2029",
},
// Struct inlining
{
"a: 1\nb: 2\nc: 3\n",
&struct {
A int
C inlineB `yaml:",inline"`
}{1, inlineB{2, inlineC{3}}},
},
// Struct inlining as a pointer.
{
"a: 1\nb: 2\nc: 3\n",
&struct {
A int
C *inlineB `yaml:",inline"`
}{1, &inlineB{2, inlineC{3}}},
}, {
"a: 1\n",
&struct {
A int
C *inlineB `yaml:",inline"`
}{1, nil},
}, {
"a: 1\nc: 3\nd: 4\n",
&struct {
A int
C *inlineD `yaml:",inline"`
}{1, &inlineD{&inlineC{3}, 4}},
},
// Map inlining
{
"a: 1\nb: 2\nc: 3\n",
&struct {
A int
C map[string]int `yaml:",inline"`
}{1, map[string]int{"b": 2, "c": 3}},
},
// bug 1243827
{
"a: -b_c",
map[string]interface{}{"a": "-b_c"},
},
{
"a: +b_c",
map[string]interface{}{"a": "+b_c"},
},
{
"a: 50cent_of_dollar",
map[string]interface{}{"a": "50cent_of_dollar"},
},
// issue #295 (allow scalars with colons in flow mappings and sequences)
{
"a: {b: https://github.com/go-yaml/yaml}",
map[string]interface{}{"a": map[string]interface{}{
"b": "https://github.com/go-yaml/yaml",
}},
},
{
"a: [https://github.com/go-yaml/yaml]",
map[string]interface{}{"a": []interface{}{"https://github.com/go-yaml/yaml"}},
},
// Duration
{
"a: 3s",
map[string]time.Duration{"a": 3 * time.Second},
},
// Issue #24.
{
"a: ",
map[string]string{"a": ""},
},
// Base 60 floats are obsolete and unsupported.
{
"a: 1:1\n",
map[string]string{"a": "1:1"},
},
// Binary data.
{
"a: !!binary gIGC\n",
map[string]string{"a": "\x80\x81\x82"},
}, {
"a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n",
map[string]string{"a": strings.Repeat("\x90", 54)},
}, {
"a: !!binary |\n " + strings.Repeat("A", 70) + "\n ==\n",
map[string]string{"a": strings.Repeat("\x00", 52)},
},
// Issue #39.
{
"a:\n b:\n c: d\n",
map[string]struct{ B interface{} }{"a": {map[string]interface{}{"c": "d"}}},
},
// Custom map type.
{
"a: {b: c}",
M{"a": M{"b": "c"}},
},
// Support encoding.TextUnmarshaler.
{
"a: 1.2.3.4\n",
map[string]textUnmarshaler{"a": textUnmarshaler{S: "1.2.3.4"}},
},
{
"a: 2015-02-24T18:19:39Z\n",
map[string]textUnmarshaler{"a": textUnmarshaler{"2015-02-24T18:19:39Z"}},
},
// Timestamps
{
// Date only.
"a: 2015-01-01\n",
map[string]time.Time{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
},
{
// RFC3339
"a: 2015-02-24T18:19:39.12Z\n",
map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, .12e9, time.UTC)},
},
{
// RFC3339 with short dates.
"a: 2015-2-3T3:4:5Z",
map[string]time.Time{"a": time.Date(2015, 2, 3, 3, 4, 5, 0, time.UTC)},
},
{
// ISO8601 lower case t
"a: 2015-02-24t18:19:39Z\n",
map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)},
},
{
// space separate, no time zone
"a: 2015-02-24 18:19:39\n",
map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)},
},
// Some cases not currently handled. Uncomment these when
// the code is fixed.
// {
// // space separated with time zone
// "a: 2001-12-14 21:59:43.10 -5",
// map[string]interface{}{"a": time.Date(2001, 12, 14, 21, 59, 43, .1e9, time.UTC)},
// },
// {
// // arbitrary whitespace between fields
// "a: 2001-12-14 \t\t \t21:59:43.10 \t Z",
// map[string]interface{}{"a": time.Date(2001, 12, 14, 21, 59, 43, .1e9, time.UTC)},
// },
{
// explicit string tag
"a: !!str 2015-01-01",
map[string]interface{}{"a": "2015-01-01"},
},
{
// explicit timestamp tag on quoted string
"a: !!timestamp \"2015-01-01\"",
map[string]time.Time{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
},
{
// explicit timestamp tag on unquoted string
"a: !!timestamp 2015-01-01",
map[string]time.Time{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
},
{
// quoted string that's a valid timestamp
"a: \"2015-01-01\"",
map[string]interface{}{"a": "2015-01-01"},
},
{
// explicit timestamp tag into interface.
"a: !!timestamp \"2015-01-01\"",
map[string]interface{}{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
},
{
// implicit timestamp tag into interface.
"a: 2015-01-01",
map[string]interface{}{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
},
// Encode empty lists as zero-length slices.
{
"a: []",
&struct{ A []int }{[]int{}},
},
// UTF-16-LE
{
"\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00",
M{"ñoño": "very yes"},
},
// UTF-16-LE with surrogate.
{
"\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00",
M{"ñoño": "very yes 🟔"},
},
// UTF-16-BE
{
"\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n",
M{"ñoño": "very yes"},
},
// UTF-16-BE with surrogate.
{
"\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n",
M{"ñoño": "very yes 🟔"},
},
// This *is* in fact a float number, per the spec. #171 was a mistake.
{
"a: 123456e1\n",
M{"a": 123456e1},
}, {
"a: 123456E1\n",
M{"a": 123456e1},
},
// yaml-test-suite 3GZX: Spec Example 7.1. Alias Nodes
{
"First occurrence: &anchor Foo\nSecond occurrence: *anchor\nOverride anchor: &anchor Bar\nReuse anchor: *anchor\n",
map[string]interface{}{
"First occurrence": "Foo",
"Second occurrence": "Foo",
"Override anchor": "Bar",
"Reuse anchor": "Bar",
},
},
// Single document with garbage following it.
{
"---\nhello\n...\n}not yaml",
"hello",
},
// Comment scan exhausting the input buffer (issue #469).
{
"true\n#" + strings.Repeat(" ", 512*3),
"true",
}, {
"true #" + strings.Repeat(" ", 512*3),
"true",
},
// CRLF
{
"a: b\r\nc:\r\n- d\r\n- e\r\n",
map[string]interface{}{
"a": "b",
"c": []interface{}{"d", "e"},
},
},
}
type M map[string]interface{}
type inlineB struct {
B int
inlineC `yaml:",inline"`
}
type inlineC struct {
C int
}
type inlineD struct {
C *inlineC `yaml:",inline"`
D int
}
func (s *S) TestUnmarshal(c *C) {
for i, item := range unmarshalTests {
c.Logf("test %d: %q", i, item.data)
t := reflect.ValueOf(item.value).Type()
value := reflect.New(t)
err := yaml.Unmarshal([]byte(item.data), value.Interface())
if _, ok := err.(*yaml.TypeError); !ok {
c.Assert(err, IsNil)
}
c.Assert(value.Elem().Interface(), DeepEquals, item.value, Commentf("error: %v", err))
}
}
func (s *S) TestUnmarshalFullTimestamp(c *C) {
// Full timestamp in same format as encoded. This is confirmed to be
// properly decoded by Python as a timestamp as well.
var str = "2015-02-24T18:19:39.123456789-03:00"
var t interface{}
err := yaml.Unmarshal([]byte(str), &t)
c.Assert(err, IsNil)
c.Assert(t, Equals, time.Date(2015, 2, 24, 18, 19, 39, 123456789, t.(time.Time).Location()))
c.Assert(t.(time.Time).In(time.UTC), Equals, time.Date(2015, 2, 24, 21, 19, 39, 123456789, time.UTC))
}
func (s *S) TestDecoderSingleDocument(c *C) {
// Test that Decoder.Decode works as expected on
// all the unmarshal tests.
for i, item := range unmarshalTests {
c.Logf("test %d: %q", i, item.data)
if item.data == "" {
// Behaviour differs when there's no YAML.
continue
}
t := reflect.ValueOf(item.value).Type()
value := reflect.New(t)
err := yaml.NewDecoder(strings.NewReader(item.data)).Decode(value.Interface())
if _, ok := err.(*yaml.TypeError); !ok {
c.Assert(err, IsNil)
}
c.Assert(value.Elem().Interface(), DeepEquals, item.value)
}
}
var decoderTests = []struct {
data string
values []interface{}
}{{
"",
nil,
}, {
"a: b",
[]interface{}{
map[string]interface{}{"a": "b"},
},
}, {
"---\na: b\n...\n",
[]interface{}{
map[string]interface{}{"a": "b"},
},
}, {
"---\n'hello'\n...\n---\ngoodbye\n...\n",
[]interface{}{
"hello",
"goodbye",
},
}}
func (s *S) TestDecoder(c *C) {
for i, item := range decoderTests {
c.Logf("test %d: %q", i, item.data)
var values []interface{}
dec := yaml.NewDecoder(strings.NewReader(item.data))
for {
var value interface{}
err := dec.Decode(&value)
if err == io.EOF {
break
}
c.Assert(err, IsNil)
values = append(values, value)
}
c.Assert(values, DeepEquals, item.values)
}
}
type errReader struct{}
func (errReader) Read([]byte) (int, error) {
return 0, errors.New("some read error")
}
func (s *S) TestDecoderReadError(c *C) {
err := yaml.NewDecoder(errReader{}).Decode(&struct{}{})
c.Assert(err, ErrorMatches, `yaml: input error: some read error`)
}
func (s *S) TestUnmarshalNaN(c *C) {
value := map[string]interface{}{}
err := yaml.Unmarshal([]byte("notanum: .NaN"), &value)
c.Assert(err, IsNil)
c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true)
}
func (s *S) TestUnmarshalDurationInt(c *C) {
// Don't accept plain ints as durations as it's unclear (issue #200).
var d time.Duration
err := yaml.Unmarshal([]byte("123"), &d)
c.Assert(err, ErrorMatches, "(?s).* line 1: cannot unmarshal !!int `123` into time.Duration")
}
var unmarshalErrorTests = []struct {
data, error string
}{
{"v: !!float 'error'", "yaml: cannot decode !!str `error` as a !!float"},
{"v: [A,", "yaml: line 1: did not find expected node content"},
{"v:\n- [A,", "yaml: line 2: did not find expected node content"},
{"a:\n- b: *,", "yaml: line 2: did not find expected alphabetic or numeric character"},
{"a: *b\n", "yaml: unknown anchor 'b' referenced"},
{"a: &a\n b: *a\n", "yaml: anchor 'a' value contains itself"},
{"value: -", "yaml: block sequence entries are not allowed in this context"},
{"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"},
{"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`},
{"{{.}}", `yaml: invalid map key: map\[string]interface \{\}\{".":interface \{\}\(nil\)\}`},
{"b: *a\na: &a {c: 1}", `yaml: unknown anchor 'a' referenced`},
{"%TAG !%79! tag:yaml.org,2002:\n---\nv: !%79!int '1'", "yaml: did not find expected whitespace"},
{"a:\n 1:\nb\n 2:", ".*could not find expected ':'"},
{"a: 1\nb: 2\nc 2\nd: 3\n", "^yaml: line 3: could not find expected ':'$"},
{"#\n-\n{", "yaml: line 3: could not find expected ':'"}, // Issue #665
{"0: [:!00 \xef", "yaml: incomplete UTF-8 octet sequence"}, // Issue #666
{
"a: &a [00,00,00,00,00,00,00,00,00]\n" +
"b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]\n" +
"c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]\n" +
"d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]\n" +
"e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]\n" +
"f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e]\n" +
"g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f]\n" +
"h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g]\n" +
"i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h]\n",
"yaml: document contains excessive aliasing",
},
}
func (s *S) TestUnmarshalErrors(c *C) {
for i, item := range unmarshalErrorTests {
c.Logf("test %d: %q", i, item.data)
var value interface{}
err := yaml.Unmarshal([]byte(item.data), &value)
c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value))
}
}
func (s *S) TestDecoderErrors(c *C) {
for _, item := range unmarshalErrorTests {
var value interface{}
err := yaml.NewDecoder(strings.NewReader(item.data)).Decode(&value)
c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value))
}
}
var unmarshalerTests = []struct {
data, tag string
value interface{}
}{
{"_: {hi: there}", "!!map", map[string]interface{}{"hi": "there"}},
{"_: [1,A]", "!!seq", []interface{}{1, "A"}},
{"_: 10", "!!int", 10},
{"_: null", "!!null", nil},
{`_: BAR!`, "!!str", "BAR!"},
{`_: "BAR!"`, "!!str", "BAR!"},
{"_: !!foo 'BAR!'", "!!foo", "BAR!"},
{`_: ""`, "!!str", ""},
}
var unmarshalerResult = map[int]error{}
type unmarshalerType struct {
value interface{}
}
func (o *unmarshalerType) UnmarshalYAML(value *yaml.Node) error {
if err := value.Decode(&o.value); err != nil {
return err
}
if i, ok := o.value.(int); ok {
if result, ok := unmarshalerResult[i]; ok {
return result
}
}
return nil
}
type unmarshalerPointer struct {
Field *unmarshalerType "_"
}
type unmarshalerValue struct {
Field unmarshalerType "_"
}
type unmarshalerInlined struct {
Field *unmarshalerType "_"
Inlined unmarshalerType `yaml:",inline"`
}
type unmarshalerInlinedTwice struct {
InlinedTwice unmarshalerInlined `yaml:",inline"`
}
type obsoleteUnmarshalerType struct {
value interface{}
}
func (o *obsoleteUnmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error {
if err := unmarshal(&o.value); err != nil {
return err
}
if i, ok := o.value.(int); ok {
if result, ok := unmarshalerResult[i]; ok {
return result
}
}
return nil
}
type obsoleteUnmarshalerPointer struct {
Field *obsoleteUnmarshalerType "_"
}
type obsoleteUnmarshalerValue struct {
Field obsoleteUnmarshalerType "_"
}
func (s *S) TestUnmarshalerPointerField(c *C) {
for _, item := range unmarshalerTests {
obj := &unmarshalerPointer{}
err := yaml.Unmarshal([]byte(item.data), obj)
c.Assert(err, IsNil)
if item.value == nil {
c.Assert(obj.Field, IsNil)
} else {
c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
c.Assert(obj.Field.value, DeepEquals, item.value)
}
}
for _, item := range unmarshalerTests {
obj := &obsoleteUnmarshalerPointer{}
err := yaml.Unmarshal([]byte(item.data), obj)
c.Assert(err, IsNil)
if item.value == nil {
c.Assert(obj.Field, IsNil)
} else {
c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
c.Assert(obj.Field.value, DeepEquals, item.value)
}
}
}
func (s *S) TestUnmarshalerValueField(c *C) {
for _, item := range unmarshalerTests {
obj := &obsoleteUnmarshalerValue{}
err := yaml.Unmarshal([]byte(item.data), obj)
c.Assert(err, IsNil)
c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
c.Assert(obj.Field.value, DeepEquals, item.value)
}
}
func (s *S) TestUnmarshalerInlinedField(c *C) {
obj := &unmarshalerInlined{}
err := yaml.Unmarshal([]byte("_: a\ninlined: b\n"), obj)
c.Assert(err, IsNil)
c.Assert(obj.Field, DeepEquals, &unmarshalerType{"a"})
c.Assert(obj.Inlined, DeepEquals, unmarshalerType{map[string]interface{}{"_": "a", "inlined": "b"}})
twc := &unmarshalerInlinedTwice{}
err = yaml.Unmarshal([]byte("_: a\ninlined: b\n"), twc)
c.Assert(err, IsNil)
c.Assert(twc.InlinedTwice.Field, DeepEquals, &unmarshalerType{"a"})
c.Assert(twc.InlinedTwice.Inlined, DeepEquals, unmarshalerType{map[string]interface{}{"_": "a", "inlined": "b"}})
}
func (s *S) TestUnmarshalerWholeDocument(c *C) {
obj := &obsoleteUnmarshalerType{}
err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj)
c.Assert(err, IsNil)
value, ok := obj.value.(map[string]interface{})
c.Assert(ok, Equals, true, Commentf("value: %#v", obj.value))
c.Assert(value["_"], DeepEquals, unmarshalerTests[0].value)
}
func (s *S) TestUnmarshalerTypeError(c *C) {
unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}}
unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}}
defer func() {
delete(unmarshalerResult, 2)
delete(unmarshalerResult, 4)
}()
type T struct {
Before int
After int
M map[string]*unmarshalerType
}
var v T
data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}`
err := yaml.Unmarshal([]byte(data), &v)
c.Assert(err, ErrorMatches, ""+
"yaml: unmarshal errors:\n"+
" line 1: cannot unmarshal !!str `A` into int\n"+
" foo\n"+
" bar\n"+
" line 1: cannot unmarshal !!str `B` into int")
c.Assert(v.M["abc"], NotNil)
c.Assert(v.M["def"], IsNil)
c.Assert(v.M["ghi"], NotNil)
c.Assert(v.M["jkl"], IsNil)
c.Assert(v.M["abc"].value, Equals, 1)
c.Assert(v.M["ghi"].value, Equals, 3)
}
func (s *S) TestObsoleteUnmarshalerTypeError(c *C) {
unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}}
unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}}
defer func() {
delete(unmarshalerResult, 2)
delete(unmarshalerResult, 4)
}()
type T struct {
Before int
After int
M map[string]*obsoleteUnmarshalerType
}
var v T
data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}`
err := yaml.Unmarshal([]byte(data), &v)
c.Assert(err, ErrorMatches, ""+
"yaml: unmarshal errors:\n"+
" line 1: cannot unmarshal !!str `A` into int\n"+
" foo\n"+
" bar\n"+
" line 1: cannot unmarshal !!str `B` into int")
c.Assert(v.M["abc"], NotNil)
c.Assert(v.M["def"], IsNil)
c.Assert(v.M["ghi"], NotNil)
c.Assert(v.M["jkl"], IsNil)
c.Assert(v.M["abc"].value, Equals, 1)
c.Assert(v.M["ghi"].value, Equals, 3)
}
type proxyTypeError struct{}
func (v *proxyTypeError) UnmarshalYAML(node *yaml.Node) error {
var s string
var a int32
var b int64
if err := node.Decode(&s); err != nil {
panic(err)
}
if s == "a" {
if err := node.Decode(&b); err == nil {
panic("should have failed")
}
return node.Decode(&a)
}
if err := node.Decode(&a); err == nil {
panic("should have failed")
}
return node.Decode(&b)
}
func (s *S) TestUnmarshalerTypeErrorProxying(c *C) {
type T struct {
Before int
After int
M map[string]*proxyTypeError
}
var v T
data := `{before: A, m: {abc: a, def: b}, after: B}`
err := yaml.Unmarshal([]byte(data), &v)
c.Assert(err, ErrorMatches, ""+
"yaml: unmarshal errors:\n"+
" line 1: cannot unmarshal !!str `A` into int\n"+
" line 1: cannot unmarshal !!str `a` into int32\n"+
" line 1: cannot unmarshal !!str `b` into int64\n"+
" line 1: cannot unmarshal !!str `B` into int")
}
type obsoleteProxyTypeError struct{}
func (v *obsoleteProxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
var a int32
var b int64
if err := unmarshal(&s); err != nil {
panic(err)
}
if s == "a" {
if err := unmarshal(&b); err == nil {
panic("should have failed")
}
return unmarshal(&a)
}
if err := unmarshal(&a); err == nil {
panic("should have failed")
}
return unmarshal(&b)
}
func (s *S) TestObsoleteUnmarshalerTypeErrorProxying(c *C) {
type T struct {
Before int
After int
M map[string]*obsoleteProxyTypeError
}
var v T
data := `{before: A, m: {abc: a, def: b}, after: B}`
err := yaml.Unmarshal([]byte(data), &v)
c.Assert(err, ErrorMatches, ""+
"yaml: unmarshal errors:\n"+
" line 1: cannot unmarshal !!str `A` into int\n"+
" line 1: cannot unmarshal !!str `a` into int32\n"+
" line 1: cannot unmarshal !!str `b` into int64\n"+
" line 1: cannot unmarshal !!str `B` into int")
}
var failingErr = errors.New("failingErr")
type failingUnmarshaler struct{}
func (ft *failingUnmarshaler) UnmarshalYAML(node *yaml.Node) error {
return failingErr
}
func (s *S) TestUnmarshalerError(c *C) {
err := yaml.Unmarshal([]byte("a: b"), &failingUnmarshaler{})
c.Assert(err, Equals, failingErr)
}
type obsoleteFailingUnmarshaler struct{}
func (ft *obsoleteFailingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
return failingErr
}
func (s *S) TestObsoleteUnmarshalerError(c *C) {
err := yaml.Unmarshal([]byte("a: b"), &obsoleteFailingUnmarshaler{})
c.Assert(err, Equals, failingErr)
}
type sliceUnmarshaler []int
func (su *sliceUnmarshaler) UnmarshalYAML(node *yaml.Node) error {
var slice []int
err := node.Decode(&slice)
if err == nil {
*su = slice
return nil
}
var intVal int
err = node.Decode(&intVal)
if err == nil {
*su = []int{intVal}
return nil
}
return err
}
func (s *S) TestUnmarshalerRetry(c *C) {
var su sliceUnmarshaler
err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su)
c.Assert(err, IsNil)
c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1, 2, 3}))
err = yaml.Unmarshal([]byte("1"), &su)
c.Assert(err, IsNil)
c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1}))
}
type obsoleteSliceUnmarshaler []int
func (su *obsoleteSliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
var slice []int
err := unmarshal(&slice)
if err == nil {
*su = slice
return nil
}
var intVal int
err = unmarshal(&intVal)
if err == nil {
*su = []int{intVal}
return nil
}
return err
}
func (s *S) TestObsoleteUnmarshalerRetry(c *C) {
var su obsoleteSliceUnmarshaler
err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su)
c.Assert(err, IsNil)
c.Assert(su, DeepEquals, obsoleteSliceUnmarshaler([]int{1, 2, 3}))
err = yaml.Unmarshal([]byte("1"), &su)
c.Assert(err, IsNil)
c.Assert(su, DeepEquals, obsoleteSliceUnmarshaler([]int{1}))
}
// From http://yaml.org/type/merge.html
var mergeTests = `
anchors:
list:
- &CENTER { "x": 1, "y": 2 }
- &LEFT { "x": 0, "y": 2 }
- &BIG { "r": 10 }
- &SMALL { "r": 1 }
# All the following maps are equal:
plain:
# Explicit keys
"x": 1
"y": 2
"r": 10
label: center/big
mergeOne:
# Merge one map
<< : *CENTER
"r": 10
label: center/big
mergeMultiple:
# Merge multiple maps
<< : [ *CENTER, *BIG ]
label: center/big
override:
# Override
<< : [ *BIG, *LEFT, *SMALL ]
"x": 1
label: center/big
shortTag:
# Explicit short merge tag
!!merge "<<" : [ *CENTER, *BIG ]
label: center/big
longTag:
# Explicit merge long tag
! "<<" : [ *CENTER, *BIG ]
label: center/big
inlineMap:
# Inlined map
<< : {"x": 1, "y": 2, "r": 10}
label: center/big
inlineSequenceMap:
# Inlined map in sequence
<< : [ *CENTER, {"r": 10} ]
label: center/big
`
func (s *S) TestMerge(c *C) {
var want = map[string]interface{}{
"x": 1,
"y": 2,
"r": 10,
"label": "center/big",
}
wantStringMap := make(map[string]interface{})
for k, v := range want {
wantStringMap[fmt.Sprintf("%v", k)] = v
}
var m map[interface{}]interface{}
err := yaml.Unmarshal([]byte(mergeTests), &m)
c.Assert(err, IsNil)
for name, test := range m {
if name == "anchors" {
continue
}
if name == "plain" {
c.Assert(test, DeepEquals, wantStringMap, Commentf("test %q failed", name))
continue
}
c.Assert(test, DeepEquals, want, Commentf("test %q failed", name))
}
}
func (s *S) TestMergeStruct(c *C) {
type Data struct {
X, Y, R int
Label string
}
want := Data{1, 2, 10, "center/big"}
var m map[string]Data
err := yaml.Unmarshal([]byte(mergeTests), &m)
c.Assert(err, IsNil)
for name, test := range m {
if name == "anchors" {
continue
}
c.Assert(test, Equals, want, Commentf("test %q failed", name))
}
}
var mergeTestsNested = `
mergeouter1: &mergeouter1
d: 40
e: 50
mergeouter2: &mergeouter2
e: 5
f: 6
g: 70
mergeinner1: &mergeinner1
<<: *mergeouter1
inner:
a: 1
b: 2
mergeinner2: &mergeinner2
<<: *mergeouter2
inner:
a: -1
b: -2
outer:
<<: [*mergeinner1, *mergeinner2]
f: 60
inner:
a: 10
`
func (s *S) TestMergeNestedStruct(c *C) {
// Issue #818: Merging used to just unmarshal twice on the target
// value, which worked for maps as these were replaced by the new map,
// but not on struct values as these are preserved. This resulted in
// the nested data from the merged map to be mixed up with the data
// from the map being merged into.
//
// This test also prevents two potential bugs from showing up:
//
// 1) A simple implementation might just zero out the nested value
// before unmarshaling the second time, but this would clobber previous
// data that is usually respected ({C: 30} below).
//
// 2) A simple implementation might attempt to handle the key skipping
// directly by iterating over the merging map without recursion, but
// there are more complex cases that require recursion.
//
// Quick summary of the fields:
//
// - A must come from outer and not overriden
// - B must not be set as its in the ignored merge
// - C should still be set as it's preset in the value
// - D should be set from the recursive merge
// - E should be set from the first recursive merge, ignored on the second
// - F should be set in the inlined map from outer, ignored later
// - G should be set in the inlined map from the second recursive merge
//
type Inner struct {
A, B, C int
}
type Outer struct {
D, E int
Inner Inner
Inline map[string]int `yaml:",inline"`
}
type Data struct {
Outer Outer
}
test := Data{Outer{0, 0, Inner{C: 30}, nil}}
want := Data{Outer{40, 50, Inner{A: 10, C: 30}, map[string]int{"f": 60, "g": 70}}}
err := yaml.Unmarshal([]byte(mergeTestsNested), &test)
c.Assert(err, IsNil)
c.Assert(test, DeepEquals, want)
// Repeat test with a map.
var testm map[string]interface{}
var wantm = map[string]interface {} {
"f": 60,
"inner": map[string]interface{}{
"a": 10,
},
"d": 40,
"e": 50,
"g": 70,
}
err = yaml.Unmarshal([]byte(mergeTestsNested), &testm)
c.Assert(err, IsNil)
c.Assert(testm["outer"], DeepEquals, wantm)
}
var unmarshalNullTests = []struct {
input string
pristine, expected func() interface{}
}{{
"null",
func() interface{} { var v interface{}; v = "v"; return &v },
func() interface{} { var v interface{}; v = nil; return &v },
}, {
"null",
func() interface{} { var s = "s"; return &s },
func() interface{} { var s = "s"; return &s },
}, {
"null",
func() interface{} { var s = "s"; sptr := &s; return &sptr },
func() interface{} { var sptr *string; return &sptr },
}, {
"null",
func() interface{} { var i = 1; return &i },
func() interface{} { var i = 1; return &i },
}, {
"null",
func() interface{} { var i = 1; iptr := &i; return &iptr },
func() interface{} { var iptr *int; return &iptr },
}, {
"null",
func() interface{} { var m = map[string]int{"s": 1}; return &m },
func() interface{} { var m map[string]int; return &m },
}, {
"null",
func() interface{} { var m = map[string]int{"s": 1}; return m },
func() interface{} { var m = map[string]int{"s": 1}; return m },
}, {
"s2: null\ns3: null",
func() interface{} { var m = map[string]int{"s1": 1, "s2": 2}; return m },
func() interface{} { var m = map[string]int{"s1": 1, "s2": 2, "s3": 0}; return m },
}, {
"s2: null\ns3: null",
func() interface{} { var m = map[string]interface{}{"s1": 1, "s2": 2}; return m },
func() interface{} { var m = map[string]interface{}{"s1": 1, "s2": nil, "s3": nil}; return m },
}}
func (s *S) TestUnmarshalNull(c *C) {
for _, test := range unmarshalNullTests {
pristine := test.pristine()
expected := test.expected()
err := yaml.Unmarshal([]byte(test.input), pristine)
c.Assert(err, IsNil)
c.Assert(pristine, DeepEquals, expected)
}
}
func (s *S) TestUnmarshalPreservesData(c *C) {
var v struct {
A, B int
C int `yaml:"-"`
}
v.A = 42
v.C = 88
err := yaml.Unmarshal([]byte("---"), &v)
c.Assert(err, IsNil)
c.Assert(v.A, Equals, 42)
c.Assert(v.B, Equals, 0)
c.Assert(v.C, Equals, 88)
err = yaml.Unmarshal([]byte("b: 21\nc: 99"), &v)
c.Assert(err, IsNil)
c.Assert(v.A, Equals, 42)
c.Assert(v.B, Equals, 21)
c.Assert(v.C, Equals, 88)
}
func (s *S) TestUnmarshalSliceOnPreset(c *C) {
// Issue #48.
v := struct{ A []int }{[]int{1}}
yaml.Unmarshal([]byte("a: [2]"), &v)
c.Assert(v.A, DeepEquals, []int{2})
}
var unmarshalStrictTests = []struct {
known bool
unique bool
data string
value interface{}
error string
}{{
known: true,
data: "a: 1\nc: 2\n",
value: struct{ A, B int }{A: 1},
error: `yaml: unmarshal errors:\n line 2: field c not found in type struct { A int; B int }`,
}, {
unique: true,
data: "a: 1\nb: 2\na: 3\n",
value: struct{ A, B int }{A: 3, B: 2},
error: `yaml: unmarshal errors:\n line 3: mapping key "a" already defined at line 1`,
}, {
unique: true,
data: "c: 3\na: 1\nb: 2\nc: 4\n",
value: struct {
A int
inlineB `yaml:",inline"`
}{
A: 1,
inlineB: inlineB{
B: 2,
inlineC: inlineC{
C: 4,
},
},
},
error: `yaml: unmarshal errors:\n line 4: mapping key "c" already defined at line 1`,
}, {
unique: true,
data: "c: 0\na: 1\nb: 2\nc: 1\n",
value: struct {
A int
inlineB `yaml:",inline"`
}{
A: 1,
inlineB: inlineB{
B: 2,
inlineC: inlineC{
C: 1,
},
},
},
error: `yaml: unmarshal errors:\n line 4: mapping key "c" already defined at line 1`,
}, {
unique: true,
data: "c: 1\na: 1\nb: 2\nc: 3\n",
value: struct {
A int
M map[string]interface{} `yaml:",inline"`
}{
A: 1,
M: map[string]interface{}{
"b": 2,
"c": 3,
},
},
error: `yaml: unmarshal errors:\n line 4: mapping key "c" already defined at line 1`,
}, {
unique: true,
data: "a: 1\n9: 2\nnull: 3\n9: 4",
value: map[interface{}]interface{}{
"a": 1,
nil: 3,
9: 4,
},
error: `yaml: unmarshal errors:\n line 4: mapping key "9" already defined at line 2`,
}}
func (s *S) TestUnmarshalKnownFields(c *C) {
for i, item := range unmarshalStrictTests {
c.Logf("test %d: %q", i, item.data)
// First test that normal Unmarshal unmarshals to the expected value.
if !item.unique {
t := reflect.ValueOf(item.value).Type()
value := reflect.New(t)
err := yaml.Unmarshal([]byte(item.data), value.Interface())
c.Assert(err, Equals, nil)
c.Assert(value.Elem().Interface(), DeepEquals, item.value)
}
// Then test that it fails on the same thing with KnownFields on.
t := reflect.ValueOf(item.value).Type()
value := reflect.New(t)
dec := yaml.NewDecoder(bytes.NewBuffer([]byte(item.data)))
dec.KnownFields(item.known)
err := dec.Decode(value.Interface())
c.Assert(err, ErrorMatches, item.error)
}
}
type textUnmarshaler struct {
S string
}
func (t *textUnmarshaler) UnmarshalText(s []byte) error {
t.S = string(s)
return nil
}
func (s *S) TestFuzzCrashers(c *C) {
cases := []string{
// runtime error: index out of range
"\"\\0\\\r\n",
// should not happen
" 0: [\n] 0",
"? ? \"\n\" 0",
" - {\n000}0",
"0:\n 0: [0\n] 0",
" - \"\n000\"0",
" - \"\n000\"\"",
"0:\n - {\n000}0",
"0:\n - \"\n000\"0",
"0:\n - \"\n000\"\"",
// runtime error: index out of range
" \ufeff\n",
"? \ufeff\n",
"? \ufeff:\n",
"0: \ufeff\n",
"? \ufeff: \ufeff\n",
}
for _, data := range cases {
var v interface{}
_ = yaml.Unmarshal([]byte(data), &v)
}
}
//var data []byte
//func init() {
// var err error
// data, err = ioutil.ReadFile("/tmp/file.yaml")
// if err != nil {
// panic(err)
// }
//}
//
//func (s *S) BenchmarkUnmarshal(c *C) {
// var err error
// for i := 0; i < c.N; i++ {
// var v map[string]interface{}
// err = yaml.Unmarshal(data, &v)
// }
// if err != nil {
// panic(err)
// }
//}
//
//func (s *S) BenchmarkMarshal(c *C) {
// var v map[string]interface{}
// yaml.Unmarshal(data, &v)
// c.ResetTimer()
// for i := 0; i < c.N; i++ {
// yaml.Marshal(&v)
// }
//}
yaml-3.0.1/emitterc.go 0000664 0000000 0000000 00000153623 14244106522 0014632 0 ustar 00root root 0000000 0000000 //
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
"bytes"
"fmt"
)
// Flush the buffer if needed.
func flush(emitter *yaml_emitter_t) bool {
if emitter.buffer_pos+5 >= len(emitter.buffer) {
return yaml_emitter_flush(emitter)
}
return true
}
// Put a character to the output buffer.
func put(emitter *yaml_emitter_t, value byte) bool {
if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) {
return false
}
emitter.buffer[emitter.buffer_pos] = value
emitter.buffer_pos++
emitter.column++
return true
}
// Put a line break to the output buffer.
func put_break(emitter *yaml_emitter_t) bool {
if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) {
return false
}
switch emitter.line_break {
case yaml_CR_BREAK:
emitter.buffer[emitter.buffer_pos] = '\r'
emitter.buffer_pos += 1
case yaml_LN_BREAK:
emitter.buffer[emitter.buffer_pos] = '\n'
emitter.buffer_pos += 1
case yaml_CRLN_BREAK:
emitter.buffer[emitter.buffer_pos+0] = '\r'
emitter.buffer[emitter.buffer_pos+1] = '\n'
emitter.buffer_pos += 2
default:
panic("unknown line break setting")
}
if emitter.column == 0 {
emitter.space_above = true
}
emitter.column = 0
emitter.line++
// [Go] Do this here and below and drop from everywhere else (see commented lines).
emitter.indention = true
return true
}
// Copy a character from a string into buffer.
func write(emitter *yaml_emitter_t, s []byte, i *int) bool {
if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) {
return false
}
p := emitter.buffer_pos
w := width(s[*i])
switch w {
case 4:
emitter.buffer[p+3] = s[*i+3]
fallthrough
case 3:
emitter.buffer[p+2] = s[*i+2]
fallthrough
case 2:
emitter.buffer[p+1] = s[*i+1]
fallthrough
case 1:
emitter.buffer[p+0] = s[*i+0]
default:
panic("unknown character width")
}
emitter.column++
emitter.buffer_pos += w
*i += w
return true
}
// Write a whole string into buffer.
func write_all(emitter *yaml_emitter_t, s []byte) bool {
for i := 0; i < len(s); {
if !write(emitter, s, &i) {
return false
}
}
return true
}
// Copy a line break character from a string into buffer.
func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool {
if s[*i] == '\n' {
if !put_break(emitter) {
return false
}
*i++
} else {
if !write(emitter, s, i) {
return false
}
if emitter.column == 0 {
emitter.space_above = true
}
emitter.column = 0
emitter.line++
// [Go] Do this here and above and drop from everywhere else (see commented lines).
emitter.indention = true
}
return true
}
// Set an emitter error and return false.
func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool {
emitter.error = yaml_EMITTER_ERROR
emitter.problem = problem
return false
}
// Emit an event.
func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool {
emitter.events = append(emitter.events, *event)
for !yaml_emitter_need_more_events(emitter) {
event := &emitter.events[emitter.events_head]
if !yaml_emitter_analyze_event(emitter, event) {
return false
}
if !yaml_emitter_state_machine(emitter, event) {
return false
}
yaml_event_delete(event)
emitter.events_head++
}
return true
}
// Check if we need to accumulate more events before emitting.
//
// We accumulate extra
// - 1 event for DOCUMENT-START
// - 2 events for SEQUENCE-START
// - 3 events for MAPPING-START
//
func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool {
if emitter.events_head == len(emitter.events) {
return true
}
var accumulate int
switch emitter.events[emitter.events_head].typ {
case yaml_DOCUMENT_START_EVENT:
accumulate = 1
break
case yaml_SEQUENCE_START_EVENT:
accumulate = 2
break
case yaml_MAPPING_START_EVENT:
accumulate = 3
break
default:
return false
}
if len(emitter.events)-emitter.events_head > accumulate {
return false
}
var level int
for i := emitter.events_head; i < len(emitter.events); i++ {
switch emitter.events[i].typ {
case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT:
level++
case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT:
level--
}
if level == 0 {
return false
}
}
return true
}
// Append a directive to the directives stack.
func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool {
for i := 0; i < len(emitter.tag_directives); i++ {
if bytes.Equal(value.handle, emitter.tag_directives[i].handle) {
if allow_duplicates {
return true
}
return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive")
}
}
// [Go] Do we actually need to copy this given garbage collection
// and the lack of deallocating destructors?
tag_copy := yaml_tag_directive_t{
handle: make([]byte, len(value.handle)),
prefix: make([]byte, len(value.prefix)),
}
copy(tag_copy.handle, value.handle)
copy(tag_copy.prefix, value.prefix)
emitter.tag_directives = append(emitter.tag_directives, tag_copy)
return true
}
// Increase the indentation level.
func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool {
emitter.indents = append(emitter.indents, emitter.indent)
if emitter.indent < 0 {
if flow {
emitter.indent = emitter.best_indent
} else {
emitter.indent = 0
}
} else if !indentless {
// [Go] This was changed so that indentations are more regular.
if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE {
// The first indent inside a sequence will just skip the "- " indicator.
emitter.indent += 2
} else {
// Everything else aligns to the chosen indentation.
emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent)
}
}
return true
}
// State dispatcher.
func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool {
switch emitter.state {
default:
case yaml_EMIT_STREAM_START_STATE:
return yaml_emitter_emit_stream_start(emitter, event)
case yaml_EMIT_FIRST_DOCUMENT_START_STATE:
return yaml_emitter_emit_document_start(emitter, event, true)
case yaml_EMIT_DOCUMENT_START_STATE:
return yaml_emitter_emit_document_start(emitter, event, false)
case yaml_EMIT_DOCUMENT_CONTENT_STATE:
return yaml_emitter_emit_document_content(emitter, event)
case yaml_EMIT_DOCUMENT_END_STATE:
return yaml_emitter_emit_document_end(emitter, event)
case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false)
case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE:
return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true)
case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE:
return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false)
case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false)
case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE:
return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true)
case yaml_EMIT_FLOW_MAPPING_KEY_STATE:
return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false)
case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
return yaml_emitter_emit_flow_mapping_value(emitter, event, true)
case yaml_EMIT_FLOW_MAPPING_VALUE_STATE:
return yaml_emitter_emit_flow_mapping_value(emitter, event, false)
case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE:
return yaml_emitter_emit_block_sequence_item(emitter, event, true)
case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE:
return yaml_emitter_emit_block_sequence_item(emitter, event, false)
case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE:
return yaml_emitter_emit_block_mapping_key(emitter, event, true)
case yaml_EMIT_BLOCK_MAPPING_KEY_STATE:
return yaml_emitter_emit_block_mapping_key(emitter, event, false)
case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE:
return yaml_emitter_emit_block_mapping_value(emitter, event, true)
case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE:
return yaml_emitter_emit_block_mapping_value(emitter, event, false)
case yaml_EMIT_END_STATE:
return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END")
}
panic("invalid emitter state")
}
// Expect STREAM-START.
func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
if event.typ != yaml_STREAM_START_EVENT {
return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START")
}
if emitter.encoding == yaml_ANY_ENCODING {
emitter.encoding = event.encoding
if emitter.encoding == yaml_ANY_ENCODING {
emitter.encoding = yaml_UTF8_ENCODING
}
}
if emitter.best_indent < 2 || emitter.best_indent > 9 {
emitter.best_indent = 2
}
if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 {
emitter.best_width = 80
}
if emitter.best_width < 0 {
emitter.best_width = 1<<31 - 1
}
if emitter.line_break == yaml_ANY_BREAK {
emitter.line_break = yaml_LN_BREAK
}
emitter.indent = -1
emitter.line = 0
emitter.column = 0
emitter.whitespace = true
emitter.indention = true
emitter.space_above = true
emitter.foot_indent = -1
if emitter.encoding != yaml_UTF8_ENCODING {
if !yaml_emitter_write_bom(emitter) {
return false
}
}
emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE
return true
}
// Expect DOCUMENT-START or STREAM-END.
func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
if event.typ == yaml_DOCUMENT_START_EVENT {
if event.version_directive != nil {
if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) {
return false
}
}
for i := 0; i < len(event.tag_directives); i++ {
tag_directive := &event.tag_directives[i]
if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) {
return false
}
if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) {
return false
}
}
for i := 0; i < len(default_tag_directives); i++ {
tag_directive := &default_tag_directives[i]
if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) {
return false
}
}
implicit := event.implicit
if !first || emitter.canonical {
implicit = false
}
if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) {
if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if event.version_directive != nil {
implicit = false
if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) {
return false
}
if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if len(event.tag_directives) > 0 {
implicit = false
for i := 0; i < len(event.tag_directives); i++ {
tag_directive := &event.tag_directives[i]
if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) {
return false
}
if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) {
return false
}
if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
}
}
if yaml_emitter_check_empty_document(emitter) {
implicit = false
}
if !implicit {
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) {
return false
}
if emitter.canonical || true {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
}
if len(emitter.head_comment) > 0 {
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !put_break(emitter) {
return false
}
}
emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE
return true
}
if event.typ == yaml_STREAM_END_EVENT {
if emitter.open_ended {
if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if !yaml_emitter_flush(emitter) {
return false
}
emitter.state = yaml_EMIT_END_STATE
return true
}
return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END")
}
// Expect the root node.
func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool {
emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE)
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !yaml_emitter_emit_node(emitter, event, true, false, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect DOCUMENT-END.
func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool {
if event.typ != yaml_DOCUMENT_END_EVENT {
return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END")
}
// [Go] Force document foot separation.
emitter.foot_indent = 0
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
emitter.foot_indent = -1
if !yaml_emitter_write_indent(emitter) {
return false
}
if !event.implicit {
// [Go] Allocate the slice elsewhere.
if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if !yaml_emitter_flush(emitter) {
return false
}
emitter.state = yaml_EMIT_DOCUMENT_START_STATE
emitter.tag_directives = emitter.tag_directives[:0]
return true
}
// Expect a flow item node.
func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
if first {
if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) {
return false
}
if !yaml_emitter_increase_indent(emitter, true, false) {
return false
}
emitter.flow_level++
}
if event.typ == yaml_SEQUENCE_END_EVENT {
if emitter.canonical && !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
emitter.flow_level--
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
if emitter.column == 0 || emitter.canonical && !first {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if emitter.column == 0 {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if emitter.canonical || emitter.column > emitter.best_width {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE)
} else {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
}
if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
return false
}
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a flow key node.
func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
if first {
if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) {
return false
}
if !yaml_emitter_increase_indent(emitter, true, false) {
return false
}
emitter.flow_level++
}
if event.typ == yaml_MAPPING_END_EVENT {
if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
emitter.flow_level--
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
if emitter.canonical && !first {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if emitter.column == 0 {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if emitter.canonical || emitter.column > emitter.best_width {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if !emitter.canonical && yaml_emitter_check_simple_key(emitter) {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE)
return yaml_emitter_emit_node(emitter, event, false, false, true, true)
}
if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) {
return false
}
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE)
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
}
// Expect a flow value node.
func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool {
if simple {
if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) {
return false
}
} else {
if emitter.canonical || emitter.column > emitter.best_width {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) {
return false
}
}
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE)
} else {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
}
if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
return false
}
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a block item node.
func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
if first {
if !yaml_emitter_increase_indent(emitter, false, false) {
return false
}
}
if event.typ == yaml_SEQUENCE_END_EVENT {
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) {
return false
}
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE)
if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a block key node.
func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
if first {
if !yaml_emitter_increase_indent(emitter, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if event.typ == yaml_MAPPING_END_EVENT {
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !yaml_emitter_write_indent(emitter) {
return false
}
if len(emitter.line_comment) > 0 {
// [Go] A line comment was provided for the key. That's unusual as the
// scanner associates line comments with the value. Either way,
// save the line comment and render it appropriately later.
emitter.key_line_comment = emitter.line_comment
emitter.line_comment = nil
}
if yaml_emitter_check_simple_key(emitter) {
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE)
return yaml_emitter_emit_node(emitter, event, false, false, true, true)
}
if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) {
return false
}
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE)
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
}
// Expect a block value node.
func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool {
if simple {
if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) {
return false
}
} else {
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) {
return false
}
}
if len(emitter.key_line_comment) > 0 {
// [Go] Line comments are generally associated with the value, but when there's
// no value on the same line as a mapping key they end up attached to the
// key itself.
if event.typ == yaml_SCALAR_EVENT {
if len(emitter.line_comment) == 0 {
// A scalar is coming and it has no line comments by itself yet,
// so just let it handle the line comment as usual. If it has a
// line comment, we can't have both so the one from the key is lost.
emitter.line_comment = emitter.key_line_comment
emitter.key_line_comment = nil
}
} else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) {
// An indented block follows, so write the comment right now.
emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
if !yaml_emitter_process_line_comment(emitter) {
return false
}
emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
}
}
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE)
if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool {
return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0
}
// Expect a node.
func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t,
root bool, sequence bool, mapping bool, simple_key bool) bool {
emitter.root_context = root
emitter.sequence_context = sequence
emitter.mapping_context = mapping
emitter.simple_key_context = simple_key
switch event.typ {
case yaml_ALIAS_EVENT:
return yaml_emitter_emit_alias(emitter, event)
case yaml_SCALAR_EVENT:
return yaml_emitter_emit_scalar(emitter, event)
case yaml_SEQUENCE_START_EVENT:
return yaml_emitter_emit_sequence_start(emitter, event)
case yaml_MAPPING_START_EVENT:
return yaml_emitter_emit_mapping_start(emitter, event)
default:
return yaml_emitter_set_emitter_error(emitter,
fmt.Sprintf("expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, but got %v", event.typ))
}
}
// Expect ALIAS.
func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool {
if !yaml_emitter_process_anchor(emitter) {
return false
}
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
// Expect SCALAR.
func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool {
if !yaml_emitter_select_scalar_style(emitter, event) {
return false
}
if !yaml_emitter_process_anchor(emitter) {
return false
}
if !yaml_emitter_process_tag(emitter) {
return false
}
if !yaml_emitter_increase_indent(emitter, true, false) {
return false
}
if !yaml_emitter_process_scalar(emitter) {
return false
}
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
// Expect SEQUENCE-START.
func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
if !yaml_emitter_process_anchor(emitter) {
return false
}
if !yaml_emitter_process_tag(emitter) {
return false
}
if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE ||
yaml_emitter_check_empty_sequence(emitter) {
emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE
} else {
emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE
}
return true
}
// Expect MAPPING-START.
func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
if !yaml_emitter_process_anchor(emitter) {
return false
}
if !yaml_emitter_process_tag(emitter) {
return false
}
if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE ||
yaml_emitter_check_empty_mapping(emitter) {
emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE
} else {
emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE
}
return true
}
// Check if the document content is an empty scalar.
func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool {
return false // [Go] Huh?
}
// Check if the next events represent an empty sequence.
func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool {
if len(emitter.events)-emitter.events_head < 2 {
return false
}
return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT &&
emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT
}
// Check if the next events represent an empty mapping.
func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool {
if len(emitter.events)-emitter.events_head < 2 {
return false
}
return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT &&
emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT
}
// Check if the next node can be expressed as a simple key.
func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool {
length := 0
switch emitter.events[emitter.events_head].typ {
case yaml_ALIAS_EVENT:
length += len(emitter.anchor_data.anchor)
case yaml_SCALAR_EVENT:
if emitter.scalar_data.multiline {
return false
}
length += len(emitter.anchor_data.anchor) +
len(emitter.tag_data.handle) +
len(emitter.tag_data.suffix) +
len(emitter.scalar_data.value)
case yaml_SEQUENCE_START_EVENT:
if !yaml_emitter_check_empty_sequence(emitter) {
return false
}
length += len(emitter.anchor_data.anchor) +
len(emitter.tag_data.handle) +
len(emitter.tag_data.suffix)
case yaml_MAPPING_START_EVENT:
if !yaml_emitter_check_empty_mapping(emitter) {
return false
}
length += len(emitter.anchor_data.anchor) +
len(emitter.tag_data.handle) +
len(emitter.tag_data.suffix)
default:
return false
}
return length <= 128
}
// Determine an acceptable scalar style.
func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool {
no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0
if no_tag && !event.implicit && !event.quoted_implicit {
return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified")
}
style := event.scalar_style()
if style == yaml_ANY_SCALAR_STYLE {
style = yaml_PLAIN_SCALAR_STYLE
}
if emitter.canonical {
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
if emitter.simple_key_context && emitter.scalar_data.multiline {
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
if style == yaml_PLAIN_SCALAR_STYLE {
if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed ||
emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed {
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
}
if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) {
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
}
if no_tag && !event.implicit {
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
}
}
if style == yaml_SINGLE_QUOTED_SCALAR_STYLE {
if !emitter.scalar_data.single_quoted_allowed {
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
}
if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE {
if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context {
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
}
if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE {
emitter.tag_data.handle = []byte{'!'}
}
emitter.scalar_data.style = style
return true
}
// Write an anchor.
func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool {
if emitter.anchor_data.anchor == nil {
return true
}
c := []byte{'&'}
if emitter.anchor_data.alias {
c[0] = '*'
}
if !yaml_emitter_write_indicator(emitter, c, true, false, false) {
return false
}
return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor)
}
// Write a tag.
func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool {
if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 {
return true
}
if len(emitter.tag_data.handle) > 0 {
if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) {
return false
}
if len(emitter.tag_data.suffix) > 0 {
if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) {
return false
}
}
} else {
// [Go] Allocate these slices elsewhere.
if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) {
return false
}
if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) {
return false
}
if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) {
return false
}
}
return true
}
// Write a scalar.
func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool {
switch emitter.scalar_data.style {
case yaml_PLAIN_SCALAR_STYLE:
return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context)
case yaml_SINGLE_QUOTED_SCALAR_STYLE:
return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context)
case yaml_DOUBLE_QUOTED_SCALAR_STYLE:
return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context)
case yaml_LITERAL_SCALAR_STYLE:
return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value)
case yaml_FOLDED_SCALAR_STYLE:
return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value)
}
panic("unknown scalar style")
}
// Write a head comment.
func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool {
if len(emitter.tail_comment) > 0 {
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_comment(emitter, emitter.tail_comment) {
return false
}
emitter.tail_comment = emitter.tail_comment[:0]
emitter.foot_indent = emitter.indent
if emitter.foot_indent < 0 {
emitter.foot_indent = 0
}
}
if len(emitter.head_comment) == 0 {
return true
}
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_comment(emitter, emitter.head_comment) {
return false
}
emitter.head_comment = emitter.head_comment[:0]
return true
}
// Write an line comment.
func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool {
if len(emitter.line_comment) == 0 {
return true
}
if !emitter.whitespace {
if !put(emitter, ' ') {
return false
}
}
if !yaml_emitter_write_comment(emitter, emitter.line_comment) {
return false
}
emitter.line_comment = emitter.line_comment[:0]
return true
}
// Write a foot comment.
func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool {
if len(emitter.foot_comment) == 0 {
return true
}
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_comment(emitter, emitter.foot_comment) {
return false
}
emitter.foot_comment = emitter.foot_comment[:0]
emitter.foot_indent = emitter.indent
if emitter.foot_indent < 0 {
emitter.foot_indent = 0
}
return true
}
// Check if a %YAML directive is valid.
func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool {
if version_directive.major != 1 || version_directive.minor != 1 {
return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive")
}
return true
}
// Check if a %TAG directive is valid.
func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool {
handle := tag_directive.handle
prefix := tag_directive.prefix
if len(handle) == 0 {
return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty")
}
if handle[0] != '!' {
return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'")
}
if handle[len(handle)-1] != '!' {
return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'")
}
for i := 1; i < len(handle)-1; i += width(handle[i]) {
if !is_alpha(handle, i) {
return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only")
}
}
if len(prefix) == 0 {
return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty")
}
return true
}
// Check if an anchor is valid.
func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool {
if len(anchor) == 0 {
problem := "anchor value must not be empty"
if alias {
problem = "alias value must not be empty"
}
return yaml_emitter_set_emitter_error(emitter, problem)
}
for i := 0; i < len(anchor); i += width(anchor[i]) {
if !is_alpha(anchor, i) {
problem := "anchor value must contain alphanumerical characters only"
if alias {
problem = "alias value must contain alphanumerical characters only"
}
return yaml_emitter_set_emitter_error(emitter, problem)
}
}
emitter.anchor_data.anchor = anchor
emitter.anchor_data.alias = alias
return true
}
// Check if a tag is valid.
func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool {
if len(tag) == 0 {
return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty")
}
for i := 0; i < len(emitter.tag_directives); i++ {
tag_directive := &emitter.tag_directives[i]
if bytes.HasPrefix(tag, tag_directive.prefix) {
emitter.tag_data.handle = tag_directive.handle
emitter.tag_data.suffix = tag[len(tag_directive.prefix):]
return true
}
}
emitter.tag_data.suffix = tag
return true
}
// Check if a scalar is valid.
func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
var (
block_indicators = false
flow_indicators = false
line_breaks = false
special_characters = false
tab_characters = false
leading_space = false
leading_break = false
trailing_space = false
trailing_break = false
break_space = false
space_break = false
preceded_by_whitespace = false
followed_by_whitespace = false
previous_space = false
previous_break = false
)
emitter.scalar_data.value = value
if len(value) == 0 {
emitter.scalar_data.multiline = false
emitter.scalar_data.flow_plain_allowed = false
emitter.scalar_data.block_plain_allowed = true
emitter.scalar_data.single_quoted_allowed = true
emitter.scalar_data.block_allowed = false
return true
}
if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) {
block_indicators = true
flow_indicators = true
}
preceded_by_whitespace = true
for i, w := 0, 0; i < len(value); i += w {
w = width(value[i])
followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w)
if i == 0 {
switch value[i] {
case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`':
flow_indicators = true
block_indicators = true
case '?', ':':
flow_indicators = true
if followed_by_whitespace {
block_indicators = true
}
case '-':
if followed_by_whitespace {
flow_indicators = true
block_indicators = true
}
}
} else {
switch value[i] {
case ',', '?', '[', ']', '{', '}':
flow_indicators = true
case ':':
flow_indicators = true
if followed_by_whitespace {
block_indicators = true
}
case '#':
if preceded_by_whitespace {
flow_indicators = true
block_indicators = true
}
}
}
if value[i] == '\t' {
tab_characters = true
} else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode {
special_characters = true
}
if is_space(value, i) {
if i == 0 {
leading_space = true
}
if i+width(value[i]) == len(value) {
trailing_space = true
}
if previous_break {
break_space = true
}
previous_space = true
previous_break = false
} else if is_break(value, i) {
line_breaks = true
if i == 0 {
leading_break = true
}
if i+width(value[i]) == len(value) {
trailing_break = true
}
if previous_space {
space_break = true
}
previous_space = false
previous_break = true
} else {
previous_space = false
previous_break = false
}
// [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition.
preceded_by_whitespace = is_blankz(value, i)
}
emitter.scalar_data.multiline = line_breaks
emitter.scalar_data.flow_plain_allowed = true
emitter.scalar_data.block_plain_allowed = true
emitter.scalar_data.single_quoted_allowed = true
emitter.scalar_data.block_allowed = true
if leading_space || leading_break || trailing_space || trailing_break {
emitter.scalar_data.flow_plain_allowed = false
emitter.scalar_data.block_plain_allowed = false
}
if trailing_space {
emitter.scalar_data.block_allowed = false
}
if break_space {
emitter.scalar_data.flow_plain_allowed = false
emitter.scalar_data.block_plain_allowed = false
emitter.scalar_data.single_quoted_allowed = false
}
if space_break || tab_characters || special_characters {
emitter.scalar_data.flow_plain_allowed = false
emitter.scalar_data.block_plain_allowed = false
emitter.scalar_data.single_quoted_allowed = false
}
if space_break || special_characters {
emitter.scalar_data.block_allowed = false
}
if line_breaks {
emitter.scalar_data.flow_plain_allowed = false
emitter.scalar_data.block_plain_allowed = false
}
if flow_indicators {
emitter.scalar_data.flow_plain_allowed = false
}
if block_indicators {
emitter.scalar_data.block_plain_allowed = false
}
return true
}
// Check if the event data is valid.
func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool {
emitter.anchor_data.anchor = nil
emitter.tag_data.handle = nil
emitter.tag_data.suffix = nil
emitter.scalar_data.value = nil
if len(event.head_comment) > 0 {
emitter.head_comment = event.head_comment
}
if len(event.line_comment) > 0 {
emitter.line_comment = event.line_comment
}
if len(event.foot_comment) > 0 {
emitter.foot_comment = event.foot_comment
}
if len(event.tail_comment) > 0 {
emitter.tail_comment = event.tail_comment
}
switch event.typ {
case yaml_ALIAS_EVENT:
if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) {
return false
}
case yaml_SCALAR_EVENT:
if len(event.anchor) > 0 {
if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) {
return false
}
}
if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) {
if !yaml_emitter_analyze_tag(emitter, event.tag) {
return false
}
}
if !yaml_emitter_analyze_scalar(emitter, event.value) {
return false
}
case yaml_SEQUENCE_START_EVENT:
if len(event.anchor) > 0 {
if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) {
return false
}
}
if len(event.tag) > 0 && (emitter.canonical || !event.implicit) {
if !yaml_emitter_analyze_tag(emitter, event.tag) {
return false
}
}
case yaml_MAPPING_START_EVENT:
if len(event.anchor) > 0 {
if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) {
return false
}
}
if len(event.tag) > 0 && (emitter.canonical || !event.implicit) {
if !yaml_emitter_analyze_tag(emitter, event.tag) {
return false
}
}
}
return true
}
// Write the BOM character.
func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool {
if !flush(emitter) {
return false
}
pos := emitter.buffer_pos
emitter.buffer[pos+0] = '\xEF'
emitter.buffer[pos+1] = '\xBB'
emitter.buffer[pos+2] = '\xBF'
emitter.buffer_pos += 3
return true
}
func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool {
indent := emitter.indent
if indent < 0 {
indent = 0
}
if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) {
if !put_break(emitter) {
return false
}
}
if emitter.foot_indent == indent {
if !put_break(emitter) {
return false
}
}
for emitter.column < indent {
if !put(emitter, ' ') {
return false
}
}
emitter.whitespace = true
//emitter.indention = true
emitter.space_above = false
emitter.foot_indent = -1
return true
}
func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool {
if need_whitespace && !emitter.whitespace {
if !put(emitter, ' ') {
return false
}
}
if !write_all(emitter, indicator) {
return false
}
emitter.whitespace = is_whitespace
emitter.indention = (emitter.indention && is_indention)
emitter.open_ended = false
return true
}
func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool {
if !write_all(emitter, value) {
return false
}
emitter.whitespace = false
emitter.indention = false
return true
}
func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool {
if !emitter.whitespace {
if !put(emitter, ' ') {
return false
}
}
if !write_all(emitter, value) {
return false
}
emitter.whitespace = false
emitter.indention = false
return true
}
func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool {
if need_whitespace && !emitter.whitespace {
if !put(emitter, ' ') {
return false
}
}
for i := 0; i < len(value); {
var must_write bool
switch value[i] {
case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']':
must_write = true
default:
must_write = is_alpha(value, i)
}
if must_write {
if !write(emitter, value, &i) {
return false
}
} else {
w := width(value[i])
for k := 0; k < w; k++ {
octet := value[i]
i++
if !put(emitter, '%') {
return false
}
c := octet >> 4
if c < 10 {
c += '0'
} else {
c += 'A' - 10
}
if !put(emitter, c) {
return false
}
c = octet & 0x0f
if c < 10 {
c += '0'
} else {
c += 'A' - 10
}
if !put(emitter, c) {
return false
}
}
}
}
emitter.whitespace = false
emitter.indention = false
return true
}
func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
if len(value) > 0 && !emitter.whitespace {
if !put(emitter, ' ') {
return false
}
}
spaces := false
breaks := false
for i := 0; i < len(value); {
if is_space(value, i) {
if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) {
if !yaml_emitter_write_indent(emitter) {
return false
}
i += width(value[i])
} else {
if !write(emitter, value, &i) {
return false
}
}
spaces = true
} else if is_break(value, i) {
if !breaks && value[i] == '\n' {
if !put_break(emitter) {
return false
}
}
if !write_break(emitter, value, &i) {
return false
}
//emitter.indention = true
breaks = true
} else {
if breaks {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if !write(emitter, value, &i) {
return false
}
emitter.indention = false
spaces = false
breaks = false
}
}
if len(value) > 0 {
emitter.whitespace = false
}
emitter.indention = false
if emitter.root_context {
emitter.open_ended = true
}
return true
}
func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) {
return false
}
spaces := false
breaks := false
for i := 0; i < len(value); {
if is_space(value, i) {
if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) {
if !yaml_emitter_write_indent(emitter) {
return false
}
i += width(value[i])
} else {
if !write(emitter, value, &i) {
return false
}
}
spaces = true
} else if is_break(value, i) {
if !breaks && value[i] == '\n' {
if !put_break(emitter) {
return false
}
}
if !write_break(emitter, value, &i) {
return false
}
//emitter.indention = true
breaks = true
} else {
if breaks {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if value[i] == '\'' {
if !put(emitter, '\'') {
return false
}
}
if !write(emitter, value, &i) {
return false
}
emitter.indention = false
spaces = false
breaks = false
}
}
if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) {
return false
}
emitter.whitespace = false
emitter.indention = false
return true
}
func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
spaces := false
if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) {
return false
}
for i := 0; i < len(value); {
if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) ||
is_bom(value, i) || is_break(value, i) ||
value[i] == '"' || value[i] == '\\' {
octet := value[i]
var w int
var v rune
switch {
case octet&0x80 == 0x00:
w, v = 1, rune(octet&0x7F)
case octet&0xE0 == 0xC0:
w, v = 2, rune(octet&0x1F)
case octet&0xF0 == 0xE0:
w, v = 3, rune(octet&0x0F)
case octet&0xF8 == 0xF0:
w, v = 4, rune(octet&0x07)
}
for k := 1; k < w; k++ {
octet = value[i+k]
v = (v << 6) + (rune(octet) & 0x3F)
}
i += w
if !put(emitter, '\\') {
return false
}
var ok bool
switch v {
case 0x00:
ok = put(emitter, '0')
case 0x07:
ok = put(emitter, 'a')
case 0x08:
ok = put(emitter, 'b')
case 0x09:
ok = put(emitter, 't')
case 0x0A:
ok = put(emitter, 'n')
case 0x0b:
ok = put(emitter, 'v')
case 0x0c:
ok = put(emitter, 'f')
case 0x0d:
ok = put(emitter, 'r')
case 0x1b:
ok = put(emitter, 'e')
case 0x22:
ok = put(emitter, '"')
case 0x5c:
ok = put(emitter, '\\')
case 0x85:
ok = put(emitter, 'N')
case 0xA0:
ok = put(emitter, '_')
case 0x2028:
ok = put(emitter, 'L')
case 0x2029:
ok = put(emitter, 'P')
default:
if v <= 0xFF {
ok = put(emitter, 'x')
w = 2
} else if v <= 0xFFFF {
ok = put(emitter, 'u')
w = 4
} else {
ok = put(emitter, 'U')
w = 8
}
for k := (w - 1) * 4; ok && k >= 0; k -= 4 {
digit := byte((v >> uint(k)) & 0x0F)
if digit < 10 {
ok = put(emitter, digit+'0')
} else {
ok = put(emitter, digit+'A'-10)
}
}
}
if !ok {
return false
}
spaces = false
} else if is_space(value, i) {
if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 {
if !yaml_emitter_write_indent(emitter) {
return false
}
if is_space(value, i+1) {
if !put(emitter, '\\') {
return false
}
}
i += width(value[i])
} else if !write(emitter, value, &i) {
return false
}
spaces = true
} else {
if !write(emitter, value, &i) {
return false
}
spaces = false
}
}
if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) {
return false
}
emitter.whitespace = false
emitter.indention = false
return true
}
func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool {
if is_space(value, 0) || is_break(value, 0) {
indent_hint := []byte{'0' + byte(emitter.best_indent)}
if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) {
return false
}
}
emitter.open_ended = false
var chomp_hint [1]byte
if len(value) == 0 {
chomp_hint[0] = '-'
} else {
i := len(value) - 1
for value[i]&0xC0 == 0x80 {
i--
}
if !is_break(value, i) {
chomp_hint[0] = '-'
} else if i == 0 {
chomp_hint[0] = '+'
emitter.open_ended = true
} else {
i--
for value[i]&0xC0 == 0x80 {
i--
}
if is_break(value, i) {
chomp_hint[0] = '+'
emitter.open_ended = true
}
}
}
if chomp_hint[0] != 0 {
if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) {
return false
}
}
return true
}
func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool {
if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) {
return false
}
if !yaml_emitter_write_block_scalar_hints(emitter, value) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
//emitter.indention = true
emitter.whitespace = true
breaks := true
for i := 0; i < len(value); {
if is_break(value, i) {
if !write_break(emitter, value, &i) {
return false
}
//emitter.indention = true
breaks = true
} else {
if breaks {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if !write(emitter, value, &i) {
return false
}
emitter.indention = false
breaks = false
}
}
return true
}
func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool {
if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) {
return false
}
if !yaml_emitter_write_block_scalar_hints(emitter, value) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
//emitter.indention = true
emitter.whitespace = true
breaks := true
leading_spaces := true
for i := 0; i < len(value); {
if is_break(value, i) {
if !breaks && !leading_spaces && value[i] == '\n' {
k := 0
for is_break(value, k) {
k += width(value[k])
}
if !is_blankz(value, k) {
if !put_break(emitter) {
return false
}
}
}
if !write_break(emitter, value, &i) {
return false
}
//emitter.indention = true
breaks = true
} else {
if breaks {
if !yaml_emitter_write_indent(emitter) {
return false
}
leading_spaces = is_blank(value, i)
}
if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width {
if !yaml_emitter_write_indent(emitter) {
return false
}
i += width(value[i])
} else {
if !write(emitter, value, &i) {
return false
}
}
emitter.indention = false
breaks = false
}
}
return true
}
func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool {
breaks := false
pound := false
for i := 0; i < len(comment); {
if is_break(comment, i) {
if !write_break(emitter, comment, &i) {
return false
}
//emitter.indention = true
breaks = true
pound = false
} else {
if breaks && !yaml_emitter_write_indent(emitter) {
return false
}
if !pound {
if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) {
return false
}
pound = true
}
if !write(emitter, comment, &i) {
return false
}
emitter.indention = false
breaks = false
}
}
if !breaks && !put_break(emitter) {
return false
}
emitter.whitespace = true
//emitter.indention = true
return true
}
yaml-3.0.1/encode.go 0000664 0000000 0000000 00000034705 14244106522 0014252 0 ustar 00root root 0000000 0000000 //
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
"encoding"
"fmt"
"io"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
)
type encoder struct {
emitter yaml_emitter_t
event yaml_event_t
out []byte
flow bool
indent int
doneInit bool
}
func newEncoder() *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_string(&e.emitter, &e.out)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func newEncoderWithWriter(w io.Writer) *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_writer(&e.emitter, w)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func (e *encoder) init() {
if e.doneInit {
return
}
if e.indent == 0 {
e.indent = 4
}
e.emitter.best_indent = e.indent
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
e.emit()
e.doneInit = true
}
func (e *encoder) finish() {
e.emitter.open_ended = false
yaml_stream_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) destroy() {
yaml_emitter_delete(&e.emitter)
}
func (e *encoder) emit() {
// This will internally delete the e.event value.
e.must(yaml_emitter_emit(&e.emitter, &e.event))
}
func (e *encoder) must(ok bool) {
if !ok {
msg := e.emitter.problem
if msg == "" {
msg = "unknown problem generating YAML content"
}
failf("%s", msg)
}
}
func (e *encoder) marshalDoc(tag string, in reflect.Value) {
e.init()
var node *Node
if in.IsValid() {
node, _ = in.Interface().(*Node)
}
if node != nil && node.Kind == DocumentNode {
e.nodev(in)
} else {
yaml_document_start_event_initialize(&e.event, nil, nil, true)
e.emit()
e.marshal(tag, in)
yaml_document_end_event_initialize(&e.event, true)
e.emit()
}
}
func (e *encoder) marshal(tag string, in reflect.Value) {
tag = shortTag(tag)
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
e.nilv()
return
}
iface := in.Interface()
switch value := iface.(type) {
case *Node:
e.nodev(in)
return
case Node:
if !in.CanAddr() {
var n = reflect.New(in.Type()).Elem()
n.Set(in)
in = n
}
e.nodev(in.Addr())
return
case time.Time:
e.timev(tag, in)
return
case *time.Time:
e.timev(tag, in.Elem())
return
case time.Duration:
e.stringv(tag, reflect.ValueOf(value.String()))
return
case Marshaler:
v, err := value.MarshalYAML()
if err != nil {
fail(err)
}
if v == nil {
e.nilv()
return
}
e.marshal(tag, reflect.ValueOf(v))
return
case encoding.TextMarshaler:
text, err := value.MarshalText()
if err != nil {
fail(err)
}
in = reflect.ValueOf(string(text))
case nil:
e.nilv()
return
}
switch in.Kind() {
case reflect.Interface:
e.marshal(tag, in.Elem())
case reflect.Map:
e.mapv(tag, in)
case reflect.Ptr:
e.marshal(tag, in.Elem())
case reflect.Struct:
e.structv(tag, in)
case reflect.Slice, reflect.Array:
e.slicev(tag, in)
case reflect.String:
e.stringv(tag, in)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
e.intv(tag, in)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
e.uintv(tag, in)
case reflect.Float32, reflect.Float64:
e.floatv(tag, in)
case reflect.Bool:
e.boolv(tag, in)
default:
panic("cannot marshal type: " + in.Type().String())
}
}
func (e *encoder) mapv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
keys := keyList(in.MapKeys())
sort.Sort(keys)
for _, k := range keys {
e.marshal("", k)
e.marshal("", in.MapIndex(k))
}
})
}
func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) {
for _, num := range index {
for {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return reflect.Value{}
}
v = v.Elem()
continue
}
break
}
v = v.Field(num)
}
return v
}
func (e *encoder) structv(tag string, in reflect.Value) {
sinfo, err := getStructInfo(in.Type())
if err != nil {
panic(err)
}
e.mappingv(tag, func() {
for _, info := range sinfo.FieldsList {
var value reflect.Value
if info.Inline == nil {
value = in.Field(info.Num)
} else {
value = e.fieldByIndex(in, info.Inline)
if !value.IsValid() {
continue
}
}
if info.OmitEmpty && isZero(value) {
continue
}
e.marshal("", reflect.ValueOf(info.Key))
e.flow = info.Flow
e.marshal("", value)
}
if sinfo.InlineMap >= 0 {
m := in.Field(sinfo.InlineMap)
if m.Len() > 0 {
e.flow = false
keys := keyList(m.MapKeys())
sort.Sort(keys)
for _, k := range keys {
if _, found := sinfo.FieldsMap[k.String()]; found {
panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String()))
}
e.marshal("", k)
e.flow = false
e.marshal("", m.MapIndex(k))
}
}
}
})
}
func (e *encoder) mappingv(tag string, f func()) {
implicit := tag == ""
style := yaml_BLOCK_MAPPING_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_MAPPING_STYLE
}
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
e.emit()
f()
yaml_mapping_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) slicev(tag string, in reflect.Value) {
implicit := tag == ""
style := yaml_BLOCK_SEQUENCE_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
e.emit()
n := in.Len()
for i := 0; i < n; i++ {
e.marshal("", in.Index(i))
}
e.must(yaml_sequence_end_event_initialize(&e.event))
e.emit()
}
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
//
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
// in YAML 1.2 and by this package, but these should be marshalled quoted for
// the time being for compatibility with other parsers.
func isBase60Float(s string) (result bool) {
// Fast path.
if s == "" {
return false
}
c := s[0]
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
return false
}
// Do the full match.
return base60float.MatchString(s)
}
// From http://yaml.org/type/float.html, except the regular expression there
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
// isOldBool returns whether s is bool notation as defined in YAML 1.1.
//
// We continue to force strings that YAML 1.1 would interpret as booleans to be
// rendered as quotes strings so that the marshalled output valid for YAML 1.1
// parsing.
func isOldBool(s string) (result bool) {
switch s {
case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON",
"n", "N", "no", "No", "NO", "off", "Off", "OFF":
return true
default:
return false
}
}
func (e *encoder) stringv(tag string, in reflect.Value) {
var style yaml_scalar_style_t
s := in.String()
canUsePlain := true
switch {
case !utf8.ValidString(s):
if tag == binaryTag {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if tag != "" {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
}
// It can't be encoded directly as YAML so use a binary tag
// and encode it as base64.
tag = binaryTag
s = encodeBase64(s)
case tag == "":
// Check to see if it would resolve to a specific
// tag when encoded unquoted. If it doesn't,
// there's no need to quote it.
rtag, _ := resolve("", s)
canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s))
}
// Note: it's possible for user code to emit invalid YAML
// if they explicitly specify a tag and a string containing
// text that's incompatible with that tag.
switch {
case strings.Contains(s, "\n"):
if e.flow {
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
} else {
style = yaml_LITERAL_SCALAR_STYLE
}
case canUsePlain:
style = yaml_PLAIN_SCALAR_STYLE
default:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
e.emitScalar(s, "", tag, style, nil, nil, nil, nil)
}
func (e *encoder) boolv(tag string, in reflect.Value) {
var s string
if in.Bool() {
s = "true"
} else {
s = "false"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) intv(tag string, in reflect.Value) {
s := strconv.FormatInt(in.Int(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) uintv(tag string, in reflect.Value) {
s := strconv.FormatUint(in.Uint(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) timev(tag string, in reflect.Value) {
t := in.Interface().(time.Time)
s := t.Format(time.RFC3339Nano)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) floatv(tag string, in reflect.Value) {
// Issue #352: When formatting, use the precision of the underlying value
precision := 64
if in.Kind() == reflect.Float32 {
precision = 32
}
s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
switch s {
case "+Inf":
s = ".inf"
case "-Inf":
s = "-.inf"
case "NaN":
s = ".nan"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) nilv() {
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) {
// TODO Kill this function. Replace all initialize calls by their underlining Go literals.
implicit := tag == ""
if !implicit {
tag = longTag(tag)
}
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
e.event.head_comment = head
e.event.line_comment = line
e.event.foot_comment = foot
e.event.tail_comment = tail
e.emit()
}
func (e *encoder) nodev(in reflect.Value) {
e.node(in.Interface().(*Node), "")
}
func (e *encoder) node(node *Node, tail string) {
// Zero nodes behave as nil.
if node.Kind == 0 && node.IsZero() {
e.nilv()
return
}
// If the tag was not explicitly requested, and dropping it won't change the
// implicit tag of the value, don't include it in the presentation.
var tag = node.Tag
var stag = shortTag(tag)
var forceQuoting bool
if tag != "" && node.Style&TaggedStyle == 0 {
if node.Kind == ScalarNode {
if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 {
tag = ""
} else {
rtag, _ := resolve("", node.Value)
if rtag == stag {
tag = ""
} else if stag == strTag {
tag = ""
forceQuoting = true
}
}
} else {
var rtag string
switch node.Kind {
case MappingNode:
rtag = mapTag
case SequenceNode:
rtag = seqTag
}
if rtag == stag {
tag = ""
}
}
}
switch node.Kind {
case DocumentNode:
yaml_document_start_event_initialize(&e.event, nil, nil, true)
e.event.head_comment = []byte(node.HeadComment)
e.emit()
for _, node := range node.Content {
e.node(node, "")
}
yaml_document_end_event_initialize(&e.event, true)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case SequenceNode:
style := yaml_BLOCK_SEQUENCE_STYLE
if node.Style&FlowStyle != 0 {
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style))
e.event.head_comment = []byte(node.HeadComment)
e.emit()
for _, node := range node.Content {
e.node(node, "")
}
e.must(yaml_sequence_end_event_initialize(&e.event))
e.event.line_comment = []byte(node.LineComment)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case MappingNode:
style := yaml_BLOCK_MAPPING_STYLE
if node.Style&FlowStyle != 0 {
style = yaml_FLOW_MAPPING_STYLE
}
yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)
e.event.tail_comment = []byte(tail)
e.event.head_comment = []byte(node.HeadComment)
e.emit()
// The tail logic below moves the foot comment of prior keys to the following key,
// since the value for each key may be a nested structure and the foot needs to be
// processed only the entirety of the value is streamed. The last tail is processed
// with the mapping end event.
var tail string
for i := 0; i+1 < len(node.Content); i += 2 {
k := node.Content[i]
foot := k.FootComment
if foot != "" {
kopy := *k
kopy.FootComment = ""
k = &kopy
}
e.node(k, tail)
tail = foot
v := node.Content[i+1]
e.node(v, "")
}
yaml_mapping_end_event_initialize(&e.event)
e.event.tail_comment = []byte(tail)
e.event.line_comment = []byte(node.LineComment)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case AliasNode:
yaml_alias_event_initialize(&e.event, []byte(node.Value))
e.event.head_comment = []byte(node.HeadComment)
e.event.line_comment = []byte(node.LineComment)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case ScalarNode:
value := node.Value
if !utf8.ValidString(value) {
if stag == binaryTag {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if stag != "" {
failf("cannot marshal invalid UTF-8 data as %s", stag)
}
// It can't be encoded directly as YAML so use a binary tag
// and encode it as base64.
tag = binaryTag
value = encodeBase64(value)
}
style := yaml_PLAIN_SCALAR_STYLE
switch {
case node.Style&DoubleQuotedStyle != 0:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
case node.Style&SingleQuotedStyle != 0:
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
case node.Style&LiteralStyle != 0:
style = yaml_LITERAL_SCALAR_STYLE
case node.Style&FoldedStyle != 0:
style = yaml_FOLDED_SCALAR_STYLE
case strings.Contains(value, "\n"):
style = yaml_LITERAL_SCALAR_STYLE
case forceQuoting:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail))
default:
failf("cannot encode node with unknown kind %d", node.Kind)
}
}
yaml-3.0.1/encode_test.go 0000664 0000000 0000000 00000034711 14244106522 0015306 0 ustar 00root root 0000000 0000000 //
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml_test
import (
"bytes"
"fmt"
"math"
"strconv"
"strings"
"time"
"net"
"os"
. "gopkg.in/check.v1"
"gopkg.in/yaml.v3"
)
var marshalIntTest = 123
var marshalTests = []struct {
value interface{}
data string
}{
{
nil,
"null\n",
}, {
(*marshalerType)(nil),
"null\n",
}, {
&struct{}{},
"{}\n",
}, {
map[string]string{"v": "hi"},
"v: hi\n",
}, {
map[string]interface{}{"v": "hi"},
"v: hi\n",
}, {
map[string]string{"v": "true"},
"v: \"true\"\n",
}, {
map[string]string{"v": "false"},
"v: \"false\"\n",
}, {
map[string]interface{}{"v": true},
"v: true\n",
}, {
map[string]interface{}{"v": false},
"v: false\n",
}, {
map[string]interface{}{"v": 10},
"v: 10\n",
}, {
map[string]interface{}{"v": -10},
"v: -10\n",
}, {
map[string]uint{"v": 42},
"v: 42\n",
}, {
map[string]interface{}{"v": int64(4294967296)},
"v: 4294967296\n",
}, {
map[string]int64{"v": int64(4294967296)},
"v: 4294967296\n",
}, {
map[string]uint64{"v": 4294967296},
"v: 4294967296\n",
}, {
map[string]interface{}{"v": "10"},
"v: \"10\"\n",
}, {
map[string]interface{}{"v": 0.1},
"v: 0.1\n",
}, {
map[string]interface{}{"v": float64(0.1)},
"v: 0.1\n",
}, {
map[string]interface{}{"v": float32(0.99)},
"v: 0.99\n",
}, {
map[string]interface{}{"v": -0.1},
"v: -0.1\n",
}, {
map[string]interface{}{"v": math.Inf(+1)},
"v: .inf\n",
}, {
map[string]interface{}{"v": math.Inf(-1)},
"v: -.inf\n",
}, {
map[string]interface{}{"v": math.NaN()},
"v: .nan\n",
}, {
map[string]interface{}{"v": nil},
"v: null\n",
}, {
map[string]interface{}{"v": ""},
"v: \"\"\n",
}, {
map[string][]string{"v": []string{"A", "B"}},
"v:\n - A\n - B\n",
}, {
map[string][]string{"v": []string{"A", "B\nC"}},
"v:\n - A\n - |-\n B\n C\n",
}, {
map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
"v:\n - A\n - 1\n - B:\n - 2\n - 3\n",
}, {
map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
"a:\n b: c\n",
}, {
map[string]interface{}{"a": "-"},
"a: '-'\n",
},
// Simple values.
{
&marshalIntTest,
"123\n",
},
// Structures
{
&struct{ Hello string }{"world"},
"hello: world\n",
}, {
&struct {
A struct {
B string
}
}{struct{ B string }{"c"}},
"a:\n b: c\n",
}, {
&struct {
A *struct {
B string
}
}{&struct{ B string }{"c"}},
"a:\n b: c\n",
}, {
&struct {
A *struct {
B string
}
}{},
"a: null\n",
}, {
&struct{ A int }{1},
"a: 1\n",
}, {
&struct{ A []int }{[]int{1, 2}},
"a:\n - 1\n - 2\n",
}, {
&struct{ A [2]int }{[2]int{1, 2}},
"a:\n - 1\n - 2\n",
}, {
&struct {
B int "a"
}{1},
"a: 1\n",
}, {
&struct{ A bool }{true},
"a: true\n",
}, {
&struct{ A string }{"true"},
"a: \"true\"\n",
}, {
&struct{ A string }{"off"},
"a: \"off\"\n",
},
// Conditional flag
{
&struct {
A int "a,omitempty"
B int "b,omitempty"
}{1, 0},
"a: 1\n",
}, {
&struct {
A int "a,omitempty"
B int "b,omitempty"
}{0, 0},
"{}\n",
}, {
&struct {
A *struct{ X, y int } "a,omitempty,flow"
}{&struct{ X, y int }{1, 2}},
"a: {x: 1}\n",
}, {
&struct {
A *struct{ X, y int } "a,omitempty,flow"
}{nil},
"{}\n",
}, {
&struct {
A *struct{ X, y int } "a,omitempty,flow"
}{&struct{ X, y int }{}},
"a: {x: 0}\n",
}, {
&struct {
A struct{ X, y int } "a,omitempty,flow"
}{struct{ X, y int }{1, 2}},
"a: {x: 1}\n",
}, {
&struct {
A struct{ X, y int } "a,omitempty,flow"
}{struct{ X, y int }{0, 1}},
"{}\n",
}, {
&struct {
A float64 "a,omitempty"
B float64 "b,omitempty"
}{1, 0},
"a: 1\n",
},
{
&struct {
T1 time.Time "t1,omitempty"
T2 time.Time "t2,omitempty"
T3 *time.Time "t3,omitempty"
T4 *time.Time "t4,omitempty"
}{
T2: time.Date(2018, 1, 9, 10, 40, 47, 0, time.UTC),
T4: newTime(time.Date(2098, 1, 9, 10, 40, 47, 0, time.UTC)),
},
"t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n",
},
// Nil interface that implements Marshaler.
{
map[string]yaml.Marshaler{
"a": nil,
},
"a: null\n",
},
// Flow flag
{
&struct {
A []int "a,flow"
}{[]int{1, 2}},
"a: [1, 2]\n",
}, {
&struct {
A map[string]string "a,flow"
}{map[string]string{"b": "c", "d": "e"}},
"a: {b: c, d: e}\n",
}, {
&struct {
A struct {
B, D string
} "a,flow"
}{struct{ B, D string }{"c", "e"}},
"a: {b: c, d: e}\n",
}, {
&struct {
A string "a,flow"
}{"b\nc"},
"a: \"b\\nc\"\n",
},
// Unexported field
{
&struct {
u int
A int
}{0, 1},
"a: 1\n",
},
// Ignored field
{
&struct {
A int
B int "-"
}{1, 2},
"a: 1\n",
},
// Struct inlining
{
&struct {
A int
C inlineB `yaml:",inline"`
}{1, inlineB{2, inlineC{3}}},
"a: 1\nb: 2\nc: 3\n",
},
// Struct inlining as a pointer
{
&struct {
A int
C *inlineB `yaml:",inline"`
}{1, &inlineB{2, inlineC{3}}},
"a: 1\nb: 2\nc: 3\n",
}, {
&struct {
A int
C *inlineB `yaml:",inline"`
}{1, nil},
"a: 1\n",
}, {
&struct {
A int
D *inlineD `yaml:",inline"`
}{1, &inlineD{&inlineC{3}, 4}},
"a: 1\nc: 3\nd: 4\n",
},
// Map inlining
{
&struct {
A int
C map[string]int `yaml:",inline"`
}{1, map[string]int{"b": 2, "c": 3}},
"a: 1\nb: 2\nc: 3\n",
},
// Duration
{
map[string]time.Duration{"a": 3 * time.Second},
"a: 3s\n",
},
// Issue #24: bug in map merging logic.
{
map[string]string{"a": ""},
"a: \n",
},
// Issue #34: marshal unsupported base 60 floats quoted for compatibility
// with old YAML 1.1 parsers.
{
map[string]string{"a": "1:1"},
"a: \"1:1\"\n",
},
// Binary data.
{
map[string]string{"a": "\x00"},
"a: \"\\0\"\n",
}, {
map[string]string{"a": "\x80\x81\x82"},
"a: !!binary gIGC\n",
}, {
map[string]string{"a": strings.Repeat("\x90", 54)},
"a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n",
},
// Encode unicode as utf-8 rather than in escaped form.
{
map[string]string{"a": "你好"},
"a: 你好\n",
},
// Support encoding.TextMarshaler.
{
map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
"a: 1.2.3.4\n",
},
// time.Time gets a timestamp tag.
{
map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)},
"a: 2015-02-24T18:19:39Z\n",
},
{
map[string]*time.Time{"a": newTime(time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC))},
"a: 2015-02-24T18:19:39Z\n",
},
{
// This is confirmed to be properly decoded in Python (libyaml) without a timestamp tag.
map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 123456789, time.FixedZone("FOO", -3*60*60))},
"a: 2015-02-24T18:19:39.123456789-03:00\n",
},
// Ensure timestamp-like strings are quoted.
{
map[string]string{"a": "2015-02-24T18:19:39Z"},
"a: \"2015-02-24T18:19:39Z\"\n",
},
// Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible).
{
map[string]string{"a": "b: c"},
"a: 'b: c'\n",
},
// Containing hash mark ('#') in string should be quoted
{
map[string]string{"a": "Hello #comment"},
"a: 'Hello #comment'\n",
},
{
map[string]string{"a": "你好 #comment"},
"a: '你好 #comment'\n",
},
// Ensure MarshalYAML also gets called on the result of MarshalYAML itself.
{
&marshalerType{marshalerType{true}},
"true\n",
}, {
&marshalerType{&marshalerType{true}},
"true\n",
},
// Check indentation of maps inside sequences inside maps.
{
map[string]interface{}{"a": map[string]interface{}{"b": []map[string]int{{"c": 1, "d": 2}}}},
"a:\n b:\n - c: 1\n d: 2\n",
},
// Strings with tabs were disallowed as literals (issue #471).
{
map[string]string{"a": "\tB\n\tC\n"},
"a: |\n \tB\n \tC\n",
},
// Ensure that strings do not wrap
{
map[string]string{"a": "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 "},
"a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n",
},
// yaml.Node
{
&struct {
Value yaml.Node
}{
yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "foo",
Style: yaml.SingleQuotedStyle,
},
},
"value: 'foo'\n",
}, {
yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "foo",
Style: yaml.SingleQuotedStyle,
},
"'foo'\n",
},
// Enforced tagging with shorthand notation (issue #616).
{
&struct {
Value yaml.Node
}{
yaml.Node{
Kind: yaml.ScalarNode,
Style: yaml.TaggedStyle,
Value: "foo",
Tag: "!!str",
},
},
"value: !!str foo\n",
}, {
&struct {
Value yaml.Node
}{
yaml.Node{
Kind: yaml.MappingNode,
Style: yaml.TaggedStyle,
Tag: "!!map",
},
},
"value: !!map {}\n",
}, {
&struct {
Value yaml.Node
}{
yaml.Node{
Kind: yaml.SequenceNode,
Style: yaml.TaggedStyle,
Tag: "!!seq",
},
},
"value: !!seq []\n",
},
}
func (s *S) TestMarshal(c *C) {
defer os.Setenv("TZ", os.Getenv("TZ"))
os.Setenv("TZ", "UTC")
for i, item := range marshalTests {
c.Logf("test %d: %q", i, item.data)
data, err := yaml.Marshal(item.value)
c.Assert(err, IsNil)
c.Assert(string(data), Equals, item.data)
}
}
func (s *S) TestEncoderSingleDocument(c *C) {
for i, item := range marshalTests {
c.Logf("test %d. %q", i, item.data)
var buf bytes.Buffer
enc := yaml.NewEncoder(&buf)
err := enc.Encode(item.value)
c.Assert(err, Equals, nil)
err = enc.Close()
c.Assert(err, Equals, nil)
c.Assert(buf.String(), Equals, item.data)
}
}
func (s *S) TestEncoderMultipleDocuments(c *C) {
var buf bytes.Buffer
enc := yaml.NewEncoder(&buf)
err := enc.Encode(map[string]string{"a": "b"})
c.Assert(err, Equals, nil)
err = enc.Encode(map[string]string{"c": "d"})
c.Assert(err, Equals, nil)
err = enc.Close()
c.Assert(err, Equals, nil)
c.Assert(buf.String(), Equals, "a: b\n---\nc: d\n")
}
func (s *S) TestEncoderWriteError(c *C) {
enc := yaml.NewEncoder(errorWriter{})
err := enc.Encode(map[string]string{"a": "b"})
c.Assert(err, ErrorMatches, `yaml: write error: some write error`) // Data not flushed yet
}
type errorWriter struct{}
func (errorWriter) Write([]byte) (int, error) {
return 0, fmt.Errorf("some write error")
}
var marshalErrorTests = []struct {
value interface{}
error string
panic string
}{{
value: &struct {
B int
inlineB ",inline"
}{1, inlineB{2, inlineC{3}}},
panic: `duplicated key 'b' in struct struct \{ B int; .*`,
}, {
value: &struct {
A int
B map[string]int ",inline"
}{1, map[string]int{"a": 2}},
panic: `cannot have key "a" in inlined map: conflicts with struct field`,
}}
func (s *S) TestMarshalErrors(c *C) {
for _, item := range marshalErrorTests {
if item.panic != "" {
c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
} else {
_, err := yaml.Marshal(item.value)
c.Assert(err, ErrorMatches, item.error)
}
}
}
func (s *S) TestMarshalTypeCache(c *C) {
var data []byte
var err error
func() {
type T struct{ A int }
data, err = yaml.Marshal(&T{})
c.Assert(err, IsNil)
}()
func() {
type T struct{ B int }
data, err = yaml.Marshal(&T{})
c.Assert(err, IsNil)
}()
c.Assert(string(data), Equals, "b: 0\n")
}
var marshalerTests = []struct {
data string
value interface{}
}{
{"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}},
{"_:\n - 1\n - A\n", []interface{}{1, "A"}},
{"_: 10\n", 10},
{"_: null\n", nil},
{"_: BAR!\n", "BAR!"},
}
type marshalerType struct {
value interface{}
}
func (o marshalerType) MarshalText() ([]byte, error) {
panic("MarshalText called on type with MarshalYAML")
}
func (o marshalerType) MarshalYAML() (interface{}, error) {
return o.value, nil
}
type marshalerValue struct {
Field marshalerType "_"
}
func (s *S) TestMarshaler(c *C) {
for _, item := range marshalerTests {
obj := &marshalerValue{}
obj.Field.value = item.value
data, err := yaml.Marshal(obj)
c.Assert(err, IsNil)
c.Assert(string(data), Equals, string(item.data))
}
}
func (s *S) TestMarshalerWholeDocument(c *C) {
obj := &marshalerType{}
obj.value = map[string]string{"hello": "world!"}
data, err := yaml.Marshal(obj)
c.Assert(err, IsNil)
c.Assert(string(data), Equals, "hello: world!\n")
}
type failingMarshaler struct{}
func (ft *failingMarshaler) MarshalYAML() (interface{}, error) {
return nil, failingErr
}
func (s *S) TestMarshalerError(c *C) {
_, err := yaml.Marshal(&failingMarshaler{})
c.Assert(err, Equals, failingErr)
}
func (s *S) TestSetIndent(c *C) {
var buf bytes.Buffer
enc := yaml.NewEncoder(&buf)
enc.SetIndent(8)
err := enc.Encode(map[string]interface{}{"a": map[string]interface{}{"b": map[string]string{"c": "d"}}})
c.Assert(err, Equals, nil)
err = enc.Close()
c.Assert(err, Equals, nil)
c.Assert(buf.String(), Equals, "a:\n b:\n c: d\n")
}
func (s *S) TestSortedOutput(c *C) {
order := []interface{}{
false,
true,
1,
uint(1),
1.0,
1.1,
1.2,
2,
uint(2),
2.0,
2.1,
"",
".1",
".2",
".a",
"1",
"2",
"a!10",
"a/0001",
"a/002",
"a/3",
"a/10",
"a/11",
"a/0012",
"a/100",
"a~10",
"ab/1",
"b/1",
"b/01",
"b/2",
"b/02",
"b/3",
"b/03",
"b1",
"b01",
"b3",
"c2.10",
"c10.2",
"d1",
"d7",
"d7abc",
"d12",
"d12a",
"e2b",
"e4b",
"e21a",
}
m := make(map[interface{}]int)
for _, k := range order {
m[k] = 1
}
data, err := yaml.Marshal(m)
c.Assert(err, IsNil)
out := "\n" + string(data)
last := 0
for i, k := range order {
repr := fmt.Sprint(k)
if s, ok := k.(string); ok {
if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
repr = `"` + repr + `"`
}
}
index := strings.Index(out, "\n"+repr+":")
if index == -1 {
c.Fatalf("%#v is not in the output: %#v", k, out)
}
if index < last {
c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
}
last = index
}
}
func newTime(t time.Time) *time.Time {
return &t
}
yaml-3.0.1/example_embedded_test.go 0000664 0000000 0000000 00000002443 14244106522 0017312 0 ustar 00root root 0000000 0000000 //
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml_test
import (
"fmt"
"log"
"gopkg.in/yaml.v3"
)
// An example showing how to unmarshal embedded
// structs from YAML.
type StructA struct {
A string `yaml:"a"`
}
type StructB struct {
// Embedded structs are not treated as embedded in YAML by default. To do that,
// add the ",inline" annotation below
StructA `yaml:",inline"`
B string `yaml:"b"`
}
var data = `
a: a string from struct A
b: a string from struct B
`
func ExampleUnmarshal_embedded() {
var b StructB
err := yaml.Unmarshal([]byte(data), &b)
if err != nil {
log.Fatalf("cannot unmarshal data: %v", err)
}
fmt.Println(b.A)
fmt.Println(b.B)
// Output:
// a string from struct A
// a string from struct B
}
yaml-3.0.1/go.mod 0000664 0000000 0000000 00000000137 14244106522 0013564 0 ustar 00root root 0000000 0000000 module "gopkg.in/yaml.v3"
require (
"gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405
)
yaml-3.0.1/limit_test.go 0000664 0000000 0000000 00000007443 14244106522 0015171 0 ustar 00root root 0000000 0000000 package yaml_test
import (
"strings"
"testing"
. "gopkg.in/check.v1"
"gopkg.in/yaml.v3"
)
var limitTests = []struct {
name string
data []byte
error string
}{
{
name: "1000kb of maps with 100 aliases",
data: []byte(`{a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-100) + `], b: &b [*a` + strings.Repeat(`,*a`, 99) + `]}`),
error: "yaml: document contains excessive aliasing",
}, {
name: "1000kb of deeply nested slices",
data: []byte(strings.Repeat(`[`, 1000*1024)),
error: "yaml: exceeded max depth of 10000",
}, {
name: "1000kb of deeply nested maps",
data: []byte("x: " + strings.Repeat(`{`, 1000*1024)),
error: "yaml: exceeded max depth of 10000",
}, {
name: "1000kb of deeply nested indents",
data: []byte(strings.Repeat(`- `, 1000*1024)),
error: "yaml: exceeded max depth of 10000",
}, {
name: "1000kb of 1000-indent lines",
data: []byte(strings.Repeat(strings.Repeat(`- `, 1000)+"\n", 1024/2)),
},
{name: "1kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1*1024/4-1) + `]`)},
{name: "10kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 10*1024/4-1) + `]`)},
{name: "100kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 100*1024/4-1) + `]`)},
{name: "1000kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-1) + `]`)},
{name: "1000kb slice nested at max-depth", data: []byte(strings.Repeat(`[`, 10000) + `1` + strings.Repeat(`,1`, 1000*1024/2-20000-1) + strings.Repeat(`]`, 10000))},
{name: "1000kb slice nested in maps at max-depth", data: []byte("{a,b:\n" + strings.Repeat(" {a,b:", 10000-2) + ` [1` + strings.Repeat(",1", 1000*1024/2-6*10000-1) + `]` + strings.Repeat(`}`, 10000-1))},
{name: "1000kb of 10000-nested lines", data: []byte(strings.Repeat(`- `+strings.Repeat(`[`, 10000)+strings.Repeat(`]`, 10000)+"\n", 1000*1024/20000))},
}
func (s *S) TestLimits(c *C) {
if testing.Short() {
return
}
for _, tc := range limitTests {
var v interface{}
err := yaml.Unmarshal(tc.data, &v)
if len(tc.error) > 0 {
c.Assert(err, ErrorMatches, tc.error, Commentf("testcase: %s", tc.name))
} else {
c.Assert(err, IsNil, Commentf("testcase: %s", tc.name))
}
}
}
func Benchmark1000KB100Aliases(b *testing.B) {
benchmark(b, "1000kb of maps with 100 aliases")
}
func Benchmark1000KBDeeplyNestedSlices(b *testing.B) {
benchmark(b, "1000kb of deeply nested slices")
}
func Benchmark1000KBDeeplyNestedMaps(b *testing.B) {
benchmark(b, "1000kb of deeply nested maps")
}
func Benchmark1000KBDeeplyNestedIndents(b *testing.B) {
benchmark(b, "1000kb of deeply nested indents")
}
func Benchmark1000KB1000IndentLines(b *testing.B) {
benchmark(b, "1000kb of 1000-indent lines")
}
func Benchmark1KBMaps(b *testing.B) {
benchmark(b, "1kb of maps")
}
func Benchmark10KBMaps(b *testing.B) {
benchmark(b, "10kb of maps")
}
func Benchmark100KBMaps(b *testing.B) {
benchmark(b, "100kb of maps")
}
func Benchmark1000KBMaps(b *testing.B) {
benchmark(b, "1000kb of maps")
}
func BenchmarkDeepSlice(b *testing.B) {
benchmark(b, "1000kb slice nested at max-depth")
}
func BenchmarkDeepFlow(b *testing.B) {
benchmark(b, "1000kb slice nested in maps at max-depth")
}
func Benchmark1000KBMaxDepthNested(b *testing.B) {
benchmark(b, "1000kb of 10000-nested lines")
}
func benchmark(b *testing.B, name string) {
for _, t := range limitTests {
if t.name != name {
continue
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var v interface{}
err := yaml.Unmarshal(t.data, &v)
if len(t.error) > 0 {
if err == nil {
b.Errorf("expected error, got none")
} else if err.Error() != t.error {
b.Errorf("expected error '%s', got '%s'", t.error, err.Error())
}
} else {
if err != nil {
b.Errorf("unexpected error: %v", err)
}
}
}
return
}
b.Errorf("testcase %q not found", name)
}
yaml-3.0.1/node_test.go 0000664 0000000 0000000 00000173441 14244106522 0015002 0 ustar 00root root 0000000 0000000 //
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml_test
import (
"bytes"
"fmt"
"os"
. "gopkg.in/check.v1"
"gopkg.in/yaml.v3"
"io"
"strings"
)
var nodeTests = []struct {
yaml string
node yaml.Node
}{
{
"null\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "null",
Tag: "!!null",
Line: 1,
Column: 1,
}},
},
}, {
"[encode]null\n",
yaml.Node{},
}, {
"foo\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "foo",
Tag: "!!str",
Line: 1,
Column: 1,
}},
},
}, {
"\"foo\"\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Style: yaml.DoubleQuotedStyle,
Value: "foo",
Tag: "!!str",
Line: 1,
Column: 1,
}},
},
}, {
"'foo'\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Style: yaml.SingleQuotedStyle,
Value: "foo",
Tag: "!!str",
Line: 1,
Column: 1,
}},
},
}, {
"!!str 123\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Style: yaml.TaggedStyle,
Value: "123",
Tag: "!!str",
Line: 1,
Column: 1,
}},
},
}, {
// Although the node isn't TaggedStyle, dropping the tag would change the value.
"[encode]!!binary gIGC\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "gIGC",
Tag: "!!binary",
Line: 1,
Column: 1,
}},
},
}, {
// Item doesn't have a tag, but needs to be binary encoded due to its content.
"[encode]!!binary gIGC\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "\x80\x81\x82",
Line: 1,
Column: 1,
}},
},
}, {
// Same, but with strings we can just quote them.
"[encode]\"123\"\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "123",
Tag: "!!str",
Line: 1,
Column: 1,
}},
},
}, {
"!tag:something 123\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Style: yaml.TaggedStyle,
Value: "123",
Tag: "!tag:something",
Line: 1,
Column: 1,
}},
},
}, {
"[encode]!tag:something 123\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "123",
Tag: "!tag:something",
Line: 1,
Column: 1,
}},
},
}, {
"!tag:something {}\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Style: yaml.TaggedStyle | yaml.FlowStyle,
Tag: "!tag:something",
Line: 1,
Column: 1,
}},
},
}, {
"[encode]!tag:something {}\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Style: yaml.FlowStyle,
Tag: "!tag:something",
Line: 1,
Column: 1,
}},
},
}, {
"!tag:something []\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Style: yaml.TaggedStyle | yaml.FlowStyle,
Tag: "!tag:something",
Line: 1,
Column: 1,
}},
},
}, {
"[encode]!tag:something []\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Style: yaml.FlowStyle,
Tag: "!tag:something",
Line: 1,
Column: 1,
}},
},
}, {
"''\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Style: yaml.SingleQuotedStyle,
Value: "",
Tag: "!!str",
Line: 1,
Column: 1,
}},
},
}, {
"|\n foo\n bar\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Style: yaml.LiteralStyle,
Value: "foo\nbar\n",
Tag: "!!str",
Line: 1,
Column: 1,
}},
},
}, {
"true\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "true",
Tag: "!!bool",
Line: 1,
Column: 1,
}},
},
}, {
"-10\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "-10",
Tag: "!!int",
Line: 1,
Column: 1,
}},
},
}, {
"4294967296\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "4294967296",
Tag: "!!int",
Line: 1,
Column: 1,
}},
},
}, {
"0.1000\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "0.1000",
Tag: "!!float",
Line: 1,
Column: 1,
}},
},
}, {
"-.inf\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "-.inf",
Tag: "!!float",
Line: 1,
Column: 1,
}},
},
}, {
".nan\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: ".nan",
Tag: "!!float",
Line: 1,
Column: 1,
}},
},
}, {
"{}\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Style: yaml.FlowStyle,
Value: "",
Tag: "!!map",
Line: 1,
Column: 1,
}},
},
}, {
"a: b c\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Value: "",
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "a",
Tag: "!!str",
Line: 1,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Value: "b c",
Tag: "!!str",
Line: 1,
Column: 4,
}},
}},
},
}, {
"a:\n b: c\n d: e\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "a",
Tag: "!!str",
Line: 1,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "b",
Tag: "!!str",
Line: 2,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Value: "c",
Tag: "!!str",
Line: 2,
Column: 6,
}, {
Kind: yaml.ScalarNode,
Value: "d",
Tag: "!!str",
Line: 3,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Value: "e",
Tag: "!!str",
Line: 3,
Column: 6,
}},
}},
}},
},
}, {
"a:\n - b: c\n d: e\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "a",
Tag: "!!str",
Line: 1,
Column: 1,
}, {
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 5,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "b",
Tag: "!!str",
Line: 2,
Column: 5,
}, {
Kind: yaml.ScalarNode,
Value: "c",
Tag: "!!str",
Line: 2,
Column: 8,
}, {
Kind: yaml.ScalarNode,
Value: "d",
Tag: "!!str",
Line: 3,
Column: 5,
}, {
Kind: yaml.ScalarNode,
Value: "e",
Tag: "!!str",
Line: 3,
Column: 8,
}},
}},
}},
}},
},
}, {
"a: # AI\n - b\nc:\n - d\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "a",
LineComment: "# AI",
Line: 1,
Column: 1,
}, {
Kind: yaml.SequenceNode,
Tag: "!!seq",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "b",
Line: 2,
Column: 5,
}},
Line: 2,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "c",
Line: 3,
Column: 1,
}, {
Kind: yaml.SequenceNode,
Tag: "!!seq",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "d",
Line: 4,
Column: 5,
}},
Line: 4,
Column: 3,
}},
}},
},
}, {
"[decode]a:\n # HM\n - # HB1\n # HB2\n b: # IB\n c # IC\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Style: 0x0,
Tag: "!!str",
Value: "a",
Line: 1,
Column: 1,
}, {
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 3,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
HeadComment: "# HM",
Line: 5,
Column: 5,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "b",
HeadComment: "# HB1\n# HB2",
LineComment: "# IB",
Line: 5,
Column: 5,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "c",
LineComment: "# IC",
Line: 6,
Column: 7,
}},
}},
}},
}},
},
}, {
// When encoding the value above, it loses b's inline comment.
"[encode]a:\n # HM\n - # HB1\n # HB2\n b: c # IC\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Style: 0x0,
Tag: "!!str",
Value: "a",
Line: 1,
Column: 1,
}, {
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 3,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
HeadComment: "# HM",
Line: 5,
Column: 5,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "b",
HeadComment: "# HB1\n# HB2",
LineComment: "# IB",
Line: 5,
Column: 5,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "c",
LineComment: "# IC",
Line: 6,
Column: 7,
}},
}},
}},
}},
},
}, {
// Multiple cases of comment inlining next to mapping keys.
"a: | # IA\n str\nb: >- # IB\n str\nc: # IC\n - str\nd: # ID\n str:\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "a",
Line: 1,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Style: yaml.LiteralStyle,
Tag: "!!str",
Value: "str\n",
LineComment: "# IA",
Line: 1,
Column: 4,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "b",
Line: 3,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Style: yaml.FoldedStyle,
Tag: "!!str",
Value: "str",
LineComment: "# IB",
Line: 3,
Column: 4,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "c",
LineComment: "# IC",
Line: 5,
Column: 1,
}, {
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 6,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "str",
Line: 6,
Column: 5,
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "d",
LineComment: "# ID",
Line: 7,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 8,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "str",
Line: 8,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!null",
Line: 8,
Column: 7,
}},
}},
}},
},
}, {
// Indentless sequence.
"[decode]a:\n# HM\n- # HB1\n # HB2\n b: # IB\n c # IC\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "a",
Line: 1,
Column: 1,
}, {
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 3,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
HeadComment: "# HM",
Line: 5,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "b",
HeadComment: "# HB1\n# HB2",
LineComment: "# IB",
Line: 5,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "c",
LineComment: "# IC",
Line: 6,
Column: 5,
}},
}},
}},
}},
},
}, {
"- a\n- b\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Value: "",
Tag: "!!seq",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "a",
Tag: "!!str",
Line: 1,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Value: "b",
Tag: "!!str",
Line: 2,
Column: 3,
}},
}},
},
}, {
"- a\n- - b\n - c\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "a",
Tag: "!!str",
Line: 1,
Column: 3,
}, {
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "b",
Tag: "!!str",
Line: 2,
Column: 5,
}, {
Kind: yaml.ScalarNode,
Value: "c",
Tag: "!!str",
Line: 3,
Column: 5,
}},
}},
}},
},
}, {
"[a, b]\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Style: yaml.FlowStyle,
Value: "",
Tag: "!!seq",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "a",
Tag: "!!str",
Line: 1,
Column: 2,
}, {
Kind: yaml.ScalarNode,
Value: "b",
Tag: "!!str",
Line: 1,
Column: 5,
}},
}},
},
}, {
"- a\n- [b, c]\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "a",
Tag: "!!str",
Line: 1,
Column: 3,
}, {
Kind: yaml.SequenceNode,
Tag: "!!seq",
Style: yaml.FlowStyle,
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "b",
Tag: "!!str",
Line: 2,
Column: 4,
}, {
Kind: yaml.ScalarNode,
Value: "c",
Tag: "!!str",
Line: 2,
Column: 7,
}},
}},
}},
},
}, {
"a: &x 1\nb: &y 2\nc: *x\nd: *y\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Line: 1,
Column: 1,
Tag: "!!map",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "a",
Tag: "!!str",
Line: 1,
Column: 1,
},
saveNode("x", &yaml.Node{
Kind: yaml.ScalarNode,
Value: "1",
Tag: "!!int",
Anchor: "x",
Line: 1,
Column: 4,
}),
{
Kind: yaml.ScalarNode,
Value: "b",
Tag: "!!str",
Line: 2,
Column: 1,
},
saveNode("y", &yaml.Node{
Kind: yaml.ScalarNode,
Value: "2",
Tag: "!!int",
Anchor: "y",
Line: 2,
Column: 4,
}),
{
Kind: yaml.ScalarNode,
Value: "c",
Tag: "!!str",
Line: 3,
Column: 1,
}, {
Kind: yaml.AliasNode,
Value: "x",
Alias: dropNode("x"),
Line: 3,
Column: 4,
}, {
Kind: yaml.ScalarNode,
Value: "d",
Tag: "!!str",
Line: 4,
Column: 1,
}, {
Kind: yaml.AliasNode,
Value: "y",
Tag: "",
Alias: dropNode("y"),
Line: 4,
Column: 4,
}},
}},
},
}, {
"# One\n# Two\ntrue # Three\n# Four\n# Five\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 3,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "true",
Tag: "!!bool",
Line: 3,
Column: 1,
HeadComment: "# One\n# Two",
LineComment: "# Three",
FootComment: "# Four\n# Five",
}},
},
}, {
"# š\ntrue # š\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "true",
Tag: "!!bool",
Line: 2,
Column: 1,
HeadComment: "# š",
LineComment: "# š",
}},
},
}, {
"[decode]\n# One\n\n# Two\n\n# Three\ntrue # Four\n# Five\n\n# Six\n\n# Seven\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 7,
Column: 1,
HeadComment: "# One\n\n# Two",
FootComment: "# Six\n\n# Seven",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "true",
Tag: "!!bool",
Line: 7,
Column: 1,
HeadComment: "# Three",
LineComment: "# Four",
FootComment: "# Five",
}},
},
}, {
// Write out the pound character if missing from comments.
"[encode]# One\n# Two\ntrue # Three\n# Four\n# Five\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 3,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "true",
Tag: "!!bool",
Line: 3,
Column: 1,
HeadComment: "One\nTwo\n",
LineComment: "Three\n",
FootComment: "Four\nFive\n",
}},
},
}, {
"[encode]# One\n# Two\ntrue # Three\n# Four\n# Five\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 3,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "true",
Tag: "!!bool",
Line: 3,
Column: 1,
HeadComment: " One\n Two",
LineComment: " Three",
FootComment: " Four\n Five",
}},
},
}, {
"# DH1\n\n# DH2\n\n# H1\n# H2\ntrue # I\n# F1\n# F2\n\n# DF1\n\n# DF2\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 7,
Column: 1,
HeadComment: "# DH1\n\n# DH2",
FootComment: "# DF1\n\n# DF2",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "true",
Tag: "!!bool",
Line: 7,
Column: 1,
HeadComment: "# H1\n# H2",
LineComment: "# I",
FootComment: "# F1\n# F2",
}},
},
}, {
"# DH1\n\n# DH2\n\n# HA1\n# HA2\nka: va # IA\n# FA1\n# FA2\n\n# HB1\n# HB2\nkb: vb # IB\n# FB1\n# FB2\n\n# DF1\n\n# DF2\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 7,
Column: 1,
HeadComment: "# DH1\n\n# DH2",
FootComment: "# DF1\n\n# DF2",
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 7,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Line: 7,
Column: 1,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1\n# HA2",
FootComment: "# FA1\n# FA2",
}, {
Kind: yaml.ScalarNode,
Line: 7,
Column: 5,
Tag: "!!str",
Value: "va",
LineComment: "# IA",
}, {
Kind: yaml.ScalarNode,
Line: 13,
Column: 1,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1\n# HB2",
FootComment: "# FB1\n# FB2",
}, {
Kind: yaml.ScalarNode,
Line: 13,
Column: 5,
Tag: "!!str",
Value: "vb",
LineComment: "# IB",
}},
}},
},
}, {
"# DH1\n\n# DH2\n\n# HA1\n# HA2\n- la # IA\n# FA1\n# FA2\n\n# HB1\n# HB2\n- lb # IB\n# FB1\n# FB2\n\n# DF1\n\n# DF2\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 7,
Column: 1,
HeadComment: "# DH1\n\n# DH2",
FootComment: "# DF1\n\n# DF2",
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 7,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 7,
Column: 3,
Value: "la",
HeadComment: "# HA1\n# HA2",
LineComment: "# IA",
FootComment: "# FA1\n# FA2",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 13,
Column: 3,
Value: "lb",
HeadComment: "# HB1\n# HB2",
LineComment: "# IB",
FootComment: "# FB1\n# FB2",
}},
}},
},
}, {
"# DH1\n\n- la # IA\n# HB1\n- lb\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 3,
Column: 1,
HeadComment: "# DH1",
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 3,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 3,
Column: 3,
Value: "la",
LineComment: "# IA",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 5,
Column: 3,
Value: "lb",
HeadComment: "# HB1",
}},
}},
},
}, {
"- la # IA\n- lb # IB\n- lc # IC\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 1,
Column: 3,
Value: "la",
LineComment: "# IA",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 2,
Column: 3,
Value: "lb",
LineComment: "# IB",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 3,
Column: 3,
Value: "lc",
LineComment: "# IC",
}},
}},
},
}, {
"# DH1\n\n# HL1\n- - la\n # HB1\n - lb\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 4,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 4,
Column: 3,
HeadComment: "# HL1",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 4,
Column: 5,
Value: "la",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 5,
Value: "lb",
HeadComment: "# HB1",
}},
}},
}},
},
}, {
"# DH1\n\n# HL1\n- # HA1\n - la\n # HB1\n - lb\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 4,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 5,
Column: 3,
HeadComment: "# HL1",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 5,
Column: 5,
Value: "la",
HeadComment: "# HA1",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 7,
Column: 5,
Value: "lb",
HeadComment: "# HB1",
}},
}},
}},
},
}, {
"[decode]# DH1\n\n# HL1\n- # HA1\n\n - la\n # HB1\n - lb\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 4,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 6,
Column: 3,
HeadComment: "# HL1",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 5,
Value: "la",
HeadComment: "# HA1\n",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 8,
Column: 5,
Value: "lb",
HeadComment: "# HB1",
}},
}},
}},
},
}, {
"# DH1\n\n# HA1\nka:\n # HB1\n kb:\n # HC1\n # HC2\n - lc # IC\n # FC1\n # FC2\n\n # HD1\n - ld # ID\n # FD1\n\n# DF1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
FootComment: "# DF1",
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 4,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 4,
Column: 1,
Value: "ka",
HeadComment: "# HA1",
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 6,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 3,
Value: "kb",
HeadComment: "# HB1",
}, {
Kind: yaml.SequenceNode,
Line: 9,
Column: 5,
Tag: "!!seq",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 9,
Column: 7,
Value: "lc",
HeadComment: "# HC1\n# HC2",
LineComment: "# IC",
FootComment: "# FC1\n# FC2",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 14,
Column: 7,
Value: "ld",
HeadComment: "# HD1",
LineComment: "# ID",
FootComment: "# FD1",
}},
}},
}},
}},
},
}, {
"# DH1\n\n# HA1\nka:\n # HB1\n kb:\n # HC1\n # HC2\n - lc # IC\n # FC1\n # FC2\n\n # HD1\n - ld # ID\n # FD1\nke: ve\n\n# DF1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
FootComment: "# DF1",
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 4,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 4,
Column: 1,
Value: "ka",
HeadComment: "# HA1",
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 6,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 3,
Value: "kb",
HeadComment: "# HB1",
}, {
Kind: yaml.SequenceNode,
Line: 9,
Column: 5,
Tag: "!!seq",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 9,
Column: 7,
Value: "lc",
HeadComment: "# HC1\n# HC2",
LineComment: "# IC",
FootComment: "# FC1\n# FC2",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 14,
Column: 7,
Value: "ld",
HeadComment: "# HD1",
LineComment: "# ID",
FootComment: "# FD1",
}},
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 16,
Column: 1,
Value: "ke",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 16,
Column: 5,
Value: "ve",
}},
}},
},
}, {
"# DH1\n\n# DH2\n\n# HA1\n# HA2\nka:\n # HB1\n # HB2\n kb:\n" +
" # HC1\n # HC2\n kc:\n # HD1\n # HD2\n kd: vd\n # FD1\n # FD2\n" +
" # FC1\n # FC2\n # FB1\n # FB2\n# FA1\n# FA2\n\n# HE1\n# HE2\nke: ve\n# FE1\n# FE2\n\n# DF1\n\n# DF2\n",
yaml.Node{
Kind: yaml.DocumentNode,
HeadComment: "# DH1\n\n# DH2",
FootComment: "# DF1\n\n# DF2",
Line: 7,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 7,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1\n# HA2",
FootComment: "# FA1\n# FA2",
Line: 7,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 10,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1\n# HB2",
FootComment: "# FB1\n# FB2",
Line: 10,
Column: 3,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 13,
Column: 5,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kc",
HeadComment: "# HC1\n# HC2",
FootComment: "# FC1\n# FC2",
Line: 13,
Column: 5,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 16,
Column: 7,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kd",
HeadComment: "# HD1\n# HD2",
FootComment: "# FD1\n# FD2",
Line: 16,
Column: 7,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vd",
Line: 16,
Column: 11,
}},
}},
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ke",
HeadComment: "# HE1\n# HE2",
FootComment: "# FE1\n# FE2",
Line: 28,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ve",
Line: 28,
Column: 5,
}},
}},
},
}, {
// Same as above but indenting ke in so it's also part of ka's value.
"# DH1\n\n# DH2\n\n# HA1\n# HA2\nka:\n # HB1\n # HB2\n kb:\n" +
" # HC1\n # HC2\n kc:\n # HD1\n # HD2\n kd: vd\n # FD1\n # FD2\n" +
" # FC1\n # FC2\n # FB1\n # FB2\n\n # HE1\n # HE2\n ke: ve\n # FE1\n # FE2\n# FA1\n# FA2\n\n# DF1\n\n# DF2\n",
yaml.Node{
Kind: yaml.DocumentNode,
HeadComment: "# DH1\n\n# DH2",
FootComment: "# DF1\n\n# DF2",
Line: 7,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 7,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1\n# HA2",
FootComment: "# FA1\n# FA2",
Line: 7,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 10,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1\n# HB2",
FootComment: "# FB1\n# FB2",
Line: 10,
Column: 3,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 13,
Column: 5,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kc",
HeadComment: "# HC1\n# HC2",
FootComment: "# FC1\n# FC2",
Line: 13,
Column: 5,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 16,
Column: 7,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kd",
HeadComment: "# HD1\n# HD2",
FootComment: "# FD1\n# FD2",
Line: 16,
Column: 7,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vd",
Line: 16,
Column: 11,
}},
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ke",
HeadComment: "# HE1\n# HE2",
FootComment: "# FE1\n# FE2",
Line: 26,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ve",
Line: 26,
Column: 7,
}},
}},
}},
},
}, {
// Decode only due to lack of newline at the end.
"[decode]# HA1\nka:\n # HB1\n kb: vb\n # FB1\n# FA1",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1",
FootComment: "# FA1",
Line: 2,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 4,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1",
FootComment: "# FB1",
Line: 4,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 4,
Column: 7,
}},
}},
}},
},
}, {
// Same as above, but with newline at the end.
"# HA1\nka:\n # HB1\n kb: vb\n # FB1\n# FA1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1",
FootComment: "# FA1",
Line: 2,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 4,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1",
FootComment: "# FB1",
Line: 4,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 4,
Column: 7,
}},
}},
}},
},
}, {
// Same as above, but without FB1.
"# HA1\nka:\n # HB1\n kb: vb\n# FA1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1",
FootComment: "# FA1",
Line: 2,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 4,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1",
Line: 4,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 4,
Column: 7,
}},
}},
}},
},
}, {
// Same as above, but with two newlines at the end. Decode-only for that.
"[decode]# HA1\nka:\n # HB1\n kb: vb\n # FB1\n# FA1\n\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1",
FootComment: "# FA1",
Line: 2,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 4,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1",
FootComment: "# FB1",
Line: 4,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 4,
Column: 7,
}},
}},
}},
},
}, {
// Similar to above, but make HB1 look more like a footer of ka.
"[decode]# HA1\nka:\n# HB1\n\n kb: vb\n# FA1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1",
FootComment: "# FA1",
Line: 2,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 5,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1\n",
Line: 5,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 5,
Column: 7,
}},
}},
}},
},
}, {
"ka:\n kb: vb\n# FA1\n\nkc: vc\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
Line: 1,
Column: 1,
FootComment: "# FA1",
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
Line: 2,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 2,
Column: 7,
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kc",
Line: 5,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vc",
Line: 5,
Column: 5,
}},
}},
},
}, {
"ka:\n kb: vb\n# HC1\nkc: vc\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
Line: 1,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
Line: 2,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 2,
Column: 7,
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kc",
HeadComment: "# HC1",
Line: 4,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vc",
Line: 4,
Column: 5,
}},
}},
},
}, {
// Decode only due to empty line before HC1.
"[decode]ka:\n kb: vb\n\n# HC1\nkc: vc\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
Line: 1,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
Line: 2,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 2,
Column: 7,
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kc",
HeadComment: "# HC1",
Line: 5,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vc",
Line: 5,
Column: 5,
}},
}},
},
}, {
// Decode-only due to empty lines around HC1.
"[decode]ka:\n kb: vb\n\n# HC1\n\nkc: vc\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
Line: 1,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
Line: 2,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 2,
Column: 7,
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kc",
HeadComment: "# HC1\n",
Line: 6,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vc",
Line: 6,
Column: 5,
}},
}},
},
}, {
"ka: # IA\n kb: # IB\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
Line: 1,
Column: 1,
LineComment: "# IA",
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
Line: 2,
Column: 3,
LineComment: "# IB",
}, {
Kind: yaml.ScalarNode,
Tag: "!!null",
Line: 2,
Column: 6,
}},
}},
}},
},
}, {
"# HA1\nka:\n # HB1\n kb: vb\n # FB1\n# HC1\n# HC2\nkc: vc\n# FC1\n# FC2\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1",
Line: 2,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 4,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1",
FootComment: "# FB1",
Line: 4,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 4,
Column: 7,
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kc",
HeadComment: "# HC1\n# HC2",
FootComment: "# FC1\n# FC2",
Line: 8,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vc",
Line: 8,
Column: 5,
}},
}},
},
}, {
// Same as above, but decode only due to empty line between ka's value and kc's headers.
"[decode]# HA1\nka:\n # HB1\n kb: vb\n # FB1\n\n# HC1\n# HC2\nkc: vc\n# FC1\n# FC2\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
HeadComment: "# HA1",
Line: 2,
Column: 1,
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 4,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
HeadComment: "# HB1",
FootComment: "# FB1",
Line: 4,
Column: 3,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vb",
Line: 4,
Column: 7,
}},
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kc",
HeadComment: "# HC1\n# HC2",
FootComment: "# FC1\n# FC2",
Line: 9,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "vc",
Line: 9,
Column: 5,
}},
}},
},
}, {
"# H1\n[la, lb] # I\n# F1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Style: yaml.FlowStyle,
Line: 2,
Column: 1,
HeadComment: "# H1",
LineComment: "# I",
FootComment: "# F1",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 2,
Column: 2,
Value: "la",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 2,
Column: 6,
Value: "lb",
}},
}},
},
}, {
"# DH1\n\n# SH1\n[\n # HA1\n la, # IA\n # FA1\n\n # HB1\n lb, # IB\n # FB1\n]\n# SF1\n\n# DF1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
FootComment: "# DF1",
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Style: yaml.FlowStyle,
Line: 4,
Column: 1,
HeadComment: "# SH1",
FootComment: "# SF1",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 3,
Value: "la",
HeadComment: "# HA1",
LineComment: "# IA",
FootComment: "# FA1",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 10,
Column: 3,
Value: "lb",
HeadComment: "# HB1",
LineComment: "# IB",
FootComment: "# FB1",
}},
}},
},
}, {
// Same as above, but with extra newlines before FB1 and FB2
"[decode]# DH1\n\n# SH1\n[\n # HA1\n la, # IA\n # FA1\n\n # HB1\n lb, # IB\n\n\n # FB1\n\n# FB2\n]\n# SF1\n\n# DF1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
FootComment: "# DF1",
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Style: yaml.FlowStyle,
Line: 4,
Column: 1,
HeadComment: "# SH1",
FootComment: "# SF1",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 3,
Value: "la",
HeadComment: "# HA1",
LineComment: "# IA",
FootComment: "# FA1",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 10,
Column: 3,
Value: "lb",
HeadComment: "# HB1",
LineComment: "# IB",
FootComment: "# FB1\n\n# FB2",
}},
}},
},
}, {
"# DH1\n\n# SH1\n[\n # HA1\n la,\n # FA1\n\n # HB1\n lb,\n # FB1\n]\n# SF1\n\n# DF1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
FootComment: "# DF1",
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Style: yaml.FlowStyle,
Line: 4,
Column: 1,
HeadComment: "# SH1",
FootComment: "# SF1",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 3,
Value: "la",
HeadComment: "# HA1",
FootComment: "# FA1",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 10,
Column: 3,
Value: "lb",
HeadComment: "# HB1",
FootComment: "# FB1",
}},
}},
},
}, {
"ka:\n kb: [\n # HA1\n la,\n # FA1\n\n # HB1\n lb,\n # FB1\n ]\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "ka",
Line: 1,
Column: 1,
}, {
Kind: 0x4,
Tag: "!!map",
Line: 2,
Column: 3,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "kb",
Line: 2,
Column: 3,
}, {
Kind: yaml.SequenceNode,
Style: 0x20,
Tag: "!!seq",
Line: 2,
Column: 7,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "la",
HeadComment: "# HA1",
FootComment: "# FA1",
Line: 4,
Column: 5,
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: "lb",
HeadComment: "# HB1",
FootComment: "# FB1",
Line: 8,
Column: 5,
}},
}},
}},
}},
},
}, {
"# DH1\n\n# MH1\n{\n # HA1\n ka: va, # IA\n # FA1\n\n # HB1\n kb: vb, # IB\n # FB1\n}\n# MF1\n\n# DF1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
FootComment: "# DF1",
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Style: yaml.FlowStyle,
Line: 4,
Column: 1,
HeadComment: "# MH1",
FootComment: "# MF1",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 3,
Value: "ka",
HeadComment: "# HA1",
FootComment: "# FA1",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 7,
Value: "va",
LineComment: "# IA",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 10,
Column: 3,
Value: "kb",
HeadComment: "# HB1",
FootComment: "# FB1",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 10,
Column: 7,
Value: "vb",
LineComment: "# IB",
}},
}},
},
}, {
"# DH1\n\n# MH1\n{\n # HA1\n ka: va,\n # FA1\n\n # HB1\n kb: vb,\n # FB1\n}\n# MF1\n\n# DF1\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
HeadComment: "# DH1",
FootComment: "# DF1",
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Style: yaml.FlowStyle,
Line: 4,
Column: 1,
HeadComment: "# MH1",
FootComment: "# MF1",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 3,
Value: "ka",
HeadComment: "# HA1",
FootComment: "# FA1",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 6,
Column: 7,
Value: "va",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 10,
Column: 3,
Value: "kb",
HeadComment: "# HB1",
FootComment: "# FB1",
}, {
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 10,
Column: 7,
Value: "vb",
}},
}},
},
}, {
"# DH1\n\n# DH2\n\n# HA1\n# HA2\n- &x la # IA\n# FA1\n# FA2\n\n# HB1\n# HB2\n- *x # IB\n# FB1\n# FB2\n\n# DF1\n\n# DF2\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 7,
Column: 1,
HeadComment: "# DH1\n\n# DH2",
FootComment: "# DF1\n\n# DF2",
Content: []*yaml.Node{{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Line: 7,
Column: 1,
Content: []*yaml.Node{
saveNode("x", &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 7,
Column: 3,
Value: "la",
HeadComment: "# HA1\n# HA2",
LineComment: "# IA",
FootComment: "# FA1\n# FA2",
Anchor: "x",
}), {
Kind: yaml.AliasNode,
Line: 13,
Column: 3,
Value: "x",
Alias: dropNode("x"),
HeadComment: "# HB1\n# HB2",
LineComment: "# IB",
FootComment: "# FB1\n# FB2",
},
},
}},
},
},
}
func (s *S) TestNodeRoundtrip(c *C) {
defer os.Setenv("TZ", os.Getenv("TZ"))
os.Setenv("TZ", "UTC")
for i, item := range nodeTests {
c.Logf("test %d: %q", i, item.yaml)
if strings.Contains(item.yaml, "#") {
var buf bytes.Buffer
fprintComments(&buf, &item.node, " ")
c.Logf(" expected comments:\n%s", buf.Bytes())
}
decode := true
encode := true
testYaml := item.yaml
if s := strings.TrimPrefix(testYaml, "[decode]"); s != testYaml {
encode = false
testYaml = s
}
if s := strings.TrimPrefix(testYaml, "[encode]"); s != testYaml {
decode = false
testYaml = s
}
if decode {
var node yaml.Node
err := yaml.Unmarshal([]byte(testYaml), &node)
c.Assert(err, IsNil)
if strings.Contains(item.yaml, "#") {
var buf bytes.Buffer
fprintComments(&buf, &node, " ")
c.Logf(" obtained comments:\n%s", buf.Bytes())
}
c.Assert(&node, DeepEquals, &item.node)
}
if encode {
node := deepCopyNode(&item.node, nil)
buf := bytes.Buffer{}
enc := yaml.NewEncoder(&buf)
enc.SetIndent(2)
err := enc.Encode(node)
c.Assert(err, IsNil)
err = enc.Close()
c.Assert(err, IsNil)
c.Assert(buf.String(), Equals, testYaml)
// Ensure there were no mutations to the tree.
c.Assert(node, DeepEquals, &item.node)
}
}
}
func deepCopyNode(node *yaml.Node, cache map[*yaml.Node]*yaml.Node) *yaml.Node {
if n, ok := cache[node]; ok {
return n
}
if cache == nil {
cache = make(map[*yaml.Node]*yaml.Node)
}
copy := *node
cache[node] = ©
copy.Content = nil
for _, elem := range node.Content {
copy.Content = append(copy.Content, deepCopyNode(elem, cache))
}
if node.Alias != nil {
copy.Alias = deepCopyNode(node.Alias, cache)
}
return ©
}
var savedNodes = make(map[string]*yaml.Node)
func saveNode(name string, node *yaml.Node) *yaml.Node {
savedNodes[name] = node
return node
}
func peekNode(name string) *yaml.Node {
return savedNodes[name]
}
func dropNode(name string) *yaml.Node {
node := savedNodes[name]
delete(savedNodes, name)
return node
}
var setStringTests = []struct {
str string
yaml string
node yaml.Node
}{
{
"something simple",
"something simple\n",
yaml.Node{
Kind: yaml.ScalarNode,
Value: "something simple",
Tag: "!!str",
},
}, {
`"quoted value"`,
"'\"quoted value\"'\n",
yaml.Node{
Kind: yaml.ScalarNode,
Value: `"quoted value"`,
Tag: "!!str",
},
}, {
"multi\nline",
"|-\n multi\n line\n",
yaml.Node{
Kind: yaml.ScalarNode,
Value: "multi\nline",
Tag: "!!str",
Style: yaml.LiteralStyle,
},
}, {
"123",
"\"123\"\n",
yaml.Node{
Kind: yaml.ScalarNode,
Value: "123",
Tag: "!!str",
},
}, {
"multi\nline\n",
"|\n multi\n line\n",
yaml.Node{
Kind: yaml.ScalarNode,
Value: "multi\nline\n",
Tag: "!!str",
Style: yaml.LiteralStyle,
},
}, {
"\x80\x81\x82",
"!!binary gIGC\n",
yaml.Node{
Kind: yaml.ScalarNode,
Value: "gIGC",
Tag: "!!binary",
},
},
}
func (s *S) TestSetString(c *C) {
defer os.Setenv("TZ", os.Getenv("TZ"))
os.Setenv("TZ", "UTC")
for i, item := range setStringTests {
c.Logf("test %d: %q", i, item.str)
var node yaml.Node
node.SetString(item.str)
c.Assert(node, DeepEquals, item.node)
buf := bytes.Buffer{}
enc := yaml.NewEncoder(&buf)
enc.SetIndent(2)
err := enc.Encode(&item.node)
c.Assert(err, IsNil)
err = enc.Close()
c.Assert(err, IsNil)
c.Assert(buf.String(), Equals, item.yaml)
var doc yaml.Node
err = yaml.Unmarshal([]byte(item.yaml), &doc)
c.Assert(err, IsNil)
var str string
err = node.Decode(&str)
c.Assert(err, IsNil)
c.Assert(str, Equals, item.str)
}
}
var nodeEncodeDecodeTests = []struct {
value interface{}
yaml string
node yaml.Node
}{{
"something simple",
"something simple\n",
yaml.Node{
Kind: yaml.ScalarNode,
Value: "something simple",
Tag: "!!str",
},
}, {
`"quoted value"`,
"'\"quoted value\"'\n",
yaml.Node{
Kind: yaml.ScalarNode,
Style: yaml.SingleQuotedStyle,
Value: `"quoted value"`,
Tag: "!!str",
},
}, {
123,
"123",
yaml.Node{
Kind: yaml.ScalarNode,
Value: `123`,
Tag: "!!int",
},
}, {
[]interface{}{1, 2},
"[1, 2]",
yaml.Node{
Kind: yaml.SequenceNode,
Tag: "!!seq",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "1",
Tag: "!!int",
}, {
Kind: yaml.ScalarNode,
Value: "2",
Tag: "!!int",
}},
},
}, {
map[string]interface{}{"a": "b"},
"a: b",
yaml.Node{
Kind: yaml.MappingNode,
Tag: "!!map",
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "a",
Tag: "!!str",
}, {
Kind: yaml.ScalarNode,
Value: "b",
Tag: "!!str",
}},
},
}}
func (s *S) TestNodeEncodeDecode(c *C) {
for i, item := range nodeEncodeDecodeTests {
c.Logf("Encode/Decode test value #%d: %#v", i, item.value)
var v interface{}
err := item.node.Decode(&v)
c.Assert(err, IsNil)
c.Assert(v, DeepEquals, item.value)
var n yaml.Node
err = n.Encode(item.value)
c.Assert(err, IsNil)
c.Assert(n, DeepEquals, item.node)
}
}
func (s *S) TestNodeZeroEncodeDecode(c *C) {
// Zero node value behaves as nil when encoding...
var n yaml.Node
data, err := yaml.Marshal(&n)
c.Assert(err, IsNil)
c.Assert(string(data), Equals, "null\n")
// ... and decoding.
var v *struct{} = &struct{}{}
c.Assert(n.Decode(&v), IsNil)
c.Assert(v, IsNil)
// ... and even when looking for its tag.
c.Assert(n.ShortTag(), Equals, "!!null")
// Kind zero is still unknown, though.
n.Line = 1
_, err = yaml.Marshal(&n)
c.Assert(err, ErrorMatches, "yaml: cannot encode node with unknown kind 0")
c.Assert(n.Decode(&v), ErrorMatches, "yaml: cannot decode node with unknown kind 0")
}
func (s *S) TestNodeOmitEmpty(c *C) {
var v struct {
A int
B yaml.Node ",omitempty"
}
v.A = 1
data, err := yaml.Marshal(&v)
c.Assert(err, IsNil)
c.Assert(string(data), Equals, "a: 1\n")
v.B.Line = 1
_, err = yaml.Marshal(&v)
c.Assert(err, ErrorMatches, "yaml: cannot encode node with unknown kind 0")
}
func fprintComments(out io.Writer, node *yaml.Node, indent string) {
switch node.Kind {
case yaml.ScalarNode:
fmt.Fprintf(out, "%s<%s> ", indent, node.Value)
fprintCommentSet(out, node)
fmt.Fprintf(out, "\n")
case yaml.DocumentNode:
fmt.Fprintf(out, "%s ", indent)
fprintCommentSet(out, node)
fmt.Fprintf(out, "\n")
for i := 0; i < len(node.Content); i++ {
fprintComments(out, node.Content[i], indent+" ")
}
case yaml.MappingNode:
fmt.Fprintf(out, "%s