From fcfb5e62f95d625836328131cc5ca851182bcae4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 13:41:39 +0200 Subject: Adding upstream version 1.2.0. Signed-off-by: Daniel Baumann --- .../github.com/goccy/go-yaml@v1.11.0/.codecov.yml | 31 - .../goccy/go-yaml@v1.11.0/.github/FUNDING.yml | 1 - .../goccy/go-yaml@v1.11.0/.github/workflows/go.yml | 79 - .../github.com/goccy/go-yaml@v1.11.0/CHANGELOG.md | 163 -- .../mod/github.com/goccy/go-yaml@v1.11.0/LICENSE | 21 - .../mod/github.com/goccy/go-yaml@v1.11.0/Makefile | 19 - .../mod/github.com/goccy/go-yaml@v1.11.0/README.md | 370 --- .../github.com/goccy/go-yaml@v1.11.0/ast/ast.go | 2117 -------------- .../goccy/go-yaml@v1.11.0/ast/ast_test.go | 36 - .../goccy/go-yaml@v1.11.0/cmd/ycat/ycat.go | 83 - .../mod/github.com/goccy/go-yaml@v1.11.0/decode.go | 1753 ------------ .../goccy/go-yaml@v1.11.0/decode_test.go | 2872 ------------------- .../mod/github.com/goccy/go-yaml@v1.11.0/encode.go | 871 ------ .../goccy/go-yaml@v1.11.0/encode_test.go | 1593 ----------- .../mod/github.com/goccy/go-yaml@v1.11.0/error.go | 62 - .../mod/github.com/goccy/go-yaml@v1.11.0/go.mod | 20 - .../mod/github.com/goccy/go-yaml@v1.11.0/go.sum | 44 - .../goccy/go-yaml@v1.11.0/internal/errors/error.go | 260 -- .../goccy/go-yaml@v1.11.0/lexer/lexer.go | 23 - .../goccy/go-yaml@v1.11.0/lexer/lexer_test.go | 621 ----- .../mod/github.com/goccy/go-yaml@v1.11.0/option.go | 278 -- .../goccy/go-yaml@v1.11.0/parser/context.go | 199 -- .../goccy/go-yaml@v1.11.0/parser/parser.go | 714 ----- .../goccy/go-yaml@v1.11.0/parser/parser_test.go | 886 ------ .../goccy/go-yaml@v1.11.0/parser/testdata/cr.yml | 1 - .../goccy/go-yaml@v1.11.0/parser/testdata/crlf.yml | 3 - .../goccy/go-yaml@v1.11.0/parser/testdata/lf.yml | 3 - .../mod/github.com/goccy/go-yaml@v1.11.0/path.go | 794 ------ .../github.com/goccy/go-yaml@v1.11.0/path_test.go | 659 ----- .../goccy/go-yaml@v1.11.0/printer/printer.go | 352 --- .../goccy/go-yaml@v1.11.0/printer/printer_test.go | 223 -- .../goccy/go-yaml@v1.11.0/scanner/context.go | 229 -- .../goccy/go-yaml@v1.11.0/scanner/scanner.go | 903 ------ .../goccy/go-yaml@v1.11.0/stdlib_quote.go | 103 - .../mod/github.com/goccy/go-yaml@v1.11.0/struct.go | 130 - .../goccy/go-yaml@v1.11.0/testdata/anchor.yml | 3 - .../goccy/go-yaml@v1.11.0/token/token.go | 1070 ------- .../goccy/go-yaml@v1.11.0/token/token_test.go | 135 - .../github.com/goccy/go-yaml@v1.11.0/validate.go | 13 - .../goccy/go-yaml@v1.11.0/validate_test.go | 198 -- .../mod/github.com/goccy/go-yaml@v1.11.0/yaml.go | 290 -- .../github.com/goccy/go-yaml@v1.11.0/yaml_test.go | 1195 -------- .../github.com/goccy/go-yaml@v1.11.3/.codecov.yml | 31 + .../goccy/go-yaml@v1.11.3/.github/FUNDING.yml | 1 + .../.github/ISSUE_TEMPLATE/bug_report.md | 29 + .../.github/ISSUE_TEMPLATE/feature_request.md | 20 + .../.github/pull_request_template.md | 4 + .../goccy/go-yaml@v1.11.3/.github/workflows/go.yml | 79 + .../github.com/goccy/go-yaml@v1.11.3/CHANGELOG.md | 186 ++ .../mod/github.com/goccy/go-yaml@v1.11.3/LICENSE | 21 + .../mod/github.com/goccy/go-yaml@v1.11.3/Makefile | 19 + .../mod/github.com/goccy/go-yaml@v1.11.3/README.md | 370 +++ .../github.com/goccy/go-yaml@v1.11.3/ast/ast.go | 2115 ++++++++++++++ .../goccy/go-yaml@v1.11.3/ast/ast_test.go | 36 + .../goccy/go-yaml@v1.11.3/cmd/ycat/ycat.go | 83 + .../mod/github.com/goccy/go-yaml@v1.11.3/decode.go | 1753 ++++++++++++ .../goccy/go-yaml@v1.11.3/decode_test.go | 2908 ++++++++++++++++++++ .../mod/github.com/goccy/go-yaml@v1.11.3/encode.go | 875 ++++++ .../goccy/go-yaml@v1.11.3/encode_test.go | 1632 +++++++++++ .../mod/github.com/goccy/go-yaml@v1.11.3/error.go | 62 + .../mod/github.com/goccy/go-yaml@v1.11.3/go.mod | 20 + .../mod/github.com/goccy/go-yaml@v1.11.3/go.sum | 44 + .../goccy/go-yaml@v1.11.3/internal/errors/error.go | 260 ++ .../goccy/go-yaml@v1.11.3/lexer/lexer.go | 23 + .../goccy/go-yaml@v1.11.3/lexer/lexer_test.go | 621 +++++ .../mod/github.com/goccy/go-yaml@v1.11.3/option.go | 278 ++ .../goccy/go-yaml@v1.11.3/parser/context.go | 192 ++ .../goccy/go-yaml@v1.11.3/parser/parser.go | 743 +++++ .../goccy/go-yaml@v1.11.3/parser/parser_test.go | 1010 +++++++ .../goccy/go-yaml@v1.11.3/parser/testdata/cr.yml | 1 + .../goccy/go-yaml@v1.11.3/parser/testdata/crlf.yml | 3 + .../goccy/go-yaml@v1.11.3/parser/testdata/lf.yml | 3 + .../mod/github.com/goccy/go-yaml@v1.11.3/path.go | 814 ++++++ .../github.com/goccy/go-yaml@v1.11.3/path_test.go | 666 +++++ .../goccy/go-yaml@v1.11.3/printer/printer.go | 352 +++ .../goccy/go-yaml@v1.11.3/printer/printer_test.go | 223 ++ .../goccy/go-yaml@v1.11.3/scanner/context.go | 236 ++ .../goccy/go-yaml@v1.11.3/scanner/scanner.go | 908 ++++++ .../goccy/go-yaml@v1.11.3/stdlib_quote.go | 103 + .../mod/github.com/goccy/go-yaml@v1.11.3/struct.go | 130 + .../goccy/go-yaml@v1.11.3/testdata/anchor.yml | 3 + .../goccy/go-yaml@v1.11.3/token/token.go | 1070 +++++++ .../goccy/go-yaml@v1.11.3/token/token_test.go | 138 + .../github.com/goccy/go-yaml@v1.11.3/validate.go | 13 + .../goccy/go-yaml@v1.11.3/validate_test.go | 198 ++ .../mod/github.com/goccy/go-yaml@v1.11.3/yaml.go | 288 ++ .../github.com/goccy/go-yaml@v1.11.3/yaml_test.go | 1284 +++++++++ 87 files changed, 19848 insertions(+), 19420 deletions(-) delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.codecov.yml delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.github/FUNDING.yml delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.github/workflows/go.yml delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/CHANGELOG.md delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/LICENSE delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/Makefile delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/README.md delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/ast/ast.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/ast/ast_test.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/cmd/ycat/ycat.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/decode.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/decode_test.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/encode.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/encode_test.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/error.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/go.mod delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/go.sum delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/internal/errors/error.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/lexer/lexer.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/lexer/lexer_test.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/option.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/context.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/parser.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/parser_test.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/cr.yml delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/crlf.yml delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/lf.yml delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/path.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/path_test.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/printer/printer.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/printer/printer_test.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/scanner/context.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/scanner/scanner.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/stdlib_quote.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/struct.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/testdata/anchor.yml delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/token/token.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/token/token_test.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/validate.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/validate_test.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/yaml.go delete mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/yaml_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.codecov.yml create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/FUNDING.yml create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/pull_request_template.md create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/workflows/go.yml create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/CHANGELOG.md create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/LICENSE create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/Makefile create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/README.md create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/ast/ast.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/ast/ast_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/cmd/ycat/ycat.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/decode.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/decode_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/encode.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/encode_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/error.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/go.mod create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/go.sum create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/internal/errors/error.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/lexer/lexer.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/lexer/lexer_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/option.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/context.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/parser.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/parser_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/cr.yml create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/crlf.yml create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/lf.yml create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/path.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/path_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/printer/printer.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/printer/printer_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/scanner/context.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/scanner/scanner.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/stdlib_quote.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/struct.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/testdata/anchor.yml create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/token/token.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/token/token_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/validate.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/validate_test.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/yaml.go create mode 100644 dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/yaml_test.go (limited to 'dependencies/pkg/mod/github.com/goccy') diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.codecov.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.codecov.yml deleted file mode 100644 index 8364eea..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.codecov.yml +++ /dev/null @@ -1,31 +0,0 @@ -codecov: - require_ci_to_pass: yes - -coverage: - precision: 2 - round: down - range: "70...100" - - status: - project: - default: - target: 75% - threshold: 2% - patch: off - changes: no - -parsers: - gcov: - branch_detection: - conditional: yes - loop: yes - method: no - macro: no - -comment: - layout: "header,diff" - behavior: default - require_changes: no - -ignore: - - ast diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.github/FUNDING.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.github/FUNDING.yml deleted file mode 100644 index ab4b632..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: [goccy] diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.github/workflows/go.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.github/workflows/go.yml deleted file mode 100644 index a935b3f..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/.github/workflows/go.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Go -on: - push: - branches: - - master - pull_request: -jobs: - race-test: - name: Test with -race - strategy: - matrix: - os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] - go-version: [ "1.18", "1.19", "1.20" ] - runs-on: ${{ matrix.os }} - steps: - - name: setup Go ${{ matrix.go-version }} - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - name: checkout - uses: actions/checkout@v3 - - name: test - run: | - make test - - i386-test: - name: Test in i386 - strategy: - matrix: - os: [ "ubuntu-latest", "windows-latest" ] - go-version: [ "1.18", "1.19", "1.20" ] - runs-on: ${{ matrix.os }} - env: - GOARCH: "386" - steps: - - name: setup Go ${{ matrix.go-version }} - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - name: checkout - uses: actions/checkout@v3 - - name: test - run: | - make simple-test - - ycat: - name: ycat - runs-on: ubuntu-latest - steps: - - name: setup Go - uses: actions/setup-go@v3 - with: - go-version: "1.20" - - name: checkout - uses: actions/checkout@v3 - - name: build - run: | - make ycat/build - - name: run - run: | - ./ycat .github/workflows/go.yml - - coverage: - name: Coverage - runs-on: ubuntu-latest - steps: - - name: setup Go - uses: actions/setup-go@v3 - with: - go-version: "1.20" - - name: checkout - uses: actions/checkout@v3 - - name: measure coverage - run: | - make cover - - uses: codecov/codecov-action@v3 - with: - fail_ci_if_error: true - verbose: true diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/CHANGELOG.md b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/CHANGELOG.md deleted file mode 100644 index 2887563..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/CHANGELOG.md +++ /dev/null @@ -1,163 +0,0 @@ -# 1.10.1 - 2023-03-28 - -### Features - -- Quote YAML 1.1 bools at encoding time for compatibility with other legacy parsers -- Add support of 32-bit architecture - -### Fix bugs - -- Don't trim all space characters in block style sequence -- Support strings starting with `@` - -# 1.10.0 - 2023-03-01 - -### Fix bugs - -Reversible conversion of comments was not working in various cases, which has been corrected. -**Breaking Change** exists in the comment map interface. However, if you are dealing with CommentMap directly, there is no problem. - - -# 1.9.8 - 2022-12-19 - -### Fix feature - -- Append new line at the end of file ( #329 ) - -### Fix bugs - -- Fix custom marshaler ( #333, #334 ) -- Fix behavior when struct fields conflicted( #335 ) -- Fix position calculation for literal, folded and raw folded strings ( #330 ) - -# 1.9.7 - 2022-12-03 - -### Fix bugs - -- Fix handling of quoted map key ( #328 ) -- Fix resusing process of scanning context ( #322 ) - -## v1.9.6 - 2022-10-26 - -### New Features - -- Introduce MapKeyNode interface to limit node types for map key ( #312 ) - -### Fix bugs - -- Quote strings with special characters in flow mode ( #270 ) -- typeError implements PrettyPrinter interface ( #280 ) -- Fix incorrect const type ( #284 ) -- Fix large literals type inference on 32 bits ( #293 ) -- Fix UTF-8 characters ( #294 ) -- Fix decoding of unknown aliases ( #317 ) -- Fix stream encoder for insert a separator between each encoded document ( #318 ) - -### Update - -- Update golang.org/x/sys ( #289 ) -- Update Go version in CI ( #295 ) -- Add test cases for missing keys to struct literals ( #300 ) - -## v1.9.5 - 2022-01-12 - -### New Features - -* Add UseSingleQuote option ( #265 ) - -### Fix bugs - -* Preserve defaults while decoding nested structs ( #260 ) -* Fix minor typo in decodeInit error ( #264 ) -* Handle empty sequence entries ( #275 ) -* Fix encoding of sequence with multiline string ( #276 ) -* Fix encoding of BytesMarshaler type ( #277 ) -* Fix indentState logic for multi-line value ( #278 ) - -## v1.9.4 - 2021-10-12 - -### Fix bugs - -* Keep prev/next reference between tokens containing comments when filtering comment tokens ( #257 ) -* Supports escaping reserved keywords in PathBuilder ( #258 ) - -## v1.9.3 - 2021-09-07 - -### New Features - -* Support encoding and decoding `time.Duration` fields ( #246 ) -* Allow reserved characters for key name in YAMLPath ( #251 ) -* Support getting YAMLPath from ast.Node ( #252 ) -* Support CommentToMap option ( #253 ) - -### Fix bugs - -* Fix encoding nested sequences with `yaml.IndentSequence` ( #241 ) -* Fix error reporting on inline structs in strict mode ( #244, #245 ) -* Fix encoding of large floats ( #247 ) - -### Improve workflow - -* Migrate CI from CircleCI to GitHub Action ( #249 ) -* Add workflow for ycat ( #250 ) - -## v1.9.2 - 2021-07-26 - -### Support WithComment option ( #238 ) - -`yaml.WithComment` is a option for encoding with comment. -The position where you want to add a comment is represented by YAMLPath, and it is the key of `yaml.CommentMap`. -Also, you can select `Head` comment or `Line` comment as the comment type. - -## v1.9.1 - 2021-07-20 - -### Fix DecodeFromNode ( #237 ) - -- Fix YAML handling where anchor exists - -## v1.9.0 - 2021-07-19 - -### New features - -- Support encoding of comment node ( #233 ) -- Support `yaml.NodeToValue(ast.Node, interface{}, ...DecodeOption) error` ( #236 ) - - Can convert a AST node to a value directly - -### Fix decoder for comment - -- Fix parsing of literal with comment ( #234 ) - -### Rename API ( #235 ) - -- Rename `MarshalWithContext` to `MarshalContext` -- Rename `UnmarshalWithContext` to `UnmarshalContext` - -## v1.8.10 - 2021-07-02 - -### Fixed bugs - -- Fix searching anchor by alias name ( #212 ) -- Fixing Issue 186, scanner should account for newline characters when processing multi-line text. Without this source annotations line/column number (for this and all subsequent tokens) is inconsistent with plain text editors. e.g. https://github.com/goccy/go-yaml/issues/186. This addresses the issue specifically for single and double quote text only. ( #210 ) -- Add error for unterminated flow mapping node ( #213 ) -- Handle missing required field validation ( #221 ) -- Nicely format unexpected node type errors ( #229 ) -- Support to encode map which has defined type key ( #231 ) - -### New features - -- Support sequence indentation by EncodeOption ( #232 ) - -## v1.8.9 - 2021-03-01 - -### Fixed bugs - -- Fix origin buffer for DocumentHeader and DocumentEnd and Directive -- Fix origin buffer for anchor value -- Fix syntax error about map value -- Fix parsing MergeKey ('<<') characters -- Fix encoding of float value -- Fix incorrect column annotation when single or double quotes are used - -### New features - -- Support to encode/decode of ast.Node directly diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/LICENSE b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/LICENSE deleted file mode 100644 index 04485ce..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Masaaki Goshima - -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. diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/Makefile b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/Makefile deleted file mode 100644 index 1b1d923..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -.PHONY: test -test: - go test -v -race ./... - -.PHONY: simple-test -simple-test: - go test -v ./... - -.PHONY: cover -cover: - go test -coverprofile=cover.out ./... - -.PHONY: cover-html -cover-html: cover - go tool cover -html=cover.out - -.PHONY: ycat/build -ycat/build: - go build -o ycat ./cmd/ycat diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/README.md b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/README.md deleted file mode 100644 index 9452349..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/README.md +++ /dev/null @@ -1,370 +0,0 @@ -# YAML support for the Go language - -[![PkgGoDev](https://pkg.go.dev/badge/github.com/goccy/go-yaml)](https://pkg.go.dev/github.com/goccy/go-yaml) -![Go](https://github.com/goccy/go-yaml/workflows/Go/badge.svg) -[![codecov](https://codecov.io/gh/goccy/go-yaml/branch/master/graph/badge.svg)](https://codecov.io/gh/goccy/go-yaml) -[![Go Report Card](https://goreportcard.com/badge/github.com/goccy/go-yaml)](https://goreportcard.com/report/github.com/goccy/go-yaml) - - - -# Why a new library? - -As of this writing, there already exists a de facto standard library for YAML processing for Go: [https://github.com/go-yaml/yaml](https://github.com/go-yaml/yaml). However we feel that some features are lacking, namely: - -- Pretty format for error notifications -- Direct manipulation of YAML abstract syntax tree -- Support for `Anchor` and `Alias` when marshaling -- Allow referencing elements declared in another file via anchors - -# Features - -- Pretty format for error notifications -- Supports `Scanner` or `Lexer` or `Parser` as public API -- Supports `Anchor` and `Alias` to Marshaler -- Allow referencing elements declared in another file via anchors -- Extract value or AST by YAMLPath ( YAMLPath is like a JSONPath ) - -# Installation - -```sh -go get -u github.com/goccy/go-yaml -``` - -# Synopsis - -## 1. Simple Encode/Decode - -Has an interface like `go-yaml/yaml` using `reflect` - -```go -var v struct { - A int - B string -} -v.A = 1 -v.B = "hello" -bytes, err := yaml.Marshal(v) -if err != nil { - //... -} -fmt.Println(string(bytes)) // "a: 1\nb: hello\n" -``` - -```go - yml := ` -%YAML 1.2 ---- -a: 1 -b: c -` -var v struct { - A int - B string -} -if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - //... -} -``` - -To control marshal/unmarshal behavior, you can use the `yaml` tag. - -```go - yml := `--- -foo: 1 -bar: c -` -var v struct { - A int `yaml:"foo"` - B string `yaml:"bar"` -} -if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - //... -} -``` - -For convenience, we also accept the `json` tag. Note that not all options from -the `json` tag will have significance when parsing YAML documents. If both -tags exist, `yaml` tag will take precedence. - -```go - yml := `--- -foo: 1 -bar: c -` -var v struct { - A int `json:"foo"` - B string `json:"bar"` -} -if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - //... -} -``` - -For custom marshal/unmarshaling, implement either `Bytes` or `Interface` variant of marshaler/unmarshaler. The difference is that while `BytesMarshaler`/`BytesUnmarshaler` behaves like [`encoding/json`](https://pkg.go.dev/encoding/json) and `InterfaceMarshaler`/`InterfaceUnmarshaler` behaves like [`gopkg.in/yaml.v2`](https://pkg.go.dev/gopkg.in/yaml.v2). - -Semantically both are the same, but they differ in performance. Because indentation matters in YAML, you cannot simply accept a valid YAML fragment from a Marshaler, and expect it to work when it is attached to the parent container's serialized form. Therefore when we receive use the `BytesMarshaler`, which returns `[]byte`, we must decode it once to figure out how to make it work in the given context. If you use the `InterfaceMarshaler`, we can skip the decoding. - -If you are repeatedly marshaling complex objects, the latter is always better -performance wise. But if you are, for example, just providing a choice between -a config file format that is read only once, the former is probably easier to -code. - -## 2. Reference elements declared in another file - -`testdata` directory contains `anchor.yml` file: - -```shell -├── testdata -   └── anchor.yml -``` - -And `anchor.yml` is defined as follows: - -```yaml -a: &a - b: 1 - c: hello -``` - -Then, if `yaml.ReferenceDirs("testdata")` option is passed to `yaml.Decoder`, - `Decoder` tries to find the anchor definition from YAML files the under `testdata` directory. - -```go -buf := bytes.NewBufferString("a: *a\n") -dec := yaml.NewDecoder(buf, yaml.ReferenceDirs("testdata")) -var v struct { - A struct { - B int - C string - } -} -if err := dec.Decode(&v); err != nil { - //... -} -fmt.Printf("%+v\n", v) // {A:{B:1 C:hello}} -``` - -## 3. Encode with `Anchor` and `Alias` - -### 3.1. Explicitly declared `Anchor` name and `Alias` name - -If you want to use `anchor` or `alias`, you can define it as a struct tag. - -```go -type T struct { - A int - B string -} -var v struct { - C *T `yaml:"c,anchor=x"` - D *T `yaml:"d,alias=x"` -} -v.C = &T{A: 1, B: "hello"} -v.D = v.C -bytes, err := yaml.Marshal(v) -if err != nil { - panic(err) -} -fmt.Println(string(bytes)) -/* -c: &x - a: 1 - b: hello -d: *x -*/ -``` - -### 3.2. Implicitly declared `Anchor` and `Alias` names - -If you do not explicitly declare the anchor name, the default behavior is to -use the equivalent of `strings.ToLower($FieldName)` as the name of the anchor. - -If you do not explicitly declare the alias name AND the value is a pointer -to another element, we look up the anchor name by finding out which anchor -field the value is assigned to by looking up its pointer address. - -```go -type T struct { - I int - S string -} -var v struct { - A *T `yaml:"a,anchor"` - B *T `yaml:"b,anchor"` - C *T `yaml:"c,alias"` - D *T `yaml:"d,alias"` -} -v.A = &T{I: 1, S: "hello"} -v.B = &T{I: 2, S: "world"} -v.C = v.A // C has same pointer address to A -v.D = v.B // D has same pointer address to B -bytes, err := yaml.Marshal(v) -if err != nil { - //... -} -fmt.Println(string(bytes)) -/* -a: &a - i: 1 - s: hello -b: &b - i: 2 - s: world -c: *a -d: *b -*/ -``` - -### 3.3 MergeKey and Alias - -Merge key and alias ( `<<: *alias` ) can be used by embedding a structure with the `inline,alias` tag. - -```go -type Person struct { - *Person `yaml:",omitempty,inline,alias"` // embed Person type for default value - Name string `yaml:",omitempty"` - Age int `yaml:",omitempty"` -} -defaultPerson := &Person{ - Name: "John Smith", - Age: 20, -} -people := []*Person{ - { - Person: defaultPerson, // assign default value - Name: "Ken", // override Name property - Age: 10, // override Age property - }, - { - Person: defaultPerson, // assign default value only - }, -} -var doc struct { - Default *Person `yaml:"default,anchor"` - People []*Person `yaml:"people"` -} -doc.Default = defaultPerson -doc.People = people -bytes, err := yaml.Marshal(doc) -if err != nil { - //... -} -fmt.Println(string(bytes)) -/* -default: &default - name: John Smith - age: 20 -people: -- <<: *default - name: Ken - age: 10 -- <<: *default -*/ -``` - -## 4. Pretty Formatted Errors - -Error values produced during parsing have two extra features over regular -error values. - -First, by default, they contain extra information on the location of the error -from the source YAML document, to make it easier to find the error location. - -Second, the error messages can optionally be colorized. - -If you would like to control exactly how the output looks like, consider -using `yaml.FormatError`, which accepts two boolean values to -control turning these features on or off. - - - -## 5. Use YAMLPath - -```go -yml := ` -store: - book: - - author: john - price: 10 - - author: ken - price: 12 - bicycle: - color: red - price: 19.95 -` -path, err := yaml.PathString("$.store.book[*].author") -if err != nil { - //... -} -var authors []string -if err := path.Read(strings.NewReader(yml), &authors); err != nil { - //... -} -fmt.Println(authors) -// [john ken] -``` - -### 5.1 Print customized error with YAML source code - -```go -package main - -import ( - "fmt" - - "github.com/goccy/go-yaml" -) - -func main() { - yml := ` -a: 1 -b: "hello" -` - var v struct { - A int - B string - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - panic(err) - } - if v.A != 2 { - // output error with YAML source - path, err := yaml.PathString("$.a") - if err != nil { - panic(err) - } - source, err := path.AnnotateSource([]byte(yml), true) - if err != nil { - panic(err) - } - fmt.Printf("a value expected 2 but actual %d:\n%s\n", v.A, string(source)) - } -} -``` - -output result is the following: - - - - -# Tools - -## ycat - -print yaml file with color - -ycat - -### Installation - -```sh -go install github.com/goccy/go-yaml/cmd/ycat@latest -``` - -# Looking for Sponsors - -I'm looking for sponsors this library. This library is being developed as a personal project in my spare time. If you want a quick response or problem resolution when using this library in your project, please register as a [sponsor](https://github.com/sponsors/goccy). I will cooperate as much as possible. Of course, this library is developed as an MIT license, so you can use it freely for free. - -# License - -MIT diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/ast/ast.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/ast/ast.go deleted file mode 100644 index f535a24..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/ast/ast.go +++ /dev/null @@ -1,2117 +0,0 @@ -package ast - -import ( - "fmt" - "io" - "math" - "strconv" - "strings" - - "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" -) - -var ( - ErrInvalidTokenType = xerrors.New("invalid token type") - ErrInvalidAnchorName = xerrors.New("invalid anchor name") - ErrInvalidAliasName = xerrors.New("invalid alias name") -) - -// NodeType type identifier of node -type NodeType int - -const ( - // UnknownNodeType type identifier for default - UnknownNodeType NodeType = iota - // DocumentType type identifier for document node - DocumentType - // NullType type identifier for null node - NullType - // BoolType type identifier for boolean node - BoolType - // IntegerType type identifier for integer node - IntegerType - // FloatType type identifier for float node - FloatType - // InfinityType type identifier for infinity node - InfinityType - // NanType type identifier for nan node - NanType - // StringType type identifier for string node - StringType - // MergeKeyType type identifier for merge key node - MergeKeyType - // LiteralType type identifier for literal node - LiteralType - // MappingType type identifier for mapping node - MappingType - // MappingKeyType type identifier for mapping key node - MappingKeyType - // MappingValueType type identifier for mapping value node - MappingValueType - // SequenceType type identifier for sequence node - SequenceType - // AnchorType type identifier for anchor node - AnchorType - // AliasType type identifier for alias node - AliasType - // DirectiveType type identifier for directive node - DirectiveType - // TagType type identifier for tag node - TagType - // CommentType type identifier for comment node - CommentType - // CommentGroupType type identifier for comment group node - CommentGroupType -) - -// String node type identifier to text -func (t NodeType) String() string { - switch t { - case UnknownNodeType: - return "UnknownNode" - case DocumentType: - return "Document" - case NullType: - return "Null" - case BoolType: - return "Bool" - case IntegerType: - return "Integer" - case FloatType: - return "Float" - case InfinityType: - return "Infinity" - case NanType: - return "Nan" - case StringType: - return "String" - case MergeKeyType: - return "MergeKey" - case LiteralType: - return "Literal" - case MappingType: - return "Mapping" - case MappingKeyType: - return "MappingKey" - case MappingValueType: - return "MappingValue" - case SequenceType: - return "Sequence" - case AnchorType: - return "Anchor" - case AliasType: - return "Alias" - case DirectiveType: - return "Directive" - case TagType: - return "Tag" - case CommentType: - return "Comment" - case CommentGroupType: - return "CommentGroup" - } - return "" -} - -// String node type identifier to YAML Structure name -// based on https://yaml.org/spec/1.2/spec.html -func (t NodeType) YAMLName() string { - switch t { - case UnknownNodeType: - return "unknown" - case DocumentType: - return "document" - case NullType: - return "null" - case BoolType: - return "boolean" - case IntegerType: - return "int" - case FloatType: - return "float" - case InfinityType: - return "inf" - case NanType: - return "nan" - case StringType: - return "string" - case MergeKeyType: - return "merge key" - case LiteralType: - return "scalar" - case MappingType: - return "mapping" - case MappingKeyType: - return "key" - case MappingValueType: - return "value" - case SequenceType: - return "sequence" - case AnchorType: - return "anchor" - case AliasType: - return "alias" - case DirectiveType: - return "directive" - case TagType: - return "tag" - case CommentType: - return "comment" - case CommentGroupType: - return "comment" - } - return "" -} - -// Node type of node -type Node interface { - io.Reader - // String node to text - String() string - // GetToken returns token instance - GetToken() *token.Token - // Type returns type of node - Type() NodeType - // AddColumn add column number to child nodes recursively - AddColumn(int) - // SetComment set comment token to node - SetComment(*CommentGroupNode) error - // Comment returns comment token instance - GetComment() *CommentGroupNode - // GetPath returns YAMLPath for the current node - GetPath() string - // SetPath set YAMLPath for the current node - SetPath(string) - // MarshalYAML - MarshalYAML() ([]byte, error) - // already read length - readLen() int - // append read length - addReadLen(int) - // clean read length - clearLen() -} - -// MapKeyNode type for map key node -type MapKeyNode interface { - Node - // String node to text without comment - stringWithoutComment() string -} - -// ScalarNode type for scalar node -type ScalarNode interface { - MapKeyNode - GetValue() interface{} -} - -type BaseNode struct { - Path string - Comment *CommentGroupNode - read int -} - -func addCommentString(base string, node *CommentGroupNode) string { - return fmt.Sprintf("%s %s", base, node.String()) -} - -func (n *BaseNode) readLen() int { - return n.read -} - -func (n *BaseNode) clearLen() { - n.read = 0 -} - -func (n *BaseNode) addReadLen(len int) { - n.read += len -} - -// GetPath returns YAMLPath for the current node. -func (n *BaseNode) GetPath() string { - if n == nil { - return "" - } - return n.Path -} - -// SetPath set YAMLPath for the current node. -func (n *BaseNode) SetPath(path string) { - if n == nil { - return - } - n.Path = path -} - -// GetComment returns comment token instance -func (n *BaseNode) GetComment() *CommentGroupNode { - return n.Comment -} - -// SetComment set comment token -func (n *BaseNode) SetComment(node *CommentGroupNode) error { - n.Comment = node - return nil -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func readNode(p []byte, node Node) (int, error) { - s := node.String() - readLen := node.readLen() - remain := len(s) - readLen - if remain == 0 { - node.clearLen() - return 0, io.EOF - } - size := min(remain, len(p)) - for idx, b := range []byte(s[readLen : readLen+size]) { - p[idx] = byte(b) - } - node.addReadLen(size) - return size, nil -} - -// Null create node for null value -func Null(tk *token.Token) *NullNode { - return &NullNode{ - BaseNode: &BaseNode{}, - Token: tk, - } -} - -// Bool create node for boolean value -func Bool(tk *token.Token) *BoolNode { - b, _ := strconv.ParseBool(tk.Value) - return &BoolNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: b, - } -} - -// Integer create node for integer value -func Integer(tk *token.Token) *IntegerNode { - value := removeUnderScoreFromNumber(tk.Value) - switch tk.Type { - case token.BinaryIntegerType: - // skip two characters because binary token starts with '0b' - skipCharacterNum := 2 - negativePrefix := "" - if value[0] == '-' { - skipCharacterNum++ - negativePrefix = "-" - } - if len(negativePrefix) > 0 { - i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 2, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - } - i, _ := strconv.ParseUint(negativePrefix+value[skipCharacterNum:], 2, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - case token.OctetIntegerType: - // octet token starts with '0o' or '-0o' or '0' or '-0' - skipCharacterNum := 1 - negativePrefix := "" - if value[0] == '-' { - skipCharacterNum++ - if len(value) > 2 && value[2] == 'o' { - skipCharacterNum++ - } - negativePrefix = "-" - } else { - if value[1] == 'o' { - skipCharacterNum++ - } - } - if len(negativePrefix) > 0 { - i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 8, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - } - i, _ := strconv.ParseUint(value[skipCharacterNum:], 8, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - case token.HexIntegerType: - // hex token starts with '0x' or '-0x' - skipCharacterNum := 2 - negativePrefix := "" - if value[0] == '-' { - skipCharacterNum++ - negativePrefix = "-" - } - if len(negativePrefix) > 0 { - i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 16, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - } - i, _ := strconv.ParseUint(value[skipCharacterNum:], 16, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - } - if value[0] == '-' || value[0] == '+' { - i, _ := strconv.ParseInt(value, 10, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - } - i, _ := strconv.ParseUint(value, 10, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } -} - -// Float create node for float value -func Float(tk *token.Token) *FloatNode { - f, _ := strconv.ParseFloat(removeUnderScoreFromNumber(tk.Value), 64) - return &FloatNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: f, - } -} - -// Infinity create node for .inf or -.inf value -func Infinity(tk *token.Token) *InfinityNode { - node := &InfinityNode{ - BaseNode: &BaseNode{}, - Token: tk, - } - switch tk.Value { - case ".inf", ".Inf", ".INF": - node.Value = math.Inf(0) - case "-.inf", "-.Inf", "-.INF": - node.Value = math.Inf(-1) - } - return node -} - -// Nan create node for .nan value -func Nan(tk *token.Token) *NanNode { - return &NanNode{ - BaseNode: &BaseNode{}, - Token: tk, - } -} - -// String create node for string value -func String(tk *token.Token) *StringNode { - return &StringNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: tk.Value, - } -} - -// Comment create node for comment -func Comment(tk *token.Token) *CommentNode { - return &CommentNode{ - BaseNode: &BaseNode{}, - Token: tk, - } -} - -func CommentGroup(comments []*token.Token) *CommentGroupNode { - nodes := []*CommentNode{} - for _, comment := range comments { - nodes = append(nodes, Comment(comment)) - } - return &CommentGroupNode{ - BaseNode: &BaseNode{}, - Comments: nodes, - } -} - -// MergeKey create node for merge key ( << ) -func MergeKey(tk *token.Token) *MergeKeyNode { - return &MergeKeyNode{ - BaseNode: &BaseNode{}, - Token: tk, - } -} - -// Mapping create node for map -func Mapping(tk *token.Token, isFlowStyle bool, values ...*MappingValueNode) *MappingNode { - node := &MappingNode{ - BaseNode: &BaseNode{}, - Start: tk, - IsFlowStyle: isFlowStyle, - Values: []*MappingValueNode{}, - } - node.Values = append(node.Values, values...) - return node -} - -// MappingValue create node for mapping value -func MappingValue(tk *token.Token, key MapKeyNode, value Node) *MappingValueNode { - return &MappingValueNode{ - BaseNode: &BaseNode{}, - Start: tk, - Key: key, - Value: value, - } -} - -// MappingKey create node for map key ( '?' ). -func MappingKey(tk *token.Token) *MappingKeyNode { - return &MappingKeyNode{ - BaseNode: &BaseNode{}, - Start: tk, - } -} - -// Sequence create node for sequence -func Sequence(tk *token.Token, isFlowStyle bool) *SequenceNode { - return &SequenceNode{ - BaseNode: &BaseNode{}, - Start: tk, - IsFlowStyle: isFlowStyle, - Values: []Node{}, - } -} - -func Anchor(tk *token.Token) *AnchorNode { - return &AnchorNode{ - BaseNode: &BaseNode{}, - Start: tk, - } -} - -func Alias(tk *token.Token) *AliasNode { - return &AliasNode{ - BaseNode: &BaseNode{}, - Start: tk, - } -} - -func Document(tk *token.Token, body Node) *DocumentNode { - return &DocumentNode{ - BaseNode: &BaseNode{}, - Start: tk, - Body: body, - } -} - -func Directive(tk *token.Token) *DirectiveNode { - return &DirectiveNode{ - BaseNode: &BaseNode{}, - Start: tk, - } -} - -func Literal(tk *token.Token) *LiteralNode { - return &LiteralNode{ - BaseNode: &BaseNode{}, - Start: tk, - } -} - -func Tag(tk *token.Token) *TagNode { - return &TagNode{ - BaseNode: &BaseNode{}, - Start: tk, - } -} - -// File contains all documents in YAML file -type File struct { - Name string - Docs []*DocumentNode -} - -// Read implements (io.Reader).Read -func (f *File) Read(p []byte) (int, error) { - for _, doc := range f.Docs { - n, err := doc.Read(p) - if err == io.EOF { - continue - } - return n, nil - } - return 0, io.EOF -} - -// String all documents to text -func (f *File) String() string { - docs := []string{} - for _, doc := range f.Docs { - docs = append(docs, doc.String()) - } - if len(docs) > 0 { - return strings.Join(docs, "\n") + "\n" - } else { - return "" - } -} - -// DocumentNode type of Document -type DocumentNode struct { - *BaseNode - Start *token.Token // position of DocumentHeader ( `---` ) - End *token.Token // position of DocumentEnd ( `...` ) - Body Node -} - -// Read implements (io.Reader).Read -func (d *DocumentNode) Read(p []byte) (int, error) { - return readNode(p, d) -} - -// Type returns DocumentNodeType -func (d *DocumentNode) Type() NodeType { return DocumentType } - -// GetToken returns token instance -func (d *DocumentNode) GetToken() *token.Token { - return d.Body.GetToken() -} - -// AddColumn add column number to child nodes recursively -func (d *DocumentNode) AddColumn(col int) { - if d.Body != nil { - d.Body.AddColumn(col) - } -} - -// String document to text -func (d *DocumentNode) String() string { - doc := []string{} - if d.Start != nil { - doc = append(doc, d.Start.Value) - } - doc = append(doc, d.Body.String()) - if d.End != nil { - doc = append(doc, d.End.Value) - } - return strings.Join(doc, "\n") -} - -// MarshalYAML encodes to a YAML text -func (d *DocumentNode) MarshalYAML() ([]byte, error) { - return []byte(d.String()), nil -} - -func removeUnderScoreFromNumber(num string) string { - return strings.ReplaceAll(num, "_", "") -} - -// NullNode type of null node -type NullNode struct { - *BaseNode - Token *token.Token -} - -// Read implements (io.Reader).Read -func (n *NullNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns NullType -func (n *NullNode) Type() NodeType { return NullType } - -// GetToken returns token instance -func (n *NullNode) GetToken() *token.Token { - return n.Token -} - -// AddColumn add column number to child nodes recursively -func (n *NullNode) AddColumn(col int) { - n.Token.AddColumn(col) -} - -// GetValue returns nil value -func (n *NullNode) GetValue() interface{} { - return nil -} - -// String returns `null` text -func (n *NullNode) String() string { - if n.Comment != nil { - return addCommentString("null", n.Comment) - } - return n.stringWithoutComment() -} - -func (n *NullNode) stringWithoutComment() string { - return "null" -} - -// MarshalYAML encodes to a YAML text -func (n *NullNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// IntegerNode type of integer node -type IntegerNode struct { - *BaseNode - Token *token.Token - Value interface{} // int64 or uint64 value -} - -// Read implements (io.Reader).Read -func (n *IntegerNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns IntegerType -func (n *IntegerNode) Type() NodeType { return IntegerType } - -// GetToken returns token instance -func (n *IntegerNode) GetToken() *token.Token { - return n.Token -} - -// AddColumn add column number to child nodes recursively -func (n *IntegerNode) AddColumn(col int) { - n.Token.AddColumn(col) -} - -// GetValue returns int64 value -func (n *IntegerNode) GetValue() interface{} { - return n.Value -} - -// String int64 to text -func (n *IntegerNode) String() string { - if n.Comment != nil { - return addCommentString(n.Token.Value, n.Comment) - } - return n.stringWithoutComment() -} - -func (n *IntegerNode) stringWithoutComment() string { - return n.Token.Value -} - -// MarshalYAML encodes to a YAML text -func (n *IntegerNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// FloatNode type of float node -type FloatNode struct { - *BaseNode - Token *token.Token - Precision int - Value float64 -} - -// Read implements (io.Reader).Read -func (n *FloatNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns FloatType -func (n *FloatNode) Type() NodeType { return FloatType } - -// GetToken returns token instance -func (n *FloatNode) GetToken() *token.Token { - return n.Token -} - -// AddColumn add column number to child nodes recursively -func (n *FloatNode) AddColumn(col int) { - n.Token.AddColumn(col) -} - -// GetValue returns float64 value -func (n *FloatNode) GetValue() interface{} { - return n.Value -} - -// String float64 to text -func (n *FloatNode) String() string { - if n.Comment != nil { - return addCommentString(n.Token.Value, n.Comment) - } - return n.stringWithoutComment() -} - -func (n *FloatNode) stringWithoutComment() string { - return n.Token.Value -} - -// MarshalYAML encodes to a YAML text -func (n *FloatNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// StringNode type of string node -type StringNode struct { - *BaseNode - Token *token.Token - Value string -} - -// Read implements (io.Reader).Read -func (n *StringNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns StringType -func (n *StringNode) Type() NodeType { return StringType } - -// GetToken returns token instance -func (n *StringNode) GetToken() *token.Token { - return n.Token -} - -// AddColumn add column number to child nodes recursively -func (n *StringNode) AddColumn(col int) { - n.Token.AddColumn(col) -} - -// GetValue returns string value -func (n *StringNode) GetValue() interface{} { - return n.Value -} - -// escapeSingleQuote escapes s to a single quoted scalar. -// https://yaml.org/spec/1.2.2/#732-single-quoted-style -func escapeSingleQuote(s string) string { - var sb strings.Builder - growLen := len(s) + // s includes also one ' from the doubled pair - 2 + // opening and closing ' - strings.Count(s, "'") // ' added by ReplaceAll - sb.Grow(growLen) - sb.WriteString("'") - sb.WriteString(strings.ReplaceAll(s, "'", "''")) - sb.WriteString("'") - return sb.String() -} - -// String string value to text with quote or literal header if required -func (n *StringNode) String() string { - switch n.Token.Type { - case token.SingleQuoteType: - quoted := escapeSingleQuote(n.Value) - if n.Comment != nil { - return addCommentString(quoted, n.Comment) - } - return quoted - case token.DoubleQuoteType: - quoted := strconv.Quote(n.Value) - if n.Comment != nil { - return addCommentString(quoted, n.Comment) - } - return quoted - } - - lbc := token.DetectLineBreakCharacter(n.Value) - if strings.Contains(n.Value, lbc) { - // This block assumes that the line breaks in this inside scalar content and the Outside scalar content are the same. - // It works mostly, but inconsistencies occur if line break characters are mixed. - header := token.LiteralBlockHeader(n.Value) - space := strings.Repeat(" ", n.Token.Position.Column-1) - values := []string{} - for _, v := range strings.Split(n.Value, lbc) { - values = append(values, fmt.Sprintf("%s %s", space, v)) - } - block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space)) - return fmt.Sprintf("%s%s%s", header, lbc, block) - } else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') { - return fmt.Sprintf(`'%s'`, n.Value) - } - if n.Comment != nil { - return addCommentString(n.Value, n.Comment) - } - return n.Value -} - -func (n *StringNode) stringWithoutComment() string { - switch n.Token.Type { - case token.SingleQuoteType: - quoted := fmt.Sprintf(`'%s'`, n.Value) - return quoted - case token.DoubleQuoteType: - quoted := strconv.Quote(n.Value) - return quoted - } - - lbc := token.DetectLineBreakCharacter(n.Value) - if strings.Contains(n.Value, lbc) { - // This block assumes that the line breaks in this inside scalar content and the Outside scalar content are the same. - // It works mostly, but inconsistencies occur if line break characters are mixed. - header := token.LiteralBlockHeader(n.Value) - space := strings.Repeat(" ", n.Token.Position.Column-1) - values := []string{} - for _, v := range strings.Split(n.Value, lbc) { - values = append(values, fmt.Sprintf("%s %s", space, v)) - } - block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space)) - return fmt.Sprintf("%s%s%s", header, lbc, block) - } else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') { - return fmt.Sprintf(`'%s'`, n.Value) - } - return n.Value -} - -// MarshalYAML encodes to a YAML text -func (n *StringNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// LiteralNode type of literal node -type LiteralNode struct { - *BaseNode - Start *token.Token - Value *StringNode -} - -// Read implements (io.Reader).Read -func (n *LiteralNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns LiteralType -func (n *LiteralNode) Type() NodeType { return LiteralType } - -// GetToken returns token instance -func (n *LiteralNode) GetToken() *token.Token { - return n.Start -} - -// AddColumn add column number to child nodes recursively -func (n *LiteralNode) AddColumn(col int) { - n.Start.AddColumn(col) - if n.Value != nil { - n.Value.AddColumn(col) - } -} - -// GetValue returns string value -func (n *LiteralNode) GetValue() interface{} { - return n.String() -} - -// String literal to text -func (n *LiteralNode) String() string { - origin := n.Value.GetToken().Origin - lit := strings.TrimRight(strings.TrimRight(origin, " "), "\n") - if n.Comment != nil { - return fmt.Sprintf("%s %s\n%s", n.Start.Value, n.Comment.String(), lit) - } - return fmt.Sprintf("%s\n%s", n.Start.Value, lit) -} - -func (n *LiteralNode) stringWithoutComment() string { - return n.String() -} - -// MarshalYAML encodes to a YAML text -func (n *LiteralNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// MergeKeyNode type of merge key node -type MergeKeyNode struct { - *BaseNode - Token *token.Token -} - -// Read implements (io.Reader).Read -func (n *MergeKeyNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns MergeKeyType -func (n *MergeKeyNode) Type() NodeType { return MergeKeyType } - -// GetToken returns token instance -func (n *MergeKeyNode) GetToken() *token.Token { - return n.Token -} - -// GetValue returns '<<' value -func (n *MergeKeyNode) GetValue() interface{} { - return n.Token.Value -} - -// String returns '<<' value -func (n *MergeKeyNode) String() string { - return n.stringWithoutComment() -} - -func (n *MergeKeyNode) stringWithoutComment() string { - return n.Token.Value -} - -// AddColumn add column number to child nodes recursively -func (n *MergeKeyNode) AddColumn(col int) { - n.Token.AddColumn(col) -} - -// MarshalYAML encodes to a YAML text -func (n *MergeKeyNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// BoolNode type of boolean node -type BoolNode struct { - *BaseNode - Token *token.Token - Value bool -} - -// Read implements (io.Reader).Read -func (n *BoolNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns BoolType -func (n *BoolNode) Type() NodeType { return BoolType } - -// GetToken returns token instance -func (n *BoolNode) GetToken() *token.Token { - return n.Token -} - -// AddColumn add column number to child nodes recursively -func (n *BoolNode) AddColumn(col int) { - n.Token.AddColumn(col) -} - -// GetValue returns boolean value -func (n *BoolNode) GetValue() interface{} { - return n.Value -} - -// String boolean to text -func (n *BoolNode) String() string { - if n.Comment != nil { - return addCommentString(n.Token.Value, n.Comment) - } - return n.stringWithoutComment() -} - -func (n *BoolNode) stringWithoutComment() string { - return n.Token.Value -} - -// MarshalYAML encodes to a YAML text -func (n *BoolNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// InfinityNode type of infinity node -type InfinityNode struct { - *BaseNode - Token *token.Token - Value float64 -} - -// Read implements (io.Reader).Read -func (n *InfinityNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns InfinityType -func (n *InfinityNode) Type() NodeType { return InfinityType } - -// GetToken returns token instance -func (n *InfinityNode) GetToken() *token.Token { - return n.Token -} - -// AddColumn add column number to child nodes recursively -func (n *InfinityNode) AddColumn(col int) { - n.Token.AddColumn(col) -} - -// GetValue returns math.Inf(0) or math.Inf(-1) -func (n *InfinityNode) GetValue() interface{} { - return n.Value -} - -// String infinity to text -func (n *InfinityNode) String() string { - if n.Comment != nil { - return addCommentString(n.Token.Value, n.Comment) - } - return n.stringWithoutComment() -} - -func (n *InfinityNode) stringWithoutComment() string { - return n.Token.Value -} - -// MarshalYAML encodes to a YAML text -func (n *InfinityNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// NanNode type of nan node -type NanNode struct { - *BaseNode - Token *token.Token -} - -// Read implements (io.Reader).Read -func (n *NanNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns NanType -func (n *NanNode) Type() NodeType { return NanType } - -// GetToken returns token instance -func (n *NanNode) GetToken() *token.Token { - return n.Token -} - -// AddColumn add column number to child nodes recursively -func (n *NanNode) AddColumn(col int) { - n.Token.AddColumn(col) -} - -// GetValue returns math.NaN() -func (n *NanNode) GetValue() interface{} { - return math.NaN() -} - -// String returns .nan -func (n *NanNode) String() string { - if n.Comment != nil { - return addCommentString(n.Token.Value, n.Comment) - } - return n.stringWithoutComment() -} - -func (n *NanNode) stringWithoutComment() string { - return n.Token.Value -} - -// MarshalYAML encodes to a YAML text -func (n *NanNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// MapNode interface of MappingValueNode / MappingNode -type MapNode interface { - MapRange() *MapNodeIter -} - -// MapNodeIter is an iterator for ranging over a MapNode -type MapNodeIter struct { - values []*MappingValueNode - idx int -} - -const ( - startRangeIndex = -1 -) - -// Next advances the map iterator and reports whether there is another entry. -// It returns false when the iterator is exhausted. -func (m *MapNodeIter) Next() bool { - m.idx++ - next := m.idx < len(m.values) - return next -} - -// Key returns the key of the iterator's current map node entry. -func (m *MapNodeIter) Key() MapKeyNode { - return m.values[m.idx].Key -} - -// Value returns the value of the iterator's current map node entry. -func (m *MapNodeIter) Value() Node { - return m.values[m.idx].Value -} - -// MappingNode type of mapping node -type MappingNode struct { - *BaseNode - Start *token.Token - End *token.Token - IsFlowStyle bool - Values []*MappingValueNode - FootComment *CommentGroupNode -} - -func (n *MappingNode) startPos() *token.Position { - if len(n.Values) == 0 { - return n.Start.Position - } - return n.Values[0].Key.GetToken().Position -} - -// Merge merge key/value of map. -func (n *MappingNode) Merge(target *MappingNode) { - keyToMapValueMap := map[string]*MappingValueNode{} - for _, value := range n.Values { - key := value.Key.String() - keyToMapValueMap[key] = value - } - column := n.startPos().Column - target.startPos().Column - target.AddColumn(column) - for _, value := range target.Values { - mapValue, exists := keyToMapValueMap[value.Key.String()] - if exists { - mapValue.Value = value.Value - } else { - n.Values = append(n.Values, value) - } - } -} - -// SetIsFlowStyle set value to IsFlowStyle field recursively. -func (n *MappingNode) SetIsFlowStyle(isFlow bool) { - n.IsFlowStyle = isFlow - for _, value := range n.Values { - value.SetIsFlowStyle(isFlow) - } -} - -// Read implements (io.Reader).Read -func (n *MappingNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns MappingType -func (n *MappingNode) Type() NodeType { return MappingType } - -// GetToken returns token instance -func (n *MappingNode) GetToken() *token.Token { - return n.Start -} - -// AddColumn add column number to child nodes recursively -func (n *MappingNode) AddColumn(col int) { - n.Start.AddColumn(col) - n.End.AddColumn(col) - for _, value := range n.Values { - value.AddColumn(col) - } -} - -func (n *MappingNode) flowStyleString(commentMode bool) string { - values := []string{} - for _, value := range n.Values { - values = append(values, strings.TrimLeft(value.String(), " ")) - } - mapText := fmt.Sprintf("{%s}", strings.Join(values, ", ")) - if commentMode && n.Comment != nil { - return addCommentString(mapText, n.Comment) - } - return mapText -} - -func (n *MappingNode) blockStyleString(commentMode bool) string { - values := []string{} - for _, value := range n.Values { - values = append(values, value.String()) - } - mapText := strings.Join(values, "\n") - if commentMode && n.Comment != nil { - value := values[0] - var spaceNum int - for i := 0; i < len(value); i++ { - if value[i] != ' ' { - break - } - spaceNum++ - } - comment := n.Comment.StringWithSpace(spaceNum) - return fmt.Sprintf("%s\n%s", comment, mapText) - } - return mapText -} - -// String mapping values to text -func (n *MappingNode) String() string { - if len(n.Values) == 0 { - if n.Comment != nil { - return addCommentString("{}", n.Comment) - } - return "{}" - } - - commentMode := true - if n.IsFlowStyle || len(n.Values) == 0 { - return n.flowStyleString(commentMode) - } - return n.blockStyleString(commentMode) -} - -// MapRange implements MapNode protocol -func (n *MappingNode) MapRange() *MapNodeIter { - return &MapNodeIter{ - idx: startRangeIndex, - values: n.Values, - } -} - -// MarshalYAML encodes to a YAML text -func (n *MappingNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// MappingKeyNode type of tag node -type MappingKeyNode struct { - *BaseNode - Start *token.Token - Value Node -} - -// Read implements (io.Reader).Read -func (n *MappingKeyNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns MappingKeyType -func (n *MappingKeyNode) Type() NodeType { return MappingKeyType } - -// GetToken returns token instance -func (n *MappingKeyNode) GetToken() *token.Token { - return n.Start -} - -// AddColumn add column number to child nodes recursively -func (n *MappingKeyNode) AddColumn(col int) { - n.Start.AddColumn(col) - if n.Value != nil { - n.Value.AddColumn(col) - } -} - -// String tag to text -func (n *MappingKeyNode) String() string { - return n.stringWithoutComment() -} - -func (n *MappingKeyNode) stringWithoutComment() string { - return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String()) -} - -// MarshalYAML encodes to a YAML text -func (n *MappingKeyNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// MappingValueNode type of mapping value -type MappingValueNode struct { - *BaseNode - Start *token.Token - Key MapKeyNode - Value Node - FootComment *CommentGroupNode -} - -// Replace replace value node. -func (n *MappingValueNode) Replace(value Node) error { - column := n.Value.GetToken().Position.Column - value.GetToken().Position.Column - value.AddColumn(column) - n.Value = value - return nil -} - -// Read implements (io.Reader).Read -func (n *MappingValueNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns MappingValueType -func (n *MappingValueNode) Type() NodeType { return MappingValueType } - -// GetToken returns token instance -func (n *MappingValueNode) GetToken() *token.Token { - return n.Start -} - -// AddColumn add column number to child nodes recursively -func (n *MappingValueNode) AddColumn(col int) { - n.Start.AddColumn(col) - if n.Key != nil { - n.Key.AddColumn(col) - } - if n.Value != nil { - n.Value.AddColumn(col) - } -} - -// SetIsFlowStyle set value to IsFlowStyle field recursively. -func (n *MappingValueNode) SetIsFlowStyle(isFlow bool) { - switch value := n.Value.(type) { - case *MappingNode: - value.SetIsFlowStyle(isFlow) - case *MappingValueNode: - value.SetIsFlowStyle(isFlow) - case *SequenceNode: - value.SetIsFlowStyle(isFlow) - } -} - -// String mapping value to text -func (n *MappingValueNode) String() string { - var text string - if n.Comment != nil { - text = fmt.Sprintf( - "%s\n%s", - n.Comment.StringWithSpace(n.Key.GetToken().Position.Column-1), - n.toString(), - ) - } else { - text = n.toString() - } - if n.FootComment != nil { - text += fmt.Sprintf("\n%s", n.FootComment.StringWithSpace(n.Key.GetToken().Position.Column-1)) - } - return text -} - -func (n *MappingValueNode) toString() string { - space := strings.Repeat(" ", n.Key.GetToken().Position.Column-1) - keyIndentLevel := n.Key.GetToken().Position.IndentLevel - valueIndentLevel := n.Value.GetToken().Position.IndentLevel - keyComment := n.Key.GetComment() - if _, ok := n.Value.(ScalarNode); ok { - return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) - } else if keyIndentLevel < valueIndentLevel { - if keyComment != nil { - return fmt.Sprintf( - "%s%s: %s\n%s", - space, - n.Key.stringWithoutComment(), - keyComment.String(), - n.Value.String(), - ) - } - return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String()) - } else if m, ok := n.Value.(*MappingNode); ok && (m.IsFlowStyle || len(m.Values) == 0) { - return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) - } else if s, ok := n.Value.(*SequenceNode); ok && (s.IsFlowStyle || len(s.Values) == 0) { - return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) - } else if _, ok := n.Value.(*AnchorNode); ok { - return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) - } else if _, ok := n.Value.(*AliasNode); ok { - return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) - } - if keyComment != nil { - return fmt.Sprintf( - "%s%s: %s\n%s", - space, - n.Key.stringWithoutComment(), - keyComment.String(), - n.Value.String(), - ) - } - if m, ok := n.Value.(*MappingNode); ok && m.Comment != nil { - return fmt.Sprintf( - "%s%s: %s", - space, - n.Key.String(), - strings.TrimLeft(n.Value.String(), " "), - ) - } - return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String()) -} - -// MapRange implements MapNode protocol -func (n *MappingValueNode) MapRange() *MapNodeIter { - return &MapNodeIter{ - idx: startRangeIndex, - values: []*MappingValueNode{n}, - } -} - -// MarshalYAML encodes to a YAML text -func (n *MappingValueNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// ArrayNode interface of SequenceNode -type ArrayNode interface { - ArrayRange() *ArrayNodeIter -} - -// ArrayNodeIter is an iterator for ranging over a ArrayNode -type ArrayNodeIter struct { - values []Node - idx int -} - -// Next advances the array iterator and reports whether there is another entry. -// It returns false when the iterator is exhausted. -func (m *ArrayNodeIter) Next() bool { - m.idx++ - next := m.idx < len(m.values) - return next -} - -// Value returns the value of the iterator's current array entry. -func (m *ArrayNodeIter) Value() Node { - return m.values[m.idx] -} - -// Len returns length of array -func (m *ArrayNodeIter) Len() int { - return len(m.values) -} - -// SequenceNode type of sequence node -type SequenceNode struct { - *BaseNode - Start *token.Token - End *token.Token - IsFlowStyle bool - Values []Node - ValueHeadComments []*CommentGroupNode - FootComment *CommentGroupNode -} - -// Replace replace value node. -func (n *SequenceNode) Replace(idx int, value Node) error { - if len(n.Values) <= idx { - return xerrors.Errorf( - "invalid index for sequence: sequence length is %d, but specified %d index", - len(n.Values), idx, - ) - } - column := n.Values[idx].GetToken().Position.Column - value.GetToken().Position.Column - value.AddColumn(column) - n.Values[idx] = value - return nil -} - -// Merge merge sequence value. -func (n *SequenceNode) Merge(target *SequenceNode) { - column := n.Start.Position.Column - target.Start.Position.Column - target.AddColumn(column) - for _, value := range target.Values { - n.Values = append(n.Values, value) - } -} - -// SetIsFlowStyle set value to IsFlowStyle field recursively. -func (n *SequenceNode) SetIsFlowStyle(isFlow bool) { - n.IsFlowStyle = isFlow - for _, value := range n.Values { - switch value := value.(type) { - case *MappingNode: - value.SetIsFlowStyle(isFlow) - case *MappingValueNode: - value.SetIsFlowStyle(isFlow) - case *SequenceNode: - value.SetIsFlowStyle(isFlow) - } - } -} - -// Read implements (io.Reader).Read -func (n *SequenceNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns SequenceType -func (n *SequenceNode) Type() NodeType { return SequenceType } - -// GetToken returns token instance -func (n *SequenceNode) GetToken() *token.Token { - return n.Start -} - -// AddColumn add column number to child nodes recursively -func (n *SequenceNode) AddColumn(col int) { - n.Start.AddColumn(col) - n.End.AddColumn(col) - for _, value := range n.Values { - value.AddColumn(col) - } -} - -func (n *SequenceNode) flowStyleString() string { - values := []string{} - for _, value := range n.Values { - values = append(values, value.String()) - } - return fmt.Sprintf("[%s]", strings.Join(values, ", ")) -} - -func (n *SequenceNode) blockStyleString() string { - space := strings.Repeat(" ", n.Start.Position.Column-1) - values := []string{} - if n.Comment != nil { - values = append(values, n.Comment.StringWithSpace(n.Start.Position.Column-1)) - } - - for idx, value := range n.Values { - valueStr := value.String() - splittedValues := strings.Split(valueStr, "\n") - trimmedFirstValue := strings.TrimLeft(splittedValues[0], " ") - diffLength := len(splittedValues[0]) - len(trimmedFirstValue) - if len(splittedValues) > 1 && value.Type() == StringType || value.Type() == LiteralType { - // If multi-line string, the space characters for indent have already been added, so delete them. - prefix := space + " " - for i := 1; i < len(splittedValues); i++ { - splittedValues[i] = strings.TrimPrefix(splittedValues[i], prefix) - } - } - newValues := []string{trimmedFirstValue} - for i := 1; i < len(splittedValues); i++ { - if len(splittedValues[i]) <= diffLength { - // this line is \n or white space only - newValues = append(newValues, "") - continue - } - trimmed := splittedValues[i][diffLength:] - newValues = append(newValues, fmt.Sprintf("%s %s", space, trimmed)) - } - newValue := strings.Join(newValues, "\n") - if len(n.ValueHeadComments) == len(n.Values) && n.ValueHeadComments[idx] != nil { - values = append(values, n.ValueHeadComments[idx].StringWithSpace(n.Start.Position.Column-1)) - } - values = append(values, fmt.Sprintf("%s- %s", space, newValue)) - } - if n.FootComment != nil { - values = append(values, n.FootComment.StringWithSpace(n.Start.Position.Column-1)) - } - return strings.Join(values, "\n") -} - -// String sequence to text -func (n *SequenceNode) String() string { - if n.IsFlowStyle || len(n.Values) == 0 { - return n.flowStyleString() - } - return n.blockStyleString() -} - -// ArrayRange implements ArrayNode protocol -func (n *SequenceNode) ArrayRange() *ArrayNodeIter { - return &ArrayNodeIter{ - idx: startRangeIndex, - values: n.Values, - } -} - -// MarshalYAML encodes to a YAML text -func (n *SequenceNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// AnchorNode type of anchor node -type AnchorNode struct { - *BaseNode - Start *token.Token - Name Node - Value Node -} - -func (n *AnchorNode) SetName(name string) error { - if n.Name == nil { - return ErrInvalidAnchorName - } - s, ok := n.Name.(*StringNode) - if !ok { - return ErrInvalidAnchorName - } - s.Value = name - return nil -} - -// Read implements (io.Reader).Read -func (n *AnchorNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns AnchorType -func (n *AnchorNode) Type() NodeType { return AnchorType } - -// GetToken returns token instance -func (n *AnchorNode) GetToken() *token.Token { - return n.Start -} - -// AddColumn add column number to child nodes recursively -func (n *AnchorNode) AddColumn(col int) { - n.Start.AddColumn(col) - if n.Name != nil { - n.Name.AddColumn(col) - } - if n.Value != nil { - n.Value.AddColumn(col) - } -} - -// String anchor to text -func (n *AnchorNode) String() string { - value := n.Value.String() - if len(strings.Split(value, "\n")) > 1 { - return fmt.Sprintf("&%s\n%s", n.Name.String(), value) - } else if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle { - return fmt.Sprintf("&%s\n%s", n.Name.String(), value) - } else if m, ok := n.Value.(*MappingNode); ok && !m.IsFlowStyle { - return fmt.Sprintf("&%s\n%s", n.Name.String(), value) - } - return fmt.Sprintf("&%s %s", n.Name.String(), value) -} - -// MarshalYAML encodes to a YAML text -func (n *AnchorNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// AliasNode type of alias node -type AliasNode struct { - *BaseNode - Start *token.Token - Value Node -} - -func (n *AliasNode) SetName(name string) error { - if n.Value == nil { - return ErrInvalidAliasName - } - s, ok := n.Value.(*StringNode) - if !ok { - return ErrInvalidAliasName - } - s.Value = name - return nil -} - -// Read implements (io.Reader).Read -func (n *AliasNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns AliasType -func (n *AliasNode) Type() NodeType { return AliasType } - -// GetToken returns token instance -func (n *AliasNode) GetToken() *token.Token { - return n.Start -} - -// AddColumn add column number to child nodes recursively -func (n *AliasNode) AddColumn(col int) { - n.Start.AddColumn(col) - if n.Value != nil { - n.Value.AddColumn(col) - } -} - -// String alias to text -func (n *AliasNode) String() string { - return fmt.Sprintf("*%s", n.Value.String()) -} - -// MarshalYAML encodes to a YAML text -func (n *AliasNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// DirectiveNode type of directive node -type DirectiveNode struct { - *BaseNode - Start *token.Token - Value Node -} - -// Read implements (io.Reader).Read -func (n *DirectiveNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns DirectiveType -func (n *DirectiveNode) Type() NodeType { return DirectiveType } - -// GetToken returns token instance -func (n *DirectiveNode) GetToken() *token.Token { - return n.Start -} - -// AddColumn add column number to child nodes recursively -func (n *DirectiveNode) AddColumn(col int) { - if n.Value != nil { - n.Value.AddColumn(col) - } -} - -// String directive to text -func (n *DirectiveNode) String() string { - return fmt.Sprintf("%s%s", n.Start.Value, n.Value.String()) -} - -// MarshalYAML encodes to a YAML text -func (n *DirectiveNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// TagNode type of tag node -type TagNode struct { - *BaseNode - Start *token.Token - Value Node -} - -// Read implements (io.Reader).Read -func (n *TagNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns TagType -func (n *TagNode) Type() NodeType { return TagType } - -// GetToken returns token instance -func (n *TagNode) GetToken() *token.Token { - return n.Start -} - -// AddColumn add column number to child nodes recursively -func (n *TagNode) AddColumn(col int) { - n.Start.AddColumn(col) - if n.Value != nil { - n.Value.AddColumn(col) - } -} - -// String tag to text -func (n *TagNode) String() string { - return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String()) -} - -// MarshalYAML encodes to a YAML text -func (n *TagNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// CommentNode type of comment node -type CommentNode struct { - *BaseNode - Token *token.Token -} - -// Read implements (io.Reader).Read -func (n *CommentNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns TagType -func (n *CommentNode) Type() NodeType { return CommentType } - -// GetToken returns token instance -func (n *CommentNode) GetToken() *token.Token { return n.Token } - -// AddColumn add column number to child nodes recursively -func (n *CommentNode) AddColumn(col int) { - if n.Token == nil { - return - } - n.Token.AddColumn(col) -} - -// String comment to text -func (n *CommentNode) String() string { - return fmt.Sprintf("#%s", n.Token.Value) -} - -// MarshalYAML encodes to a YAML text -func (n *CommentNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// CommentGroupNode type of comment node -type CommentGroupNode struct { - *BaseNode - Comments []*CommentNode -} - -// Read implements (io.Reader).Read -func (n *CommentGroupNode) Read(p []byte) (int, error) { - return readNode(p, n) -} - -// Type returns TagType -func (n *CommentGroupNode) Type() NodeType { return CommentType } - -// GetToken returns token instance -func (n *CommentGroupNode) GetToken() *token.Token { - if len(n.Comments) > 0 { - return n.Comments[0].Token - } - return nil -} - -// AddColumn add column number to child nodes recursively -func (n *CommentGroupNode) AddColumn(col int) { - for _, comment := range n.Comments { - comment.AddColumn(col) - } -} - -// String comment to text -func (n *CommentGroupNode) String() string { - values := []string{} - for _, comment := range n.Comments { - values = append(values, comment.String()) - } - return strings.Join(values, "\n") -} - -func (n *CommentGroupNode) StringWithSpace(col int) string { - values := []string{} - space := strings.Repeat(" ", col) - for _, comment := range n.Comments { - values = append(values, space+comment.String()) - } - return strings.Join(values, "\n") - -} - -// MarshalYAML encodes to a YAML text -func (n *CommentGroupNode) MarshalYAML() ([]byte, error) { - return []byte(n.String()), nil -} - -// Visitor has Visit method that is invokded for each node encountered by Walk. -// If the result visitor w is not nil, Walk visits each of the children of node with the visitor w, -// followed by a call of w.Visit(nil). -type Visitor interface { - Visit(Node) Visitor -} - -// Walk traverses an AST in depth-first order: It starts by calling v.Visit(node); node must not be nil. -// If the visitor w returned by v.Visit(node) is not nil, -// Walk is invoked recursively with visitor w for each of the non-nil children of node, -// followed by a call of w.Visit(nil). -func Walk(v Visitor, node Node) { - if v = v.Visit(node); v == nil { - return - } - - switch n := node.(type) { - case *CommentNode: - case *NullNode: - walkComment(v, n.BaseNode) - case *IntegerNode: - walkComment(v, n.BaseNode) - case *FloatNode: - walkComment(v, n.BaseNode) - case *StringNode: - walkComment(v, n.BaseNode) - case *MergeKeyNode: - walkComment(v, n.BaseNode) - case *BoolNode: - walkComment(v, n.BaseNode) - case *InfinityNode: - walkComment(v, n.BaseNode) - case *NanNode: - walkComment(v, n.BaseNode) - case *LiteralNode: - walkComment(v, n.BaseNode) - Walk(v, n.Value) - case *DirectiveNode: - walkComment(v, n.BaseNode) - Walk(v, n.Value) - case *TagNode: - walkComment(v, n.BaseNode) - Walk(v, n.Value) - case *DocumentNode: - walkComment(v, n.BaseNode) - Walk(v, n.Body) - case *MappingNode: - walkComment(v, n.BaseNode) - for _, value := range n.Values { - Walk(v, value) - } - case *MappingKeyNode: - walkComment(v, n.BaseNode) - Walk(v, n.Value) - case *MappingValueNode: - walkComment(v, n.BaseNode) - Walk(v, n.Key) - Walk(v, n.Value) - case *SequenceNode: - walkComment(v, n.BaseNode) - for _, value := range n.Values { - Walk(v, value) - } - case *AnchorNode: - walkComment(v, n.BaseNode) - Walk(v, n.Name) - Walk(v, n.Value) - case *AliasNode: - walkComment(v, n.BaseNode) - Walk(v, n.Value) - } -} - -func walkComment(v Visitor, base *BaseNode) { - if base == nil { - return - } - if base.Comment == nil { - return - } - Walk(v, base.Comment) -} - -type filterWalker struct { - typ NodeType - results []Node -} - -func (v *filterWalker) Visit(n Node) Visitor { - if v.typ == n.Type() { - v.results = append(v.results, n) - } - return v -} - -type parentFinder struct { - target Node -} - -func (f *parentFinder) walk(parent, node Node) Node { - if f.target == node { - return parent - } - switch n := node.(type) { - case *CommentNode: - return nil - case *NullNode: - return nil - case *IntegerNode: - return nil - case *FloatNode: - return nil - case *StringNode: - return nil - case *MergeKeyNode: - return nil - case *BoolNode: - return nil - case *InfinityNode: - return nil - case *NanNode: - return nil - case *LiteralNode: - return f.walk(node, n.Value) - case *DirectiveNode: - return f.walk(node, n.Value) - case *TagNode: - return f.walk(node, n.Value) - case *DocumentNode: - return f.walk(node, n.Body) - case *MappingNode: - for _, value := range n.Values { - if found := f.walk(node, value); found != nil { - return found - } - } - case *MappingKeyNode: - return f.walk(node, n.Value) - case *MappingValueNode: - if found := f.walk(node, n.Key); found != nil { - return found - } - return f.walk(node, n.Value) - case *SequenceNode: - for _, value := range n.Values { - if found := f.walk(node, value); found != nil { - return found - } - } - case *AnchorNode: - if found := f.walk(node, n.Name); found != nil { - return found - } - return f.walk(node, n.Value) - case *AliasNode: - return f.walk(node, n.Value) - } - return nil -} - -// Parent get parent node from child node. -func Parent(root, child Node) Node { - finder := &parentFinder{target: child} - return finder.walk(root, root) -} - -// Filter returns a list of nodes that match the given type. -func Filter(typ NodeType, node Node) []Node { - walker := &filterWalker{typ: typ} - Walk(walker, node) - return walker.results -} - -// FilterFile returns a list of nodes that match the given type. -func FilterFile(typ NodeType, file *File) []Node { - results := []Node{} - for _, doc := range file.Docs { - walker := &filterWalker{typ: typ} - Walk(walker, doc) - results = append(results, walker.results...) - } - return results -} - -type ErrInvalidMergeType struct { - dst Node - src Node -} - -func (e *ErrInvalidMergeType) Error() string { - return fmt.Sprintf("cannot merge %s into %s", e.src.Type(), e.dst.Type()) -} - -// Merge merge document, map, sequence node. -func Merge(dst Node, src Node) error { - if doc, ok := src.(*DocumentNode); ok { - src = doc.Body - } - err := &ErrInvalidMergeType{dst: dst, src: src} - switch dst.Type() { - case DocumentType: - node := dst.(*DocumentNode) - return Merge(node.Body, src) - case MappingType: - node := dst.(*MappingNode) - target, ok := src.(*MappingNode) - if !ok { - return err - } - node.Merge(target) - return nil - case SequenceType: - node := dst.(*SequenceNode) - target, ok := src.(*SequenceNode) - if !ok { - return err - } - node.Merge(target) - return nil - } - return err -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/ast/ast_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/ast/ast_test.go deleted file mode 100644 index e7022b6..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/ast/ast_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package ast - -import ( - "testing" - - "github.com/goccy/go-yaml/token" -) - -func TestEscapeSingleQuote(t *testing.T) { - expected := `'Victor''s victory'` - got := escapeSingleQuote("Victor's victory") - if got != expected { - t.Fatalf("expected:%s\ngot:%s", expected, got) - } -} - -func TestReadNode(t *testing.T) { - t.Run("utf-8", func(t *testing.T) { - value := "éɛทᛞ⠻チ▓🦄" - node := &StringNode{ - BaseNode: &BaseNode{}, - Token: &token.Token{}, - Value: value, - } - expectedSize := len(value) - gotBuffer := make([]byte, expectedSize) - expectedBuffer := []byte(value) - gotSize, _ := readNode(gotBuffer, node) - if gotSize != expectedSize { - t.Fatalf("expected size:%d\ngot:%d", expectedSize, gotSize) - } - if string(gotBuffer) != string(expectedBuffer) { - t.Fatalf("expected buffer:%s\ngot:%s", expectedBuffer, gotBuffer) - } - }) -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/cmd/ycat/ycat.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/cmd/ycat/ycat.go deleted file mode 100644 index c70cb3b..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/cmd/ycat/ycat.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "io/ioutil" - "os" - - "github.com/fatih/color" - "github.com/goccy/go-yaml" - "github.com/goccy/go-yaml/lexer" - "github.com/goccy/go-yaml/printer" - "github.com/mattn/go-colorable" -) - -const escape = "\x1b" - -func format(attr color.Attribute) string { - return fmt.Sprintf("%s[%dm", escape, attr) -} - -func _main(args []string) error { - if len(args) < 2 { - return errors.New("ycat: usage: ycat file.yml") - } - filename := args[1] - bytes, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - tokens := lexer.Tokenize(string(bytes)) - var p printer.Printer - p.LineNumber = true - p.LineNumberFormat = func(num int) string { - fn := color.New(color.Bold, color.FgHiWhite).SprintFunc() - return fn(fmt.Sprintf("%2d | ", num)) - } - p.Bool = func() *printer.Property { - return &printer.Property{ - Prefix: format(color.FgHiMagenta), - Suffix: format(color.Reset), - } - } - p.Number = func() *printer.Property { - return &printer.Property{ - Prefix: format(color.FgHiMagenta), - Suffix: format(color.Reset), - } - } - p.MapKey = func() *printer.Property { - return &printer.Property{ - Prefix: format(color.FgHiCyan), - Suffix: format(color.Reset), - } - } - p.Anchor = func() *printer.Property { - return &printer.Property{ - Prefix: format(color.FgHiYellow), - Suffix: format(color.Reset), - } - } - p.Alias = func() *printer.Property { - return &printer.Property{ - Prefix: format(color.FgHiYellow), - Suffix: format(color.Reset), - } - } - p.String = func() *printer.Property { - return &printer.Property{ - Prefix: format(color.FgHiGreen), - Suffix: format(color.Reset), - } - } - writer := colorable.NewColorableStdout() - writer.Write([]byte(p.PrintTokens(tokens) + "\n")) - return nil -} - -func main() { - if err := _main(os.Args); err != nil { - fmt.Printf("%v\n", yaml.FormatError(err, true, true)) - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/decode.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/decode.go deleted file mode 100644 index d3dbabc..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/decode.go +++ /dev/null @@ -1,1753 +0,0 @@ -package yaml - -import ( - "bytes" - "context" - "encoding" - "encoding/base64" - "fmt" - "io" - "io/ioutil" - "math" - "os" - "path/filepath" - "reflect" - "sort" - "strconv" - "time" - - "github.com/goccy/go-yaml/ast" - "github.com/goccy/go-yaml/internal/errors" - "github.com/goccy/go-yaml/parser" - "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" -) - -// Decoder reads and decodes YAML values from an input stream. -type Decoder struct { - reader io.Reader - referenceReaders []io.Reader - anchorNodeMap map[string]ast.Node - anchorValueMap map[string]reflect.Value - customUnmarshalerMap map[reflect.Type]func(interface{}, []byte) error - toCommentMap CommentMap - opts []DecodeOption - referenceFiles []string - referenceDirs []string - isRecursiveDir bool - isResolvedReference bool - validator StructValidator - disallowUnknownField bool - disallowDuplicateKey bool - useOrderedMap bool - useJSONUnmarshaler bool - parsedFile *ast.File - streamIndex int -} - -// NewDecoder returns a new decoder that reads from r. -func NewDecoder(r io.Reader, opts ...DecodeOption) *Decoder { - return &Decoder{ - reader: r, - anchorNodeMap: map[string]ast.Node{}, - anchorValueMap: map[string]reflect.Value{}, - customUnmarshalerMap: map[reflect.Type]func(interface{}, []byte) error{}, - opts: opts, - referenceReaders: []io.Reader{}, - referenceFiles: []string{}, - referenceDirs: []string{}, - isRecursiveDir: false, - isResolvedReference: false, - disallowUnknownField: false, - disallowDuplicateKey: false, - useOrderedMap: false, - } -} - -func (d *Decoder) castToFloat(v interface{}) interface{} { - switch vv := v.(type) { - case int: - return float64(vv) - case int8: - return float64(vv) - case int16: - return float64(vv) - case int32: - return float64(vv) - case int64: - return float64(vv) - case uint: - return float64(vv) - case uint8: - return float64(vv) - case uint16: - return float64(vv) - case uint32: - return float64(vv) - case uint64: - return float64(vv) - case float32: - return float64(vv) - case float64: - return vv - case string: - // if error occurred, return zero value - f, _ := strconv.ParseFloat(vv, 64) - return f - } - return 0 -} - -func (d *Decoder) mergeValueNode(value ast.Node) ast.Node { - if value.Type() == ast.AliasType { - aliasNode := value.(*ast.AliasNode) - aliasName := aliasNode.Value.GetToken().Value - return d.anchorNodeMap[aliasName] - } - return value -} - -func (d *Decoder) mapKeyNodeToString(node ast.MapKeyNode) string { - key := d.nodeToValue(node) - if key == nil { - return "null" - } - if k, ok := key.(string); ok { - return k - } - return fmt.Sprint(key) -} - -func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) { - d.setPathToCommentMap(node) - switch n := node.(type) { - case *ast.MappingValueNode: - if n.Key.Type() == ast.MergeKeyType { - d.setToMapValue(d.mergeValueNode(n.Value), m) - } else { - key := d.mapKeyNodeToString(n.Key) - m[key] = d.nodeToValue(n.Value) - } - case *ast.MappingNode: - for _, value := range n.Values { - d.setToMapValue(value, m) - } - case *ast.AnchorNode: - anchorName := n.Name.GetToken().Value - d.anchorNodeMap[anchorName] = n.Value - } -} - -func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) { - switch n := node.(type) { - case *ast.MappingValueNode: - if n.Key.Type() == ast.MergeKeyType { - d.setToOrderedMapValue(d.mergeValueNode(n.Value), m) - } else { - key := d.mapKeyNodeToString(n.Key) - *m = append(*m, MapItem{Key: key, Value: d.nodeToValue(n.Value)}) - } - case *ast.MappingNode: - for _, value := range n.Values { - d.setToOrderedMapValue(value, m) - } - } -} - -func (d *Decoder) setPathToCommentMap(node ast.Node) { - if d.toCommentMap == nil { - return - } - d.addHeadOrLineCommentToMap(node) - d.addFootCommentToMap(node) -} - -func (d *Decoder) addHeadOrLineCommentToMap(node ast.Node) { - sequence, ok := node.(*ast.SequenceNode) - if ok { - d.addSequenceNodeCommentToMap(sequence) - return - } - commentGroup := node.GetComment() - if commentGroup == nil { - return - } - texts := []string{} - targetLine := node.GetToken().Position.Line - minCommentLine := math.MaxInt - for _, comment := range commentGroup.Comments { - if minCommentLine > comment.Token.Position.Line { - minCommentLine = comment.Token.Position.Line - } - texts = append(texts, comment.Token.Value) - } - if len(texts) == 0 { - return - } - commentPath := node.GetPath() - if minCommentLine < targetLine { - d.addCommentToMap(commentPath, HeadComment(texts...)) - } else { - d.addCommentToMap(commentPath, LineComment(texts[0])) - } -} - -func (d *Decoder) addSequenceNodeCommentToMap(node *ast.SequenceNode) { - if len(node.ValueHeadComments) != 0 { - for idx, headComment := range node.ValueHeadComments { - if headComment == nil { - continue - } - texts := make([]string, 0, len(headComment.Comments)) - for _, comment := range headComment.Comments { - texts = append(texts, comment.Token.Value) - } - if len(texts) != 0 { - d.addCommentToMap(node.Values[idx].GetPath(), HeadComment(texts...)) - } - } - } - firstElemHeadComment := node.GetComment() - if firstElemHeadComment != nil { - texts := make([]string, 0, len(firstElemHeadComment.Comments)) - for _, comment := range firstElemHeadComment.Comments { - texts = append(texts, comment.Token.Value) - } - if len(texts) != 0 { - d.addCommentToMap(node.Values[0].GetPath(), HeadComment(texts...)) - } - } -} - -func (d *Decoder) addFootCommentToMap(node ast.Node) { - var ( - footComment *ast.CommentGroupNode - footCommentPath string = node.GetPath() - ) - switch n := node.(type) { - case *ast.SequenceNode: - if len(n.Values) != 0 { - footCommentPath = n.Values[len(n.Values)-1].GetPath() - } - footComment = n.FootComment - case *ast.MappingNode: - footComment = n.FootComment - case *ast.MappingValueNode: - footComment = n.FootComment - } - if footComment == nil { - return - } - var texts []string - for _, comment := range footComment.Comments { - texts = append(texts, comment.Token.Value) - } - if len(texts) != 0 { - d.addCommentToMap(footCommentPath, FootComment(texts...)) - } -} - -func (d *Decoder) addCommentToMap(path string, comment *Comment) { - for _, c := range d.toCommentMap[path] { - if c.Position == comment.Position { - // already added same comment - return - } - } - d.toCommentMap[path] = append(d.toCommentMap[path], comment) - sort.Slice(d.toCommentMap[path], func(i, j int) bool { - return d.toCommentMap[path][i].Position < d.toCommentMap[path][j].Position - }) -} - -func (d *Decoder) nodeToValue(node ast.Node) interface{} { - d.setPathToCommentMap(node) - switch n := node.(type) { - case *ast.NullNode: - return nil - case *ast.StringNode: - return n.GetValue() - case *ast.IntegerNode: - return n.GetValue() - case *ast.FloatNode: - return n.GetValue() - case *ast.BoolNode: - return n.GetValue() - case *ast.InfinityNode: - return n.GetValue() - case *ast.NanNode: - return n.GetValue() - case *ast.TagNode: - switch token.ReservedTagKeyword(n.Start.Value) { - case token.TimestampTag: - t, _ := d.castToTime(n.Value) - return t - case token.IntegerTag: - i, _ := strconv.Atoi(fmt.Sprint(d.nodeToValue(n.Value))) - return i - case token.FloatTag: - return d.castToFloat(d.nodeToValue(n.Value)) - case token.NullTag: - return nil - case token.BinaryTag: - b, _ := base64.StdEncoding.DecodeString(d.nodeToValue(n.Value).(string)) - return b - case token.StringTag: - return d.nodeToValue(n.Value) - case token.MappingTag: - return d.nodeToValue(n.Value) - } - case *ast.AnchorNode: - anchorName := n.Name.GetToken().Value - anchorValue := d.nodeToValue(n.Value) - d.anchorNodeMap[anchorName] = n.Value - return anchorValue - case *ast.AliasNode: - aliasName := n.Value.GetToken().Value - node := d.anchorNodeMap[aliasName] - return d.nodeToValue(node) - case *ast.LiteralNode: - return n.Value.GetValue() - case *ast.MappingKeyNode: - return d.nodeToValue(n.Value) - case *ast.MappingValueNode: - if n.Key.Type() == ast.MergeKeyType { - value := d.mergeValueNode(n.Value) - if d.useOrderedMap { - m := MapSlice{} - d.setToOrderedMapValue(value, &m) - return m - } - m := map[string]interface{}{} - d.setToMapValue(value, m) - return m - } - key := d.mapKeyNodeToString(n.Key) - if d.useOrderedMap { - return MapSlice{{Key: key, Value: d.nodeToValue(n.Value)}} - } - return map[string]interface{}{ - key: d.nodeToValue(n.Value), - } - case *ast.MappingNode: - if d.useOrderedMap { - m := make(MapSlice, 0, len(n.Values)) - for _, value := range n.Values { - d.setToOrderedMapValue(value, &m) - } - return m - } - m := make(map[string]interface{}, len(n.Values)) - for _, value := range n.Values { - d.setToMapValue(value, m) - } - return m - case *ast.SequenceNode: - v := make([]interface{}, 0, len(n.Values)) - for _, value := range n.Values { - v = append(v, d.nodeToValue(value)) - } - return v - } - return nil -} - -func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) { - switch n := node.(type) { - case *ast.MappingNode: - for idx, v := range n.Values { - value, err := d.resolveAlias(v) - if err != nil { - return nil, err - } - n.Values[idx] = value.(*ast.MappingValueNode) - } - case *ast.TagNode: - value, err := d.resolveAlias(n.Value) - if err != nil { - return nil, err - } - n.Value = value - case *ast.MappingKeyNode: - value, err := d.resolveAlias(n.Value) - if err != nil { - return nil, err - } - n.Value = value - case *ast.MappingValueNode: - if n.Key.Type() == ast.MergeKeyType && n.Value.Type() == ast.AliasType { - value, err := d.resolveAlias(n.Value) - if err != nil { - return nil, err - } - keyColumn := n.Key.GetToken().Position.Column - requiredColumn := keyColumn + 2 - value.AddColumn(requiredColumn) - n.Value = value - } else { - key, err := d.resolveAlias(n.Key) - if err != nil { - return nil, err - } - n.Key = key.(ast.MapKeyNode) - value, err := d.resolveAlias(n.Value) - if err != nil { - return nil, err - } - n.Value = value - } - case *ast.SequenceNode: - for idx, v := range n.Values { - value, err := d.resolveAlias(v) - if err != nil { - return nil, err - } - n.Values[idx] = value - } - case *ast.AliasNode: - aliasName := n.Value.GetToken().Value - node := d.anchorNodeMap[aliasName] - if node == nil { - return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) - } - return d.resolveAlias(node) - } - return node, nil -} - -func (d *Decoder) getMapNode(node ast.Node) (ast.MapNode, error) { - if _, ok := node.(*ast.NullNode); ok { - return nil, nil - } - if anchor, ok := node.(*ast.AnchorNode); ok { - mapNode, ok := anchor.Value.(ast.MapNode) - if ok { - return mapNode, nil - } - return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.MappingType, node.GetToken()) - } - if alias, ok := node.(*ast.AliasNode); ok { - aliasName := alias.Value.GetToken().Value - node := d.anchorNodeMap[aliasName] - if node == nil { - return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) - } - mapNode, ok := node.(ast.MapNode) - if ok { - return mapNode, nil - } - return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) - } - mapNode, ok := node.(ast.MapNode) - if !ok { - return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) - } - return mapNode, nil -} - -func (d *Decoder) getArrayNode(node ast.Node) (ast.ArrayNode, error) { - if _, ok := node.(*ast.NullNode); ok { - return nil, nil - } - if anchor, ok := node.(*ast.AnchorNode); ok { - arrayNode, ok := anchor.Value.(ast.ArrayNode) - if ok { - return arrayNode, nil - } - - return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.SequenceType, node.GetToken()) - } - if alias, ok := node.(*ast.AliasNode); ok { - aliasName := alias.Value.GetToken().Value - node := d.anchorNodeMap[aliasName] - if node == nil { - return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) - } - arrayNode, ok := node.(ast.ArrayNode) - if ok { - return arrayNode, nil - } - return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) - } - arrayNode, ok := node.(ast.ArrayNode) - if !ok { - return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) - } - return arrayNode, nil -} - -func (d *Decoder) fileToNode(f *ast.File) ast.Node { - for _, doc := range f.Docs { - if v := d.nodeToValue(doc.Body); v != nil { - return doc.Body - } - } - return nil -} - -func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) (reflect.Value, error) { - if typ.Kind() != reflect.String { - if !v.Type().ConvertibleTo(typ) { - return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken()) - } - return v.Convert(typ), nil - } - // cast value to string - switch v.Type().Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return reflect.ValueOf(fmt.Sprint(v.Int())), nil - case reflect.Float32, reflect.Float64: - return reflect.ValueOf(fmt.Sprint(v.Float())), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return reflect.ValueOf(fmt.Sprint(v.Uint())), nil - case reflect.Bool: - return reflect.ValueOf(fmt.Sprint(v.Bool())), nil - } - if !v.Type().ConvertibleTo(typ) { - return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken()) - } - return v.Convert(typ), nil -} - -type overflowError struct { - dstType reflect.Type - srcNum string -} - -func (e *overflowError) Error() string { - return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.srcNum, e.dstType) -} - -func errOverflow(dstType reflect.Type, num string) *overflowError { - return &overflowError{dstType: dstType, srcNum: num} -} - -func errTypeMismatch(dstType, srcType reflect.Type, token *token.Token) *errors.TypeError { - return &errors.TypeError{DstType: dstType, SrcType: srcType, Token: token} -} - -type unknownFieldError struct { - err error -} - -func (e *unknownFieldError) Error() string { - return e.err.Error() -} - -func errUnknownField(msg string, tk *token.Token) *unknownFieldError { - return &unknownFieldError{err: errors.ErrSyntax(msg, tk)} -} - -func errUnexpectedNodeType(actual, expected ast.NodeType, tk *token.Token) error { - return errors.ErrSyntax(fmt.Sprintf("%s was used where %s is expected", actual.YAMLName(), expected.YAMLName()), tk) -} - -type duplicateKeyError struct { - err error -} - -func (e *duplicateKeyError) Error() string { - return e.err.Error() -} - -func errDuplicateKey(msg string, tk *token.Token) *duplicateKeyError { - return &duplicateKeyError{err: errors.ErrSyntax(msg, tk)} -} - -func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[string]ast.Node) error { - if structType.Kind() == reflect.Ptr { - structType = structType.Elem() - } - structFieldMap, err := structFieldMap(structType) - if err != nil { - return errors.Wrapf(err, "failed to create struct field map") - } - - for j := 0; j < structType.NumField(); j++ { - field := structType.Field(j) - if isIgnoredStructField(field) { - continue - } - - structField, exists := structFieldMap[field.Name] - if !exists { - continue - } - - if structField.IsInline { - d.deleteStructKeys(field.Type, unknownFields) - } else { - delete(unknownFields, structField.RenderName) - } - } - return nil -} - -func (d *Decoder) lastNode(node ast.Node) ast.Node { - switch n := node.(type) { - case *ast.MappingNode: - if len(n.Values) > 0 { - return d.lastNode(n.Values[len(n.Values)-1]) - } - case *ast.MappingValueNode: - return d.lastNode(n.Value) - case *ast.SequenceNode: - if len(n.Values) > 0 { - return d.lastNode(n.Values[len(n.Values)-1]) - } - } - return node -} - -func (d *Decoder) unmarshalableDocument(node ast.Node) ([]byte, error) { - var err error - node, err = d.resolveAlias(node) - if err != nil { - return nil, err - } - doc := node.String() - last := d.lastNode(node) - if last != nil && last.Type() == ast.LiteralType { - doc += "\n" - } - return []byte(doc), nil -} - -func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool, error) { - var err error - node, err = d.resolveAlias(node) - if err != nil { - return nil, false, err - } - if node.Type() == ast.AnchorType { - node = node.(*ast.AnchorNode).Value - } - switch n := node.(type) { - case *ast.StringNode: - return []byte(n.Value), true, nil - case *ast.LiteralNode: - return []byte(n.Value.GetToken().Value), true, nil - default: - scalar, ok := n.(ast.ScalarNode) - if ok { - return []byte(fmt.Sprint(scalar.GetValue())), true, nil - } - } - return nil, false, nil -} - -type jsonUnmarshaler interface { - UnmarshalJSON([]byte) error -} - -func (d *Decoder) existsTypeInCustomUnmarshalerMap(t reflect.Type) bool { - if _, exists := d.customUnmarshalerMap[t]; exists { - return true - } - - globalCustomUnmarshalerMu.Lock() - defer globalCustomUnmarshalerMu.Unlock() - if _, exists := globalCustomUnmarshalerMap[t]; exists { - return true - } - return false -} - -func (d *Decoder) unmarshalerFromCustomUnmarshalerMap(t reflect.Type) (func(interface{}, []byte) error, bool) { - if unmarshaler, exists := d.customUnmarshalerMap[t]; exists { - return unmarshaler, exists - } - - globalCustomUnmarshalerMu.Lock() - defer globalCustomUnmarshalerMu.Unlock() - if unmarshaler, exists := globalCustomUnmarshalerMap[t]; exists { - return unmarshaler, exists - } - return nil, false -} - -func (d *Decoder) canDecodeByUnmarshaler(dst reflect.Value) bool { - ptrValue := dst.Addr() - if d.existsTypeInCustomUnmarshalerMap(ptrValue.Type()) { - return true - } - iface := ptrValue.Interface() - switch iface.(type) { - case BytesUnmarshalerContext: - return true - case BytesUnmarshaler: - return true - case InterfaceUnmarshalerContext: - return true - case InterfaceUnmarshaler: - return true - case *time.Time: - return true - case *time.Duration: - return true - case encoding.TextUnmarshaler: - return true - case jsonUnmarshaler: - return d.useJSONUnmarshaler - } - return false -} - -func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, src ast.Node) error { - ptrValue := dst.Addr() - if unmarshaler, exists := d.unmarshalerFromCustomUnmarshalerMap(ptrValue.Type()); exists { - b, err := d.unmarshalableDocument(src) - if err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") - } - if err := unmarshaler(ptrValue.Interface(), b); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") - } - return nil - } - iface := ptrValue.Interface() - - if unmarshaler, ok := iface.(BytesUnmarshalerContext); ok { - b, err := d.unmarshalableDocument(src) - if err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") - } - if err := unmarshaler.UnmarshalYAML(ctx, b); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") - } - return nil - } - - if unmarshaler, ok := iface.(BytesUnmarshaler); ok { - b, err := d.unmarshalableDocument(src) - if err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") - } - if err := unmarshaler.UnmarshalYAML(b); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") - } - return nil - } - - if unmarshaler, ok := iface.(InterfaceUnmarshalerContext); ok { - if err := unmarshaler.UnmarshalYAML(ctx, func(v interface{}) error { - rv := reflect.ValueOf(v) - if rv.Type().Kind() != reflect.Ptr { - return errors.ErrDecodeRequiredPointerType - } - if err := d.decodeValue(ctx, rv.Elem(), src); err != nil { - return errors.Wrapf(err, "failed to decode value") - } - return nil - }); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") - } - return nil - } - - if unmarshaler, ok := iface.(InterfaceUnmarshaler); ok { - if err := unmarshaler.UnmarshalYAML(func(v interface{}) error { - rv := reflect.ValueOf(v) - if rv.Type().Kind() != reflect.Ptr { - return errors.ErrDecodeRequiredPointerType - } - if err := d.decodeValue(ctx, rv.Elem(), src); err != nil { - return errors.Wrapf(err, "failed to decode value") - } - return nil - }); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") - } - return nil - } - - if _, ok := iface.(*time.Time); ok { - return d.decodeTime(ctx, dst, src) - } - - if _, ok := iface.(*time.Duration); ok { - return d.decodeDuration(ctx, dst, src) - } - - if unmarshaler, isText := iface.(encoding.TextUnmarshaler); isText { - b, ok, err := d.unmarshalableText(src) - if err != nil { - return errors.Wrapf(err, "failed to UnmarshalText") - } - if ok { - if err := unmarshaler.UnmarshalText(b); err != nil { - return errors.Wrapf(err, "failed to UnmarshalText") - } - return nil - } - } - - if d.useJSONUnmarshaler { - if unmarshaler, ok := iface.(jsonUnmarshaler); ok { - b, err := d.unmarshalableDocument(src) - if err != nil { - return errors.Wrapf(err, "failed to UnmarshalJSON") - } - jsonBytes, err := YAMLToJSON(b) - if err != nil { - return errors.Wrapf(err, "failed to convert yaml to json") - } - jsonBytes = bytes.TrimRight(jsonBytes, "\n") - if err := unmarshaler.UnmarshalJSON(jsonBytes); err != nil { - return errors.Wrapf(err, "failed to UnmarshalJSON") - } - return nil - } - } - - return xerrors.Errorf("does not implemented Unmarshaler") -} - -var ( - astNodeType = reflect.TypeOf((*ast.Node)(nil)).Elem() -) - -func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.Node) error { - if src.Type() == ast.AnchorType { - anchorName := src.(*ast.AnchorNode).Name.GetToken().Value - if _, exists := d.anchorValueMap[anchorName]; !exists { - d.anchorValueMap[anchorName] = dst - } - } - if d.canDecodeByUnmarshaler(dst) { - if err := d.decodeByUnmarshaler(ctx, dst, src); err != nil { - return errors.Wrapf(err, "failed to decode by unmarshaler") - } - return nil - } - valueType := dst.Type() - switch valueType.Kind() { - case reflect.Ptr: - if dst.IsNil() { - return nil - } - if src.Type() == ast.NullType { - // set nil value to pointer - dst.Set(reflect.Zero(valueType)) - return nil - } - v := d.createDecodableValue(dst.Type()) - if err := d.decodeValue(ctx, v, src); err != nil { - return errors.Wrapf(err, "failed to decode ptr value") - } - dst.Set(d.castToAssignableValue(v, dst.Type())) - case reflect.Interface: - if dst.Type() == astNodeType { - dst.Set(reflect.ValueOf(src)) - return nil - } - v := reflect.ValueOf(d.nodeToValue(src)) - if v.IsValid() { - dst.Set(v) - } - case reflect.Map: - return d.decodeMap(ctx, dst, src) - case reflect.Array: - return d.decodeArray(ctx, dst, src) - case reflect.Slice: - if mapSlice, ok := dst.Addr().Interface().(*MapSlice); ok { - return d.decodeMapSlice(ctx, mapSlice, src) - } - return d.decodeSlice(ctx, dst, src) - case reflect.Struct: - if mapItem, ok := dst.Addr().Interface().(*MapItem); ok { - return d.decodeMapItem(ctx, mapItem, src) - } - return d.decodeStruct(ctx, dst, src) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - v := d.nodeToValue(src) - switch vv := v.(type) { - case int64: - if !dst.OverflowInt(vv) { - dst.SetInt(vv) - return nil - } - case uint64: - if vv <= math.MaxInt64 && !dst.OverflowInt(int64(vv)) { - dst.SetInt(int64(vv)) - return nil - } - case float64: - if vv <= math.MaxInt64 && !dst.OverflowInt(int64(vv)) { - dst.SetInt(int64(vv)) - return nil - } - default: - return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) - } - return errOverflow(valueType, fmt.Sprint(v)) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - v := d.nodeToValue(src) - switch vv := v.(type) { - case int64: - if 0 <= vv && !dst.OverflowUint(uint64(vv)) { - dst.SetUint(uint64(vv)) - return nil - } - case uint64: - if !dst.OverflowUint(vv) { - dst.SetUint(vv) - return nil - } - case float64: - if 0 <= vv && vv <= math.MaxUint64 && !dst.OverflowUint(uint64(vv)) { - dst.SetUint(uint64(vv)) - return nil - } - default: - return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) - } - return errOverflow(valueType, fmt.Sprint(v)) - } - v := reflect.ValueOf(d.nodeToValue(src)) - if v.IsValid() { - convertedValue, err := d.convertValue(v, dst.Type(), src) - if err != nil { - return errors.Wrapf(err, "failed to convert value") - } - dst.Set(convertedValue) - } - return nil -} - -func (d *Decoder) createDecodableValue(typ reflect.Type) reflect.Value { - for { - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - continue - } - break - } - return reflect.New(typ).Elem() -} - -func (d *Decoder) castToAssignableValue(value reflect.Value, target reflect.Type) reflect.Value { - if target.Kind() != reflect.Ptr { - return value - } - maxTryCount := 5 - tryCount := 0 - for { - if tryCount > maxTryCount { - return value - } - if value.Type().AssignableTo(target) { - break - } - value = value.Addr() - tryCount++ - } - return value -} - -func (d *Decoder) createDecodedNewValue( - ctx context.Context, typ reflect.Type, defaultVal reflect.Value, node ast.Node, -) (reflect.Value, error) { - if node.Type() == ast.AliasType { - aliasName := node.(*ast.AliasNode).Value.GetToken().Value - newValue := d.anchorValueMap[aliasName] - if newValue.IsValid() { - return newValue, nil - } - } - if node.Type() == ast.NullType { - return reflect.Zero(typ), nil - } - newValue := d.createDecodableValue(typ) - for defaultVal.Kind() == reflect.Ptr { - defaultVal = defaultVal.Elem() - } - if defaultVal.IsValid() && defaultVal.Type().AssignableTo(newValue.Type()) { - newValue.Set(defaultVal) - } - if err := d.decodeValue(ctx, newValue, node); err != nil { - return newValue, errors.Wrapf(err, "failed to decode value") - } - return newValue, nil -} - -func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValueNode func(*ast.MapNodeIter) ast.Node) (map[string]ast.Node, error) { - mapNode, err := d.getMapNode(node) - if err != nil { - return nil, errors.Wrapf(err, "failed to get map node") - } - keyMap := map[string]struct{}{} - keyToNodeMap := map[string]ast.Node{} - if mapNode == nil { - return keyToNodeMap, nil - } - mapIter := mapNode.MapRange() - for mapIter.Next() { - keyNode := mapIter.Key() - if keyNode.Type() == ast.MergeKeyType { - if ignoreMergeKey { - continue - } - mergeMap, err := d.keyToNodeMap(mapIter.Value(), ignoreMergeKey, getKeyOrValueNode) - if err != nil { - return nil, errors.Wrapf(err, "failed to get keyToNodeMap by MergeKey node") - } - for k, v := range mergeMap { - if err := d.validateDuplicateKey(keyMap, k, v); err != nil { - return nil, errors.Wrapf(err, "invalid struct key") - } - keyToNodeMap[k] = v - } - } else { - key, ok := d.nodeToValue(keyNode).(string) - if !ok { - return nil, errors.Wrapf(err, "failed to decode map key") - } - if err := d.validateDuplicateKey(keyMap, key, keyNode); err != nil { - return nil, errors.Wrapf(err, "invalid struct key") - } - keyToNodeMap[key] = getKeyOrValueNode(mapIter) - } - } - return keyToNodeMap, nil -} - -func (d *Decoder) keyToKeyNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { - m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Key() }) - if err != nil { - return nil, errors.Wrapf(err, "failed to get keyToNodeMap") - } - return m, nil -} - -func (d *Decoder) keyToValueNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { - m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Value() }) - if err != nil { - return nil, errors.Wrapf(err, "failed to get keyToNodeMap") - } - return m, nil -} - -func (d *Decoder) setDefaultValueIfConflicted(v reflect.Value, fieldMap StructFieldMap) error { - typ := v.Type() - if typ.Kind() != reflect.Struct { - return nil - } - embeddedStructFieldMap, err := structFieldMap(typ) - if err != nil { - return errors.Wrapf(err, "failed to get struct field map by embedded type") - } - for i := 0; i < typ.NumField(); i++ { - field := typ.Field(i) - if isIgnoredStructField(field) { - continue - } - structField := embeddedStructFieldMap[field.Name] - if !fieldMap.isIncludedRenderName(structField.RenderName) { - continue - } - // if declared same key name, set default value - fieldValue := v.Field(i) - if fieldValue.CanSet() { - fieldValue.Set(reflect.Zero(fieldValue.Type())) - } - } - return nil -} - -// This is a subset of the formats allowed by the regular expression -// defined at http://yaml.org/type/timestamp.html. -var allowedTimestampFormats = []string{ - "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. - "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". - "2006-1-2 15:4:5.999999999", // space separated with no time zone - "2006-1-2", // date only -} - -func (d *Decoder) castToTime(src ast.Node) (time.Time, error) { - if src == nil { - return time.Time{}, nil - } - v := d.nodeToValue(src) - if t, ok := v.(time.Time); ok { - return t, nil - } - s, ok := v.(string) - if !ok { - return time.Time{}, errTypeMismatch(reflect.TypeOf(time.Time{}), reflect.TypeOf(v), src.GetToken()) - } - for _, format := range allowedTimestampFormats { - t, err := time.Parse(format, s) - if err != nil { - // invalid format - continue - } - return t, nil - } - return time.Time{}, nil -} - -func (d *Decoder) decodeTime(ctx context.Context, dst reflect.Value, src ast.Node) error { - t, err := d.castToTime(src) - if err != nil { - return errors.Wrapf(err, "failed to convert to time") - } - dst.Set(reflect.ValueOf(t)) - return nil -} - -func (d *Decoder) castToDuration(src ast.Node) (time.Duration, error) { - if src == nil { - return 0, nil - } - v := d.nodeToValue(src) - if t, ok := v.(time.Duration); ok { - return t, nil - } - s, ok := v.(string) - if !ok { - return 0, errTypeMismatch(reflect.TypeOf(time.Duration(0)), reflect.TypeOf(v), src.GetToken()) - } - t, err := time.ParseDuration(s) - if err != nil { - return 0, errors.Wrapf(err, "failed to parse duration") - } - return t, nil -} - -func (d *Decoder) decodeDuration(ctx context.Context, dst reflect.Value, src ast.Node) error { - t, err := d.castToDuration(src) - if err != nil { - return errors.Wrapf(err, "failed to convert to duration") - } - dst.Set(reflect.ValueOf(t)) - return nil -} - -// getMergeAliasName support single alias only -func (d *Decoder) getMergeAliasName(src ast.Node) string { - mapNode, err := d.getMapNode(src) - if err != nil { - return "" - } - if mapNode == nil { - return "" - } - mapIter := mapNode.MapRange() - for mapIter.Next() { - key := mapIter.Key() - value := mapIter.Value() - if key.Type() == ast.MergeKeyType && value.Type() == ast.AliasType { - return value.(*ast.AliasNode).Value.GetToken().Value - } - } - return "" -} - -func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.Node) error { - if src == nil { - return nil - } - structType := dst.Type() - srcValue := reflect.ValueOf(src) - srcType := srcValue.Type() - if srcType.Kind() == reflect.Ptr { - srcType = srcType.Elem() - srcValue = srcValue.Elem() - } - if structType == srcType { - // dst value implements ast.Node - dst.Set(srcValue) - return nil - } - structFieldMap, err := structFieldMap(structType) - if err != nil { - return errors.Wrapf(err, "failed to create struct field map") - } - ignoreMergeKey := structFieldMap.hasMergeProperty() - keyToNodeMap, err := d.keyToValueNodeMap(src, ignoreMergeKey) - if err != nil { - return errors.Wrapf(err, "failed to get keyToValueNodeMap") - } - var unknownFields map[string]ast.Node - if d.disallowUnknownField { - unknownFields, err = d.keyToKeyNodeMap(src, ignoreMergeKey) - if err != nil { - return errors.Wrapf(err, "failed to get keyToKeyNodeMap") - } - } - - aliasName := d.getMergeAliasName(src) - var foundErr error - - for i := 0; i < structType.NumField(); i++ { - field := structType.Field(i) - if isIgnoredStructField(field) { - continue - } - structField := structFieldMap[field.Name] - if structField.IsInline { - fieldValue := dst.FieldByName(field.Name) - if structField.IsAutoAlias { - if aliasName != "" { - newFieldValue := d.anchorValueMap[aliasName] - if newFieldValue.IsValid() { - fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) - } - } - continue - } - if !fieldValue.CanSet() { - return xerrors.Errorf("cannot set embedded type as unexported field %s.%s", field.PkgPath, field.Name) - } - if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType { - // set nil value to pointer - fieldValue.Set(reflect.Zero(fieldValue.Type())) - continue - } - mapNode := ast.Mapping(nil, false) - for k, v := range keyToNodeMap { - key := &ast.StringNode{BaseNode: &ast.BaseNode{}, Value: k} - mapNode.Values = append(mapNode.Values, ast.MappingValue(nil, key, v)) - } - newFieldValue, err := d.createDecodedNewValue(ctx, fieldValue.Type(), fieldValue, mapNode) - if d.disallowUnknownField { - if err := d.deleteStructKeys(fieldValue.Type(), unknownFields); err != nil { - return errors.Wrapf(err, "cannot delete struct keys") - } - } - - if err != nil { - if foundErr != nil { - continue - } - var te *errors.TypeError - if xerrors.As(err, &te) { - if te.StructFieldName != nil { - fieldName := fmt.Sprintf("%s.%s", structType.Name(), *te.StructFieldName) - te.StructFieldName = &fieldName - } else { - fieldName := fmt.Sprintf("%s.%s", structType.Name(), field.Name) - te.StructFieldName = &fieldName - } - foundErr = te - continue - } else { - foundErr = err - } - continue - } - d.setDefaultValueIfConflicted(newFieldValue, structFieldMap) - fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) - continue - } - v, exists := keyToNodeMap[structField.RenderName] - if !exists { - continue - } - delete(unknownFields, structField.RenderName) - fieldValue := dst.FieldByName(field.Name) - if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType { - // set nil value to pointer - fieldValue.Set(reflect.Zero(fieldValue.Type())) - continue - } - newFieldValue, err := d.createDecodedNewValue(ctx, fieldValue.Type(), fieldValue, v) - if err != nil { - if foundErr != nil { - continue - } - var te *errors.TypeError - if xerrors.As(err, &te) { - fieldName := fmt.Sprintf("%s.%s", structType.Name(), field.Name) - te.StructFieldName = &fieldName - foundErr = te - } else { - foundErr = err - } - continue - } - fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) - } - if foundErr != nil { - return errors.Wrapf(foundErr, "failed to decode value") - } - - // Ignore unknown fields when parsing an inline struct (recognized by a nil token). - // Unknown fields are expected (they could be fields from the parent struct). - if len(unknownFields) != 0 && d.disallowUnknownField && src.GetToken() != nil { - for key, node := range unknownFields { - return errUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken()) - } - } - - if d.validator != nil { - if err := d.validator.Struct(dst.Interface()); err != nil { - ev := reflect.ValueOf(err) - if ev.Type().Kind() == reflect.Slice { - for i := 0; i < ev.Len(); i++ { - fieldErr, ok := ev.Index(i).Interface().(FieldError) - if !ok { - continue - } - fieldName := fieldErr.StructField() - structField, exists := structFieldMap[fieldName] - if !exists { - continue - } - node, exists := keyToNodeMap[structField.RenderName] - if exists { - // TODO: to make FieldError message cutomizable - return errors.ErrSyntax(fmt.Sprintf("%s", err), node.GetToken()) - } else if t := src.GetToken(); t != nil && t.Prev != nil && t.Prev.Prev != nil { - // A missing required field will not be in the keyToNodeMap - // the error needs to be associated with the parent of the source node - return errors.ErrSyntax(fmt.Sprintf("%s", err), t.Prev.Prev) - } - } - } - return err - } - } - return nil -} - -func (d *Decoder) decodeArray(ctx context.Context, dst reflect.Value, src ast.Node) error { - arrayNode, err := d.getArrayNode(src) - if err != nil { - return errors.Wrapf(err, "failed to get array node") - } - if arrayNode == nil { - return nil - } - iter := arrayNode.ArrayRange() - arrayValue := reflect.New(dst.Type()).Elem() - arrayType := dst.Type() - elemType := arrayType.Elem() - idx := 0 - - var foundErr error - for iter.Next() { - v := iter.Value() - if elemType.Kind() == reflect.Ptr && v.Type() == ast.NullType { - // set nil value to pointer - arrayValue.Index(idx).Set(reflect.Zero(elemType)) - } else { - dstValue, err := d.createDecodedNewValue(ctx, elemType, reflect.Value{}, v) - if err != nil { - if foundErr == nil { - foundErr = err - } - continue - } else { - arrayValue.Index(idx).Set(d.castToAssignableValue(dstValue, elemType)) - } - } - idx++ - } - dst.Set(arrayValue) - if foundErr != nil { - return errors.Wrapf(foundErr, "failed to decode value") - } - return nil -} - -func (d *Decoder) decodeSlice(ctx context.Context, dst reflect.Value, src ast.Node) error { - arrayNode, err := d.getArrayNode(src) - if err != nil { - return errors.Wrapf(err, "failed to get array node") - } - if arrayNode == nil { - return nil - } - iter := arrayNode.ArrayRange() - sliceType := dst.Type() - sliceValue := reflect.MakeSlice(sliceType, 0, iter.Len()) - elemType := sliceType.Elem() - - var foundErr error - for iter.Next() { - v := iter.Value() - if elemType.Kind() == reflect.Ptr && v.Type() == ast.NullType { - // set nil value to pointer - sliceValue = reflect.Append(sliceValue, reflect.Zero(elemType)) - continue - } - dstValue, err := d.createDecodedNewValue(ctx, elemType, reflect.Value{}, v) - if err != nil { - if foundErr == nil { - foundErr = err - } - continue - } - sliceValue = reflect.Append(sliceValue, d.castToAssignableValue(dstValue, elemType)) - } - dst.Set(sliceValue) - if foundErr != nil { - return errors.Wrapf(foundErr, "failed to decode value") - } - return nil -} - -func (d *Decoder) decodeMapItem(ctx context.Context, dst *MapItem, src ast.Node) error { - mapNode, err := d.getMapNode(src) - if err != nil { - return errors.Wrapf(err, "failed to get map node") - } - if mapNode == nil { - return nil - } - mapIter := mapNode.MapRange() - if !mapIter.Next() { - return nil - } - key := mapIter.Key() - value := mapIter.Value() - if key.Type() == ast.MergeKeyType { - if err := d.decodeMapItem(ctx, dst, value); err != nil { - return errors.Wrapf(err, "failed to decode map with merge key") - } - return nil - } - *dst = MapItem{ - Key: d.nodeToValue(key), - Value: d.nodeToValue(value), - } - return nil -} - -func (d *Decoder) validateDuplicateKey(keyMap map[string]struct{}, key interface{}, keyNode ast.Node) error { - k, ok := key.(string) - if !ok { - return nil - } - if d.disallowDuplicateKey { - if _, exists := keyMap[k]; exists { - return errDuplicateKey(fmt.Sprintf(`duplicate key "%s"`, k), keyNode.GetToken()) - } - } - keyMap[k] = struct{}{} - return nil -} - -func (d *Decoder) decodeMapSlice(ctx context.Context, dst *MapSlice, src ast.Node) error { - mapNode, err := d.getMapNode(src) - if err != nil { - return errors.Wrapf(err, "failed to get map node") - } - if mapNode == nil { - return nil - } - mapSlice := MapSlice{} - mapIter := mapNode.MapRange() - keyMap := map[string]struct{}{} - for mapIter.Next() { - key := mapIter.Key() - value := mapIter.Value() - if key.Type() == ast.MergeKeyType { - var m MapSlice - if err := d.decodeMapSlice(ctx, &m, value); err != nil { - return errors.Wrapf(err, "failed to decode map with merge key") - } - for _, v := range m { - if err := d.validateDuplicateKey(keyMap, v.Key, value); err != nil { - return errors.Wrapf(err, "invalid map key") - } - mapSlice = append(mapSlice, v) - } - continue - } - k := d.nodeToValue(key) - if err := d.validateDuplicateKey(keyMap, k, key); err != nil { - return errors.Wrapf(err, "invalid map key") - } - mapSlice = append(mapSlice, MapItem{ - Key: k, - Value: d.nodeToValue(value), - }) - } - *dst = mapSlice - return nil -} - -func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node) error { - mapNode, err := d.getMapNode(src) - if err != nil { - return errors.Wrapf(err, "failed to get map node") - } - if mapNode == nil { - return nil - } - mapType := dst.Type() - mapValue := reflect.MakeMap(mapType) - keyType := mapValue.Type().Key() - valueType := mapValue.Type().Elem() - mapIter := mapNode.MapRange() - keyMap := map[string]struct{}{} - var foundErr error - for mapIter.Next() { - key := mapIter.Key() - value := mapIter.Value() - if key.Type() == ast.MergeKeyType { - if err := d.decodeMap(ctx, dst, value); err != nil { - return errors.Wrapf(err, "failed to decode map with merge key") - } - iter := dst.MapRange() - for iter.Next() { - if err := d.validateDuplicateKey(keyMap, iter.Key(), value); err != nil { - return errors.Wrapf(err, "invalid map key") - } - mapValue.SetMapIndex(iter.Key(), iter.Value()) - } - continue - } - k := reflect.ValueOf(d.nodeToValue(key)) - if k.IsValid() && k.Type().ConvertibleTo(keyType) { - k = k.Convert(keyType) - } - if k.IsValid() { - if err := d.validateDuplicateKey(keyMap, k.Interface(), key); err != nil { - return errors.Wrapf(err, "invalid map key") - } - } - if valueType.Kind() == reflect.Ptr && value.Type() == ast.NullType { - // set nil value to pointer - mapValue.SetMapIndex(k, reflect.Zero(valueType)) - continue - } - dstValue, err := d.createDecodedNewValue(ctx, valueType, reflect.Value{}, value) - if err != nil { - if foundErr == nil { - foundErr = err - } - } - if !k.IsValid() { - // expect nil key - mapValue.SetMapIndex(d.createDecodableValue(keyType), d.castToAssignableValue(dstValue, valueType)) - continue - } - mapValue.SetMapIndex(k, d.castToAssignableValue(dstValue, valueType)) - } - dst.Set(mapValue) - if foundErr != nil { - return errors.Wrapf(foundErr, "failed to decode value") - } - return nil -} - -func (d *Decoder) fileToReader(file string) (io.Reader, error) { - reader, err := os.Open(file) - if err != nil { - return nil, errors.Wrapf(err, "failed to open file") - } - return reader, nil -} - -func (d *Decoder) isYAMLFile(file string) bool { - ext := filepath.Ext(file) - if ext == ".yml" { - return true - } - if ext == ".yaml" { - return true - } - return false -} - -func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) { - pattern := fmt.Sprintf("%s/*", dir) - matches, err := filepath.Glob(pattern) - if err != nil { - return nil, errors.Wrapf(err, "failed to get files by %s", pattern) - } - readers := []io.Reader{} - for _, match := range matches { - if !d.isYAMLFile(match) { - continue - } - reader, err := d.fileToReader(match) - if err != nil { - return nil, errors.Wrapf(err, "failed to get reader") - } - readers = append(readers, reader) - } - return readers, nil -} - -func (d *Decoder) readersUnderDirRecursive(dir string) ([]io.Reader, error) { - readers := []io.Reader{} - if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if !d.isYAMLFile(path) { - return nil - } - reader, err := d.fileToReader(path) - if err != nil { - return errors.Wrapf(err, "failed to get reader") - } - readers = append(readers, reader) - return nil - }); err != nil { - return nil, errors.Wrapf(err, "interrupt walk in %s", dir) - } - return readers, nil -} - -func (d *Decoder) resolveReference() error { - for _, opt := range d.opts { - if err := opt(d); err != nil { - return errors.Wrapf(err, "failed to exec option") - } - } - for _, file := range d.referenceFiles { - reader, err := d.fileToReader(file) - if err != nil { - return errors.Wrapf(err, "failed to get reader") - } - d.referenceReaders = append(d.referenceReaders, reader) - } - for _, dir := range d.referenceDirs { - if !d.isRecursiveDir { - readers, err := d.readersUnderDir(dir) - if err != nil { - return errors.Wrapf(err, "failed to get readers from under the %s", dir) - } - d.referenceReaders = append(d.referenceReaders, readers...) - } else { - readers, err := d.readersUnderDirRecursive(dir) - if err != nil { - return errors.Wrapf(err, "failed to get readers from under the %s", dir) - } - d.referenceReaders = append(d.referenceReaders, readers...) - } - } - for _, reader := range d.referenceReaders { - bytes, err := ioutil.ReadAll(reader) - if err != nil { - return errors.Wrapf(err, "failed to read buffer") - } - - // assign new anchor definition to anchorMap - if _, err := d.parse(bytes); err != nil { - return errors.Wrapf(err, "failed to decode") - } - } - d.isResolvedReference = true - return nil -} - -func (d *Decoder) parse(bytes []byte) (*ast.File, error) { - var parseMode parser.Mode - if d.toCommentMap != nil { - parseMode = parser.ParseComments - } - f, err := parser.ParseBytes(bytes, parseMode) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse yaml") - } - normalizedFile := &ast.File{} - for _, doc := range f.Docs { - // try to decode ast.Node to value and map anchor value to anchorMap - if v := d.nodeToValue(doc.Body); v != nil { - normalizedFile.Docs = append(normalizedFile.Docs, doc) - } - } - return normalizedFile, nil -} - -func (d *Decoder) isInitialized() bool { - return d.parsedFile != nil -} - -func (d *Decoder) decodeInit() error { - if !d.isResolvedReference { - if err := d.resolveReference(); err != nil { - return errors.Wrapf(err, "failed to resolve reference") - } - } - var buf bytes.Buffer - if _, err := io.Copy(&buf, d.reader); err != nil { - return errors.Wrapf(err, "failed to copy from reader") - } - file, err := d.parse(buf.Bytes()) - if err != nil { - return errors.Wrapf(err, "failed to decode") - } - d.parsedFile = file - return nil -} - -func (d *Decoder) decode(ctx context.Context, v reflect.Value) error { - if len(d.parsedFile.Docs) <= d.streamIndex { - return io.EOF - } - body := d.parsedFile.Docs[d.streamIndex].Body - if body == nil { - return nil - } - if err := d.decodeValue(ctx, v.Elem(), body); err != nil { - return errors.Wrapf(err, "failed to decode value") - } - d.streamIndex++ - return nil -} - -// Decode reads the next YAML-encoded value from its input -// and stores it in the value pointed to by v. -// -// See the documentation for Unmarshal for details about the -// conversion of YAML into a Go value. -func (d *Decoder) Decode(v interface{}) error { - return d.DecodeContext(context.Background(), v) -} - -// DecodeContext reads the next YAML-encoded value from its input -// and stores it in the value pointed to by v with context.Context. -func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error { - rv := reflect.ValueOf(v) - if rv.Type().Kind() != reflect.Ptr { - return errors.ErrDecodeRequiredPointerType - } - if d.isInitialized() { - if err := d.decode(ctx, rv); err != nil { - if err == io.EOF { - return err - } - return errors.Wrapf(err, "failed to decode") - } - return nil - } - if err := d.decodeInit(); err != nil { - return errors.Wrapf(err, "failed to decodeInit") - } - if err := d.decode(ctx, rv); err != nil { - if err == io.EOF { - return err - } - return errors.Wrapf(err, "failed to decode") - } - return nil -} - -// DecodeFromNode decodes node into the value pointed to by v. -func (d *Decoder) DecodeFromNode(node ast.Node, v interface{}) error { - return d.DecodeFromNodeContext(context.Background(), node, v) -} - -// DecodeFromNodeContext decodes node into the value pointed to by v with context.Context. -func (d *Decoder) DecodeFromNodeContext(ctx context.Context, node ast.Node, v interface{}) error { - rv := reflect.ValueOf(v) - if rv.Type().Kind() != reflect.Ptr { - return errors.ErrDecodeRequiredPointerType - } - if !d.isInitialized() { - if err := d.decodeInit(); err != nil { - return errors.Wrapf(err, "failed to decodInit") - } - } - // resolve references to the anchor on the same file - d.nodeToValue(node) - if err := d.decodeValue(ctx, rv.Elem(), node); err != nil { - return errors.Wrapf(err, "failed to decode value") - } - return nil -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/decode_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/decode_test.go deleted file mode 100644 index 2549bea..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/decode_test.go +++ /dev/null @@ -1,2872 +0,0 @@ -package yaml_test - -import ( - "bytes" - "context" - "fmt" - "io" - "log" - "math" - "net" - "reflect" - "strconv" - "strings" - "testing" - "time" - - "github.com/goccy/go-yaml" - "github.com/goccy/go-yaml/ast" - "github.com/goccy/go-yaml/internal/errors" - "github.com/goccy/go-yaml/parser" - "golang.org/x/xerrors" -) - -type Child struct { - B int - C int `yaml:"-"` -} - -func TestDecoder(t *testing.T) { - tests := []struct { - source string - value interface{} - }{ - { - "null\n", - (*struct{})(nil), - }, - { - "v: hi\n", - map[string]string{"v": "hi"}, - }, - { - "v: \"true\"\n", - map[string]string{"v": "true"}, - }, - { - "v: \"false\"\n", - map[string]string{"v": "false"}, - }, - { - "v: true\n", - map[string]interface{}{"v": true}, - }, - { - "v: true\n", - map[string]string{"v": "true"}, - }, - { - "v: 10\n", - map[string]string{"v": "10"}, - }, - { - "v: -10\n", - map[string]string{"v": "-10"}, - }, - { - "v: 1.234\n", - map[string]string{"v": "1.234"}, - }, - { - "v: false\n", - map[string]bool{"v": false}, - }, - { - "v: 10\n", - map[string]int{"v": 10}, - }, - { - "v: 10", - map[string]interface{}{"v": 10}, - }, - { - "v: 0b10", - map[string]interface{}{"v": 2}, - }, - { - "v: -0b101010", - map[string]interface{}{"v": -42}, - }, - { - "v: -0b1000000000000000000000000000000000000000000000000000000000000000", - map[string]interface{}{"v": int64(-9223372036854775808)}, - }, - { - "v: 0xA", - map[string]interface{}{"v": 10}, - }, - { - "v: .1", - map[string]interface{}{"v": 0.1}, - }, - { - "v: -.1", - map[string]interface{}{"v": -0.1}, - }, - { - "v: -10\n", - map[string]int{"v": -10}, - }, - { - "v: 4294967296\n", - map[string]int64{"v": int64(4294967296)}, - }, - { - "v: 0.1\n", - map[string]interface{}{"v": 0.1}, - }, - { - "v: 0.99\n", - map[string]float32{"v": 0.99}, - }, - { - "v: -0.1\n", - map[string]float64{"v": -0.1}, - }, - { - "v: 6.8523e+5", - map[string]interface{}{"v": 6.8523e+5}, - }, - { - "v: 685.230_15e+03", - map[string]interface{}{"v": 685.23015e+03}, - }, - { - "v: 685_230.15", - map[string]interface{}{"v": 685230.15}, - }, - { - "v: 685_230.15", - map[string]float64{"v": 685230.15}, - }, - { - "v: 685230", - map[string]interface{}{"v": 685230}, - }, - { - "v: +685_230", - map[string]interface{}{"v": 685230}, - }, - { - "v: 02472256", - map[string]interface{}{"v": 685230}, - }, - { - "v: 0x_0A_74_AE", - map[string]interface{}{"v": 685230}, - }, - { - "v: 0b1010_0111_0100_1010_1110", - map[string]interface{}{"v": 685230}, - }, - { - "v: +685_230", - map[string]int{"v": 685230}, - }, - - // Bools from spec - { - "v: True", - map[string]interface{}{"v": true}, - }, - { - "v: TRUE", - map[string]interface{}{"v": true}, - }, - { - "v: False", - map[string]interface{}{"v": false}, - }, - { - "v: FALSE", - map[string]interface{}{"v": false}, - }, - { - "v: y", - map[string]interface{}{"v": "y"}, // y or yes or Yes is string - }, - { - "v: NO", - map[string]interface{}{"v": "NO"}, // no or No or NO is string - }, - { - "v: on", - map[string]interface{}{"v": "on"}, // on is string - }, - - // Some cross type conversions - { - "v: 42", - map[string]uint{"v": 42}, - }, { - "v: 4294967296", - map[string]uint64{"v": uint64(4294967296)}, - }, - - // int - { - "v: 2147483647", - map[string]int{"v": math.MaxInt32}, - }, - { - "v: -2147483648", - map[string]int{"v": math.MinInt32}, - }, - - // int64 - { - "v: 9223372036854775807", - map[string]int64{"v": math.MaxInt64}, - }, - { - "v: 0b111111111111111111111111111111111111111111111111111111111111111", - map[string]int64{"v": math.MaxInt64}, - }, - { - "v: -9223372036854775808", - map[string]int64{"v": math.MinInt64}, - }, - { - "v: -0b111111111111111111111111111111111111111111111111111111111111111", - map[string]int64{"v": -math.MaxInt64}, - }, - - // uint - { - "v: 0", - map[string]uint{"v": 0}, - }, - { - "v: 4294967295", - map[string]uint{"v": math.MaxUint32}, - }, - - // uint64 - { - "v: 0", - map[string]uint{"v": 0}, - }, - { - "v: 18446744073709551615", - map[string]uint64{"v": math.MaxUint64}, - }, - { - "v: 0b1111111111111111111111111111111111111111111111111111111111111111", - map[string]uint64{"v": math.MaxUint64}, - }, - { - "v: 9223372036854775807", - map[string]uint64{"v": math.MaxInt64}, - }, - - // float32 - { - "v: 3.40282346638528859811704183484516925440e+38", - map[string]float32{"v": math.MaxFloat32}, - }, - { - "v: 1.401298464324817070923729583289916131280e-45", - map[string]float32{"v": math.SmallestNonzeroFloat32}, - }, - { - "v: 18446744073709551615", - map[string]float32{"v": float32(math.MaxUint64)}, - }, - { - "v: 18446744073709551616", - map[string]float32{"v": float32(math.MaxUint64 + 1)}, - }, - - // float64 - { - "v: 1.797693134862315708145274237317043567981e+308", - map[string]float64{"v": math.MaxFloat64}, - }, - { - "v: 4.940656458412465441765687928682213723651e-324", - map[string]float64{"v": math.SmallestNonzeroFloat64}, - }, - { - "v: 18446744073709551615", - map[string]float64{"v": float64(math.MaxUint64)}, - }, - { - "v: 18446744073709551616", - map[string]float64{"v": float64(math.MaxUint64 + 1)}, - }, - - // Timestamps - { - // Date only. - "v: 2015-01-01\n", - map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, - }, - { - // RFC3339 - "v: 2015-02-24T18:19:39.12Z\n", - map[string]time.Time{"v": time.Date(2015, 2, 24, 18, 19, 39, .12e9, time.UTC)}, - }, - { - // RFC3339 with short dates. - "v: 2015-2-3T3:4:5Z", - map[string]time.Time{"v": time.Date(2015, 2, 3, 3, 4, 5, 0, time.UTC)}, - }, - { - // ISO8601 lower case t - "v: 2015-02-24t18:19:39Z\n", - map[string]time.Time{"v": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)}, - }, - { - // space separate, no time zone - "v: 2015-02-24 18:19:39\n", - map[string]time.Time{"v": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)}, - }, - { - "v: 60s\n", - map[string]time.Duration{"v": time.Minute}, - }, - { - "v: -0.5h\n", - map[string]time.Duration{"v": -30 * time.Minute}, - }, - - // Single Quoted values. - { - `'1': '2'`, - map[interface{}]interface{}{"1": `2`}, - }, - { - `'1': '"2"'`, - map[interface{}]interface{}{"1": `"2"`}, - }, - { - `'1': ''''`, - map[interface{}]interface{}{"1": `'`}, - }, - { - `'1': '''2'''`, - map[interface{}]interface{}{"1": `'2'`}, - }, - { - `'1': 'B''z'`, - map[interface{}]interface{}{"1": `B'z`}, - }, - { - `'1': '\'`, - map[interface{}]interface{}{"1": `\`}, - }, - { - `'1': '\\'`, - map[interface{}]interface{}{"1": `\\`}, - }, - { - `'1': '\"2\"'`, - map[interface{}]interface{}{"1": `\"2\"`}, - }, - { - `'1': '\\"2\\"'`, - map[interface{}]interface{}{"1": `\\"2\\"`}, - }, - { - "'1': ' 1\n 2\n 3'", - map[interface{}]interface{}{"1": " 1 2 3"}, - }, - { - "'1': '\n 2\n 3'", - map[interface{}]interface{}{"1": " 2 3"}, - }, - - // Double Quoted values. - { - `"1": "2"`, - map[interface{}]interface{}{"1": `2`}, - }, - { - `"1": "\"2\""`, - map[interface{}]interface{}{"1": `"2"`}, - }, - { - `"1": "\""`, - map[interface{}]interface{}{"1": `"`}, - }, - { - `"1": "X\"z"`, - map[interface{}]interface{}{"1": `X"z`}, - }, - { - `"1": "\\"`, - map[interface{}]interface{}{"1": `\`}, - }, - { - `"1": "\\\\"`, - map[interface{}]interface{}{"1": `\\`}, - }, - { - `"1": "\\\"2\\\""`, - map[interface{}]interface{}{"1": `\"2\"`}, - }, - { - "'1': \" 1\n 2\n 3\"", - map[interface{}]interface{}{"1": " 1 2 3"}, - }, - { - "'1': \"\n 2\n 3\"", - map[interface{}]interface{}{"1": " 2 3"}, - }, - { - `"1": "a\x2Fb"`, - map[interface{}]interface{}{"1": `a/b`}, - }, - { - `"1": "a\u002Fb"`, - map[interface{}]interface{}{"1": `a/b`}, - }, - { - `"1": "a\x2Fb\u002Fc\U0000002Fd"`, - map[interface{}]interface{}{"1": `a/b/c/d`}, - }, - - { - "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"}, - }, - - // Nulls - { - "v:", - map[string]interface{}{"v": nil}, - }, - { - "v: ~", - map[string]interface{}{"v": nil}, - }, - { - "~: null key", - map[interface{}]string{nil: "null key"}, - }, - { - "v:", - map[string]*bool{"v": nil}, - }, - { - "v: null", - map[string]*string{"v": nil}, - }, - { - "v: null", - map[string]string{"v": ""}, - }, - { - "v: null", - map[string]interface{}{"v": nil}, - }, - { - "v: Null", - map[string]interface{}{"v": nil}, - }, - { - "v: NULL", - map[string]interface{}{"v": nil}, - }, - { - "v: ~", - map[string]*string{"v": nil}, - }, - { - "v: ~", - map[string]string{"v": ""}, - }, - - { - "v: .inf\n", - map[string]interface{}{"v": math.Inf(0)}, - }, - { - "v: .Inf\n", - map[string]interface{}{"v": math.Inf(0)}, - }, - { - "v: .INF\n", - map[string]interface{}{"v": math.Inf(0)}, - }, - { - "v: -.inf\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.Inf(-1)}, - }, - { - "v: .nan\n", - map[string]interface{}{"v": math.NaN()}, - }, - { - "v: .NaN\n", - map[string]interface{}{"v": math.NaN()}, - }, - { - "v: .NAN\n", - map[string]interface{}{"v": math.NaN()}, - }, - - // 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}, - }, - { - "v: !!timestamp \"2015-01-01\"", - map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, - }, - { - "v: !!timestamp 2015-01-01", - map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, - }, - - // Flow sequence - { - "v: [A,B]", - map[string]interface{}{"v": []interface{}{"A", "B"}}, - }, - { - "v: [A,B,C,]", - map[string][]string{"v": []string{"A", "B", "C"}}, - }, - { - "v: [A,1,C]", - map[string][]string{"v": []string{"A", "1", "C"}}, - }, - { - "v: [A,1,C]", - map[string]interface{}{"v": []interface{}{"A", 1, "C"}}, - }, - - // Block sequence - { - "v:\n - A\n - B", - map[string]interface{}{"v": []interface{}{"A", "B"}}, - }, - { - "v:\n - A\n - B\n - C", - map[string][]string{"v": []string{"A", "B", "C"}}, - }, - { - "v:\n - A\n - 1\n - C", - map[string][]string{"v": []string{"A", "1", "C"}}, - }, - { - "v:\n - A\n - 1\n - C", - map[string]interface{}{"v": []interface{}{"A", 1, "C"}}, - }, - - // Map inside interface with no type hints. - { - "a: {b: c}", - map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, - }, - - { - "v: \"\"\n", - map[string]string{"v": ""}, - }, - { - "v:\n- A\n- B\n", - map[string][]string{"v": {"A", "B"}}, - }, - { - "a: '-'\n", - map[string]string{"a": "-"}, - }, - { - "123\n", - 123, - }, - { - "hello: world\n", - map[string]string{"hello": "world"}, - }, - { - "hello: world\r\n", - map[string]string{"hello": "world"}, - }, - { - "hello: world\rGo: Gopher", - map[string]string{"hello": "world", "Go": "Gopher"}, - }, - - // 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 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 `yaml:"a"` - }{1}, - }, - - { - "a: 1\n", - yaml.MapItem{Key: "a", Value: 1}, - }, - { - "a: 1\nb: 2\nc: 3\n", - yaml.MapSlice{ - {Key: "a", Value: 1}, - {Key: "b", Value: 2}, - {Key: "c", Value: 3}, - }, - }, - { - "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", - map[string]interface{}{ - "v": []interface{}{ - "A", - 1, - map[string][]int{ - "B": {2, 3}, - }, - }, - }, - }, - { - "a:\n b: c\n", - map[string]interface{}{ - "a": map[string]string{ - "b": "c", - }, - }, - }, - { - "a: {x: 1}\n", - map[string]map[string]int{ - "a": { - "x": 1, - }, - }, - }, - { - "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", - map[string]string{ - "t2": "2018-01-09T10:40:47Z", - "t4": "2098-01-09T10:40:47Z", - }, - }, - { - "a: [1, 2]\n", - map[string][]int{ - "a": {1, 2}, - }, - }, - { - "a: {b: c, d: e}\n", - map[string]interface{}{ - "a": map[string]string{ - "b": "c", - "d": "e", - }, - }, - }, - { - "a: 3s\n", - map[string]string{ - "a": "3s", - }, - }, - { - "a: \n", - map[string]string{"a": ""}, - }, - { - "a: \"1:1\"\n", - map[string]string{"a": "1:1"}, - }, - { - "a: 1.2.3.4\n", - map[string]string{"a": "1.2.3.4"}, - }, - { - "a: 'b: c'\n", - map[string]string{"a": "b: c"}, - }, - { - "a: 'Hello #comment'\n", - map[string]string{"a": "Hello #comment"}, - }, - { - "a: 100.5\n", - map[string]interface{}{ - "a": 100.5, - }, - }, - { - "a: \"\\0\"\n", - map[string]string{"a": "\\0"}, - }, - { - "b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n", - map[string]interface{}{ - "b": 2, - "a": 1, - "d": 4, - "c": 3, - "sub": map[string]int{ - "e": 5, - }, - }, - }, - { - " a : b \n", - map[string]string{"a": "b"}, - }, - { - "a: b # comment\nb: c\n", - map[string]string{ - "a": "b", - "b": "c", - }, - }, - { - "---\na: b\n", - map[string]string{"a": "b"}, - }, - { - "a: b\n...\n", - map[string]string{"a": "b"}, - }, - { - "%YAML 1.2\n---\n", - (*struct{})(nil), - }, - { - "---\n", - (*struct{})(nil), - }, - { - "...", - (*struct{})(nil), - }, - { - "v: go test ./...", - map[string]string{"v": "go test ./..."}, - }, - { - "v: echo ---", - map[string]string{"v": "echo ---"}, - }, - { - "v: |\n hello\n ...\n world\n", - map[string]string{"v": "hello\n...\nworld\n"}, - }, - { - "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)}, - }, - { - "v:\n- A\n- |-\n B\n C\n", - map[string][]string{ - "v": { - "A", "B\nC", - }, - }, - }, - { - "v:\n- A\n- >-\n B\n C\n", - map[string][]string{ - "v": { - "A", "B C", - }, - }, - }, - { - "a: b\nc: d\n", - struct { - A string - C string `yaml:"c"` - }{ - "b", "d", - }, - }, - { - "a: 1\nb: 2\n", - struct { - A int - B int `yaml:"-"` - }{ - 1, 0, - }, - }, - { - "a: 1\nb: 2\n", - struct { - A int - Child `yaml:",inline"` - }{ - 1, - Child{ - B: 2, - C: 0, - }, - }, - }, - - // 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\n", - struct { - A, B struct { - C int - } - }{struct{ C int }{1}, struct{ C int }{1}}, - }, - { - "a: &a [1, 2]\nb: *a\n", - struct{ B []int }{[]int{1, 2}}, - }, - - { - "tags:\n- hello-world\na: foo", - struct { - Tags []string - A string - }{Tags: []string{"hello-world"}, A: "foo"}, - }, - { - "", - (*struct{})(nil), - }, - { - "{}", struct{}{}, - }, - { - "v: /a/{b}", - map[string]string{"v": "/a/{b}"}, - }, - { - "v: 1[]{},!%?&*", - map[string]string{"v": "1[]{},!%?&*"}, - }, - { - "v: user's item", - map[string]string{"v": "user's item"}, - }, - { - "v: [1,[2,[3,[4,5],6],7],8]", - map[string]interface{}{ - "v": []interface{}{ - 1, - []interface{}{ - 2, - []interface{}{ - 3, - []int{4, 5}, - 6, - }, - 7, - }, - 8, - }, - }, - }, - { - "v: {a: {b: {c: {d: e},f: g},h: i},j: k}", - map[string]interface{}{ - "v": map[string]interface{}{ - "a": map[string]interface{}{ - "b": map[string]interface{}{ - "c": map[string]string{ - "d": "e", - }, - "f": "g", - }, - "h": "i", - }, - "j": "k", - }, - }, - }, - { - `--- -- a: - b: -- c: d -`, - []map[string]interface{}{ - { - "a": map[string]interface{}{ - "b": nil, - }, - }, - { - "c": "d", - }, - }, - }, - { - `--- -a: - b: -c: d -`, - map[string]interface{}{ - "a": map[string]interface{}{ - "b": nil, - }, - "c": "d", - }, - }, - { - `--- -a: -b: -c: -`, - map[string]interface{}{ - "a": nil, - "b": nil, - "c": nil, - }, - }, - { - `--- -a: go test ./... -b: -c: -`, - map[string]interface{}{ - "a": "go test ./...", - "b": nil, - "c": nil, - }, - }, - { - `--- -a: | - hello - ... - world -b: -c: -`, - map[string]interface{}{ - "a": "hello\n...\nworld\n", - "b": nil, - "c": nil, - }, - }, - - // Multi bytes - { - "v: あいうえお\nv2: かきくけこ", - map[string]string{"v": "あいうえお", "v2": "かきくけこ"}, - }, - } - for _, test := range tests { - t.Run(test.source, func(t *testing.T) { - buf := bytes.NewBufferString(test.source) - dec := yaml.NewDecoder(buf) - typ := reflect.ValueOf(test.value).Type() - value := reflect.New(typ) - if err := dec.Decode(value.Interface()); err != nil { - if err == io.EOF { - return - } - t.Fatalf("%s: %+v", test.source, err) - } - actual := fmt.Sprintf("%+v", value.Elem().Interface()) - expect := fmt.Sprintf("%+v", test.value) - if actual != expect { - t.Fatalf("failed to test [%s], actual=[%s], expect=[%s]", test.source, actual, expect) - } - }) - } -} - -func TestDecoder_TypeConversionError(t *testing.T) { - t.Run("type conversion for struct", func(t *testing.T) { - type T struct { - A int - B uint - C float32 - D bool - } - type U struct { - *T `yaml:",inline"` - } - t.Run("string to int", func(t *testing.T) { - var v T - err := yaml.Unmarshal([]byte(`a: str`), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal string into Go struct field T.A of type int" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - }) - t.Run("string to bool", func(t *testing.T) { - var v T - err := yaml.Unmarshal([]byte(`d: str`), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal string into Go struct field T.D of type bool" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - }) - t.Run("string to int at inline", func(t *testing.T) { - var v U - err := yaml.Unmarshal([]byte(`a: str`), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal string into Go struct field U.T.A of type int" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - }) - }) - t.Run("type conversion for array", func(t *testing.T) { - t.Run("string to int", func(t *testing.T) { - var v map[string][]int - err := yaml.Unmarshal([]byte(`v: [A,1,C]`), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal string into Go value of type int" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - if len(v) == 0 || len(v["v"]) == 0 { - t.Fatal("failed to decode value") - } - if v["v"][0] != 1 { - t.Fatal("failed to decode value") - } - }) - t.Run("string to int", func(t *testing.T) { - var v map[string][]int - err := yaml.Unmarshal([]byte("v:\n - A\n - 1\n - C"), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal string into Go value of type int" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - if len(v) == 0 || len(v["v"]) == 0 { - t.Fatal("failed to decode value") - } - if v["v"][0] != 1 { - t.Fatal("failed to decode value") - } - }) - }) - t.Run("overflow error", func(t *testing.T) { - t.Run("negative number to uint", func(t *testing.T) { - var v map[string]uint - err := yaml.Unmarshal([]byte("v: -42"), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal -42 into Go value of type uint ( overflow )" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - if v["v"] != 0 { - t.Fatal("failed to decode value") - } - }) - t.Run("negative number to uint64", func(t *testing.T) { - var v map[string]uint64 - err := yaml.Unmarshal([]byte("v: -4294967296"), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal -4294967296 into Go value of type uint64 ( overflow )" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - if v["v"] != 0 { - t.Fatal("failed to decode value") - } - }) - t.Run("larger number for int32", func(t *testing.T) { - var v map[string]int32 - err := yaml.Unmarshal([]byte("v: 4294967297"), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal 4294967297 into Go value of type int32 ( overflow )" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - if v["v"] != 0 { - t.Fatal("failed to decode value") - } - }) - t.Run("larger number for int8", func(t *testing.T) { - var v map[string]int8 - err := yaml.Unmarshal([]byte("v: 128"), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal 128 into Go value of type int8 ( overflow )" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - if v["v"] != 0 { - t.Fatal("failed to decode value") - } - }) - }) - t.Run("type conversion for time", func(t *testing.T) { - type T struct { - A time.Time - B time.Duration - } - t.Run("int to time", func(t *testing.T) { - var v T - err := yaml.Unmarshal([]byte(`a: 123`), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal uint64 into Go struct field T.A of type time.Time" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - }) - t.Run("string to duration", func(t *testing.T) { - var v T - err := yaml.Unmarshal([]byte(`b: str`), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := `time: invalid duration "str"` - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - }) - t.Run("int to duration", func(t *testing.T) { - var v T - err := yaml.Unmarshal([]byte(`b: 10`), &v) - if err == nil { - t.Fatal("expected to error") - } - msg := "cannot unmarshal uint64 into Go struct field T.B of type time.Duration" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) - } - }) - }) -} - -func TestDecoder_AnchorReferenceDirs(t *testing.T) { - buf := bytes.NewBufferString("a: *a\n") - dec := yaml.NewDecoder(buf, yaml.ReferenceDirs("testdata")) - var v struct { - A struct { - B int - C string - } - } - if err := dec.Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - if v.A.B != 1 { - t.Fatal("failed to decode by reference dirs") - } - if v.A.C != "hello" { - t.Fatal("failed to decode by reference dirs") - } -} - -func TestDecoder_AnchorReferenceDirsRecursive(t *testing.T) { - buf := bytes.NewBufferString("a: *a\n") - dec := yaml.NewDecoder( - buf, - yaml.RecursiveDir(true), - yaml.ReferenceDirs("testdata"), - ) - var v struct { - A struct { - B int - C string - } - } - if err := dec.Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - if v.A.B != 1 { - t.Fatal("failed to decode by reference dirs") - } - if v.A.C != "hello" { - t.Fatal("failed to decode by reference dirs") - } -} - -func TestDecoder_AnchorFiles(t *testing.T) { - buf := bytes.NewBufferString("a: *a\n") - dec := yaml.NewDecoder(buf, yaml.ReferenceFiles("testdata/anchor.yml")) - var v struct { - A struct { - B int - C string - } - } - if err := dec.Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - if v.A.B != 1 { - t.Fatal("failed to decode by reference dirs") - } - if v.A.C != "hello" { - t.Fatal("failed to decode by reference dirs") - } -} - -func TestDecodeWithMergeKey(t *testing.T) { - yml := ` -a: &a - b: 1 - c: hello -items: -- <<: *a -- <<: *a - c: world -` - type Item struct { - B int - C string - } - type T struct { - Items []*Item - } - buf := bytes.NewBufferString(yml) - dec := yaml.NewDecoder(buf) - var v T - if err := dec.Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - if len(v.Items) != 2 { - t.Fatal("failed to decode with merge key") - } - if v.Items[0].B != 1 || v.Items[0].C != "hello" { - t.Fatal("failed to decode with merge key") - } - if v.Items[1].B != 1 || v.Items[1].C != "world" { - t.Fatal("failed to decode with merge key") - } - t.Run("decode with interface{}", func(t *testing.T) { - buf := bytes.NewBufferString(yml) - dec := yaml.NewDecoder(buf) - var v interface{} - if err := dec.Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - items := v.(map[string]interface{})["items"].([]interface{}) - if len(items) != 2 { - t.Fatal("failed to decode with merge key") - } - b0 := items[0].(map[string]interface{})["b"] - if _, ok := b0.(uint64); !ok { - t.Fatal("failed to decode with merge key") - } - if b0.(uint64) != 1 { - t.Fatal("failed to decode with merge key") - } - c0 := items[0].(map[string]interface{})["c"] - if _, ok := c0.(string); !ok { - t.Fatal("failed to decode with merge key") - } - if c0.(string) != "hello" { - t.Fatal("failed to decode with merge key") - } - b1 := items[1].(map[string]interface{})["b"] - if _, ok := b1.(uint64); !ok { - t.Fatal("failed to decode with merge key") - } - if b1.(uint64) != 1 { - t.Fatal("failed to decode with merge key") - } - c1 := items[1].(map[string]interface{})["c"] - if _, ok := c1.(string); !ok { - t.Fatal("failed to decode with merge key") - } - if c1.(string) != "world" { - t.Fatal("failed to decode with merge key") - } - }) - t.Run("decode with map", func(t *testing.T) { - var v struct { - Items []map[string]interface{} - } - buf := bytes.NewBufferString(yml) - dec := yaml.NewDecoder(buf) - if err := dec.Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - if len(v.Items) != 2 { - t.Fatal("failed to decode with merge key") - } - b0 := v.Items[0]["b"] - if _, ok := b0.(uint64); !ok { - t.Fatal("failed to decode with merge key") - } - if b0.(uint64) != 1 { - t.Fatal("failed to decode with merge key") - } - c0 := v.Items[0]["c"] - if _, ok := c0.(string); !ok { - t.Fatal("failed to decode with merge key") - } - if c0.(string) != "hello" { - t.Fatal("failed to decode with merge key") - } - b1 := v.Items[1]["b"] - if _, ok := b1.(uint64); !ok { - t.Fatal("failed to decode with merge key") - } - if b1.(uint64) != 1 { - t.Fatal("failed to decode with merge key") - } - c1 := v.Items[1]["c"] - if _, ok := c1.(string); !ok { - t.Fatal("failed to decode with merge key") - } - if c1.(string) != "world" { - t.Fatal("failed to decode with merge key") - } - }) -} - -func TestDecoder_Inline(t *testing.T) { - type Base struct { - A int - B string - } - yml := `--- -a: 1 -b: hello -c: true -` - var v struct { - *Base `yaml:",inline"` - C bool - } - if err := yaml.NewDecoder(strings.NewReader(yml)).Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - if v.A != 1 { - t.Fatal("failed to decode with inline key") - } - if v.B != "hello" { - t.Fatal("failed to decode with inline key") - } - if !v.C { - t.Fatal("failed to decode with inline key") - } - - t.Run("multiple inline with strict", func(t *testing.T) { - type Base struct { - A int - B string - } - type Base2 struct { - Base *Base `yaml:",inline"` - } - yml := `--- -a: 1 -b: hello -` - var v struct { - Base2 *Base2 `yaml:",inline"` - } - if err := yaml.NewDecoder(strings.NewReader(yml), yaml.Strict()).Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - if v.Base2.Base.A != 1 { - t.Fatal("failed to decode with inline key") - } - if v.Base2.Base.B != "hello" { - t.Fatal("failed to decode with inline key") - } - }) -} - -func TestDecoder_InlineAndConflictKey(t *testing.T) { - type Base struct { - A int - B string - } - yml := `--- -a: 1 -b: hello -c: true -` - var v struct { - *Base `yaml:",inline"` - A int - C bool - } - if err := yaml.NewDecoder(strings.NewReader(yml)).Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - if v.A != 1 { - t.Fatal("failed to decode with inline key") - } - if v.B != "hello" { - t.Fatal("failed to decode with inline key") - } - if !v.C { - t.Fatal("failed to decode with inline key") - } - if v.Base.A != 0 { - t.Fatal("failed to decode with inline key") - } -} - -func TestDecoder_InlineAndWrongTypeStrict(t *testing.T) { - type Base struct { - A int - B string - } - yml := `--- -a: notanint -b: hello -c: true -` - var v struct { - *Base `yaml:",inline"` - C bool - } - err := yaml.NewDecoder(strings.NewReader(yml), yaml.Strict()).Decode(&v) - if err == nil { - t.Fatalf("expected error") - } - - //TODO: properly check if errors are colored/have source - t.Logf("%s", err) - t.Logf("%s", yaml.FormatError(err, true, false)) - t.Logf("%s", yaml.FormatError(err, false, true)) - t.Logf("%s", yaml.FormatError(err, true, true)) -} - -func TestDecoder_InvalidCases(t *testing.T) { - const src = `--- -a: -- b - c: d -` - var v struct { - A []string - } - err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v) - if err == nil { - t.Fatalf("expected error") - } - - if err.Error() != yaml.FormatError(err, false, true) { - t.Logf("err.Error() = %s", err.Error()) - t.Logf("yaml.FormatError(err, false, true) = %s", yaml.FormatError(err, false, true)) - t.Fatal(`err.Error() should match yaml.FormatError(err, false, true)`) - } - - //TODO: properly check if errors are colored/have source - t.Logf("%s", err) - t.Logf("%s", yaml.FormatError(err, true, false)) - t.Logf("%s", yaml.FormatError(err, false, true)) - t.Logf("%s", yaml.FormatError(err, true, true)) -} - -func TestDecoder_JSONTags(t *testing.T) { - var v struct { - A string `json:"a_json"` // no YAML tag - B string `json:"b_json" yaml:"b_yaml"` // both tags - } - - const src = `--- -a_json: a_json_value -b_json: b_json_value -b_yaml: b_yaml_value -` - if err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v); err != nil { - t.Fatalf(`parsing should succeed: %s`, err) - } - - if v.A != "a_json_value" { - t.Fatalf("v.A should be `a_json_value`, got `%s`", v.A) - } - - if v.B != "b_yaml_value" { - t.Fatalf("v.B should be `b_yaml_value`, got `%s`", v.B) - } -} - -func TestDecoder_DisallowUnknownField(t *testing.T) { - t.Run("different level keys with same name", func(t *testing.T) { - var v struct { - C Child `yaml:"c"` - } - yml := `--- -b: 1 -c: - b: 1 -` - - err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField()).Decode(&v) - if err == nil { - t.Fatalf("error expected") - } - }) - t.Run("inline", func(t *testing.T) { - var v struct { - *Child `yaml:",inline"` - A string `yaml:"a"` - } - yml := `--- -a: a -b: 1 -` - - if err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField()).Decode(&v); err != nil { - t.Fatalf(`parsing should succeed: %s`, err) - } - if v.A != "a" { - t.Fatalf("v.A should be `a`, got `%s`", v.A) - } - if v.B != 1 { - t.Fatalf("v.B should be 1, got %d", v.B) - } - if v.C != 0 { - t.Fatalf("v.C should be 0, got %d", v.C) - } - }) - t.Run("list", func(t *testing.T) { - type C struct { - Child `yaml:",inline"` - } - - var v struct { - Children []C `yaml:"children"` - } - - yml := `--- -children: -- b: 1 -- b: 2 -` - - if err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField()).Decode(&v); err != nil { - t.Fatalf(`parsing should succeed: %s`, err) - } - - if len(v.Children) != 2 { - t.Fatalf(`len(v.Children) should be 2, got %d`, len(v.Children)) - } - - if v.Children[0].B != 1 { - t.Fatalf(`v.Children[0].B should be 1, got %d`, v.Children[0].B) - } - - if v.Children[1].B != 2 { - t.Fatalf(`v.Children[1].B should be 2, got %d`, v.Children[1].B) - } - }) -} - -func TestDecoder_DisallowDuplicateKey(t *testing.T) { - yml := ` -a: b -a: c -` - expected := ` -[3:1] duplicate key "a" - 2 | a: b -> 3 | a: c - ^ -` - t.Run("map", func(t *testing.T) { - var v map[string]string - err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowDuplicateKey()).Decode(&v) - if err == nil { - t.Fatal("decoding should fail") - } - actual := "\n" + err.Error() - if expected != actual { - t.Fatalf("expected:[%s] actual:[%s]", expected, actual) - } - }) - t.Run("struct", func(t *testing.T) { - var v struct { - A string - } - err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowDuplicateKey()).Decode(&v) - if err == nil { - t.Fatal("decoding should fail") - } - actual := "\n" + err.Error() - if expected != actual { - t.Fatalf("expected:[%s] actual:[%s]", expected, actual) - } - }) -} - -func TestDecoder_DefaultValues(t *testing.T) { - v := struct { - A string `yaml:"a"` - B string `yaml:"b"` - c string // private - D struct { - E string `yaml:"e"` - F struct { - G string `yaml:"g"` - } `yaml:"f"` - H struct { - I string `yaml:"i"` - } `yaml:",inline"` - } `yaml:"d"` - J struct { - K string `yaml:"k"` - L struct { - M string `yaml:"m"` - } `yaml:"l"` - N struct { - O string `yaml:"o"` - } `yaml:",inline"` - } `yaml:",inline"` - P struct { - Q string `yaml:"q"` - R struct { - S string `yaml:"s"` - } `yaml:"r"` - T struct { - U string `yaml:"u"` - } `yaml:",inline"` - } `yaml:"p"` - V struct { - W string `yaml:"w"` - X struct { - Y string `yaml:"y"` - } `yaml:"x"` - Z struct { - Ä string `yaml:"ä"` - } `yaml:",inline"` - } `yaml:",inline"` - }{ - B: "defaultBValue", - c: "defaultCValue", - } - - v.D.E = "defaultEValue" - v.D.F.G = "defaultGValue" - v.D.H.I = "defaultIValue" - v.J.K = "defaultKValue" - v.J.L.M = "defaultMValue" - v.J.N.O = "defaultOValue" - v.P.R.S = "defaultSValue" - v.P.T.U = "defaultUValue" - v.V.X.Y = "defaultYValue" - v.V.Z.Ä = "defaultÄValue" - - const src = `--- -a: a_value -p: - q: q_value -w: w_value -` - if err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v); err != nil { - t.Fatalf(`parsing should succeed: %s`, err) - } - if v.A != "a_value" { - t.Fatalf("v.A should be `a_value`, got `%s`", v.A) - } - - if v.B != "defaultBValue" { - t.Fatalf("v.B should be `defaultValue`, got `%s`", v.B) - } - - if v.c != "defaultCValue" { - t.Fatalf("v.c should be `defaultCValue`, got `%s`", v.c) - } - - if v.D.E != "defaultEValue" { - t.Fatalf("v.D.E should be `defaultEValue`, got `%s`", v.D.E) - } - - if v.D.F.G != "defaultGValue" { - t.Fatalf("v.D.F.G should be `defaultGValue`, got `%s`", v.D.F.G) - } - - if v.D.H.I != "defaultIValue" { - t.Fatalf("v.D.H.I should be `defaultIValue`, got `%s`", v.D.H.I) - } - - if v.J.K != "defaultKValue" { - t.Fatalf("v.J.K should be `defaultKValue`, got `%s`", v.J.K) - } - - if v.J.L.M != "defaultMValue" { - t.Fatalf("v.J.L.M should be `defaultMValue`, got `%s`", v.J.L.M) - } - - if v.J.N.O != "defaultOValue" { - t.Fatalf("v.J.N.O should be `defaultOValue`, got `%s`", v.J.N.O) - } - - if v.P.Q != "q_value" { - t.Fatalf("v.P.Q should be `q_value`, got `%s`", v.P.Q) - } - - if v.P.R.S != "defaultSValue" { - t.Fatalf("v.P.R.S should be `defaultSValue`, got `%s`", v.P.R.S) - } - - if v.P.T.U != "defaultUValue" { - t.Fatalf("v.P.T.U should be `defaultUValue`, got `%s`", v.P.T.U) - } - - if v.V.W != "w_value" { - t.Fatalf("v.V.W should be `w_value`, got `%s`", v.V.W) - } - - if v.V.X.Y != "defaultYValue" { - t.Fatalf("v.V.X.Y should be `defaultYValue`, got `%s`", v.V.X.Y) - } - - if v.V.Z.Ä != "defaultÄValue" { - t.Fatalf("v.V.Z.Ä should be `defaultÄValue`, got `%s`", v.V.Z.Ä) - } -} - -func Example_YAMLTags() { - yml := `--- -foo: 1 -bar: c -A: 2 -B: d -` - var v struct { - A int `yaml:"foo" json:"A"` - B string `yaml:"bar" json:"B"` - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - log.Fatal(err) - } - fmt.Println(v.A) - fmt.Println(v.B) - // OUTPUT: - // 1 - // c -} - -type useJSONUnmarshalerTest struct { - s string -} - -func (t *useJSONUnmarshalerTest) UnmarshalJSON(b []byte) error { - s, err := strconv.Unquote(string(b)) - if err != nil { - return err - } - t.s = s - return nil -} - -func TestDecoder_UseJSONUnmarshaler(t *testing.T) { - var v useJSONUnmarshalerTest - if err := yaml.UnmarshalWithOptions([]byte(`"a"`), &v, yaml.UseJSONUnmarshaler()); err != nil { - t.Fatal(err) - } - if v.s != "a" { - t.Fatalf("unexpected decoded value: %s", v.s) - } -} - -func TestDecoder_CustomUnmarshaler(t *testing.T) { - t.Run("override struct type", func(t *testing.T) { - type T struct { - Foo string `yaml:"foo"` - } - src := []byte(`foo: "bar"`) - var v T - if err := yaml.UnmarshalWithOptions(src, &v, yaml.CustomUnmarshaler[T](func(dst *T, b []byte) error { - if !bytes.Equal(src, b) { - t.Fatalf("failed to get decode target buffer. expected %q but got %q", src, b) - } - var v T - if err := yaml.Unmarshal(b, &v); err != nil { - return err - } - if v.Foo != "bar" { - t.Fatal("failed to decode") - } - dst.Foo = "bazbaz" // assign another value to target - return nil - })); err != nil { - t.Fatal(err) - } - if v.Foo != "bazbaz" { - t.Fatalf("failed to switch to custom unmarshaler. got: %v", v.Foo) - } - }) - t.Run("override bytes type", func(t *testing.T) { - type T struct { - Foo []byte `yaml:"foo"` - } - src := []byte(`foo: "bar"`) - var v T - if err := yaml.UnmarshalWithOptions(src, &v, yaml.CustomUnmarshaler[[]byte](func(dst *[]byte, b []byte) error { - if !bytes.Equal(b, []byte(`"bar"`)) { - t.Fatalf("failed to get target buffer: %q", b) - } - *dst = []byte("bazbaz") - return nil - })); err != nil { - t.Fatal(err) - } - if !bytes.Equal(v.Foo, []byte("bazbaz")) { - t.Fatalf("failed to switch to custom unmarshaler. got: %q", v.Foo) - } - }) -} - -type unmarshalContext struct { - v int -} - -func (c *unmarshalContext) UnmarshalYAML(ctx context.Context, b []byte) error { - v, ok := ctx.Value("k").(int) - if !ok { - return fmt.Errorf("cannot get valid context") - } - if v != 1 { - return fmt.Errorf("cannot get valid context") - } - if string(b) != "1" { - return fmt.Errorf("cannot get valid bytes") - } - c.v = v - return nil -} - -func Test_UnmarshalerContext(t *testing.T) { - ctx := context.WithValue(context.Background(), "k", 1) - var v unmarshalContext - if err := yaml.UnmarshalContext(ctx, []byte(`1`), &v); err != nil { - t.Fatalf("%+v", err) - } - if v.v != 1 { - t.Fatal("cannot call UnmarshalYAML") - } -} - -func TestDecoder_DecodeFromNode(t *testing.T) { - t.Run("has reference", func(t *testing.T) { - str := ` -anchor: &map - text: hello -map: *map` - var buf bytes.Buffer - dec := yaml.NewDecoder(&buf) - f, err := parser.ParseBytes([]byte(str), 0) - if err != nil { - t.Fatalf("failed to parse: %s", err) - } - type T struct { - Map map[string]string - } - var v T - if err := dec.DecodeFromNode(f.Docs[0].Body, &v); err != nil { - t.Fatalf("failed to decode: %s", err) - } - actual := fmt.Sprintf("%+v", v) - expect := fmt.Sprintf("%+v", T{map[string]string{"text": "hello"}}) - if actual != expect { - t.Fatalf("actual=[%s], expect=[%s]", actual, expect) - } - }) - t.Run("with reference option", func(t *testing.T) { - anchor := strings.NewReader(` -map: &map - text: hello`) - var buf bytes.Buffer - dec := yaml.NewDecoder(&buf, yaml.ReferenceReaders(anchor)) - f, err := parser.ParseBytes([]byte("map: *map"), 0) - if err != nil { - t.Fatalf("failed to parse: %s", err) - } - type T struct { - Map map[string]string - } - var v T - if err := dec.DecodeFromNode(f.Docs[0].Body, &v); err != nil { - t.Fatalf("failed to decode: %s", err) - } - actual := fmt.Sprintf("%+v", v) - expect := fmt.Sprintf("%+v", T{map[string]string{"text": "hello"}}) - if actual != expect { - t.Fatalf("actual=[%s], expect=[%s]", actual, expect) - } - }) - t.Run("value is not pointer", func(t *testing.T) { - var buf bytes.Buffer - var v bool - err := yaml.NewDecoder(&buf).DecodeFromNode(nil, v) - if !xerrors.Is(err, errors.ErrDecodeRequiredPointerType) { - t.Fatalf("unexpected error: %s", err) - } - }) -} - -func Example_JSONTags() { - yml := `--- -foo: 1 -bar: c -` - var v struct { - A int `json:"foo"` - B string `json:"bar"` - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - log.Fatal(err) - } - fmt.Println(v.A) - fmt.Println(v.B) - // OUTPUT: - // 1 - // c -} - -func Example_DisallowUnknownField() { - var v struct { - A string `yaml:"simple"` - C string `yaml:"complicated"` - } - - const src = `--- -simple: string -complecated: string -` - err := yaml.NewDecoder(strings.NewReader(src), yaml.DisallowUnknownField()).Decode(&v) - fmt.Printf("%v\n", err) - - // OUTPUT: - // [3:1] unknown field "complecated" - // 1 | --- - // 2 | simple: string - // > 3 | complecated: string - // ^ -} - -func Example_Unmarshal_Node() { - f, err := parser.ParseBytes([]byte("text: node example"), 0) - if err != nil { - panic(err) - } - var v struct { - Text string `yaml:"text"` - } - if err := yaml.NodeToValue(f.Docs[0].Body, &v); err != nil { - panic(err) - } - fmt.Println(v.Text) - // OUTPUT: - // node example -} - -type unmarshalableYAMLStringValue string - -func (v *unmarshalableYAMLStringValue) UnmarshalYAML(b []byte) error { - var s string - if err := yaml.Unmarshal(b, &s); err != nil { - return err - } - *v = unmarshalableYAMLStringValue(s) - return nil -} - -type unmarshalableTextStringValue string - -func (v *unmarshalableTextStringValue) UnmarshalText(b []byte) error { - *v = unmarshalableTextStringValue(string(b)) - return nil -} - -type unmarshalableStringContainer struct { - A unmarshalableYAMLStringValue `yaml:"a"` - B unmarshalableTextStringValue `yaml:"b"` -} - -func TestUnmarshalableString(t *testing.T) { - t.Run("empty string", func(t *testing.T) { - t.Parallel() - yml := ` -a: "" -b: "" -` - var container unmarshalableStringContainer - if err := yaml.Unmarshal([]byte(yml), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.A != "" { - t.Fatalf("expected empty string, but %q is set", container.A) - } - if container.B != "" { - t.Fatalf("expected empty string, but %q is set", container.B) - } - }) - t.Run("filled string", func(t *testing.T) { - t.Parallel() - yml := ` -a: "aaa" -b: "bbb" -` - var container unmarshalableStringContainer - if err := yaml.Unmarshal([]byte(yml), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.A != "aaa" { - t.Fatalf("expected \"aaa\", but %q is set", container.A) - } - if container.B != "bbb" { - t.Fatalf("expected \"bbb\", but %q is set", container.B) - } - }) - t.Run("single-quoted string", func(t *testing.T) { - t.Parallel() - yml := ` -a: 'aaa' -b: 'bbb' -` - var container unmarshalableStringContainer - if err := yaml.Unmarshal([]byte(yml), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.A != "aaa" { - t.Fatalf("expected \"aaa\", but %q is set", container.A) - } - if container.B != "bbb" { - t.Fatalf("expected \"aaa\", but %q is set", container.B) - } - }) - t.Run("literal", func(t *testing.T) { - t.Parallel() - yml := ` -a: | - a - b - c -b: | - a - b - c -` - var container unmarshalableStringContainer - if err := yaml.Unmarshal([]byte(yml), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.A != "a\nb\nc\n" { - t.Fatalf("expected \"a\nb\nc\n\", but %q is set", container.A) - } - if container.B != "a\nb\nc\n" { - t.Fatalf("expected \"a\nb\nc\n\", but %q is set", container.B) - } - }) - t.Run("anchor/alias", func(t *testing.T) { - yml := ` -a: &x 1 -b: *x -c: &y hello -d: *y -` - var v struct { - A, B, C, D unmarshalableTextStringValue - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatal(err) - } - if v.A != "1" { - t.Fatal("failed to unmarshal") - } - if v.B != "1" { - t.Fatal("failed to unmarshal") - } - if v.C != "hello" { - t.Fatal("failed to unmarshal") - } - if v.D != "hello" { - t.Fatal("failed to unmarshal") - } - }) - t.Run("net.IP", func(t *testing.T) { - yml := ` -a: &a 127.0.0.1 -b: *a -` - var v struct { - A, B net.IP - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatal(err) - } - if v.A.String() != net.IPv4(127, 0, 0, 1).String() { - t.Fatal("failed to unmarshal") - } - if v.B.String() != net.IPv4(127, 0, 0, 1).String() { - t.Fatal("failed to unmarshal") - } - }) - t.Run("quoted map keys", func(t *testing.T) { - t.Parallel() - yml := ` -a: - "b" : 2 - 'c': true -` - var v struct { - A struct { - B int - C bool - } - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if v.A.B != 2 { - t.Fatalf("expected a.b to equal 2 but was %d", v.A.B) - } - if !v.A.C { - t.Fatal("expected a.c to be true but was false") - } - }) -} - -type unmarshalablePtrStringContainer struct { - V *string `yaml:"value"` -} - -func TestUnmarshalablePtrString(t *testing.T) { - t.Run("empty string", func(t *testing.T) { - t.Parallel() - var container unmarshalablePtrStringContainer - if err := yaml.Unmarshal([]byte(`value: ""`), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.V == nil || *container.V != "" { - t.Fatalf("expected empty string, but %q is set", *container.V) - } - }) - - t.Run("null", func(t *testing.T) { - t.Parallel() - var container unmarshalablePtrStringContainer - if err := yaml.Unmarshal([]byte(`value: null`), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.V != (*string)(nil) { - t.Fatalf("expected nil, but %q is set", *container.V) - } - }) -} - -type unmarshalableIntValue int - -func (v *unmarshalableIntValue) UnmarshalYAML(raw []byte) error { - i, err := strconv.Atoi(string(raw)) - if err != nil { - return err - } - *v = unmarshalableIntValue(i) - return nil -} - -type unmarshalableIntContainer struct { - V unmarshalableIntValue `yaml:"value"` -} - -func TestUnmarshalableInt(t *testing.T) { - t.Run("empty int", func(t *testing.T) { - t.Parallel() - var container unmarshalableIntContainer - if err := yaml.Unmarshal([]byte(``), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.V != 0 { - t.Fatalf("expected empty int, but %d is set", container.V) - } - }) - t.Run("filled int", func(t *testing.T) { - t.Parallel() - var container unmarshalableIntContainer - if err := yaml.Unmarshal([]byte(`value: 9`), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.V != 9 { - t.Fatalf("expected 9, but %d is set", container.V) - } - }) - t.Run("filled number", func(t *testing.T) { - t.Parallel() - var container unmarshalableIntContainer - if err := yaml.Unmarshal([]byte(`value: 9`), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.V != 9 { - t.Fatalf("expected 9, but %d is set", container.V) - } - }) -} - -type unmarshalablePtrIntContainer struct { - V *int `yaml:"value"` -} - -func TestUnmarshalablePtrInt(t *testing.T) { - t.Run("empty int", func(t *testing.T) { - t.Parallel() - var container unmarshalablePtrIntContainer - if err := yaml.Unmarshal([]byte(`value: 0`), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.V == nil || *container.V != 0 { - t.Fatalf("expected 0, but %q is set", *container.V) - } - }) - - t.Run("null", func(t *testing.T) { - t.Parallel() - var container unmarshalablePtrIntContainer - if err := yaml.Unmarshal([]byte(`value: null`), &container); err != nil { - t.Fatalf("failed to unmarshal %v", err) - } - if container.V != (*int)(nil) { - t.Fatalf("expected nil, but %q is set", *container.V) - } - }) -} - -type literalContainer struct { - v string -} - -func (c *literalContainer) UnmarshalYAML(v []byte) error { - var lit string - if err := yaml.Unmarshal(v, &lit); err != nil { - return err - } - c.v = lit - return nil -} - -func TestDecode_Literal(t *testing.T) { - yml := `--- -value: | - { - "key": "value" - } -` - var v map[string]*literalContainer - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatalf("failed to unmarshal %+v", err) - } - if v["value"] == nil { - t.Fatal("failed to unmarshal literal with bytes unmarshaler") - } - if v["value"].v == "" { - t.Fatal("failed to unmarshal literal with bytes unmarshaler") - } -} - -func TestDecoder_UseOrderedMap(t *testing.T) { - yml := ` -a: b -c: d -e: - f: g - h: i -j: k -` - var v interface{} - if err := yaml.NewDecoder(strings.NewReader(yml), yaml.UseOrderedMap()).Decode(&v); err != nil { - t.Fatalf("%+v", err) - } - if _, ok := v.(yaml.MapSlice); !ok { - t.Fatalf("failed to convert to ordered map: %T", v) - } - bytes, err := yaml.Marshal(v) - if err != nil { - t.Fatalf("%+v", err) - } - if string(yml) != "\n"+string(bytes) { - t.Fatalf("expected:[%s] actual:[%s]", string(yml), "\n"+string(bytes)) - } -} - -func TestDecoder_Stream(t *testing.T) { - yml := ` ---- -a: b -c: d ---- -e: f -g: h ---- -i: j -k: l -` - dec := yaml.NewDecoder(strings.NewReader(yml)) - values := []map[string]string{} - for { - var v map[string]string - if err := dec.Decode(&v); err != nil { - if err == io.EOF { - break - } - t.Fatalf("%+v", err) - } - values = append(values, v) - } - if len(values) != 3 { - t.Fatal("failed to stream decoding") - } - if values[0]["a"] != "b" { - t.Fatal("failed to stream decoding") - } - if values[1]["e"] != "f" { - t.Fatal("failed to stream decoding") - } - if values[2]["i"] != "j" { - t.Fatal("failed to stream decoding") - } -} - -type unmarshalYAMLWithAliasString string - -func (v *unmarshalYAMLWithAliasString) UnmarshalYAML(b []byte) error { - var s string - if err := yaml.Unmarshal(b, &s); err != nil { - return err - } - *v = unmarshalYAMLWithAliasString(s) - return nil -} - -type unmarshalYAMLWithAliasMap map[string]interface{} - -func (v *unmarshalYAMLWithAliasMap) UnmarshalYAML(b []byte) error { - var m map[string]interface{} - if err := yaml.Unmarshal(b, &m); err != nil { - return err - } - *v = unmarshalYAMLWithAliasMap(m) - return nil -} - -func TestDecoder_UnmarshalYAMLWithAlias(t *testing.T) { - type value struct { - String unmarshalYAMLWithAliasString - Map unmarshalYAMLWithAliasMap - } - tests := []struct { - name string - yaml string - expectedValue value - err string - }{ - { - name: "ok", - yaml: ` -anchors: - w: &w "\"hello\" \"world\"" - map: &x - a: b - c: d - d: *w -string: *w -map: - <<: *x - e: f -`, - expectedValue: value{ - String: unmarshalYAMLWithAliasString(`"hello" "world"`), - Map: unmarshalYAMLWithAliasMap(map[string]interface{}{ - "a": "b", - "c": "d", - "d": `"hello" "world"`, - "e": "f", - }), - }, - }, - { - name: "unknown alias", - yaml: ` -anchors: - w: &w "\"hello\" \"world\"" - map: &x - a: b - c: d - d: *w -string: *y -map: - <<: *z - e: f -`, - err: "cannot find anchor by alias name y", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var v value - err := yaml.Unmarshal([]byte(test.yaml), &v) - - if test.err != "" { - if err == nil { - t.Fatal("expected to error") - } - if !strings.Contains(err.Error(), test.err) { - t.Fatalf("expected error message: %s to contain: %s", err.Error(), test.err) - } - } else { - if err != nil { - t.Fatalf("%+v", err) - } - if !reflect.DeepEqual(test.expectedValue, v) { - t.Fatalf("non matching values:\nexpected[%s]\ngot [%s]", test.expectedValue, v) - } - } - }) - } -} - -type unmarshalString string - -func (u *unmarshalString) UnmarshalYAML(b []byte) error { - *u = unmarshalString(string(b)) - return nil -} - -type unmarshalList struct { - v []map[string]unmarshalString -} - -func (u *unmarshalList) UnmarshalYAML(b []byte) error { - expected := ` - - b: c - d: | - hello - - hello - f: g - - h: i` - actual := "\n" + string(b) - if expected != actual { - return xerrors.Errorf("unexpected bytes: expected [%q] but got [%q]", expected, actual) - } - var v []map[string]unmarshalString - if err := yaml.Unmarshal(b, &v); err != nil { - return err - } - u.v = v - return nil -} - -func TestDecoder_UnmarshalBytesWithSeparatedList(t *testing.T) { - yml := ` -a: - - b: c - d: | - hello - - hello - f: g - - h: i -` - var v struct { - A unmarshalList - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatal(err) - } - if len(v.A.v) != 2 { - t.Fatalf("failed to unmarshal %+v", v) - } - if len(v.A.v[0]) != 3 { - t.Fatalf("failed to unmarshal %+v", v.A.v[0]) - } - if len(v.A.v[1]) != 1 { - t.Fatalf("failed to unmarshal %+v", v.A.v[1]) - } -} - -func TestDecoder_LiteralWithNewLine(t *testing.T) { - type A struct { - Node string `yaml:"b"` - LastNode string `yaml:"last"` - } - tests := []A{ - A{ - Node: "hello\nworld", - }, - A{ - Node: "hello\nworld\n", - }, - A{ - Node: "hello\nworld\n\n", - }, - A{ - LastNode: "hello\nworld", - }, - A{ - LastNode: "hello\nworld\n", - }, - A{ - LastNode: "hello\nworld\n\n", - }, - } - // struct(want) -> Marshal -> Unmarchal -> struct(got) - for _, want := range tests { - bytes, _ := yaml.Marshal(want) - got := A{} - if err := yaml.Unmarshal(bytes, &got); err != nil { - t.Fatal(err) - } - if want.Node != got.Node { - t.Fatalf("expected:%q but got %q", want.Node, got.Node) - } - if want.LastNode != got.LastNode { - t.Fatalf("expected:%q but got %q", want.LastNode, got.LastNode) - } - } -} - -func TestDecoder_TabCharacterAtRight(t *testing.T) { - yml := ` -- a: [2 , 2] - b: [2 , 2] - c: [2 , 2]` - var v []map[string][]int - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatal(err) - } - if len(v) != 1 { - t.Fatalf("failed to unmarshal %+v", v) - } - if len(v[0]) != 3 { - t.Fatalf("failed to unmarshal %+v", v) - } -} - -func TestDecoder_Canonical(t *testing.T) { - yml := ` -!!map { - ? !!str "explicit":!!str "entry", - ? !!str "implicit" : !!str "entry", - ? !!null "" : !!null "", -} -` - var v interface{} - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatalf("%+v", err) - } - m, ok := v.(map[string]interface{}) - if !ok { - t.Fatalf("failed to decode canonical yaml: %+v", v) - } - if m["explicit"] != "entry" { - t.Fatalf("failed to decode canonical yaml: %+v", m) - } - if m["implicit"] != "entry" { - t.Fatalf("failed to decode canonical yaml: %+v", m) - } - if m["null"] != nil { - t.Fatalf("failed to decode canonical yaml: %+v", m) - } -} - -func TestDecoder_DecodeFromFile(t *testing.T) { - yml := ` -a: b -c: d -` - file, err := parser.ParseBytes([]byte(yml), 0) - if err != nil { - t.Fatal(err) - } - var v map[string]string - if err := yaml.NewDecoder(file).Decode(&v); err != nil { - t.Fatal(err) - } - if len(v) != 2 { - t.Fatal("failed to decode from ast.File") - } - if v["a"] != "b" { - t.Fatal("failed to decode from ast.File") - } - if v["c"] != "d" { - t.Fatal("failed to decode from ast.File") - } -} - -func TestDecoder_DecodeWithNode(t *testing.T) { - t.Run("abstract node", func(t *testing.T) { - type T struct { - Text ast.Node `yaml:"text"` - } - var v T - if err := yaml.Unmarshal([]byte(`text: hello`), &v); err != nil { - t.Fatalf("%+v", err) - } - expected := "hello" - got := v.Text.String() - if expected != got { - t.Fatalf("failed to decode to ast.Node: expected %s but got %s", expected, got) - } - }) - t.Run("concrete node", func(t *testing.T) { - type T struct { - Text *ast.StringNode `yaml:"text"` - } - var v T - if err := yaml.Unmarshal([]byte(`text: hello`), &v); err != nil { - t.Fatalf("%+v", err) - } - expected := "hello" - got := v.Text.String() - if expected != got { - t.Fatalf("failed to decode to ast.Node: expected %s but got %s", expected, got) - } - }) -} - -func TestRoundtripAnchorAlias(t *testing.T) { - t.Run("irreversible", func(t *testing.T) { - type foo struct { - K1 string - K2 string - } - - type bar struct { - K1 string - K3 string - } - - type doc struct { - Foo foo - Bar bar - } - yml := ` -foo: - <<: &test-anchor - k1: "One" - k2: "Two" - -bar: - <<: *test-anchor - k3: "Three" -` - var v doc - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatalf("%+v", err) - } - bytes, err := yaml.Marshal(v) - if err != nil { - t.Fatalf("%+v", err) - } - expected := ` -foo: - k1: One - k2: Two -bar: - k1: One - k3: Three -` - got := "\n" + string(bytes) - if expected != got { - t.Fatalf("expected:[%s] but got [%s]", expected, got) - } - }) - t.Run("reversible", func(t *testing.T) { - type TestAnchor struct { - K1 string - } - type foo struct { - *TestAnchor `yaml:",inline,alias"` - K2 string - } - type bar struct { - *TestAnchor `yaml:",inline,alias"` - K3 string - } - type doc struct { - TestAnchor *TestAnchor `yaml:"test-anchor,anchor"` - Foo foo - Bar bar - } - yml := ` -test-anchor: &test-anchor - k1: One -foo: - <<: *test-anchor - k2: Two -bar: - <<: *test-anchor - k3: Three -` - var v doc - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatalf("%+v", err) - } - bytes, err := yaml.Marshal(v) - if err != nil { - t.Fatalf("%+v", err) - } - got := "\n" + string(bytes) - if yml != got { - t.Fatalf("expected:[%s] but got [%s]", yml, got) - } - }) -} - -func TestUnmarshalMapSliceParallel(t *testing.T) { - content := ` -steps: - req0: - desc: Get /users/1 - req: - /users/1: - get: nil - test: | - current.res.status == 200 - req1: - desc: Get /private - req: - /private: - get: nil - test: | - current.res.status == 403 - req2: - desc: Get /users - req: - /users: - get: nil - test: | - current.res.status == 200 -` - type mappedSteps struct { - Steps yaml.MapSlice `yaml:"steps,omitempty"` - } - for i := 0; i < 100; i++ { - t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) { - t.Parallel() - for i := 0; i < 10; i++ { - m := mappedSteps{ - Steps: yaml.MapSlice{}, - } - if err := yaml.Unmarshal([]byte(content), &m); err != nil { - t.Fatal(err) - } - for _, s := range m.Steps { - _, ok := s.Value.(map[string]interface{}) - if !ok { - t.Fatal("unexpected error") - } - } - } - }) - } -} - -func TestSameNameInineStruct(t *testing.T) { - type X struct { - X float64 `yaml:"x"` - } - - type T struct { - X `yaml:",inline"` - } - - var v T - if err := yaml.Unmarshal([]byte(`x: 0.7`), &v); err != nil { - t.Fatal(err) - } - if fmt.Sprint(v.X.X) != "0.7" { - t.Fatalf("failed to decode") - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/encode.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/encode.go deleted file mode 100644 index 7d8d81e..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/encode.go +++ /dev/null @@ -1,871 +0,0 @@ -package yaml - -import ( - "context" - "encoding" - "fmt" - "io" - "math" - "reflect" - "sort" - "strconv" - "strings" - "time" - - "github.com/goccy/go-yaml/ast" - "github.com/goccy/go-yaml/internal/errors" - "github.com/goccy/go-yaml/parser" - "github.com/goccy/go-yaml/printer" - "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" -) - -const ( - // DefaultIndentSpaces default number of space for indent - DefaultIndentSpaces = 2 -) - -// Encoder writes YAML values to an output stream. -type Encoder struct { - writer io.Writer - opts []EncodeOption - indent int - indentSequence bool - singleQuote bool - isFlowStyle bool - isJSONStyle bool - useJSONMarshaler bool - anchorCallback func(*ast.AnchorNode, interface{}) error - anchorPtrToNameMap map[uintptr]string - customMarshalerMap map[reflect.Type]func(interface{}) ([]byte, error) - useLiteralStyleIfMultiline bool - commentMap map[*Path][]*Comment - written bool - - line int - column int - offset int - indentNum int - indentLevel int -} - -// NewEncoder returns a new encoder that writes to w. -// The Encoder should be closed after use to flush all data to w. -func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder { - return &Encoder{ - writer: w, - opts: opts, - indent: DefaultIndentSpaces, - anchorPtrToNameMap: map[uintptr]string{}, - customMarshalerMap: map[reflect.Type]func(interface{}) ([]byte, error){}, - line: 1, - column: 1, - offset: 0, - } -} - -// Close closes the encoder by writing any remaining data. -// It does not write a stream terminating string "...". -func (e *Encoder) Close() error { - return nil -} - -// Encode writes the YAML encoding of v to the stream. -// If multiple items are encoded to the stream, -// the second and subsequent document will be preceded with a "---" document separator, -// but the first will not. -// -// See the documentation for Marshal for details about the conversion of Go values to YAML. -func (e *Encoder) Encode(v interface{}) error { - return e.EncodeContext(context.Background(), v) -} - -// EncodeContext writes the YAML encoding of v to the stream with context.Context. -func (e *Encoder) EncodeContext(ctx context.Context, v interface{}) error { - node, err := e.EncodeToNodeContext(ctx, v) - if err != nil { - return errors.Wrapf(err, "failed to encode to node") - } - if err := e.setCommentByCommentMap(node); err != nil { - return errors.Wrapf(err, "failed to set comment by comment map") - } - if !e.written { - e.written = true - } else { - // write document separator - e.writer.Write([]byte("---\n")) - } - var p printer.Printer - e.writer.Write(p.PrintNode(node)) - return nil -} - -// EncodeToNode convert v to ast.Node. -func (e *Encoder) EncodeToNode(v interface{}) (ast.Node, error) { - return e.EncodeToNodeContext(context.Background(), v) -} - -// EncodeToNodeContext convert v to ast.Node with context.Context. -func (e *Encoder) EncodeToNodeContext(ctx context.Context, v interface{}) (ast.Node, error) { - for _, opt := range e.opts { - if err := opt(e); err != nil { - return nil, errors.Wrapf(err, "failed to run option for encoder") - } - } - node, err := e.encodeValue(ctx, reflect.ValueOf(v), 1) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode value") - } - return node, nil -} - -func (e *Encoder) setCommentByCommentMap(node ast.Node) error { - if e.commentMap == nil { - return nil - } - for path, comments := range e.commentMap { - n, err := path.FilterNode(node) - if err != nil { - return errors.Wrapf(err, "failed to filter node") - } - if n == nil { - continue - } - for _, comment := range comments { - commentTokens := []*token.Token{} - for _, text := range comment.Texts { - commentTokens = append(commentTokens, token.New(text, text, nil)) - } - commentGroup := ast.CommentGroup(commentTokens) - switch comment.Position { - case CommentHeadPosition: - if err := e.setHeadComment(node, n, commentGroup); err != nil { - return errors.Wrapf(err, "failed to set head comment") - } - case CommentLinePosition: - if err := e.setLineComment(node, n, commentGroup); err != nil { - return errors.Wrapf(err, "failed to set line comment") - } - case CommentFootPosition: - if err := e.setFootComment(node, n, commentGroup); err != nil { - return errors.Wrapf(err, "failed to set foot comment") - } - default: - return ErrUnknownCommentPositionType - } - } - } - return nil -} - -func (e *Encoder) setHeadComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { - parent := ast.Parent(node, filtered) - if parent == nil { - return ErrUnsupportedHeadPositionType(node) - } - switch p := parent.(type) { - case *ast.MappingValueNode: - if err := p.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") - } - case *ast.MappingNode: - if err := p.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") - } - case *ast.SequenceNode: - if len(p.ValueHeadComments) == 0 { - p.ValueHeadComments = make([]*ast.CommentGroupNode, len(p.Values)) - } - var foundIdx int - for idx, v := range p.Values { - if v == filtered { - foundIdx = idx - break - } - } - p.ValueHeadComments[foundIdx] = comment - default: - return ErrUnsupportedHeadPositionType(node) - } - return nil -} - -func (e *Encoder) setLineComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { - switch filtered.(type) { - case *ast.MappingValueNode, *ast.SequenceNode: - // Line comment cannot be set for mapping value node. - // It should probably be set for the parent map node - if err := e.setLineCommentToParentMapNode(node, filtered, comment); err != nil { - return errors.Wrapf(err, "failed to set line comment to parent node") - } - default: - if err := filtered.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") - } - } - return nil -} - -func (e *Encoder) setLineCommentToParentMapNode(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { - parent := ast.Parent(node, filtered) - if parent == nil { - return ErrUnsupportedLinePositionType(node) - } - switch p := parent.(type) { - case *ast.MappingValueNode: - if err := p.Key.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") - } - case *ast.MappingNode: - if err := p.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") - } - default: - return ErrUnsupportedLinePositionType(parent) - } - return nil -} - -func (e *Encoder) setFootComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { - parent := ast.Parent(node, filtered) - if parent == nil { - return ErrUnsupportedFootPositionType(node) - } - switch n := parent.(type) { - case *ast.MappingValueNode: - n.FootComment = comment - case *ast.MappingNode: - n.FootComment = comment - case *ast.SequenceNode: - n.FootComment = comment - default: - return ErrUnsupportedFootPositionType(n) - } - return nil -} - -func (e *Encoder) encodeDocument(doc []byte) (ast.Node, error) { - f, err := parser.ParseBytes(doc, 0) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse yaml") - } - for _, docNode := range f.Docs { - if docNode.Body != nil { - return docNode.Body, nil - } - } - return nil, nil -} - -func (e *Encoder) isInvalidValue(v reflect.Value) bool { - if !v.IsValid() { - return true - } - kind := v.Type().Kind() - if kind == reflect.Ptr && v.IsNil() { - return true - } - if kind == reflect.Interface && v.IsNil() { - return true - } - return false -} - -type jsonMarshaler interface { - MarshalJSON() ([]byte, error) -} - -func (e *Encoder) existsTypeInCustomMarshalerMap(t reflect.Type) bool { - if _, exists := e.customMarshalerMap[t]; exists { - return true - } - - globalCustomMarshalerMu.Lock() - defer globalCustomMarshalerMu.Unlock() - if _, exists := globalCustomMarshalerMap[t]; exists { - return true - } - return false -} - -func (e *Encoder) marshalerFromCustomMarshalerMap(t reflect.Type) (func(interface{}) ([]byte, error), bool) { - if marshaler, exists := e.customMarshalerMap[t]; exists { - return marshaler, exists - } - - globalCustomMarshalerMu.Lock() - defer globalCustomMarshalerMu.Unlock() - if marshaler, exists := globalCustomMarshalerMap[t]; exists { - return marshaler, exists - } - return nil, false -} - -func (e *Encoder) canEncodeByMarshaler(v reflect.Value) bool { - if !v.CanInterface() { - return false - } - if e.existsTypeInCustomMarshalerMap(v.Type()) { - return true - } - iface := v.Interface() - switch iface.(type) { - case BytesMarshalerContext: - return true - case BytesMarshaler: - return true - case InterfaceMarshalerContext: - return true - case InterfaceMarshaler: - return true - case time.Time: - return true - case time.Duration: - return true - case encoding.TextMarshaler: - return true - case jsonMarshaler: - return e.useJSONMarshaler - } - return false -} - -func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column int) (ast.Node, error) { - iface := v.Interface() - - if marshaler, exists := e.marshalerFromCustomMarshalerMap(v.Type()); exists { - doc, err := marshaler(iface) - if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") - } - node, err := e.encodeDocument(doc) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") - } - return node, nil - } - - if marshaler, ok := iface.(BytesMarshalerContext); ok { - doc, err := marshaler.MarshalYAML(ctx) - if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") - } - node, err := e.encodeDocument(doc) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") - } - return node, nil - } - - if marshaler, ok := iface.(BytesMarshaler); ok { - doc, err := marshaler.MarshalYAML() - if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") - } - node, err := e.encodeDocument(doc) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") - } - return node, nil - } - - if marshaler, ok := iface.(InterfaceMarshalerContext); ok { - marshalV, err := marshaler.MarshalYAML(ctx) - if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") - } - return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) - } - - if marshaler, ok := iface.(InterfaceMarshaler); ok { - marshalV, err := marshaler.MarshalYAML() - if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") - } - return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) - } - - if t, ok := iface.(time.Time); ok { - return e.encodeTime(t, column), nil - } - - if t, ok := iface.(time.Duration); ok { - return e.encodeDuration(t, column), nil - } - - if marshaler, ok := iface.(encoding.TextMarshaler); ok { - doc, err := marshaler.MarshalText() - if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalText") - } - node, err := e.encodeDocument(doc) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") - } - return node, nil - } - - if e.useJSONMarshaler { - if marshaler, ok := iface.(jsonMarshaler); ok { - jsonBytes, err := marshaler.MarshalJSON() - if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalJSON") - } - doc, err := JSONToYAML(jsonBytes) - if err != nil { - return nil, errors.Wrapf(err, "failed to convert json to yaml") - } - node, err := e.encodeDocument(doc) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") - } - return node, nil - } - } - - return nil, xerrors.Errorf("does not implemented Marshaler") -} - -func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) (ast.Node, error) { - if e.isInvalidValue(v) { - return e.encodeNil(), nil - } - if e.canEncodeByMarshaler(v) { - node, err := e.encodeByMarshaler(ctx, v, column) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode by marshaler") - } - return node, nil - } - switch v.Type().Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return e.encodeInt(v.Int()), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return e.encodeUint(v.Uint()), nil - case reflect.Float32: - return e.encodeFloat(v.Float(), 32), nil - case reflect.Float64: - return e.encodeFloat(v.Float(), 64), nil - case reflect.Ptr: - anchorName := e.anchorPtrToNameMap[v.Pointer()] - if anchorName != "" { - aliasName := anchorName - alias := ast.Alias(token.New("*", "*", e.pos(column))) - alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) - return alias, nil - } - return e.encodeValue(ctx, v.Elem(), column) - case reflect.Interface: - return e.encodeValue(ctx, v.Elem(), column) - case reflect.String: - return e.encodeString(v.String(), column), nil - case reflect.Bool: - return e.encodeBool(v.Bool()), nil - case reflect.Slice: - if mapSlice, ok := v.Interface().(MapSlice); ok { - return e.encodeMapSlice(ctx, mapSlice, column) - } - return e.encodeSlice(ctx, v) - case reflect.Array: - return e.encodeArray(ctx, v) - case reflect.Struct: - if v.CanInterface() { - if mapItem, ok := v.Interface().(MapItem); ok { - return e.encodeMapItem(ctx, mapItem, column) - } - if t, ok := v.Interface().(time.Time); ok { - return e.encodeTime(t, column), nil - } - } - return e.encodeStruct(ctx, v, column) - case reflect.Map: - return e.encodeMap(ctx, v, column), nil - default: - return nil, xerrors.Errorf("unknown value type %s", v.Type().String()) - } -} - -func (e *Encoder) pos(column int) *token.Position { - return &token.Position{ - Line: e.line, - Column: column, - Offset: e.offset, - IndentNum: e.indentNum, - IndentLevel: e.indentLevel, - } -} - -func (e *Encoder) encodeNil() *ast.NullNode { - value := "null" - return ast.Null(token.New(value, value, e.pos(e.column))) -} - -func (e *Encoder) encodeInt(v int64) *ast.IntegerNode { - value := fmt.Sprint(v) - return ast.Integer(token.New(value, value, e.pos(e.column))) -} - -func (e *Encoder) encodeUint(v uint64) *ast.IntegerNode { - value := fmt.Sprint(v) - return ast.Integer(token.New(value, value, e.pos(e.column))) -} - -func (e *Encoder) encodeFloat(v float64, bitSize int) ast.Node { - if v == math.Inf(0) { - value := ".inf" - return ast.Infinity(token.New(value, value, e.pos(e.column))) - } else if v == math.Inf(-1) { - value := "-.inf" - return ast.Infinity(token.New(value, value, e.pos(e.column))) - } else if math.IsNaN(v) { - value := ".nan" - return ast.Nan(token.New(value, value, e.pos(e.column))) - } - value := strconv.FormatFloat(v, 'g', -1, bitSize) - if !strings.Contains(value, ".") && !strings.Contains(value, "e") { - // append x.0 suffix to keep float value context - value = fmt.Sprintf("%s.0", value) - } - return ast.Float(token.New(value, value, e.pos(e.column))) -} - -func (e *Encoder) isNeedQuoted(v string) bool { - if e.isJSONStyle { - return true - } - if e.useLiteralStyleIfMultiline && strings.ContainsAny(v, "\n\r") { - return false - } - if e.isFlowStyle && strings.ContainsAny(v, `]},'"`) { - return true - } - if token.IsNeedQuoted(v) { - return true - } - return false -} - -func (e *Encoder) encodeString(v string, column int) *ast.StringNode { - if e.isNeedQuoted(v) { - if e.singleQuote { - v = quoteWith(v, '\'') - } else { - v = strconv.Quote(v) - } - } - return ast.String(token.New(v, v, e.pos(column))) -} - -func (e *Encoder) encodeBool(v bool) *ast.BoolNode { - value := fmt.Sprint(v) - return ast.Bool(token.New(value, value, e.pos(e.column))) -} - -func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { - if e.indentSequence { - e.column += e.indent - } - column := e.column - sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) - for i := 0; i < value.Len(); i++ { - node, err := e.encodeValue(ctx, value.Index(i), column) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode value for slice") - } - sequence.Values = append(sequence.Values, node) - } - if e.indentSequence { - e.column -= e.indent - } - return sequence, nil -} - -func (e *Encoder) encodeArray(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { - if e.indentSequence { - e.column += e.indent - } - column := e.column - sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) - for i := 0; i < value.Len(); i++ { - node, err := e.encodeValue(ctx, value.Index(i), column) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode value for array") - } - sequence.Values = append(sequence.Values, node) - } - if e.indentSequence { - e.column -= e.indent - } - return sequence, nil -} - -func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) (*ast.MappingValueNode, error) { - k := reflect.ValueOf(item.Key) - v := reflect.ValueOf(item.Value) - value, err := e.encodeValue(ctx, v, column) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode MapItem") - } - if e.isMapNode(value) { - value.AddColumn(e.indent) - } - return ast.MappingValue( - token.New("", "", e.pos(column)), - e.encodeString(k.Interface().(string), column), - value, - ), nil -} - -func (e *Encoder) encodeMapSlice(ctx context.Context, value MapSlice, column int) (*ast.MappingNode, error) { - node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) - for _, item := range value { - value, err := e.encodeMapItem(ctx, item, column) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode MapItem for MapSlice") - } - node.Values = append(node.Values, value) - } - return node, nil -} - -func (e *Encoder) isMapNode(node ast.Node) bool { - _, ok := node.(ast.MapNode) - return ok -} - -func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) ast.Node { - node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) - keys := make([]interface{}, len(value.MapKeys())) - for i, k := range value.MapKeys() { - keys[i] = k.Interface() - } - sort.Slice(keys, func(i, j int) bool { - return fmt.Sprint(keys[i]) < fmt.Sprint(keys[j]) - }) - for _, key := range keys { - k := reflect.ValueOf(key) - v := value.MapIndex(k) - value, err := e.encodeValue(ctx, v, column) - if err != nil { - return nil - } - if e.isMapNode(value) { - value.AddColumn(e.indent) - } - node.Values = append(node.Values, ast.MappingValue( - nil, - e.encodeString(fmt.Sprint(key), column), - value, - )) - } - return node -} - -// IsZeroer is used to check whether an object is zero to determine -// whether it should be omitted when marshaling with the omitempty flag. -// One notable implementation is time.Time. -type IsZeroer interface { - IsZero() bool -} - -func (e *Encoder) isZeroValue(v reflect.Value) bool { - kind := v.Kind() - if z, ok := v.Interface().(IsZeroer); ok { - if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { - return true - } - return z.IsZero() - } - switch kind { - case reflect.String: - return len(v.String()) == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - case reflect.Slice: - return v.Len() == 0 - case reflect.Map: - return v.Len() == 0 - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Struct: - vt := v.Type() - for i := v.NumField() - 1; i >= 0; i-- { - if vt.Field(i).PkgPath != "" { - continue // private field - } - if !e.isZeroValue(v.Field(i)) { - return false - } - } - return true - } - return false -} - -func (e *Encoder) encodeTime(v time.Time, column int) *ast.StringNode { - value := v.Format(time.RFC3339Nano) - if e.isJSONStyle { - value = strconv.Quote(value) - } - return ast.String(token.New(value, value, e.pos(column))) -} - -func (e *Encoder) encodeDuration(v time.Duration, column int) *ast.StringNode { - value := v.String() - if e.isJSONStyle { - value = strconv.Quote(value) - } - return ast.String(token.New(value, value, e.pos(column))) -} - -func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue reflect.Value, column int) (*ast.AnchorNode, error) { - anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) - anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) - anchorNode.Value = value - if e.anchorCallback != nil { - if err := e.anchorCallback(anchorNode, fieldValue.Interface()); err != nil { - return nil, errors.Wrapf(err, "failed to marshal anchor") - } - if snode, ok := anchorNode.Name.(*ast.StringNode); ok { - anchorName = snode.Value - } - } - if fieldValue.Kind() == reflect.Ptr { - e.anchorPtrToNameMap[fieldValue.Pointer()] = anchorName - } - return anchorNode, nil -} - -func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column int) (ast.Node, error) { - node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) - structType := value.Type() - structFieldMap, err := structFieldMap(structType) - if err != nil { - return nil, errors.Wrapf(err, "failed to get struct field map") - } - hasInlineAnchorField := false - var inlineAnchorValue reflect.Value - for i := 0; i < value.NumField(); i++ { - field := structType.Field(i) - if isIgnoredStructField(field) { - continue - } - fieldValue := value.FieldByName(field.Name) - structField := structFieldMap[field.Name] - if structField.IsOmitEmpty && e.isZeroValue(fieldValue) { - // omit encoding - continue - } - ve := e - if !e.isFlowStyle && structField.IsFlow { - ve = &Encoder{} - *ve = *e - ve.isFlowStyle = true - } - value, err := ve.encodeValue(ctx, fieldValue, column) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode value") - } - if e.isMapNode(value) { - value.AddColumn(e.indent) - } - var key ast.MapKeyNode = e.encodeString(structField.RenderName, column) - switch { - case structField.AnchorName != "": - anchorNode, err := e.encodeAnchor(structField.AnchorName, value, fieldValue, column) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode anchor") - } - value = anchorNode - case structField.IsAutoAlias: - if fieldValue.Kind() != reflect.Ptr { - return nil, xerrors.Errorf( - "%s in struct is not pointer type. but required automatically alias detection", - structField.FieldName, - ) - } - anchorName := e.anchorPtrToNameMap[fieldValue.Pointer()] - if anchorName == "" { - return nil, xerrors.Errorf( - "cannot find anchor name from pointer address for automatically alias detection", - ) - } - aliasName := anchorName - alias := ast.Alias(token.New("*", "*", e.pos(column))) - alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) - value = alias - if structField.IsInline { - // if both used alias and inline, output `<<: *alias` - key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) - } - case structField.AliasName != "": - aliasName := structField.AliasName - alias := ast.Alias(token.New("*", "*", e.pos(column))) - alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) - value = alias - if structField.IsInline { - // if both used alias and inline, output `<<: *alias` - key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) - } - case structField.IsInline: - isAutoAnchor := structField.IsAutoAnchor - if !hasInlineAnchorField { - hasInlineAnchorField = isAutoAnchor - } - if isAutoAnchor { - inlineAnchorValue = fieldValue - } - mapNode, ok := value.(ast.MapNode) - if !ok { - return nil, xerrors.Errorf("inline value is must be map or struct type") - } - mapIter := mapNode.MapRange() - for mapIter.Next() { - key := mapIter.Key() - value := mapIter.Value() - keyName := key.GetToken().Value - if structFieldMap.isIncludedRenderName(keyName) { - // if declared same key name, skip encoding this field - continue - } - key.AddColumn(-e.indent) - value.AddColumn(-e.indent) - node.Values = append(node.Values, ast.MappingValue(nil, key, value)) - } - continue - case structField.IsAutoAnchor: - anchorNode, err := e.encodeAnchor(structField.RenderName, value, fieldValue, column) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode anchor") - } - value = anchorNode - } - node.Values = append(node.Values, ast.MappingValue(nil, key, value)) - } - if hasInlineAnchorField { - node.AddColumn(e.indent) - anchorName := "anchor" - anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) - anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) - anchorNode.Value = node - if e.anchorCallback != nil { - if err := e.anchorCallback(anchorNode, value.Addr().Interface()); err != nil { - return nil, errors.Wrapf(err, "failed to marshal anchor") - } - if snode, ok := anchorNode.Name.(*ast.StringNode); ok { - anchorName = snode.Value - } - } - if inlineAnchorValue.Kind() == reflect.Ptr { - e.anchorPtrToNameMap[inlineAnchorValue.Pointer()] = anchorName - } - return anchorNode, nil - } - return node, nil -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/encode_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/encode_test.go deleted file mode 100644 index 74b1aa5..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/encode_test.go +++ /dev/null @@ -1,1593 +0,0 @@ -package yaml_test - -import ( - "bytes" - "context" - "fmt" - "math" - "reflect" - "strconv" - "testing" - "time" - - "github.com/goccy/go-yaml/parser" - - "github.com/goccy/go-yaml" - "github.com/goccy/go-yaml/ast" -) - -var zero = 0 -var emptyStr = "" - -func TestEncoder(t *testing.T) { - tests := []struct { - source string - value interface{} - options []yaml.EncodeOption - }{ - { - "null\n", - (*struct{})(nil), - nil, - }, - { - "v: hi\n", - map[string]string{"v": "hi"}, - nil, - }, - { - "v: \"true\"\n", - map[string]string{"v": "true"}, - nil, - }, - { - "v: \"false\"\n", - map[string]string{"v": "false"}, - nil, - }, - { - "v: true\n", - map[string]interface{}{"v": true}, - nil, - }, - { - "v: false\n", - map[string]bool{"v": false}, - nil, - }, - { - "v: 10\n", - map[string]int{"v": 10}, - nil, - }, - { - "v: -10\n", - map[string]int{"v": -10}, - nil, - }, - { - "v: 4294967296\n", - map[string]int64{"v": int64(4294967296)}, - nil, - }, - { - "v: 0.1\n", - map[string]interface{}{"v": 0.1}, - nil, - }, - { - "v: 0.99\n", - map[string]float32{"v": 0.99}, - nil, - }, - { - "v: 0.123456789\n", - map[string]float64{"v": 0.123456789}, - nil, - }, - { - "v: -0.1\n", - map[string]float64{"v": -0.1}, - nil, - }, - { - "v: 1.0\n", - map[string]float64{"v": 1.0}, - nil, - }, - { - "v: 1e+06\n", - map[string]float64{"v": 1000000}, - nil, - }, - { - "v: .inf\n", - map[string]interface{}{"v": math.Inf(0)}, - nil, - }, - { - "v: -.inf\n", - map[string]interface{}{"v": math.Inf(-1)}, - nil, - }, - { - "v: .nan\n", - map[string]interface{}{"v": math.NaN()}, - nil, - }, - { - "v: null\n", - map[string]interface{}{"v": nil}, - nil, - }, - { - "v: \"\"\n", - map[string]string{"v": ""}, - nil, - }, - { - "v:\n- A\n- B\n", - map[string][]string{"v": {"A", "B"}}, - nil, - }, - { - "v:\n - A\n - B\n", - map[string][]string{"v": {"A", "B"}}, - []yaml.EncodeOption{ - yaml.IndentSequence(true), - }, - }, - { - "v:\n- A\n- B\n", - map[string][2]string{"v": {"A", "B"}}, - nil, - }, - { - "v:\n - A\n - B\n", - map[string][2]string{"v": {"A", "B"}}, - []yaml.EncodeOption{ - yaml.IndentSequence(true), - }, - }, - { - "a: -\n", - map[string]string{"a": "-"}, - nil, - }, - { - "123\n", - 123, - nil, - }, - { - "hello: world\n", - map[string]string{"hello": "world"}, - nil, - }, - { - "hello: |\n hello\n world\n", - map[string]string{"hello": "hello\nworld\n"}, - nil, - }, - { - "hello: |-\n hello\n world\n", - map[string]string{"hello": "hello\nworld"}, - nil, - }, - { - "hello: |+\n hello\n world\n\n", - map[string]string{"hello": "hello\nworld\n\n"}, - nil, - }, - { - "hello:\n hello: |\n hello\n world\n", - map[string]map[string]string{"hello": {"hello": "hello\nworld\n"}}, - nil, - }, - { - "hello: |\r hello\r world\n", - map[string]string{"hello": "hello\rworld\r"}, - nil, - }, - { - "hello: |\r\n hello\r\n world\n", - map[string]string{"hello": "hello\r\nworld\r\n"}, - nil, - }, - { - "v: |-\n username: hello\n password: hello123\n", - map[string]interface{}{"v": "username: hello\npassword: hello123"}, - []yaml.EncodeOption{ - yaml.UseLiteralStyleIfMultiline(true), - }, - }, - { - "v: |-\n # comment\n username: hello\n password: hello123\n", - map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, - []yaml.EncodeOption{ - yaml.UseLiteralStyleIfMultiline(true), - }, - }, - { - "v: \"# comment\\nusername: hello\\npassword: hello123\"\n", - map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, - []yaml.EncodeOption{ - yaml.UseLiteralStyleIfMultiline(false), - }, - }, - { - "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", - map[string]interface{}{ - "v": []interface{}{ - "A", - 1, - map[string][]int{ - "B": {2, 3}, - }, - }, - }, - nil, - }, - { - "v:\n - A\n - 1\n - B:\n - 2\n - 3\n - 2\n", - map[string]interface{}{ - "v": []interface{}{ - "A", - 1, - map[string][]int{ - "B": {2, 3}, - }, - 2, - }, - }, - []yaml.EncodeOption{ - yaml.IndentSequence(true), - }, - }, - { - "a:\n b: c\n", - map[string]interface{}{ - "a": map[string]string{ - "b": "c", - }, - }, - nil, - }, - { - "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", - map[string]string{ - "t2": "2018-01-09T10:40:47Z", - "t4": "2098-01-09T10:40:47Z", - }, - nil, - }, - { - "a:\n b: c\n d: e\n", - map[string]interface{}{ - "a": map[string]string{ - "b": "c", - "d": "e", - }, - }, - nil, - }, - { - "a: 3s\n", - map[string]string{ - "a": "3s", - }, - nil, - }, - { - "a: \n", - map[string]string{"a": ""}, - nil, - }, - { - "a: \"1:1\"\n", - map[string]string{"a": "1:1"}, - nil, - }, - { - "a: 1.2.3.4\n", - map[string]string{"a": "1.2.3.4"}, - nil, - }, - { - "a: \"b: c\"\n", - map[string]string{"a": "b: c"}, - nil, - }, - { - "a: \"Hello #comment\"\n", - map[string]string{"a": "Hello #comment"}, - nil, - }, - { - "a: 100.5\n", - map[string]interface{}{ - "a": 100.5, - }, - nil, - }, - { - "a: \"\\\\0\"\n", - map[string]string{"a": "\\0"}, - nil, - }, - { - "a: 1\nb: 2\nc: 3\nd: 4\nsub:\n e: 5\n", - map[string]interface{}{ - "a": 1, - "b": 2, - "c": 3, - "d": 4, - "sub": map[string]int{ - "e": 5, - }, - }, - nil, - }, - { - "a: 1\nb: []\n", - struct { - A int - B []string - }{ - 1, ([]string)(nil), - }, - nil, - }, - { - "a: 1\nb: []\n", - struct { - A int - B []string - }{ - 1, []string{}, - }, - nil, - }, - { - "a: {}\n", - struct { - A map[string]interface{} - }{ - map[string]interface{}{}, - }, - nil, - }, - { - "a: b\nc: d\n", - struct { - A string - C string `yaml:"c"` - }{ - "b", "d", - }, - nil, - }, - { - "a: 1\n", - struct { - A int - B int `yaml:"-"` - }{ - 1, 0, - }, - nil, - }, - { - "a: \"\"\n", - struct { - A string - }{ - "", - }, - nil, - }, - { - "a: null\n", - struct { - A *string - }{ - nil, - }, - nil, - }, - { - "a: \"\"\n", - struct { - A *string - }{ - &emptyStr, - }, - nil, - }, - { - "a: null\n", - struct { - A *int - }{ - nil, - }, - nil, - }, - { - "a: 0\n", - struct { - A *int - }{ - &zero, - }, - nil, - }, - - // Conditional flag - { - "a: 1\n", - struct { - A int `yaml:"a,omitempty"` - B int `yaml:"b,omitempty"` - }{1, 0}, - nil, - }, - { - "{}\n", - struct { - A int `yaml:"a,omitempty"` - B int `yaml:"b,omitempty"` - }{0, 0}, - nil, - }, - - { - "a:\n \"y\": \"\"\n", - struct { - A *struct { - X string `yaml:"x,omitempty"` - Y string - } - }{&struct { - X string `yaml:"x,omitempty"` - Y string - }{}}, - nil, - }, - - { - "a: {}\n", - struct { - A *struct { - X string `yaml:"x,omitempty"` - Y string `yaml:"y,omitempty"` - } - }{&struct { - X string `yaml:"x,omitempty"` - Y string `yaml:"y,omitempty"` - }{}}, - nil, - }, - - { - "a: {x: 1}\n", - struct { - A *struct{ X, y int } `yaml:"a,omitempty,flow"` - }{&struct{ X, y int }{1, 2}}, - nil, - }, - - { - "{}\n", - struct { - A *struct{ X, y int } `yaml:"a,omitempty,flow"` - }{nil}, - nil, - }, - - { - "a: {x: 0}\n", - struct { - A *struct{ X, y int } `yaml:"a,omitempty,flow"` - }{&struct{ X, y int }{}}, - nil, - }, - - { - "a: {x: 1}\n", - struct { - A struct{ X, y int } `yaml:"a,omitempty,flow"` - }{struct{ X, y int }{1, 2}}, - nil, - }, - { - "{}\n", - struct { - A struct{ X, y int } `yaml:"a,omitempty,flow"` - }{struct{ X, y int }{0, 1}}, - nil, - }, - { - "a: 1.0\n", - struct { - A float64 `yaml:"a,omitempty"` - B float64 `yaml:"b,omitempty"` - }{1, 0}, - nil, - }, - { - "a: 1\n", - struct { - A int - B []string `yaml:"b,omitempty"` - }{ - 1, []string{}, - }, - nil, - }, - - // Flow flag - { - "a: [1, 2]\n", - struct { - A []int `yaml:"a,flow"` - }{[]int{1, 2}}, - nil, - }, - { - "a: {b: c, d: e}\n", - &struct { - A map[string]string `yaml:"a,flow"` - }{map[string]string{"b": "c", "d": "e"}}, - nil, - }, - { - "a: {b: c, d: e}\n", - struct { - A struct { - B, D string - } `yaml:"a,flow"` - }{struct{ B, D string }{"c", "e"}}, - nil, - }, - // Quoting in flow mode - { - `a: [b, "c,d", e]` + "\n", - struct { - A []string `yaml:"a,flow"` - }{[]string{"b", "c,d", "e"}}, - []yaml.EncodeOption{ - yaml.UseSingleQuote(false), - }, - }, - { - `a: [b, "c]", d]` + "\n", - struct { - A []string `yaml:"a,flow"` - }{[]string{"b", "c]", "d"}}, - []yaml.EncodeOption{ - yaml.UseSingleQuote(false), - }, - }, - { - `a: [b, "c}", d]` + "\n", - struct { - A []string `yaml:"a,flow"` - }{[]string{"b", "c}", "d"}}, - []yaml.EncodeOption{ - yaml.UseSingleQuote(false), - }, - }, - { - `a: [b, "c\"", d]` + "\n", - struct { - A []string `yaml:"a,flow"` - }{[]string{"b", `c"`, "d"}}, - []yaml.EncodeOption{ - yaml.UseSingleQuote(false), - }, - }, - { - `a: [b, "c'", d]` + "\n", - struct { - A []string `yaml:"a,flow"` - }{[]string{"b", "c'", "d"}}, - []yaml.EncodeOption{ - yaml.UseSingleQuote(false), - }, - }, - // No quoting in non-flow mode - { - "a:\n- b\n- c,d\n- e\n", - struct { - A []string `yaml:"a"` - }{[]string{"b", "c,d", "e"}}, - nil, - }, - { - `a: [b, "c]", d]` + "\n", - struct { - A []string `yaml:"a,flow"` - }{[]string{"b", "c]", "d"}}, - nil, - }, - { - `a: [b, "c}", d]` + "\n", - struct { - A []string `yaml:"a,flow"` - }{[]string{"b", "c}", "d"}}, - nil, - }, - { - `a: [b, "c\"", d]` + "\n", - struct { - A []string `yaml:"a,flow"` - }{[]string{"b", `c"`, "d"}}, - nil, - }, - { - `a: [b, "c'", d]` + "\n", - struct { - A []string `yaml:"a,flow"` - }{[]string{"b", "c'", "d"}}, - nil, - }, - - // Multi bytes - { - "v: あいうえお\nv2: かきくけこ\n", - map[string]string{"v": "あいうえお", "v2": "かきくけこ"}, - nil, - }, - - // time value - { - "v: 0001-01-01T00:00:00Z\n", - map[string]time.Time{"v": time.Time{}}, - nil, - }, - { - "v: 0001-01-01T00:00:00Z\n", - map[string]*time.Time{"v": &time.Time{}}, - nil, - }, - { - "v: null\n", - map[string]*time.Time{"v": nil}, - nil, - }, - { - "v: 30s\n", - map[string]time.Duration{"v": 30 * time.Second}, - nil, - }, - // Quote style - { - `v: '\'a\'b'` + "\n", - map[string]string{"v": `'a'b`}, - []yaml.EncodeOption{ - yaml.UseSingleQuote(true), - }, - }, - { - `v: "'a'b"` + "\n", - map[string]string{"v": `'a'b`}, - []yaml.EncodeOption{ - yaml.UseSingleQuote(false), - }, - }, - } - for _, test := range tests { - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf, test.options...) - if err := enc.Encode(test.value); err != nil { - t.Fatalf("%+v", err) - } - if test.source != buf.String() { - t.Fatalf("expect = [%s], actual = [%s]", test.source, buf.String()) - } - } -} - -func TestEncodeStructIncludeMap(t *testing.T) { - type U struct { - M map[string]string - } - type T struct { - A U - } - bytes, err := yaml.Marshal(T{ - A: U{ - M: map[string]string{"x": "y"}, - }, - }) - if err != nil { - t.Fatalf("%+v", err) - } - expect := "a:\n m:\n x: \"y\"\n" - actual := string(bytes) - if actual != expect { - t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual) - } -} - -func TestEncodeDefinedTypeKeyMap(t *testing.T) { - type K string - type U struct { - M map[K]string - } - bytes, err := yaml.Marshal(U{ - M: map[K]string{K("x"): "y"}, - }) - if err != nil { - t.Fatalf("%+v", err) - } - expect := "m:\n x: \"y\"\n" - actual := string(bytes) - if actual != expect { - t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual) - } -} - -func TestEncodeWithAnchorAndAlias(t *testing.T) { - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf) - type T struct { - A int - B string - } - var v struct { - A *T `yaml:"a,anchor=c"` - B *T `yaml:"b,alias=c"` - } - v.A = &T{A: 1, B: "hello"} - v.B = v.A - if err := enc.Encode(v); err != nil { - t.Fatalf("%+v", err) - } - expect := "a: &c\n a: 1\n b: hello\nb: *c\n" - if expect != buf.String() { - t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) - } -} - -func TestEncodeWithAutoAlias(t *testing.T) { - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf) - type T struct { - I int - S string - } - var v struct { - A *T `yaml:"a,anchor=a"` - B *T `yaml:"b,anchor=b"` - C *T `yaml:"c,alias"` - D *T `yaml:"d,alias"` - } - v.A = &T{I: 1, S: "hello"} - v.B = &T{I: 2, S: "world"} - v.C = v.A - v.D = v.B - if err := enc.Encode(v); err != nil { - t.Fatalf("%+v", err) - } - expect := `a: &a - i: 1 - s: hello -b: &b - i: 2 - s: world -c: *a -d: *b -` - if expect != buf.String() { - t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) - } -} - -func TestEncodeWithImplicitAnchorAndAlias(t *testing.T) { - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf) - type T struct { - I int - S string - } - var v struct { - A *T `yaml:"a,anchor"` - B *T `yaml:"b,anchor"` - C *T `yaml:"c,alias"` - D *T `yaml:"d,alias"` - } - v.A = &T{I: 1, S: "hello"} - v.B = &T{I: 2, S: "world"} - v.C = v.A - v.D = v.B - if err := enc.Encode(v); err != nil { - t.Fatalf("%+v", err) - } - expect := `a: &a - i: 1 - s: hello -b: &b - i: 2 - s: world -c: *a -d: *b -` - if expect != buf.String() { - t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) - } -} - -func TestEncodeWithMerge(t *testing.T) { - type Person struct { - *Person `yaml:",omitempty,inline,alias"` - Name string `yaml:",omitempty"` - Age int `yaml:",omitempty"` - } - defaultPerson := &Person{ - Name: "John Smith", - Age: 20, - } - people := []*Person{ - { - Person: defaultPerson, - Name: "Ken", - Age: 10, - }, - { - Person: defaultPerson, - }, - } - var doc struct { - Default *Person `yaml:"default,anchor"` - People []*Person `yaml:"people"` - } - doc.Default = defaultPerson - doc.People = people - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf) - if err := enc.Encode(doc); err != nil { - t.Fatalf("%+v", err) - } - expect := `default: &default - name: John Smith - age: 20 -people: -- <<: *default - name: Ken - age: 10 -- <<: *default -` - if expect != buf.String() { - t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) - } -} - -func TestEncodeWithNestedYAML(t *testing.T) { - // Represents objects containing stringified YAML, and special chars - tests := []struct { - value interface{} - // If true, expects a different result between when using forced literal style or not - expectDifferent bool - }{ - { - value: map[string]interface{}{"v": "# comment\nname: hello\npassword: hello123\nspecial: \":ghost:\"\ntext: |\n nested multiline!"}, - expectDifferent: true, - }, - { - value: map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, - expectDifferent: true, - }, - { - value: map[string]interface{}{"v": "# comment\n"}, - expectDifferent: true, - }, - { - value: map[string]interface{}{"v": "\n"}, - }, - } - - for _, test := range tests { - yamlBytesForced, err := yaml.MarshalWithOptions(test.value, yaml.UseLiteralStyleIfMultiline(true)) - if err != nil { - t.Fatalf("%+v", err) - } - - // Convert it back for proper equality testing - var unmarshaled interface{} - - if err := yaml.Unmarshal(yamlBytesForced, &unmarshaled); err != nil { - t.Fatalf("%+v", err) - } - - if !reflect.DeepEqual(test.value, unmarshaled) { - t.Fatalf("expected %v(%T). but actual %v(%T)", test.value, test.value, unmarshaled, unmarshaled) - } - - if test.expectDifferent { - yamlBytesNotForced, err := yaml.MarshalWithOptions(test.value) - if err != nil { - t.Fatalf("%+v", err) - } - - if string(yamlBytesForced) == string(yamlBytesNotForced) { - t.Fatalf("expected different strings when force literal style is not enabled. forced: %s, not forced: %s", string(yamlBytesForced), string(yamlBytesNotForced)) - } - } - } -} - -func TestEncoder_Inline(t *testing.T) { - type base struct { - A int - B string - } - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf) - if err := enc.Encode(struct { - *base `yaml:",inline"` - C bool - }{ - base: &base{ - A: 1, - B: "hello", - }, - C: true, - }); err != nil { - t.Fatalf("%+v", err) - } - expect := ` -a: 1 -b: hello -c: true -` - actual := "\n" + buf.String() - if expect != actual { - t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual) - } -} - -func TestEncoder_InlineAndConflictKey(t *testing.T) { - type base struct { - A int - B string - } - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf) - if err := enc.Encode(struct { - *base `yaml:",inline"` - A int // conflict - C bool - }{ - base: &base{ - A: 1, - B: "hello", - }, - A: 0, // default value - C: true, - }); err != nil { - t.Fatalf("%+v", err) - } - expect := ` -b: hello -a: 0 -c: true -` - actual := "\n" + buf.String() - if expect != actual { - t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual) - } -} - -func TestEncoder_Flow(t *testing.T) { - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf, yaml.Flow(true)) - var v struct { - A int - B string - C struct { - D int - E string - } - F []int - } - v.A = 1 - v.B = "hello" - v.C.D = 3 - v.C.E = "world" - v.F = []int{1, 2} - if err := enc.Encode(v); err != nil { - t.Fatalf("%+v", err) - } - expect := ` -{a: 1, b: hello, c: {d: 3, e: world}, f: [1, 2]} -` - actual := "\n" + buf.String() - if expect != actual { - t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual) - } -} - -func TestEncoder_FlowRecursive(t *testing.T) { - var v struct { - M map[string][]int `yaml:",flow"` - } - v.M = map[string][]int{ - "test": []int{1, 2, 3}, - } - var buf bytes.Buffer - if err := yaml.NewEncoder(&buf).Encode(v); err != nil { - t.Fatalf("%+v", err) - } - expect := ` -m: {test: [1, 2, 3]} -` - actual := "\n" + buf.String() - if expect != actual { - t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual) - } -} - -func TestEncoder_JSON(t *testing.T) { - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf, yaml.JSON()) - type st struct { - I int8 - S string - F float32 - } - if err := enc.Encode(struct { - I int - U uint - S string - F float64 - Struct *st - Slice []int - Map map[string]interface{} - Time time.Time - Duration time.Duration - }{ - I: -10, - U: 10, - S: "hello", - F: 3.14, - Struct: &st{ - I: 2, - S: "world", - F: 1.23, - }, - Slice: []int{1, 2, 3, 4, 5}, - Map: map[string]interface{}{ - "a": 1, - "b": 1.23, - "c": "json", - }, - Time: time.Time{}, - Duration: 5 * time.Minute, - }); err != nil { - t.Fatalf("%+v", err) - } - expect := ` -{"i": -10, "u": 10, "s": "hello", "f": 3.14, "struct": {"i": 2, "s": "world", "f": 1.23}, "slice": [1, 2, 3, 4, 5], "map": {"a": 1, "b": 1.23, "c": "json"}, "time": "0001-01-01T00:00:00Z", "duration": "5m0s"} -` - actual := "\n" + buf.String() - if expect != actual { - t.Fatalf("JSON style marshal error: expect=[%s] actual=[%s]", expect, actual) - } -} - -func TestEncoder_MarshalAnchor(t *testing.T) { - type Host struct { - Hostname string - Username string - Password string - } - type HostDecl struct { - Host *Host `yaml:",anchor"` - } - type Queue struct { - Name string `yaml:","` - *Host `yaml:",alias"` - } - var doc struct { - Hosts []*HostDecl `yaml:"hosts"` - Queues []*Queue `yaml:"queues"` - } - host1 := &Host{ - Hostname: "host1.example.com", - Username: "userA", - Password: "pass1", - } - host2 := &Host{ - Hostname: "host2.example.com", - Username: "userB", - Password: "pass2", - } - doc.Hosts = []*HostDecl{ - { - Host: host1, - }, - { - Host: host2, - }, - } - doc.Queues = []*Queue{ - { - Name: "queue", - Host: host1, - }, { - Name: "queue2", - Host: host2, - }, - } - hostIdx := 1 - opt := yaml.MarshalAnchor(func(anchor *ast.AnchorNode, value interface{}) error { - if _, ok := value.(*Host); ok { - nameNode := anchor.Name.(*ast.StringNode) - nameNode.Value = fmt.Sprintf("host%d", hostIdx) - hostIdx++ - } - return nil - }) - - var buf bytes.Buffer - if err := yaml.NewEncoder(&buf, opt).Encode(doc); err != nil { - t.Fatalf("%+v", err) - } - expect := ` -hosts: -- host: &host1 - hostname: host1.example.com - username: userA - password: pass1 -- host: &host2 - hostname: host2.example.com - username: userB - password: pass2 -queues: -- name: queue - host: *host1 -- name: queue2 - host: *host2 -` - if "\n"+buf.String() != expect { - t.Fatalf("unexpected output. %s", buf.String()) - } -} - -type useJSONMarshalerTest struct{} - -func (t useJSONMarshalerTest) MarshalJSON() ([]byte, error) { - return []byte(`{"a":[1, 2, 3]}`), nil -} - -func TestEncoder_UseJSONMarshaler(t *testing.T) { - got, err := yaml.MarshalWithOptions(useJSONMarshalerTest{}, yaml.UseJSONMarshaler()) - if err != nil { - t.Fatal(err) - } - expected := ` -a: -- 1 -- 2 -- 3 -` - if expected != "\n"+string(got) { - t.Fatalf("failed to use json marshaler. expected [%q] but got [%q]", expected, string(got)) - } -} - -func TestEncoder_CustomMarshaler(t *testing.T) { - t.Run("override struct type", func(t *testing.T) { - type T struct { - Foo string `yaml:"foo"` - } - b, err := yaml.MarshalWithOptions(&T{Foo: "bar"}, yaml.CustomMarshaler[T](func(v T) ([]byte, error) { - return []byte(`"override"`), nil - })) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(b, []byte("\"override\"\n")) { - t.Fatalf("failed to switch to custom marshaler. got: %q", b) - } - }) - t.Run("override bytes type", func(t *testing.T) { - type T struct { - Foo []byte `yaml:"foo"` - } - b, err := yaml.MarshalWithOptions(&T{Foo: []byte("bar")}, yaml.CustomMarshaler[[]byte](func(v []byte) ([]byte, error) { - if !bytes.Equal(v, []byte("bar")) { - t.Fatalf("failed to get src buffer: %q", v) - } - return []byte(`override`), nil - })) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(b, []byte("foo: override\n")) { - t.Fatalf("failed to switch to custom marshaler. got: %q", b) - } - }) -} - -func TestEncoder_MultipleDocuments(t *testing.T) { - var buf bytes.Buffer - enc := yaml.NewEncoder(&buf) - if err := enc.Encode(1); err != nil { - t.Fatalf("failed to encode: %s", err) - } - if err := enc.Encode(2); err != nil { - t.Fatalf("failed to encode: %s", err) - } - if actual, expect := buf.String(), "1\n---\n2\n"; actual != expect { - t.Errorf("expect:\n%s\nactual\n%s\n", expect, actual) - } -} - -func Example_Marshal_Node() { - type T struct { - Text ast.Node `yaml:"text"` - } - stringNode, err := yaml.ValueToNode("node example") - if err != nil { - panic(err) - } - bytes, err := yaml.Marshal(T{Text: stringNode}) - if err != nil { - panic(err) - } - fmt.Println(string(bytes)) - // OUTPUT: - // text: node example -} - -func Example_Marshal_ExplicitAnchorAlias() { - type T struct { - A int - B string - } - var v struct { - C *T `yaml:"c,anchor=x"` - D *T `yaml:"d,alias=x"` - } - v.C = &T{A: 1, B: "hello"} - v.D = v.C - bytes, err := yaml.Marshal(v) - if err != nil { - panic(err) - } - fmt.Println(string(bytes)) - // OUTPUT: - // c: &x - // a: 1 - // b: hello - // d: *x -} - -func Example_Marshal_ImplicitAnchorAlias() { - type T struct { - I int - S string - } - var v struct { - A *T `yaml:"a,anchor"` - B *T `yaml:"b,anchor"` - C *T `yaml:"c,alias"` - D *T `yaml:"d,alias"` - } - v.A = &T{I: 1, S: "hello"} - v.B = &T{I: 2, S: "world"} - v.C = v.A // C has same pointer address to A - v.D = v.B // D has same pointer address to B - bytes, err := yaml.Marshal(v) - if err != nil { - panic(err) - } - fmt.Println(string(bytes)) - // OUTPUT: - // a: &a - // i: 1 - // s: hello - // b: &b - // i: 2 - // s: world - // c: *a - // d: *b -} - -type tMarshal []string - -func (t *tMarshal) MarshalYAML() ([]byte, error) { - var buf bytes.Buffer - buf.WriteString("tags:\n") - for i, v := range *t { - if i == 0 { - fmt.Fprintf(&buf, "- %s\n", v) - } else { - fmt.Fprintf(&buf, " %s\n", v) - } - } - return buf.Bytes(), nil -} -func Test_Marshaler(t *testing.T) { - const expected = `- hello-world -` - - // sanity check - var l []string - if err := yaml.Unmarshal([]byte(expected), &l); err != nil { - t.Fatalf("failed to parse string: %s", err) - } - - buf, err := yaml.Marshal(tMarshal{"hello-world"}) - if err != nil { - t.Fatalf("failed to marshal: %s", err) - } - - if string(buf) != expected { - t.Fatalf("expected '%s', got '%s'", expected, buf) - } - - t.Logf("%s", buf) -} - -type marshalContext struct{} - -func (c *marshalContext) MarshalYAML(ctx context.Context) ([]byte, error) { - v, ok := ctx.Value("k").(int) - if !ok { - return nil, fmt.Errorf("cannot get valid context") - } - if v != 1 { - return nil, fmt.Errorf("cannot get valid context") - } - return []byte("1"), nil -} - -func Test_MarshalerContext(t *testing.T) { - ctx := context.WithValue(context.Background(), "k", 1) - bytes, err := yaml.MarshalContext(ctx, &marshalContext{}) - if err != nil { - t.Fatalf("%+v", err) - } - if string(bytes) != "1\n" { - t.Fatalf("failed marshal: %q", string(bytes)) - } -} - -type SlowMarshaler struct { - A string - B int -} -type FastMarshaler struct { - A string - B int -} -type TextMarshaler int64 -type TextMarshalerContainer struct { - Field TextMarshaler `yaml:"field"` -} - -func (v SlowMarshaler) MarshalYAML() ([]byte, error) { - var buf bytes.Buffer - buf.WriteString("tags:\n") - buf.WriteString("- slow-marshaler\n") - buf.WriteString("a: " + v.A + "\n") - buf.WriteString("b: " + strconv.FormatInt(int64(v.B), 10) + "\n") - return buf.Bytes(), nil -} - -func (v FastMarshaler) MarshalYAML() (interface{}, error) { - return yaml.MapSlice{ - {"tags", []string{"fast-marshaler"}}, - {"a", v.A}, - {"b", v.B}, - }, nil -} - -func (t TextMarshaler) MarshalText() ([]byte, error) { - return []byte(strconv.FormatInt(int64(t), 8)), nil -} - -func Example_MarshalYAML() { - var slow SlowMarshaler - slow.A = "Hello slow poke" - slow.B = 100 - buf, err := yaml.Marshal(slow) - if err != nil { - panic(err.Error()) - } - - fmt.Println(string(buf)) - - var fast FastMarshaler - fast.A = "Hello speed demon" - fast.B = 100 - buf, err = yaml.Marshal(fast) - if err != nil { - panic(err.Error()) - } - - fmt.Println(string(buf)) - - text := TextMarshalerContainer{ - Field: 11, - } - buf, err = yaml.Marshal(text) - if err != nil { - panic(err.Error()) - } - - fmt.Println(string(buf)) - // OUTPUT: - // tags: - // - slow-marshaler - // a: Hello slow poke - // b: 100 - // - // tags: - // - fast-marshaler - // a: Hello speed demon - // b: 100 - // - // field: 13 -} - -func TestIssue356(t *testing.T) { - tests := map[string]struct { - in string - }{ - "content on first line": { - in: `args: - - | - - key: - nest1: something - nest2: - nest2a: b -`, - }, - "empty first line": { - in: `args: - - | - - key: - nest1: something - nest2: - nest2a: b -`, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - f, err := parser.ParseBytes([]byte(test.in), 0) - if err != nil { - t.Fatalf("parse: %v", err) - } - got := f.String() - if test.in != got { - t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", test.in, got) - } - }) - } -} - -func TestMarshalIndentWithMultipleText(t *testing.T) { - t.Run("depth1", func(t *testing.T) { - b, err := yaml.MarshalWithOptions(map[string]interface{}{ - "key": []string{`line1 -line2 -line3`}, - }, yaml.Indent(2)) - if err != nil { - t.Fatal(err) - } - got := string(b) - expected := `key: -- |- - line1 - line2 - line3 -` - if expected != got { - t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", expected, got) - } - }) - t.Run("depth2", func(t *testing.T) { - b, err := yaml.MarshalWithOptions(map[string]interface{}{ - "key": map[string]interface{}{ - "key2": []string{`line1 -line2 -line3`}, - }, - }, yaml.Indent(2)) - if err != nil { - t.Fatal(err) - } - got := string(b) - expected := `key: - key2: - - |- - line1 - line2 - line3 -` - if expected != got { - t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", expected, got) - } - }) -} - -type bytesMarshaler struct{} - -func (b *bytesMarshaler) MarshalYAML() ([]byte, error) { - return yaml.Marshal(map[string]interface{}{"d": "foo"}) -} - -func TestBytesMarshaler(t *testing.T) { - b, err := yaml.Marshal(map[string]interface{}{ - "a": map[string]interface{}{ - "b": map[string]interface{}{ - "c": &bytesMarshaler{}, - }, - }, - }) - if err != nil { - t.Fatal(err) - } - expected := ` -a: - b: - c: - d: foo -` - got := "\n" + string(b) - if expected != got { - t.Fatalf("failed to encode. expected %s but got %s", expected, got) - } -} - -type customMapSliceOneItemMarshaler struct{} - -func (m *customMapSliceOneItemMarshaler) MarshalYAML() ([]byte, error) { - var v yaml.MapSlice - v = append(v, yaml.MapItem{"a", "b"}) - return yaml.Marshal(v) -} - -type customMapSliceTwoItemMarshaler struct{} - -func (m *customMapSliceTwoItemMarshaler) MarshalYAML() ([]byte, error) { - var v yaml.MapSlice - v = append(v, yaml.MapItem{"a", "b"}) - v = append(v, yaml.MapItem{"b", "c"}) - return yaml.Marshal(v) -} - -func TestCustomMapSliceMarshaler(t *testing.T) { - type T struct { - A *customMapSliceOneItemMarshaler `yaml:"a"` - B *customMapSliceTwoItemMarshaler `yaml:"b"` - } - b, err := yaml.Marshal(&T{ - A: &customMapSliceOneItemMarshaler{}, - B: &customMapSliceTwoItemMarshaler{}, - }) - if err != nil { - t.Fatal(err) - } - expected := ` -a: - a: b -b: - a: b - b: c -` - got := "\n" + string(b) - if expected != got { - t.Fatalf("failed to encode. expected %s but got %s", expected, got) - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/error.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/error.go deleted file mode 100644 index 163dcc5..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/error.go +++ /dev/null @@ -1,62 +0,0 @@ -package yaml - -import ( - "github.com/goccy/go-yaml/ast" - "golang.org/x/xerrors" -) - -var ( - ErrInvalidQuery = xerrors.New("invalid query") - ErrInvalidPath = xerrors.New("invalid path instance") - ErrInvalidPathString = xerrors.New("invalid path string") - ErrNotFoundNode = xerrors.New("node not found") - ErrUnknownCommentPositionType = xerrors.New("unknown comment position type") - ErrInvalidCommentMapValue = xerrors.New("invalid comment map value. it must be not nil value") -) - -func ErrUnsupportedHeadPositionType(node ast.Node) error { - return xerrors.Errorf("unsupported comment head position for %s", node.Type()) -} - -func ErrUnsupportedLinePositionType(node ast.Node) error { - return xerrors.Errorf("unsupported comment line position for %s", node.Type()) -} - -func ErrUnsupportedFootPositionType(node ast.Node) error { - return xerrors.Errorf("unsupported comment foot position for %s", node.Type()) -} - -// IsInvalidQueryError whether err is ErrInvalidQuery or not. -func IsInvalidQueryError(err error) bool { - return xerrors.Is(err, ErrInvalidQuery) -} - -// IsInvalidPathError whether err is ErrInvalidPath or not. -func IsInvalidPathError(err error) bool { - return xerrors.Is(err, ErrInvalidPath) -} - -// IsInvalidPathStringError whether err is ErrInvalidPathString or not. -func IsInvalidPathStringError(err error) bool { - return xerrors.Is(err, ErrInvalidPathString) -} - -// IsNotFoundNodeError whether err is ErrNotFoundNode or not. -func IsNotFoundNodeError(err error) bool { - return xerrors.Is(err, ErrNotFoundNode) -} - -// IsInvalidTokenTypeError whether err is ast.ErrInvalidTokenType or not. -func IsInvalidTokenTypeError(err error) bool { - return xerrors.Is(err, ast.ErrInvalidTokenType) -} - -// IsInvalidAnchorNameError whether err is ast.ErrInvalidAnchorName or not. -func IsInvalidAnchorNameError(err error) bool { - return xerrors.Is(err, ast.ErrInvalidAnchorName) -} - -// IsInvalidAliasNameError whether err is ast.ErrInvalidAliasName or not. -func IsInvalidAliasNameError(err error) bool { - return xerrors.Is(err, ast.ErrInvalidAliasName) -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/go.mod b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/go.mod deleted file mode 100644 index f6e74c3..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/go.mod +++ /dev/null @@ -1,20 +0,0 @@ -module github.com/goccy/go-yaml - -go 1.18 - -require ( - github.com/fatih/color v1.10.0 - github.com/go-playground/validator/v10 v10.4.1 - github.com/google/go-cmp v0.5.9 - github.com/mattn/go-colorable v0.1.8 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 -) - -require ( - github.com/go-playground/locales v0.13.0 // indirect - github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/leodido/go-urn v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/sys v0.6.0 // indirect -) diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/go.sum b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/go.sum deleted file mode 100644 index 7249df6..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/go.sum +++ /dev/null @@ -1,44 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/internal/errors/error.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/internal/errors/error.go deleted file mode 100644 index 7f1ea9a..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/internal/errors/error.go +++ /dev/null @@ -1,260 +0,0 @@ -package errors - -import ( - "bytes" - "fmt" - "reflect" - - "github.com/goccy/go-yaml/printer" - "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" -) - -const ( - defaultColorize = false - defaultIncludeSource = true -) - -var ( - ErrDecodeRequiredPointerType = xerrors.New("required pointer type value") -) - -// Wrapf wrap error for stack trace -func Wrapf(err error, msg string, args ...interface{}) error { - return &wrapError{ - baseError: &baseError{}, - err: xerrors.Errorf(msg, args...), - nextErr: err, - frame: xerrors.Caller(1), - } -} - -// ErrSyntax create syntax error instance with message and token -func ErrSyntax(msg string, tk *token.Token) *syntaxError { - return &syntaxError{ - baseError: &baseError{}, - msg: msg, - token: tk, - frame: xerrors.Caller(1), - } -} - -type baseError struct { - state fmt.State - verb rune -} - -func (e *baseError) Error() string { - return "" -} - -func (e *baseError) chainStateAndVerb(err error) { - wrapErr, ok := err.(*wrapError) - if ok { - wrapErr.state = e.state - wrapErr.verb = e.verb - } - syntaxErr, ok := err.(*syntaxError) - if ok { - syntaxErr.state = e.state - syntaxErr.verb = e.verb - } -} - -type wrapError struct { - *baseError - err error - nextErr error - frame xerrors.Frame -} - -type FormatErrorPrinter struct { - xerrors.Printer - Colored bool - InclSource bool -} - -func (e *wrapError) As(target interface{}) bool { - err := e.nextErr - for { - if wrapErr, ok := err.(*wrapError); ok { - err = wrapErr.nextErr - continue - } - break - } - return xerrors.As(err, target) -} - -func (e *wrapError) Unwrap() error { - return e.nextErr -} - -func (e *wrapError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { - return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) -} - -func (e *wrapError) FormatError(p xerrors.Printer) error { - if _, ok := p.(*FormatErrorPrinter); !ok { - p = &FormatErrorPrinter{ - Printer: p, - Colored: defaultColorize, - InclSource: defaultIncludeSource, - } - } - if e.verb == 'v' && e.state.Flag('+') { - // print stack trace for debugging - p.Print(e.err, "\n") - e.frame.Format(p) - e.chainStateAndVerb(e.nextErr) - return e.nextErr - } - err := e.nextErr - for { - if wrapErr, ok := err.(*wrapError); ok { - err = wrapErr.nextErr - continue - } - break - } - e.chainStateAndVerb(err) - if fmtErr, ok := err.(xerrors.Formatter); ok { - fmtErr.FormatError(p) - } else { - p.Print(err) - } - return nil -} - -type wrapState struct { - org fmt.State -} - -func (s *wrapState) Write(b []byte) (n int, err error) { - return s.org.Write(b) -} - -func (s *wrapState) Width() (wid int, ok bool) { - return s.org.Width() -} - -func (s *wrapState) Precision() (prec int, ok bool) { - return s.org.Precision() -} - -func (s *wrapState) Flag(c int) bool { - // set true to 'printDetail' forced because when p.Detail() is false, xerrors.Printer no output any text - if c == '#' { - // ignore '#' keyword because xerrors.FormatError doesn't set true to printDetail. - // ( see https://github.com/golang/xerrors/blob/master/adaptor.go#L39-L43 ) - return false - } - return true -} - -func (e *wrapError) Format(state fmt.State, verb rune) { - e.state = state - e.verb = verb - xerrors.FormatError(e, &wrapState{org: state}, verb) -} - -func (e *wrapError) Error() string { - var buf bytes.Buffer - e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource) - return buf.String() -} - -type syntaxError struct { - *baseError - msg string - token *token.Token - frame xerrors.Frame -} - -func (e *syntaxError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { - return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) -} - -func (e *syntaxError) FormatError(p xerrors.Printer) error { - var pp printer.Printer - - var colored, inclSource bool - if fep, ok := p.(*FormatErrorPrinter); ok { - colored = fep.Colored - inclSource = fep.InclSource - } - - pos := fmt.Sprintf("[%d:%d] ", e.token.Position.Line, e.token.Position.Column) - msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.msg), colored) - if inclSource { - msg += "\n" + pp.PrintErrorToken(e.token, colored) - } - p.Print(msg) - - if e.verb == 'v' && e.state.Flag('+') { - // %+v - // print stack trace for debugging - e.frame.Format(p) - } - return nil -} - -type PrettyPrinter interface { - PrettyPrint(xerrors.Printer, bool, bool) error -} -type Sink struct{ *bytes.Buffer } - -func (es *Sink) Print(args ...interface{}) { - fmt.Fprint(es.Buffer, args...) -} - -func (es *Sink) Printf(f string, args ...interface{}) { - fmt.Fprintf(es.Buffer, f, args...) -} - -func (es *Sink) Detail() bool { - return false -} - -func (e *syntaxError) Error() string { - var buf bytes.Buffer - e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource) - return buf.String() -} - -type TypeError struct { - DstType reflect.Type - SrcType reflect.Type - StructFieldName *string - Token *token.Token -} - -func (e *TypeError) Error() string { - if e.StructFieldName != nil { - return fmt.Sprintf("cannot unmarshal %s into Go struct field %s of type %s", e.SrcType, *e.StructFieldName, e.DstType) - } - return fmt.Sprintf("cannot unmarshal %s into Go value of type %s", e.SrcType, e.DstType) -} - -func (e *TypeError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { - return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) -} - -func (e *TypeError) FormatError(p xerrors.Printer) error { - var pp printer.Printer - - var colored, inclSource bool - if fep, ok := p.(*FormatErrorPrinter); ok { - colored = fep.Colored - inclSource = fep.InclSource - } - - pos := fmt.Sprintf("[%d:%d] ", e.Token.Position.Line, e.Token.Position.Column) - msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.Error()), colored) - if inclSource { - msg += "\n" + pp.PrintErrorToken(e.Token, colored) - } - p.Print(msg) - - return nil -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/lexer/lexer.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/lexer/lexer.go deleted file mode 100644 index 3207f4f..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/lexer/lexer.go +++ /dev/null @@ -1,23 +0,0 @@ -package lexer - -import ( - "io" - - "github.com/goccy/go-yaml/scanner" - "github.com/goccy/go-yaml/token" -) - -// Tokenize split to token instances from string -func Tokenize(src string) token.Tokens { - var s scanner.Scanner - s.Init(src) - var tokens token.Tokens - for { - subTokens, err := s.Scan() - if err == io.EOF { - break - } - tokens.Add(subTokens...) - } - return tokens -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/lexer/lexer_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/lexer/lexer_test.go deleted file mode 100644 index 0d8f648..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/lexer/lexer_test.go +++ /dev/null @@ -1,621 +0,0 @@ -package lexer_test - -import ( - "sort" - "strings" - "testing" - - "github.com/goccy/go-yaml/lexer" - "github.com/goccy/go-yaml/token" -) - -func TestTokenize(t *testing.T) { - sources := []string{ - "null\n", - "{}\n", - "v: hi\n", - "v: \"true\"\n", - "v: \"false\"\n", - "v: true\n", - "v: false\n", - "v: 10\n", - "v: -10\n", - "v: 42\n", - "v: 4294967296\n", - "v: \"10\"\n", - "v: 0.1\n", - "v: 0.99\n", - "v: -0.1\n", - "v: .inf\n", - "v: -.inf\n", - "v: .nan\n", - "v: null\n", - "v: \"\"\n", - "v:\n- A\n- B\n", - "v:\n- A\n- |-\n B\n C\n", - "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", - "a:\n b: c\n", - "a: '-'\n", - "123\n", - "hello: world\n", - "a: null\n", - "a: {x: 1}\n", - "a: [1, 2]\n", - "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", - "a: {b: c, d: e}\n", - "a: 3s\n", - "a: \n", - "a: \"1:1\"\n", - "a: \"\\0\"\n", - "a: !!binary gIGC\n", - "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", - "b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n", - "a: 1.2.3.4\n", - "a: \"2015-02-24T18:19:39Z\"\n", - "a: 'b: c'\n", - "a: 'Hello #comment'\n", - "a: 100.5\n", - "a: bogus\n", - "\"a\": double quoted map key", - "'a': single quoted map key", - "a: \"double quoted\"\nb: \"value map\"", - "a: 'single quoted'\nb: 'value map'", - } - for _, src := range sources { - lexer.Tokenize(src).Dump() - } -} - -type testToken struct { - line int - column int - value string -} - -func TestSingleLineToken_ValueLineColumnPosition(t *testing.T) { - tests := []struct { - name string - src string - expect map[int]string // Column -> Value map. - }{ - { - name: "single quote, single value array", - src: "test: ['test']", - expect: map[int]string{ - 1: "test", - 5: ":", - 7: "[", - 8: "test", - 14: "]", - }, - }, - { - name: "double quote, single value array", - src: `test: ["test"]`, - expect: map[int]string{ - 1: "test", - 5: ":", - 7: "[", - 8: "test", - 14: "]", - }, - }, - { - name: "no quotes, single value array", - src: "test: [somevalue]", - expect: map[int]string{ - 1: "test", - 5: ":", - 7: "[", - 8: "somevalue", - 17: "]", - }, - }, - { - name: "single quote, multi value array", - src: "myarr: ['1','2','3', '444' , '55','66' , '77' ]", - expect: map[int]string{ - 1: "myarr", - 6: ":", - 8: "[", - 9: "1", - 12: ",", - 13: "2", - 16: ",", - 17: "3", - 20: ",", - 22: "444", - 28: ",", - 30: "55", - 34: ",", - 35: "66", - 40: ",", - 43: "77", - 49: "]", - }, - }, - { - name: "double quote, multi value array", - src: `myarr: ["1","2","3", "444" , "55","66" , "77" ]`, - expect: map[int]string{ - 1: "myarr", - 6: ":", - 8: "[", - 9: "1", - 12: ",", - 13: "2", - 16: ",", - 17: "3", - 20: ",", - 22: "444", - 28: ",", - 30: "55", - 34: ",", - 35: "66", - 40: ",", - 43: "77", - 49: "]", - }, - }, - { - name: "no quote, multi value array", - src: "numbers: [1, 5, 99,100, 3, 7 ]", - expect: map[int]string{ - 1: "numbers", - 8: ":", - 10: "[", - 11: "1", - 12: ",", - 14: "5", - 15: ",", - 17: "99", - 19: ",", - 20: "100", - 23: ",", - 25: "3", - 26: ",", - 28: "7", - 30: "]", - }, - }, - { - name: "double quotes, nested arrays", - src: `Strings: ["1",["2",["3"]]]`, - expect: map[int]string{ - 1: "Strings", - 8: ":", - 10: "[", - 11: "1", - 14: ",", - 15: "[", - 16: "2", - 19: ",", - 20: "[", - 21: "3", - 24: "]", - 25: "]", - 26: "]", - }, - }, - { - name: "mixed quotes, nested arrays", - src: `Values: [1,['2',"3",4,["5",6]]]`, - expect: map[int]string{ - 1: "Values", - 7: ":", - 9: "[", - 10: "1", - 11: ",", - 12: "[", - 13: "2", - 16: ",", - 17: "3", - 20: ",", - 21: "4", - 22: ",", - 23: "[", - 24: "5", - 27: ",", - 28: "6", - 29: "]", - 30: "]", - 31: "]", - }, - }, - { - name: "double quote, empty array", - src: `Empty: ["", ""]`, - expect: map[int]string{ - 1: "Empty", - 6: ":", - 8: "[", - 9: "", - 11: ",", - 13: "", - 15: "]", - }, - }, - { - name: "double quote key", - src: `"a": b`, - expect: map[int]string{ - 1: "a", - 4: ":", - 6: "b", - }, - }, - { - name: "single quote key", - src: `'a': b`, - expect: map[int]string{ - 1: "a", - 4: ":", - 6: "b", - }, - }, - { - name: "double quote key and value", - src: `"a": "b"`, - expect: map[int]string{ - 1: "a", - 4: ":", - 6: "b", - }, - }, - { - name: "single quote key and value", - src: `'a': 'b'`, - expect: map[int]string{ - 1: "a", - 4: ":", - 6: "b", - }, - }, - { - name: "double quote key, single quote value", - src: `"a": 'b'`, - expect: map[int]string{ - 1: "a", - 4: ":", - 6: "b", - }, - }, - { - name: "single quote key, double quote value", - src: `'a': "b"`, - expect: map[int]string{ - 1: "a", - 4: ":", - 6: "b", - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := lexer.Tokenize(tc.src) - sort.Slice(got, func(i, j int) bool { - return got[i].Position.Column < got[j].Position.Column - }) - var expected []testToken - for k, v := range tc.expect { - tt := testToken{ - line: 1, - column: k, - value: v, - } - expected = append(expected, tt) - } - sort.Slice(expected, func(i, j int) bool { - return expected[i].column < expected[j].column - }) - if len(got) != len(expected) { - t.Errorf("Tokenize(%s) token count mismatch, expected:%d got:%d", tc.src, len(expected), len(got)) - } - for i, tok := range got { - if !tokenMatches(tok, expected[i]) { - t.Errorf("Tokenize(%s) expected:%+v got line:%d column:%d value:%s", tc.src, expected[i], tok.Position.Line, tok.Position.Column, tok.Value) - } - } - }) - } -} - -func tokenMatches(t *token.Token, e testToken) bool { - return t != nil && t.Position != nil && - t.Value == e.value && - t.Position.Line == e.line && - t.Position.Column == e.column -} - -func TestMultiLineToken_ValueLineColumnPosition(t *testing.T) { - tests := []struct { - name string - src string - expect []testToken - }{ - { - name: "double quote", - src: `one: "1 2 3 4 5" -two: "1 2 -3 4 -5" -three: "1 2 3 4 -5"`, - expect: []testToken{ - { - line: 1, - column: 1, - value: "one", - }, - { - line: 1, - column: 4, - value: ":", - }, - { - line: 1, - column: 6, - value: "1 2 3 4 5", - }, - { - line: 2, - column: 1, - value: "two", - }, - { - line: 2, - column: 4, - value: ":", - }, - { - line: 2, - column: 6, - value: "1 2 3 4 5", - }, - { - line: 5, - column: 1, - value: "three", - }, - { - line: 5, - column: 6, - value: ":", - }, - { - line: 5, - column: 8, - value: "1 2 3 4 5", - }, - }, - }, - { - name: "single quote in an array", - src: `arr: ['1', 'and -two'] -last: 'hello'`, - expect: []testToken{ - { - line: 1, - column: 1, - value: "arr", - }, - { - line: 1, - column: 4, - value: ":", - }, - { - line: 1, - column: 6, - value: "[", - }, - { - line: 1, - column: 7, - value: "1", - }, - { - line: 1, - column: 10, - value: ",", - }, - { - line: 1, - column: 12, - value: "and two", - }, - { - line: 2, - column: 5, - value: "]", - }, - { - line: 3, - column: 1, - value: "last", - }, - { - line: 3, - column: 5, - value: ":", - }, - { - line: 3, - column: 7, - value: "hello", - }, - }, - }, - { - name: "single quote and double quote", - src: `foo: "test - - - - -bar" -foo2: 'bar2'`, - expect: []testToken{ - { - line: 1, - column: 1, - value: "foo", - }, - { - line: 1, - column: 4, - value: ":", - }, - { - line: 1, - column: 6, - value: "test bar", - }, - { - line: 7, - column: 1, - value: "foo2", - }, - { - line: 7, - column: 5, - value: ":", - }, - { - line: 7, - column: 7, - value: "bar2", - }, - }, - }, - { - name: "single and double quote map keys", - src: `"a": test -'b': 1 -c: true`, - expect: []testToken{ - { - line: 1, - column: 1, - value: "a", - }, - { - line: 1, - column: 4, - value: ":", - }, - { - line: 1, - column: 6, - value: "test", - }, - { - line: 2, - column: 1, - value: "b", - }, - { - line: 2, - column: 4, - value: ":", - }, - { - line: 2, - column: 6, - value: "1", - }, - { - line: 3, - column: 1, - value: "c", - }, - { - line: 3, - column: 2, - value: ":", - }, - { - line: 3, - column: 4, - value: "true", - }, - }, - }, - { - name: "issue326", - src: `a: | - Text -b: 1`, - expect: []testToken{ - { - line: 1, - column: 1, - value: "a", - }, - { - line: 1, - column: 2, - value: ":", - }, - { - line: 1, - column: 4, - value: "|", - }, - { - line: 2, - column: 3, - value: "Text\n", - }, - { - line: 3, - column: 1, - value: "b", - }, - { - line: 3, - column: 2, - value: ":", - }, - { - line: 3, - column: 4, - value: "1", - }, - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := lexer.Tokenize(tc.src) - sort.Slice(got, func(i, j int) bool { - // sort by line, then column - if got[i].Position.Line < got[j].Position.Line { - return true - } else if got[i].Position.Line == got[j].Position.Line { - return got[i].Position.Column < got[j].Position.Column - } - return false - }) - sort.Slice(tc.expect, func(i, j int) bool { - if tc.expect[i].line < tc.expect[j].line { - return true - } else if tc.expect[i].line == tc.expect[j].line { - return tc.expect[i].column < tc.expect[j].column - } - return false - }) - if len(got) != len(tc.expect) { - t.Errorf("Tokenize() token count mismatch, expected:%d got:%d", len(tc.expect), len(got)) - } - for i, tok := range got { - if !tokenMatches(tok, tc.expect[i]) { - t.Errorf("Tokenize() expected:%+v got line:%d column:%d value:%s", tc.expect[i], tok.Position.Line, tok.Position.Column, tok.Value) - } - } - }) - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/option.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/option.go deleted file mode 100644 index eab5d43..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/option.go +++ /dev/null @@ -1,278 +0,0 @@ -package yaml - -import ( - "io" - "reflect" - - "github.com/goccy/go-yaml/ast" -) - -// DecodeOption functional option type for Decoder -type DecodeOption func(d *Decoder) error - -// ReferenceReaders pass to Decoder that reference to anchor defined by passed readers -func ReferenceReaders(readers ...io.Reader) DecodeOption { - return func(d *Decoder) error { - d.referenceReaders = append(d.referenceReaders, readers...) - return nil - } -} - -// ReferenceFiles pass to Decoder that reference to anchor defined by passed files -func ReferenceFiles(files ...string) DecodeOption { - return func(d *Decoder) error { - d.referenceFiles = files - return nil - } -} - -// ReferenceDirs pass to Decoder that reference to anchor defined by files under the passed dirs -func ReferenceDirs(dirs ...string) DecodeOption { - return func(d *Decoder) error { - d.referenceDirs = dirs - return nil - } -} - -// RecursiveDir search yaml file recursively from passed dirs by ReferenceDirs option -func RecursiveDir(isRecursive bool) DecodeOption { - return func(d *Decoder) error { - d.isRecursiveDir = isRecursive - return nil - } -} - -// Validator set StructValidator instance to Decoder -func Validator(v StructValidator) DecodeOption { - return func(d *Decoder) error { - d.validator = v - return nil - } -} - -// Strict enable DisallowUnknownField and DisallowDuplicateKey -func Strict() DecodeOption { - return func(d *Decoder) error { - d.disallowUnknownField = true - d.disallowDuplicateKey = true - return nil - } -} - -// DisallowUnknownField causes the Decoder to return an error when the destination -// is a struct and the input contains object keys which do not match any -// non-ignored, exported fields in the destination. -func DisallowUnknownField() DecodeOption { - return func(d *Decoder) error { - d.disallowUnknownField = true - return nil - } -} - -// DisallowDuplicateKey causes an error when mapping keys that are duplicates -func DisallowDuplicateKey() DecodeOption { - return func(d *Decoder) error { - d.disallowDuplicateKey = true - return nil - } -} - -// UseOrderedMap can be interpreted as a map, -// and uses MapSlice ( ordered map ) aggressively if there is no type specification -func UseOrderedMap() DecodeOption { - return func(d *Decoder) error { - d.useOrderedMap = true - return nil - } -} - -// UseJSONUnmarshaler if neither `BytesUnmarshaler` nor `InterfaceUnmarshaler` is implemented -// and `UnmashalJSON([]byte)error` is implemented, convert the argument from `YAML` to `JSON` and then call it. -func UseJSONUnmarshaler() DecodeOption { - return func(d *Decoder) error { - d.useJSONUnmarshaler = true - return nil - } -} - -// CustomUnmarshaler overrides any decoding process for the type specified in generics. -// -// NOTE: If RegisterCustomUnmarshaler and CustomUnmarshaler of DecodeOption are specified for the same type, -// the CustomUnmarshaler specified in DecodeOption takes precedence. -func CustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) DecodeOption { - return func(d *Decoder) error { - var typ *T - d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(v interface{}, b []byte) error { - return unmarshaler(v.(*T), b) - } - return nil - } -} - -// EncodeOption functional option type for Encoder -type EncodeOption func(e *Encoder) error - -// Indent change indent number -func Indent(spaces int) EncodeOption { - return func(e *Encoder) error { - e.indent = spaces - return nil - } -} - -// IndentSequence causes sequence values to be indented the same value as Indent -func IndentSequence(indent bool) EncodeOption { - return func(e *Encoder) error { - e.indentSequence = indent - return nil - } -} - -// UseSingleQuote determines if single or double quotes should be preferred for strings. -func UseSingleQuote(sq bool) EncodeOption { - return func(e *Encoder) error { - e.singleQuote = sq - return nil - } -} - -// Flow encoding by flow style -func Flow(isFlowStyle bool) EncodeOption { - return func(e *Encoder) error { - e.isFlowStyle = isFlowStyle - return nil - } -} - -// UseLiteralStyleIfMultiline causes encoding multiline strings with a literal syntax, -// no matter what characters they include -func UseLiteralStyleIfMultiline(useLiteralStyleIfMultiline bool) EncodeOption { - return func(e *Encoder) error { - e.useLiteralStyleIfMultiline = useLiteralStyleIfMultiline - return nil - } -} - -// JSON encode in JSON format -func JSON() EncodeOption { - return func(e *Encoder) error { - e.isJSONStyle = true - e.isFlowStyle = true - return nil - } -} - -// MarshalAnchor call back if encoder find an anchor during encoding -func MarshalAnchor(callback func(*ast.AnchorNode, interface{}) error) EncodeOption { - return func(e *Encoder) error { - e.anchorCallback = callback - return nil - } -} - -// UseJSONMarshaler if neither `BytesMarshaler` nor `InterfaceMarshaler` -// nor `encoding.TextMarshaler` is implemented and `MarshalJSON()([]byte, error)` is implemented, -// call `MarshalJSON` to convert the returned `JSON` to `YAML` for processing. -func UseJSONMarshaler() EncodeOption { - return func(e *Encoder) error { - e.useJSONMarshaler = true - return nil - } -} - -// CustomMarshaler overrides any encoding process for the type specified in generics. -// -// NOTE: If type T implements MarshalYAML for pointer receiver, the type specified in CustomMarshaler must be *T. -// If RegisterCustomMarshaler and CustomMarshaler of EncodeOption are specified for the same type, -// the CustomMarshaler specified in EncodeOption takes precedence. -func CustomMarshaler[T any](marshaler func(T) ([]byte, error)) EncodeOption { - return func(e *Encoder) error { - var typ T - e.customMarshalerMap[reflect.TypeOf(typ)] = func(v interface{}) ([]byte, error) { - return marshaler(v.(T)) - } - return nil - } -} - -// CommentPosition type of the position for comment. -type CommentPosition int - -const ( - CommentHeadPosition CommentPosition = CommentPosition(iota) - CommentLinePosition - CommentFootPosition -) - -func (p CommentPosition) String() string { - switch p { - case CommentHeadPosition: - return "Head" - case CommentLinePosition: - return "Line" - case CommentFootPosition: - return "Foot" - default: - return "" - } -} - -// LineComment create a one-line comment for CommentMap. -func LineComment(text string) *Comment { - return &Comment{ - Texts: []string{text}, - Position: CommentLinePosition, - } -} - -// HeadComment create a multiline comment for CommentMap. -func HeadComment(texts ...string) *Comment { - return &Comment{ - Texts: texts, - Position: CommentHeadPosition, - } -} - -// FootComment create a multiline comment for CommentMap. -func FootComment(texts ...string) *Comment { - return &Comment{ - Texts: texts, - Position: CommentFootPosition, - } -} - -// Comment raw data for comment. -type Comment struct { - Texts []string - Position CommentPosition -} - -// CommentMap map of the position of the comment and the comment information. -type CommentMap map[string][]*Comment - -// WithComment add a comment using the location and text information given in the CommentMap. -func WithComment(cm CommentMap) EncodeOption { - return func(e *Encoder) error { - commentMap := map[*Path][]*Comment{} - for k, v := range cm { - path, err := PathString(k) - if err != nil { - return err - } - commentMap[path] = v - } - e.commentMap = commentMap - return nil - } -} - -// CommentToMap apply the position and content of comments in a YAML document to a CommentMap. -func CommentToMap(cm CommentMap) DecodeOption { - return func(d *Decoder) error { - if cm == nil { - return ErrInvalidCommentMapValue - } - d.toCommentMap = cm - return nil - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/context.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/context.go deleted file mode 100644 index 99f18b1..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/context.go +++ /dev/null @@ -1,199 +0,0 @@ -package parser - -import ( - "fmt" - "strings" - - "github.com/goccy/go-yaml/token" -) - -// context context at parsing -type context struct { - parent *context - idx int - size int - tokens token.Tokens - mode Mode - path string -} - -var pathSpecialChars = []string{ - "$", "*", ".", "[", "]", -} - -func containsPathSpecialChar(path string) bool { - for _, char := range pathSpecialChars { - if strings.Contains(path, char) { - return true - } - } - return false -} - -func normalizePath(path string) string { - if containsPathSpecialChar(path) { - return fmt.Sprintf("'%s'", path) - } - return path -} - -func (c *context) withChild(path string) *context { - ctx := c.copy() - path = normalizePath(path) - ctx.path += fmt.Sprintf(".%s", path) - return ctx -} - -func (c *context) withIndex(idx uint) *context { - ctx := c.copy() - ctx.path += fmt.Sprintf("[%d]", idx) - return ctx -} - -func (c *context) copy() *context { - return &context{ - parent: c, - idx: c.idx, - size: c.size, - tokens: append(token.Tokens{}, c.tokens...), - mode: c.mode, - path: c.path, - } -} - -func (c *context) next() bool { - return c.idx < c.size -} - -func (c *context) previousToken() *token.Token { - if c.idx > 0 { - return c.tokens[c.idx-1] - } - return nil -} - -func (c *context) insertToken(idx int, tk *token.Token) { - if c.parent != nil { - c.parent.insertToken(idx, tk) - } - if c.size < idx { - return - } - if c.size == idx { - curToken := c.tokens[c.size-1] - tk.Next = curToken - curToken.Prev = tk - - c.tokens = append(c.tokens, tk) - c.size = len(c.tokens) - return - } - - curToken := c.tokens[idx] - tk.Next = curToken - curToken.Prev = tk - - c.tokens = append(c.tokens[:idx+1], c.tokens[idx:]...) - c.tokens[idx] = tk - c.size = len(c.tokens) -} - -func (c *context) currentToken() *token.Token { - if c.idx >= c.size { - return nil - } - return c.tokens[c.idx] -} - -func (c *context) nextToken() *token.Token { - if c.idx+1 >= c.size { - return nil - } - return c.tokens[c.idx+1] -} - -func (c *context) afterNextToken() *token.Token { - if c.idx+2 >= c.size { - return nil - } - return c.tokens[c.idx+2] -} - -func (c *context) nextNotCommentToken() *token.Token { - for i := c.idx + 1; i < c.size; i++ { - tk := c.tokens[i] - if tk.Type == token.CommentType { - continue - } - return tk - } - return nil -} - -func (c *context) afterNextNotCommentToken() *token.Token { - notCommentTokenCount := 0 - for i := c.idx + 1; i < c.size; i++ { - tk := c.tokens[i] - if tk.Type == token.CommentType { - continue - } - notCommentTokenCount++ - if notCommentTokenCount == 2 { - return tk - } - } - return nil -} - -func (c *context) enabledComment() bool { - return c.mode&ParseComments != 0 -} - -func (c *context) isCurrentCommentToken() bool { - tk := c.currentToken() - if tk == nil { - return false - } - return tk.Type == token.CommentType -} - -func (c *context) progressIgnoreComment(num int) { - if c.parent != nil { - c.parent.progressIgnoreComment(num) - } - if c.size <= c.idx+num { - c.idx = c.size - } else { - c.idx += num - } -} - -func (c *context) progress(num int) { - if c.isCurrentCommentToken() { - return - } - c.progressIgnoreComment(num) -} - -func newContext(tokens token.Tokens, mode Mode) *context { - filteredTokens := []*token.Token{} - if mode&ParseComments != 0 { - filteredTokens = tokens - } else { - for _, tk := range tokens { - if tk.Type == token.CommentType { - continue - } - // keep prev/next reference between tokens containing comments - // https://github.com/goccy/go-yaml/issues/254 - filteredTokens = append(filteredTokens, tk) - } - } - return &context{ - idx: 0, - size: len(filteredTokens), - tokens: token.Tokens(filteredTokens), - mode: mode, - path: "$", - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/parser.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/parser.go deleted file mode 100644 index 568e6ad..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/parser.go +++ /dev/null @@ -1,714 +0,0 @@ -package parser - -import ( - "fmt" - "io/ioutil" - "strings" - - "github.com/goccy/go-yaml/ast" - "github.com/goccy/go-yaml/internal/errors" - "github.com/goccy/go-yaml/lexer" - "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" -) - -type parser struct{} - -func (p *parser) parseMapping(ctx *context) (*ast.MappingNode, error) { - mapTk := ctx.currentToken() - node := ast.Mapping(mapTk, true) - node.SetPath(ctx.path) - ctx.progress(1) // skip MappingStart token - for ctx.next() { - tk := ctx.currentToken() - if tk.Type == token.MappingEndType { - node.End = tk - return node, nil - } else if tk.Type == token.CollectEntryType { - ctx.progress(1) - continue - } - - value, err := p.parseMappingValue(ctx) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse mapping value in mapping node") - } - mvnode, ok := value.(*ast.MappingValueNode) - if !ok { - return nil, errors.ErrSyntax("failed to parse flow mapping node", value.GetToken()) - } - node.Values = append(node.Values, mvnode) - ctx.progress(1) - } - return nil, errors.ErrSyntax("unterminated flow mapping", node.GetToken()) -} - -func (p *parser) parseSequence(ctx *context) (*ast.SequenceNode, error) { - node := ast.Sequence(ctx.currentToken(), true) - node.SetPath(ctx.path) - ctx.progress(1) // skip SequenceStart token - for ctx.next() { - tk := ctx.currentToken() - if tk.Type == token.SequenceEndType { - node.End = tk - break - } else if tk.Type == token.CollectEntryType { - ctx.progress(1) - continue - } - - value, err := p.parseToken(ctx.withIndex(uint(len(node.Values))), tk) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse sequence value in flow sequence node") - } - node.Values = append(node.Values, value) - ctx.progress(1) - } - return node, nil -} - -func (p *parser) parseTag(ctx *context) (*ast.TagNode, error) { - tagToken := ctx.currentToken() - node := ast.Tag(tagToken) - node.SetPath(ctx.path) - ctx.progress(1) // skip tag token - var ( - value ast.Node - err error - ) - switch token.ReservedTagKeyword(tagToken.Value) { - case token.MappingTag, - token.OrderedMapTag: - value, err = p.parseMapping(ctx) - case token.IntegerTag, - token.FloatTag, - token.StringTag, - token.BinaryTag, - token.TimestampTag, - token.NullTag: - typ := ctx.currentToken().Type - if typ == token.LiteralType || typ == token.FoldedType { - value, err = p.parseLiteral(ctx) - } else { - value = p.parseScalarValue(ctx.currentToken()) - } - case token.SequenceTag, - token.SetTag: - err = errors.ErrSyntax(fmt.Sprintf("sorry, currently not supported %s tag", tagToken.Value), tagToken) - default: - // custom tag - value, err = p.parseToken(ctx, ctx.currentToken()) - } - if err != nil { - return nil, errors.Wrapf(err, "failed to parse tag value") - } - node.Value = value - return node, nil -} - -func (p *parser) removeLeftSideNewLineCharacter(src string) string { - // CR or LF or CRLF - return strings.TrimLeft(strings.TrimLeft(strings.TrimLeft(src, "\r"), "\n"), "\r\n") -} - -func (p *parser) existsNewLineCharacter(src string) bool { - if strings.Index(src, "\n") > 0 { - return true - } - if strings.Index(src, "\r") > 0 { - return true - } - return false -} - -func (p *parser) validateMapKey(tk *token.Token) error { - if tk.Type != token.StringType { - return nil - } - origin := p.removeLeftSideNewLineCharacter(tk.Origin) - if p.existsNewLineCharacter(origin) { - return errors.ErrSyntax("unexpected key name", tk) - } - return nil -} - -func (p *parser) createNullToken(base *token.Token) *token.Token { - pos := *(base.Position) - pos.Column++ - return token.New("null", "null", &pos) -} - -func (p *parser) parseMapValue(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) { - node, err := p.createMapValueNode(ctx, key, colonToken) - if err != nil { - return nil, errors.Wrapf(err, "failed to create map value node") - } - if node != nil && node.GetPath() == "" { - node.SetPath(ctx.path) - } - return node, nil -} - -func (p *parser) createMapValueNode(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) { - tk := ctx.currentToken() - if tk == nil { - nullToken := p.createNullToken(colonToken) - ctx.insertToken(ctx.idx, nullToken) - return ast.Null(nullToken), nil - } - - if tk.Position.Column == key.GetToken().Position.Column && tk.Type == token.StringType { - // in this case, - // ---- - // key: - // next - nullToken := p.createNullToken(colonToken) - ctx.insertToken(ctx.idx, nullToken) - return ast.Null(nullToken), nil - } - - if tk.Position.Column < key.GetToken().Position.Column { - // in this case, - // ---- - // key: - // next - nullToken := p.createNullToken(colonToken) - ctx.insertToken(ctx.idx, nullToken) - return ast.Null(nullToken), nil - } - - value, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse mapping 'value' node") - } - return value, nil -} - -func (p *parser) validateMapValue(ctx *context, key, value ast.Node) error { - keyColumn := key.GetToken().Position.Column - valueColumn := value.GetToken().Position.Column - if keyColumn != valueColumn { - return nil - } - if value.Type() != ast.StringType { - return nil - } - ntk := ctx.nextToken() - if ntk == nil || (ntk.Type != token.MappingValueType && ntk.Type != token.SequenceEntryType) { - return errors.ErrSyntax("could not found expected ':' token", value.GetToken()) - } - return nil -} - -func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) { - key, err := p.parseMapKey(ctx) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse map key") - } - keyText := key.GetToken().Value - key.SetPath(ctx.withChild(keyText).path) - if err := p.validateMapKey(key.GetToken()); err != nil { - return nil, errors.Wrapf(err, "validate mapping key error") - } - ctx.progress(1) // progress to mapping value token - tk := ctx.currentToken() // get mapping value token - if tk == nil { - return nil, errors.ErrSyntax("unexpected map", key.GetToken()) - } - ctx.progress(1) // progress to value token - if err := p.setSameLineCommentIfExists(ctx.withChild(keyText), key); err != nil { - return nil, errors.Wrapf(err, "failed to set same line comment to node") - } - if key.GetComment() != nil { - // if current token is comment, GetComment() is not nil. - // then progress to value token - ctx.progressIgnoreComment(1) - } - - value, err := p.parseMapValue(ctx.withChild(keyText), key, tk) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse map value") - } - if err := p.validateMapValue(ctx, key, value); err != nil { - return nil, errors.Wrapf(err, "failed to validate map value") - } - - mvnode := ast.MappingValue(tk, key, value) - mvnode.SetPath(ctx.withChild(keyText).path) - node := ast.Mapping(tk, false, mvnode) - node.SetPath(ctx.withChild(keyText).path) - - ntk := ctx.nextNotCommentToken() - antk := ctx.afterNextNotCommentToken() - for antk != nil && antk.Type == token.MappingValueType && - ntk.Position.Column == key.GetToken().Position.Column { - ctx.progressIgnoreComment(1) - value, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse mapping node") - } - switch value.Type() { - case ast.MappingType: - c := value.(*ast.MappingNode) - comment := c.GetComment() - for idx, v := range c.Values { - if idx == 0 && comment != nil { - if err := v.SetComment(comment); err != nil { - return nil, errors.Wrapf(err, "failed to set comment token to node") - } - } - node.Values = append(node.Values, v) - } - case ast.MappingValueType: - node.Values = append(node.Values, value.(*ast.MappingValueNode)) - default: - return nil, xerrors.Errorf("failed to parse mapping value node node is %s", value.Type()) - } - ntk = ctx.nextNotCommentToken() - antk = ctx.afterNextNotCommentToken() - } - if len(node.Values) == 1 { - mapKeyCol := mvnode.Key.GetToken().Position.Column - commentTk := ctx.nextToken() - if commentTk != nil && commentTk.Type == token.CommentType && mapKeyCol <= commentTk.Position.Column { - // If the comment is in the same or deeper column as the last element column in map value, - // treat it as a footer comment for the last element. - comment := p.parseFootComment(ctx, mapKeyCol) - mvnode.FootComment = comment - } - return mvnode, nil - } - mapCol := node.GetToken().Position.Column - commentTk := ctx.nextToken() - if commentTk != nil && commentTk.Type == token.CommentType && mapCol <= commentTk.Position.Column { - // If the comment is in the same or deeper column as the last element column in map value, - // treat it as a footer comment for the last element. - comment := p.parseFootComment(ctx, mapCol) - node.FootComment = comment - } - return node, nil -} - -func (p *parser) parseSequenceEntry(ctx *context) (*ast.SequenceNode, error) { - tk := ctx.currentToken() - sequenceNode := ast.Sequence(tk, false) - sequenceNode.SetPath(ctx.path) - curColumn := tk.Position.Column - for tk.Type == token.SequenceEntryType { - ctx.progress(1) // skip sequence token - tk = ctx.currentToken() - if tk == nil { - return nil, errors.ErrSyntax("empty sequence entry", ctx.previousToken()) - } - var comment *ast.CommentGroupNode - if tk.Type == token.CommentType { - comment = p.parseCommentOnly(ctx) - tk = ctx.currentToken() - if tk.Type != token.SequenceEntryType { - break - } - ctx.progress(1) // skip sequence token - } - value, err := p.parseToken(ctx.withIndex(uint(len(sequenceNode.Values))), ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse sequence") - } - if comment != nil { - comment.SetPath(ctx.withIndex(uint(len(sequenceNode.Values))).path) - sequenceNode.ValueHeadComments = append(sequenceNode.ValueHeadComments, comment) - } else { - sequenceNode.ValueHeadComments = append(sequenceNode.ValueHeadComments, nil) - } - sequenceNode.Values = append(sequenceNode.Values, value) - tk = ctx.nextNotCommentToken() - if tk == nil { - break - } - if tk.Type != token.SequenceEntryType { - break - } - if tk.Position.Column != curColumn { - break - } - ctx.progressIgnoreComment(1) - } - commentTk := ctx.nextToken() - if commentTk != nil && commentTk.Type == token.CommentType && curColumn <= commentTk.Position.Column { - // If the comment is in the same or deeper column as the last element column in sequence value, - // treat it as a footer comment for the last element. - comment := p.parseFootComment(ctx, curColumn) - sequenceNode.FootComment = comment - } - return sequenceNode, nil -} - -func (p *parser) parseAnchor(ctx *context) (*ast.AnchorNode, error) { - tk := ctx.currentToken() - anchor := ast.Anchor(tk) - anchor.SetPath(ctx.path) - ntk := ctx.nextToken() - if ntk == nil { - return nil, errors.ErrSyntax("unexpected anchor. anchor name is undefined", tk) - } - ctx.progress(1) // skip anchor token - name, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parser anchor name node") - } - anchor.Name = name - ntk = ctx.nextToken() - if ntk == nil { - return nil, errors.ErrSyntax("unexpected anchor. anchor value is undefined", ctx.currentToken()) - } - ctx.progress(1) - value, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parser anchor name node") - } - anchor.Value = value - return anchor, nil -} - -func (p *parser) parseAlias(ctx *context) (*ast.AliasNode, error) { - tk := ctx.currentToken() - alias := ast.Alias(tk) - alias.SetPath(ctx.path) - ntk := ctx.nextToken() - if ntk == nil { - return nil, errors.ErrSyntax("unexpected alias. alias name is undefined", tk) - } - ctx.progress(1) // skip alias token - name, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parser alias name node") - } - alias.Value = name - return alias, nil -} - -func (p *parser) parseMapKey(ctx *context) (ast.MapKeyNode, error) { - tk := ctx.currentToken() - if value := p.parseScalarValue(tk); value != nil { - return value, nil - } - switch tk.Type { - case token.MergeKeyType: - return ast.MergeKey(tk), nil - case token.MappingKeyType: - return p.parseMappingKey(ctx) - } - return nil, errors.ErrSyntax("unexpected mapping key", tk) -} - -func (p *parser) parseStringValue(tk *token.Token) *ast.StringNode { - switch tk.Type { - case token.StringType, - token.SingleQuoteType, - token.DoubleQuoteType: - return ast.String(tk) - } - return nil -} - -func (p *parser) parseScalarValueWithComment(ctx *context, tk *token.Token) (ast.ScalarNode, error) { - node := p.parseScalarValue(tk) - if node == nil { - return nil, nil - } - node.SetPath(ctx.path) - if p.isSameLineComment(ctx.nextToken(), node) { - ctx.progress(1) - if err := p.setSameLineCommentIfExists(ctx, node); err != nil { - return nil, errors.Wrapf(err, "failed to set same line comment to node") - } - } - return node, nil -} - -func (p *parser) parseScalarValue(tk *token.Token) ast.ScalarNode { - if node := p.parseStringValue(tk); node != nil { - return node - } - switch tk.Type { - case token.NullType: - return ast.Null(tk) - case token.BoolType: - return ast.Bool(tk) - case token.IntegerType, - token.BinaryIntegerType, - token.OctetIntegerType, - token.HexIntegerType: - return ast.Integer(tk) - case token.FloatType: - return ast.Float(tk) - case token.InfinityType: - return ast.Infinity(tk) - case token.NanType: - return ast.Nan(tk) - } - return nil -} - -func (p *parser) parseDirective(ctx *context) (*ast.DirectiveNode, error) { - node := ast.Directive(ctx.currentToken()) - ctx.progress(1) // skip directive token - value, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse directive value") - } - node.Value = value - ctx.progress(1) - tk := ctx.currentToken() - if tk == nil { - // Since current token is nil, use the previous token to specify - // the syntax error location. - return nil, errors.ErrSyntax("unexpected directive value. document not started", ctx.previousToken()) - } - if tk.Type != token.DocumentHeaderType { - return nil, errors.ErrSyntax("unexpected directive value. document not started", ctx.currentToken()) - } - return node, nil -} - -func (p *parser) parseLiteral(ctx *context) (*ast.LiteralNode, error) { - node := ast.Literal(ctx.currentToken()) - ctx.progress(1) // skip literal/folded token - - tk := ctx.currentToken() - var comment *ast.CommentGroupNode - if tk.Type == token.CommentType { - comment = p.parseCommentOnly(ctx) - comment.SetPath(ctx.path) - if err := node.SetComment(comment); err != nil { - return nil, errors.Wrapf(err, "failed to set comment to literal") - } - tk = ctx.currentToken() - } - value, err := p.parseToken(ctx, tk) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse literal/folded value") - } - snode, ok := value.(*ast.StringNode) - if !ok { - return nil, errors.ErrSyntax("unexpected token. required string token", value.GetToken()) - } - node.Value = snode - return node, nil -} - -func (p *parser) isSameLineComment(tk *token.Token, node ast.Node) bool { - if tk == nil { - return false - } - if tk.Type != token.CommentType { - return false - } - return tk.Position.Line == node.GetToken().Position.Line -} - -func (p *parser) setSameLineCommentIfExists(ctx *context, node ast.Node) error { - tk := ctx.currentToken() - if !p.isSameLineComment(tk, node) { - return nil - } - comment := ast.CommentGroup([]*token.Token{tk}) - comment.SetPath(ctx.path) - if err := node.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment token to ast.Node") - } - return nil -} - -func (p *parser) parseDocument(ctx *context) (*ast.DocumentNode, error) { - startTk := ctx.currentToken() - ctx.progress(1) // skip document header token - body, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse document body") - } - node := ast.Document(startTk, body) - if ntk := ctx.nextToken(); ntk != nil && ntk.Type == token.DocumentEndType { - node.End = ntk - ctx.progress(1) - } - return node, nil -} - -func (p *parser) parseCommentOnly(ctx *context) *ast.CommentGroupNode { - commentTokens := []*token.Token{} - for { - tk := ctx.currentToken() - if tk == nil { - break - } - if tk.Type != token.CommentType { - break - } - commentTokens = append(commentTokens, tk) - ctx.progressIgnoreComment(1) // skip comment token - } - return ast.CommentGroup(commentTokens) -} - -func (p *parser) parseFootComment(ctx *context, col int) *ast.CommentGroupNode { - commentTokens := []*token.Token{} - for { - ctx.progressIgnoreComment(1) - commentTokens = append(commentTokens, ctx.currentToken()) - - nextTk := ctx.nextToken() - if nextTk == nil { - break - } - if nextTk.Type != token.CommentType { - break - } - if col > nextTk.Position.Column { - break - } - } - return ast.CommentGroup(commentTokens) -} - -func (p *parser) parseComment(ctx *context) (ast.Node, error) { - group := p.parseCommentOnly(ctx) - node, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse node after comment") - } - if node == nil { - return group, nil - } - group.SetPath(node.GetPath()) - if err := node.SetComment(group); err != nil { - return nil, errors.Wrapf(err, "failed to set comment token to node") - } - return node, nil -} - -func (p *parser) parseMappingKey(ctx *context) (*ast.MappingKeyNode, error) { - keyTk := ctx.currentToken() - node := ast.MappingKey(keyTk) - node.SetPath(ctx.path) - ctx.progress(1) // skip mapping key token - value, err := p.parseToken(ctx.withChild(keyTk.Value), ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse map key") - } - node.Value = value - return node, nil -} - -func (p *parser) parseToken(ctx *context, tk *token.Token) (ast.Node, error) { - node, err := p.createNodeFromToken(ctx, tk) - if err != nil { - return nil, errors.Wrapf(err, "failed to create node from token") - } - if node != nil && node.GetPath() == "" { - node.SetPath(ctx.path) - } - return node, nil -} - -func (p *parser) createNodeFromToken(ctx *context, tk *token.Token) (ast.Node, error) { - if tk == nil { - return nil, nil - } - if tk.NextType() == token.MappingValueType { - node, err := p.parseMappingValue(ctx) - return node, err - } - node, err := p.parseScalarValueWithComment(ctx, tk) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse scalar value") - } - if node != nil { - return node, nil - } - switch tk.Type { - case token.CommentType: - return p.parseComment(ctx) - case token.MappingKeyType: - return p.parseMappingKey(ctx) - case token.DocumentHeaderType: - return p.parseDocument(ctx) - case token.MappingStartType: - return p.parseMapping(ctx) - case token.SequenceStartType: - return p.parseSequence(ctx) - case token.SequenceEntryType: - return p.parseSequenceEntry(ctx) - case token.AnchorType: - return p.parseAnchor(ctx) - case token.AliasType: - return p.parseAlias(ctx) - case token.DirectiveType: - return p.parseDirective(ctx) - case token.TagType: - return p.parseTag(ctx) - case token.LiteralType, token.FoldedType: - return p.parseLiteral(ctx) - } - return nil, nil -} - -func (p *parser) parse(tokens token.Tokens, mode Mode) (*ast.File, error) { - ctx := newContext(tokens, mode) - file := &ast.File{Docs: []*ast.DocumentNode{}} - for ctx.next() { - node, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse") - } - ctx.progressIgnoreComment(1) - if node == nil { - continue - } - if doc, ok := node.(*ast.DocumentNode); ok { - file.Docs = append(file.Docs, doc) - } else { - file.Docs = append(file.Docs, ast.Document(nil, node)) - } - } - return file, nil -} - -type Mode uint - -const ( - ParseComments Mode = 1 << iota // parse comments and add them to AST -) - -// ParseBytes parse from byte slice, and returns ast.File -func ParseBytes(bytes []byte, mode Mode) (*ast.File, error) { - tokens := lexer.Tokenize(string(bytes)) - f, err := Parse(tokens, mode) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse") - } - return f, nil -} - -// Parse parse from token instances, and returns ast.File -func Parse(tokens token.Tokens, mode Mode) (*ast.File, error) { - var p parser - f, err := p.parse(tokens, mode) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse") - } - return f, nil -} - -// Parse parse from filename, and returns ast.File -func ParseFile(filename string, mode Mode) (*ast.File, error) { - file, err := ioutil.ReadFile(filename) - if err != nil { - return nil, errors.Wrapf(err, "failed to read file: %s", filename) - } - f, err := ParseBytes(file, mode) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse") - } - f.Name = filename - return f, nil -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/parser_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/parser_test.go deleted file mode 100644 index 6044699..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/parser_test.go +++ /dev/null @@ -1,886 +0,0 @@ -package parser_test - -import ( - "fmt" - "path/filepath" - "reflect" - "strings" - "testing" - - "github.com/goccy/go-yaml/ast" - "github.com/goccy/go-yaml/lexer" - "github.com/goccy/go-yaml/parser" - "github.com/goccy/go-yaml/token" -) - -func TestParser(t *testing.T) { - sources := []string{ - "null\n", - "{}\n", - "v: hi\n", - "v: \"true\"\n", - "v: \"false\"\n", - "v: true\n", - "v: false\n", - "v: 10\n", - "v: -10\n", - "v: 42\n", - "v: 4294967296\n", - "v: \"10\"\n", - "v: 0.1\n", - "v: 0.99\n", - "v: -0.1\n", - "v: .inf\n", - "v: -.inf\n", - "v: .nan\n", - "v: null\n", - "v: \"\"\n", - "v:\n- A\n- B\n", - "a: '-'\n", - "123\n", - "hello: world\n", - "a: null\n", - "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", - "a:\n b: c\n", - "a: {x: 1}\n", - "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", - "a: [1, 2]\n", - "a: {b: c, d: e}\n", - "a: 3s\n", - "a: \n", - "a: \"1:1\"\n", - "a: 1.2.3.4\n", - "a: \"2015-02-24T18:19:39Z\"\n", - "a: 'b: c'\n", - "a: 'Hello #comment'\n", - "a: abc <> ghi", - "a: <-\n B\n C\n", - "v: |-\n 0\n", - "v: |-\n 0\nx: 0", - `"a\n1\nb"`, - `{"a":"b"}`, - `!!map { - ? !!str "explicit":!!str "entry", - ? !!str "implicit" : !!str "entry", - ? !!null "" : !!null "", -}`, - "\"a\": a\n\"b\": b", - "'a': a\n'b': b", - } - for _, src := range sources { - if _, err := parser.Parse(lexer.Tokenize(src), 0); err != nil { - t.Fatalf("parse error: source [%s]: %+v", src, err) - } - } -} - -func TestParseComplicatedDocument(t *testing.T) { - tests := []struct { - source string - expect string - }{ - { - ` -american: - - Boston Red Sox - - Detroit Tigers - - New York Yankees -national: - - New York Mets - - Chicago Cubs - - Atlanta Braves -`, ` -american: - - Boston Red Sox - - Detroit Tigers - - New York Yankees -national: - - New York Mets - - Chicago Cubs - - Atlanta Braves -`, - }, - { - ` -a: - b: c - d: e - f: g -h: - i: j - k: - l: m - n: o - p: q -r: s -`, ` -a: - b: c - d: e - f: g -h: - i: j - k: - l: m - n: o - p: q -r: s -`, - }, - { - ` -- a: - - b - - c -- d -`, ` -- a: - - b - - c -- d -`, - }, - { - ` -- a -- b -- c - - d - - e -- f -`, ` -- a -- b -- c - d - e -- f -`, - }, - { - ` -a: 0 - 1 -`, - ` -a: 0 - 1 -`, - }, - {` -- a: - b: c - d: e -- f: - g: h -`, - ` -- a: - b: c - d: e -- f: null - g: h -`, - }, - { - ` -a: - b - c -d: e -`, ` -a: b c -d: e -`, - }, - { - ` -a -b -c -`, ` -a b c -`, - }, - { - ` -a: - - b - - c -`, ` -a: - - b - - c -`, - }, - { - ` -- a : - b: c -`, ` -- a: null - b: c -`, - }, - { - ` -- a: - b - c - d - hoge: fuga -`, ` -- a: b c d - hoge: fuga -`, - }, - { - ` -- a # ' " # - : % -- b # " # - : % ' -- c # # - : % ' " -- d # - : % ' " # -- e # : % ' " # - -- f # % ' : # - : -`, - ` -- a -- b -- c -- d -- e -- f -`, - }, - { - ` -# comment -a: # comment -# comment - b: c # comment - # comment -d: e # comment -# comment -`, - ` -a: - b: c -d: e -`, - }, - { - ` -a: b#notcomment -`, - ` -a: b#notcomment -`, - }, - { - ` -anchored: &anchor foo -aliased: *anchor -`, - ` -anchored: &anchor foo -aliased: *anchor -`, - }, - { - ` ---- -- &CENTER { x: 1, y: 2 } -- &LEFT { x: 0, y: 2 } -- &BIG { r: 10 } -- &SMALL { r: 1 } - -# All the following maps are equal: - -- # Explicit keys - x: 1 - y: 2 - r: 10 - label: center/big - -- # Merge one map - << : *CENTER - r: 10 - label: center/big - -- # Merge multiple maps - << : [ *CENTER, *BIG ] - label: center/big - -- # Override - << : [ *BIG, *LEFT, *SMALL ] - x: 1 - label: center/big -`, - ` ---- -- &CENTER {x: 1, y: 2} -- &LEFT {x: 0, y: 2} -- &BIG {r: 10} -- &SMALL {r: 1} -- x: 1 - y: 2 - r: 10 - label: center/big -- <<: *CENTER - r: 10 - label: center/big -- <<: [*CENTER, *BIG] - label: center/big -- <<: [*BIG, *LEFT, *SMALL] - x: 1 - label: center/big -`, - }, - { - ` -a: -- - b -- - c - - d -`, - ` -a: -- - b -- - c - - d -`, - }, - { - ` -a: - b: - c: d - e: - f: g - h: i -j: k -`, - ` -a: - b: - c: d - e: - f: g - h: i -j: k -`, - }, - { - ` ---- -a: 1 -b: 2 -... ---- -c: 3 -d: 4 -... -`, - ` ---- -a: 1 -b: 2 -... ---- -c: 3 -d: 4 -... -`, - }, - { - ` -a: - b: | - { - [ 1, 2 ] - } - c: d -`, - ` -a: - b: | - { - [ 1, 2 ] - } - c: d -`, - }, - { - ` -| - hoge - fuga - piyo`, - ` -| - hoge - fuga - piyo -`, - }, - { - ` -a: | - bbbbbbb - - - ccccccc -d: eeeeeeeeeeeeeeeee -`, - ` -a: | - bbbbbbb - - - ccccccc -d: eeeeeeeeeeeeeeeee -`, - }, - { - ` -a: b - c -`, - ` -a: b c -`, - }, - { - ` -a: - b: c -`, - ` -a: - b: c -`, - }, - { - ` -a: b -c: d -`, - ` -a: b -c: d -`, - }, - { - ` -- ab - cd -- ef - gh -`, - ` -- ab - cd -- ef - gh -`, - }, - { - ` -- 0 - 1 - - 2 - 3 -`, - ` -- 0 - 1 - 2 - 3 -`, - }, - { - ` -a - b - c: value -`, - ` -a - b - c: value -`, - }, - { - ` -a: -- - b: c - d: e -- - f: g - h: i -`, - ` -a: -- b: c - d: e -- f: g - h: i -`, - }, - { - ` -a: |- - value -b: c -`, - ` -a: |- - value -b: c -`, - }, - { - ` -a: |+ - value -b: c -`, - ` -a: |+ - value -b: c -`, - }, - { - ` -- key1: val - key2: - ( - foo - + - bar - ) -`, - ` -- key1: val - key2: ( foo + bar ) -`, - }, - { - ` -"a": b -'c': d -"e": "f" -g: "h" -i: 'j' -`, - ` -"a": b -'c': d -"e": "f" -g: "h" -i: 'j' -`, - }, - } - - for _, test := range tests { - t.Run(test.source, func(t *testing.T) { - tokens := lexer.Tokenize(test.source) - f, err := parser.Parse(tokens, 0) - if err != nil { - t.Fatalf("%+v", err) - } - var v Visitor - for _, doc := range f.Docs { - ast.Walk(&v, doc.Body) - } - expect := fmt.Sprintf("\n%+v", f) - if test.expect != expect { - tokens.Dump() - t.Fatalf("unexpected output: [%s] != [%s]", test.expect, expect) - } - }) - } -} - -func TestNewLineChar(t *testing.T) { - for _, f := range []string{ - "lf.yml", - "cr.yml", - "crlf.yml", - } { - ast, err := parser.ParseFile(filepath.Join("testdata", f), 0) - if err != nil { - t.Fatalf("%+v", err) - } - actual := fmt.Sprintf("%v", ast) - expect := `a: "a" -b: 1 -` - if expect != actual { - t.Fatal("unexpected result") - } - } -} - -func TestSyntaxError(t *testing.T) { - tests := []struct { - source string - expect string - }{ - { - ` -a: -- b - c: d - e: f - g: h`, - ` -[3:3] unexpected key name - 2 | a: -> 3 | - b - 4 | c: d - ^ - 5 | e: f - 6 | g: h`, - }, - { - ` -a -- b: c`, - ` -[2:1] unexpected key name -> 2 | a - 3 | - b: c - ^ -`, - }, - { - `%YAML 1.1 {}`, - ` -[1:2] unexpected directive value. document not started -> 1 | %YAML 1.1 {} - ^ -`, - }, - { - `{invalid`, - ` -[1:2] unexpected map -> 1 | {invalid - ^ -`, - }, - { - `{ "key": "value" `, - ` -[1:1] unterminated flow mapping -> 1 | { "key": "value" - ^ -`, - }, - { - ` -a: -- b: c -- `, - ` -[4:1] empty sequence entry - 2 | a: - 3 | - b: c -> 4 | - - ^ -`, - }, - } - for _, test := range tests { - t.Run(test.source, func(t *testing.T) { - _, err := parser.ParseBytes([]byte(test.source), 0) - if err == nil { - t.Fatal("cannot catch syntax error") - } - actual := "\n" + err.Error() - if test.expect != actual { - t.Fatalf("expected: [%s] but got [%s]", test.expect, actual) - } - }) - } -} - -func TestComment(t *testing.T) { - tests := []struct { - name string - yaml string - }{ - { - name: "map with comment", - yaml: ` -# commentA -a: #commentB - # commentC - b: c # commentD - # commentE - d: e # commentF - # commentG - f: g # commentH -# commentI -f: g # commentJ -# commentK -`, - }, - { - name: "sequence with comment", - yaml: ` -# commentA -- a # commentB -# commentC -- b: # commentD - # commentE - - d # commentF - - e # commentG -# commentH -`, - }, - { - name: "anchor and alias", - yaml: ` -a: &x b # commentA -c: *x # commentB -`, - }, - { - name: "multiline", - yaml: ` -# foo comment -# foo comment2 -foo: # map key comment - # bar above comment - # bar above comment2 - bar: 10 # comment for bar - # baz above comment - # baz above comment2 - baz: bbbb # comment for baz - piyo: # sequence key comment - # sequence1 above comment 1 - # sequence1 above comment 2 - - sequence1 # sequence1 - # sequence2 above comment 1 - # sequence2 above comment 2 - - sequence2 # sequence2 - # sequence3 above comment 1 - # sequence3 above comment 2 - - false # sequence3 -# foo2 comment -# foo2 comment2 -foo2: &anchor text # anchor comment -# foo3 comment -# foo3 comment2 -foo3: *anchor # alias comment -`, - }, - { - name: "literal", - yaml: ` -foo: | # comment - x: 42 -`, - }, - { - name: "folded", - yaml: ` -foo: > # comment - x: 42 -`, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - f, err := parser.ParseBytes([]byte(test.yaml), parser.ParseComments) - if err != nil { - t.Fatalf("%+v", err) - } - got := "\n" + f.String() - if test.yaml != got { - t.Fatalf("expected:%s\ngot:%s", test.yaml, got) - } - }) - } -} - -func TestNodePath(t *testing.T) { - yml := ` -a: # commentA - b: # commentB - c: foo # commentC - d: bar # commentD - e: baz # commentE - f: # commentF - g: hoge # commentG - h: # commentH - - list1 # comment list1 - - list2 # comment list2 - - list3 # comment list3 - i: fuga # commentI -j: piyo # commentJ -k.l.m.n: moge # commentKLMN -` - f, err := parser.ParseBytes([]byte(yml), parser.ParseComments) - if err != nil { - t.Fatalf("%+v", err) - } - var capturer pathCapturer - for _, doc := range f.Docs { - ast.Walk(&capturer, doc.Body) - } - commentPaths := []string{} - for i := 0; i < capturer.capturedNum; i++ { - if capturer.orderedTypes[i] == ast.CommentType { - commentPaths = append(commentPaths, capturer.orderedPaths[i]) - } - } - expectedPaths := []string{ - "$.a", - "$.a.b", - "$.a.b.c", - "$.a.b.d", - "$.a.b.e", - "$.a.f", - "$.a.f.g", - "$.a.h", - "$.a.h[0]", - "$.a.h[1]", - "$.a.h[2]", - "$.a.i", - "$.j", - "$.'k.l.m.n'", - } - if !reflect.DeepEqual(expectedPaths, commentPaths) { - t.Fatalf("failed to get YAMLPath to the comment node:\nexpected[%s]\ngot [%s]", expectedPaths, commentPaths) - } -} - -type pathCapturer struct { - capturedNum int - orderedPaths []string - orderedTypes []ast.NodeType - orderedTokens []*token.Token -} - -func (c *pathCapturer) Visit(node ast.Node) ast.Visitor { - c.capturedNum++ - c.orderedPaths = append(c.orderedPaths, node.GetPath()) - c.orderedTypes = append(c.orderedTypes, node.Type()) - c.orderedTokens = append(c.orderedTokens, node.GetToken()) - return c -} - -type Visitor struct { -} - -func (v *Visitor) Visit(node ast.Node) ast.Visitor { - tk := node.GetToken() - tk.Prev = nil - tk.Next = nil - return v -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/cr.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/cr.yml deleted file mode 100644 index 37b52a6..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/cr.yml +++ /dev/null @@ -1 +0,0 @@ -a: "a" b: 1 \ No newline at end of file diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/crlf.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/crlf.yml deleted file mode 100644 index 85929f9..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/crlf.yml +++ /dev/null @@ -1,3 +0,0 @@ -a: "a" - -b: 1 diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/lf.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/lf.yml deleted file mode 100644 index d2fe51f..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/parser/testdata/lf.yml +++ /dev/null @@ -1,3 +0,0 @@ -a: "a" - -b: 1 diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/path.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/path.go deleted file mode 100644 index 7a0c3b1..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/path.go +++ /dev/null @@ -1,794 +0,0 @@ -package yaml - -import ( - "bytes" - "fmt" - "io" - "strconv" - "strings" - - "github.com/goccy/go-yaml/ast" - "github.com/goccy/go-yaml/internal/errors" - "github.com/goccy/go-yaml/parser" - "github.com/goccy/go-yaml/printer" -) - -// PathString create Path from string -// -// YAMLPath rule -// $ : the root object/element -// . : child operator -// .. : recursive descent -// [num] : object/element of array by number -// [*] : all objects/elements for array. -// -// If you want to use reserved characters such as `.` and `*` as a key name, -// enclose them in single quotation as follows ( $.foo.'bar.baz-*'.hoge ). -// If you want to use a single quote with reserved characters, escape it with `\` ( $.foo.'bar.baz\'s value'.hoge ). -func PathString(s string) (*Path, error) { - buf := []rune(s) - length := len(buf) - cursor := 0 - builder := &PathBuilder{} - for cursor < length { - c := buf[cursor] - switch c { - case '$': - builder = builder.Root() - cursor++ - case '.': - b, buf, c, err := parsePathDot(builder, buf, cursor) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse path of dot") - } - length = len(buf) - builder = b - cursor = c - case '[': - b, buf, c, err := parsePathIndex(builder, buf, cursor) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse path of index") - } - length = len(buf) - builder = b - cursor = c - default: - return nil, errors.Wrapf(ErrInvalidPathString, "invalid path at %d", cursor) - } - } - return builder.Build(), nil -} - -func parsePathRecursive(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { - length := len(buf) - cursor += 2 // skip .. characters - start := cursor - for ; cursor < length; cursor++ { - c := buf[cursor] - switch c { - case '$': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '..' character") - case '*': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '..' character") - case '.', '[': - goto end - case ']': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '..' character") - } - } -end: - if start == cursor { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "not found recursive selector") - } - return b.Recursive(string(buf[start:cursor])), buf, cursor, nil -} - -func parsePathDot(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { - length := len(buf) - if cursor+1 < length && buf[cursor+1] == '.' { - b, buf, c, err := parsePathRecursive(b, buf, cursor) - if err != nil { - return nil, nil, 0, errors.Wrapf(err, "failed to parse path of recursive") - } - return b, buf, c, nil - } - cursor++ // skip . character - start := cursor - - // if started single quote, looking for end single quote char - if cursor < length && buf[cursor] == '\'' { - return parseQuotedKey(b, buf, cursor) - } - for ; cursor < length; cursor++ { - c := buf[cursor] - switch c { - case '$': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") - case '*': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") - case '.', '[': - goto end - case ']': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") - } - } -end: - if start == cursor { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "cloud not find by empty key") - } - return b.child(string(buf[start:cursor])), buf, cursor, nil -} - -func parseQuotedKey(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { - cursor++ // skip single quote - start := cursor - length := len(buf) - var foundEndDelim bool - for ; cursor < length; cursor++ { - switch buf[cursor] { - case '\\': - buf = append(append([]rune{}, buf[:cursor]...), buf[cursor+1:]...) - length = len(buf) - case '\'': - foundEndDelim = true - goto end - } - } -end: - if !foundEndDelim { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find end delimiter for key") - } - if start == cursor { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find by empty key") - } - selector := buf[start:cursor] - cursor++ - if cursor < length { - switch buf[cursor] { - case '$': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") - case '*': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") - case ']': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") - } - } - return b.child(string(selector)), buf, cursor, nil -} - -func parsePathIndex(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { - length := len(buf) - cursor++ // skip '[' character - if length <= cursor { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "unexpected end of YAML Path") - } - c := buf[cursor] - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*': - start := cursor - cursor++ - for ; cursor < length; cursor++ { - c := buf[cursor] - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - continue - } - break - } - if buf[cursor] != ']' { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", string(buf[cursor]), cursor) - } - numOrAll := string(buf[start:cursor]) - if numOrAll == "*" { - return b.IndexAll(), buf, cursor + 1, nil - } - num, err := strconv.ParseInt(numOrAll, 10, 64) - if err != nil { - return nil, nil, 0, errors.Wrapf(err, "failed to parse number") - } - return b.Index(uint(num)), buf, cursor + 1, nil - } - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", c, cursor) -} - -// Path represent YAMLPath ( like a JSONPath ). -type Path struct { - node pathNode -} - -// String path to text. -func (p *Path) String() string { - return p.node.String() -} - -// Read decode from r and set extracted value by YAMLPath to v. -func (p *Path) Read(r io.Reader, v interface{}) error { - node, err := p.ReadNode(r) - if err != nil { - return errors.Wrapf(err, "failed to read node") - } - if err := Unmarshal([]byte(node.String()), v); err != nil { - return errors.Wrapf(err, "failed to unmarshal") - } - return nil -} - -// ReadNode create AST from r and extract node by YAMLPath. -func (p *Path) ReadNode(r io.Reader) (ast.Node, error) { - if p.node == nil { - return nil, ErrInvalidPath - } - var buf bytes.Buffer - if _, err := io.Copy(&buf, r); err != nil { - return nil, errors.Wrapf(err, "failed to copy from reader") - } - f, err := parser.ParseBytes(buf.Bytes(), 0) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse yaml") - } - node, err := p.FilterFile(f) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter from ast.File") - } - return node, nil -} - -// Filter filter from target by YAMLPath and set it to v. -func (p *Path) Filter(target, v interface{}) error { - b, err := Marshal(target) - if err != nil { - return errors.Wrapf(err, "failed to marshal target value") - } - if err := p.Read(bytes.NewBuffer(b), v); err != nil { - return errors.Wrapf(err, "failed to read") - } - return nil -} - -// FilterFile filter from ast.File by YAMLPath. -func (p *Path) FilterFile(f *ast.File) (ast.Node, error) { - for _, doc := range f.Docs { - node, err := p.FilterNode(doc.Body) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node) - } - if node != nil { - return node, nil - } - } - return nil, errors.Wrapf(ErrNotFoundNode, "failed to find path ( %s )", p.node) -} - -// FilterNode filter from node by YAMLPath. -func (p *Path) FilterNode(node ast.Node) (ast.Node, error) { - n, err := p.node.filter(node) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node) - } - return n, nil -} - -// MergeFromReader merge YAML text into ast.File. -func (p *Path) MergeFromReader(dst *ast.File, src io.Reader) error { - var buf bytes.Buffer - if _, err := io.Copy(&buf, src); err != nil { - return errors.Wrapf(err, "failed to copy from reader") - } - file, err := parser.ParseBytes(buf.Bytes(), 0) - if err != nil { - return errors.Wrapf(err, "failed to parse") - } - if err := p.MergeFromFile(dst, file); err != nil { - return errors.Wrapf(err, "failed to merge file") - } - return nil -} - -// MergeFromFile merge ast.File into ast.File. -func (p *Path) MergeFromFile(dst *ast.File, src *ast.File) error { - base, err := p.FilterFile(dst) - if err != nil { - return errors.Wrapf(err, "failed to filter file") - } - for _, doc := range src.Docs { - if err := ast.Merge(base, doc); err != nil { - return errors.Wrapf(err, "failed to merge") - } - } - return nil -} - -// MergeFromNode merge ast.Node into ast.File. -func (p *Path) MergeFromNode(dst *ast.File, src ast.Node) error { - base, err := p.FilterFile(dst) - if err != nil { - return errors.Wrapf(err, "failed to filter file") - } - if err := ast.Merge(base, src); err != nil { - return errors.Wrapf(err, "failed to merge") - } - return nil -} - -// ReplaceWithReader replace ast.File with io.Reader. -func (p *Path) ReplaceWithReader(dst *ast.File, src io.Reader) error { - var buf bytes.Buffer - if _, err := io.Copy(&buf, src); err != nil { - return errors.Wrapf(err, "failed to copy from reader") - } - file, err := parser.ParseBytes(buf.Bytes(), 0) - if err != nil { - return errors.Wrapf(err, "failed to parse") - } - if err := p.ReplaceWithFile(dst, file); err != nil { - return errors.Wrapf(err, "failed to replace file") - } - return nil -} - -// ReplaceWithFile replace ast.File with ast.File. -func (p *Path) ReplaceWithFile(dst *ast.File, src *ast.File) error { - for _, doc := range src.Docs { - if err := p.ReplaceWithNode(dst, doc); err != nil { - return errors.Wrapf(err, "failed to replace file by path ( %s )", p.node) - } - } - return nil -} - -// ReplaceNode replace ast.File with ast.Node. -func (p *Path) ReplaceWithNode(dst *ast.File, node ast.Node) error { - for _, doc := range dst.Docs { - if node.Type() == ast.DocumentType { - node = node.(*ast.DocumentNode).Body - } - if err := p.node.replace(doc.Body, node); err != nil { - return errors.Wrapf(err, "failed to replace node by path ( %s )", p.node) - } - } - return nil -} - -// AnnotateSource add annotation to passed source ( see section 5.1 in README.md ). -func (p *Path) AnnotateSource(source []byte, colored bool) ([]byte, error) { - file, err := parser.ParseBytes([]byte(source), 0) - if err != nil { - return nil, err - } - node, err := p.FilterFile(file) - if err != nil { - return nil, err - } - var pp printer.Printer - return []byte(pp.PrintErrorToken(node.GetToken(), colored)), nil -} - -// PathBuilder represent builder for YAMLPath. -type PathBuilder struct { - root *rootNode - node pathNode -} - -// Root add '$' to current path. -func (b *PathBuilder) Root() *PathBuilder { - root := newRootNode() - return &PathBuilder{root: root, node: root} -} - -// IndexAll add '[*]' to current path. -func (b *PathBuilder) IndexAll() *PathBuilder { - b.node = b.node.chain(newIndexAllNode()) - return b -} - -// Recursive add '..selector' to current path. -func (b *PathBuilder) Recursive(selector string) *PathBuilder { - b.node = b.node.chain(newRecursiveNode(selector)) - return b -} - -func (b *PathBuilder) containsReservedPathCharacters(path string) bool { - if strings.Contains(path, ".") { - return true - } - if strings.Contains(path, "*") { - return true - } - return false -} - -func (b *PathBuilder) enclosedSingleQuote(name string) bool { - return strings.HasPrefix(name, "'") && strings.HasSuffix(name, "'") -} - -func (b *PathBuilder) normalizeSelectorName(name string) string { - if b.enclosedSingleQuote(name) { - // already escaped name - return name - } - if b.containsReservedPathCharacters(name) { - escapedName := strings.ReplaceAll(name, `'`, `\'`) - return "'" + escapedName + "'" - } - return name -} - -func (b *PathBuilder) child(name string) *PathBuilder { - b.node = b.node.chain(newSelectorNode(name)) - return b -} - -// Child add '.name' to current path. -func (b *PathBuilder) Child(name string) *PathBuilder { - return b.child(b.normalizeSelectorName(name)) -} - -// Index add '[idx]' to current path. -func (b *PathBuilder) Index(idx uint) *PathBuilder { - b.node = b.node.chain(newIndexNode(idx)) - return b -} - -// Build build YAMLPath. -func (b *PathBuilder) Build() *Path { - return &Path{node: b.root} -} - -type pathNode interface { - fmt.Stringer - chain(pathNode) pathNode - filter(ast.Node) (ast.Node, error) - replace(ast.Node, ast.Node) error -} - -type basePathNode struct { - child pathNode -} - -func (n *basePathNode) chain(node pathNode) pathNode { - n.child = node - return node -} - -type rootNode struct { - *basePathNode -} - -func newRootNode() *rootNode { - return &rootNode{basePathNode: &basePathNode{}} -} - -func (n *rootNode) String() string { - s := "$" - if n.child != nil { - s += n.child.String() - } - return s -} - -func (n *rootNode) filter(node ast.Node) (ast.Node, error) { - if n.child == nil { - return nil, nil - } - filtered, err := n.child.filter(node) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter") - } - return filtered, nil -} - -func (n *rootNode) replace(node ast.Node, target ast.Node) error { - if n.child == nil { - return nil - } - if err := n.child.replace(node, target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - return nil -} - -type selectorNode struct { - *basePathNode - selector string -} - -func newSelectorNode(selector string) *selectorNode { - return &selectorNode{ - basePathNode: &basePathNode{}, - selector: selector, - } -} - -func (n *selectorNode) filter(node ast.Node) (ast.Node, error) { - switch node.Type() { - case ast.MappingType: - for _, value := range node.(*ast.MappingNode).Values { - key := value.Key.GetToken().Value - if key == n.selector { - if n.child == nil { - return value.Value, nil - } - filtered, err := n.child.filter(value.Value) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter") - } - return filtered, nil - } - } - case ast.MappingValueType: - value := node.(*ast.MappingValueNode) - key := value.Key.GetToken().Value - if key == n.selector { - if n.child == nil { - return value.Value, nil - } - filtered, err := n.child.filter(value.Value) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter") - } - return filtered, nil - } - default: - return nil, errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type()) - } - return nil, nil -} - -func (n *selectorNode) replaceMapValue(value *ast.MappingValueNode, target ast.Node) error { - key := value.Key.GetToken().Value - if key != n.selector { - return nil - } - if n.child == nil { - if err := value.Replace(target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - } else { - if err := n.child.replace(value.Value, target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - } - return nil -} - -func (n *selectorNode) replace(node ast.Node, target ast.Node) error { - switch node.Type() { - case ast.MappingType: - for _, value := range node.(*ast.MappingNode).Values { - if err := n.replaceMapValue(value, target); err != nil { - return errors.Wrapf(err, "failed to replace map value") - } - } - case ast.MappingValueType: - value := node.(*ast.MappingValueNode) - if err := n.replaceMapValue(value, target); err != nil { - return errors.Wrapf(err, "failed to replace map value") - } - default: - return errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type()) - } - return nil -} - -func (n *selectorNode) String() string { - s := fmt.Sprintf(".%s", n.selector) - if n.child != nil { - s += n.child.String() - } - return s -} - -type indexNode struct { - *basePathNode - selector uint -} - -func newIndexNode(selector uint) *indexNode { - return &indexNode{ - basePathNode: &basePathNode{}, - selector: selector, - } -} - -func (n *indexNode) filter(node ast.Node) (ast.Node, error) { - if node.Type() != ast.SequenceType { - return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) - } - sequence := node.(*ast.SequenceNode) - if n.selector >= uint(len(sequence.Values)) { - return nil, errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values) - } - value := sequence.Values[n.selector] - if n.child == nil { - return value, nil - } - filtered, err := n.child.filter(value) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter") - } - return filtered, nil -} - -func (n *indexNode) replace(node ast.Node, target ast.Node) error { - if node.Type() != ast.SequenceType { - return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) - } - sequence := node.(*ast.SequenceNode) - if n.selector >= uint(len(sequence.Values)) { - return errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values) - } - if n.child == nil { - if err := sequence.Replace(int(n.selector), target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - return nil - } - if err := n.child.replace(sequence.Values[n.selector], target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - return nil -} - -func (n *indexNode) String() string { - s := fmt.Sprintf("[%d]", n.selector) - if n.child != nil { - s += n.child.String() - } - return s -} - -type indexAllNode struct { - *basePathNode -} - -func newIndexAllNode() *indexAllNode { - return &indexAllNode{ - basePathNode: &basePathNode{}, - } -} - -func (n *indexAllNode) String() string { - s := "[*]" - if n.child != nil { - s += n.child.String() - } - return s -} - -func (n *indexAllNode) filter(node ast.Node) (ast.Node, error) { - if node.Type() != ast.SequenceType { - return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) - } - sequence := node.(*ast.SequenceNode) - if n.child == nil { - return sequence, nil - } - out := *sequence - out.Values = []ast.Node{} - for _, value := range sequence.Values { - filtered, err := n.child.filter(value) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter") - } - out.Values = append(out.Values, filtered) - } - return &out, nil -} - -func (n *indexAllNode) replace(node ast.Node, target ast.Node) error { - if node.Type() != ast.SequenceType { - return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) - } - sequence := node.(*ast.SequenceNode) - if n.child == nil { - for idx := range sequence.Values { - if err := sequence.Replace(idx, target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - } - return nil - } - for _, value := range sequence.Values { - if err := n.child.replace(value, target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - } - return nil -} - -type recursiveNode struct { - *basePathNode - selector string -} - -func newRecursiveNode(selector string) *recursiveNode { - return &recursiveNode{ - basePathNode: &basePathNode{}, - selector: selector, - } -} - -func (n *recursiveNode) String() string { - s := fmt.Sprintf("..%s", n.selector) - if n.child != nil { - s += n.child.String() - } - return s -} - -func (n *recursiveNode) filterNode(node ast.Node) (*ast.SequenceNode, error) { - sequence := &ast.SequenceNode{BaseNode: &ast.BaseNode{}} - switch typedNode := node.(type) { - case *ast.MappingNode: - for _, value := range typedNode.Values { - seq, err := n.filterNode(value) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter") - } - sequence.Values = append(sequence.Values, seq.Values...) - } - case *ast.MappingValueNode: - key := typedNode.Key.GetToken().Value - if n.selector == key { - sequence.Values = append(sequence.Values, typedNode.Value) - } - seq, err := n.filterNode(typedNode.Value) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter") - } - sequence.Values = append(sequence.Values, seq.Values...) - case *ast.SequenceNode: - for _, value := range typedNode.Values { - seq, err := n.filterNode(value) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter") - } - sequence.Values = append(sequence.Values, seq.Values...) - } - } - return sequence, nil -} - -func (n *recursiveNode) filter(node ast.Node) (ast.Node, error) { - sequence, err := n.filterNode(node) - if err != nil { - return nil, errors.Wrapf(err, "failed to filter") - } - sequence.Start = node.GetToken() - return sequence, nil -} - -func (n *recursiveNode) replaceNode(node ast.Node, target ast.Node) error { - switch typedNode := node.(type) { - case *ast.MappingNode: - for _, value := range typedNode.Values { - if err := n.replaceNode(value, target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - } - case *ast.MappingValueNode: - key := typedNode.Key.GetToken().Value - if n.selector == key { - if err := typedNode.Replace(target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - } - if err := n.replaceNode(typedNode.Value, target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - case *ast.SequenceNode: - for _, value := range typedNode.Values { - if err := n.replaceNode(value, target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - } - } - return nil -} - -func (n *recursiveNode) replace(node ast.Node, target ast.Node) error { - if err := n.replaceNode(node, target); err != nil { - return errors.Wrapf(err, "failed to replace") - } - return nil -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/path_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/path_test.go deleted file mode 100644 index 4271da5..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/path_test.go +++ /dev/null @@ -1,659 +0,0 @@ -package yaml_test - -import ( - "fmt" - "log" - "reflect" - "strings" - "testing" - - "github.com/goccy/go-yaml" - "github.com/goccy/go-yaml/parser" -) - -func builder() *yaml.PathBuilder { return &yaml.PathBuilder{} } - -func TestPathBuilder(t *testing.T) { - tests := []struct { - expected string - path *yaml.Path - }{ - { - expected: `$.a.b[0]`, - path: builder().Root().Child("a").Child("b").Index(0).Build(), - }, - { - expected: `$.'a.b'.'c*d'`, - path: builder().Root().Child("a.b").Child("c*d").Build(), - }, - { - expected: `$.'a.b-*'.c`, - path: builder().Root().Child("a.b-*").Child("c").Build(), - }, - { - expected: `$.'a'.b`, - path: builder().Root().Child("'a'").Child("b").Build(), - }, - { - expected: `$.'a.b'.c`, - path: builder().Root().Child("'a.b'").Child("c").Build(), - }, - } - for _, test := range tests { - t.Run(test.expected, func(t *testing.T) { - expected := test.expected - got := test.path.String() - if expected != got { - t.Fatalf("failed to build path. expected:[%q] but got:[%q]", expected, got) - } - }) - } -} - -func TestPath(t *testing.T) { - yml := ` -store: - book: - - author: john - price: 10 - - author: ken - price: 12 - bicycle: - color: red - price: 19.95 -` - tests := []struct { - name string - path *yaml.Path - expected interface{} - }{ - { - name: "$.store.book[0].author", - path: builder().Root().Child("store").Child("book").Index(0).Child("author").Build(), - expected: "john", - }, - { - name: "$.store.book[1].price", - path: builder().Root().Child("store").Child("book").Index(1).Child("price").Build(), - expected: uint64(12), - }, - { - name: "$.store.book[*].author", - path: builder().Root().Child("store").Child("book").IndexAll().Child("author").Build(), - expected: []interface{}{"john", "ken"}, - }, - { - name: "$.store.book[0]", - path: builder().Root().Child("store").Child("book").Index(0).Build(), - expected: map[string]interface{}{"author": "john", "price": uint64(10)}, - }, - { - name: "$..author", - path: builder().Root().Recursive("author").Build(), - expected: []interface{}{"john", "ken"}, - }, - { - name: "$.store.bicycle.price", - path: builder().Root().Child("store").Child("bicycle").Child("price").Build(), - expected: float64(19.95), - }, - } - t.Run("PathString", func(t *testing.T) { - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - path, err := yaml.PathString(test.name) - if err != nil { - t.Fatalf("%+v", err) - } - if test.name != path.String() { - t.Fatalf("expected %s but actual %s", test.name, path.String()) - } - }) - } - }) - t.Run("string", func(t *testing.T) { - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if test.name != test.path.String() { - t.Fatalf("expected %s but actual %s", test.name, test.path.String()) - } - }) - } - }) - t.Run("read", func(t *testing.T) { - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var v interface{} - if err := test.path.Read(strings.NewReader(yml), &v); err != nil { - t.Fatalf("%+v", err) - } - if !reflect.DeepEqual(test.expected, v) { - t.Fatalf("expected %v(%T). but actual %v(%T)", test.expected, test.expected, v, v) - } - }) - } - }) - t.Run("filter", func(t *testing.T) { - var target interface{} - if err := yaml.Unmarshal([]byte(yml), &target); err != nil { - t.Fatalf("failed to unmarshal: %+v", err) - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var v interface{} - if err := test.path.Filter(target, &v); err != nil { - t.Fatalf("%+v", err) - } - if !reflect.DeepEqual(test.expected, v) { - t.Fatalf("expected %v(%T). but actual %v(%T)", test.expected, test.expected, v, v) - } - }) - } - }) -} - -func TestPath_ReservedKeyword(t *testing.T) { - tests := []struct { - name string - path string - src string - expected interface{} - failure bool - }{ - { - name: "quoted path", - path: `$.'a.b.c'.foo`, - src: ` -a.b.c: - foo: bar -`, - expected: "bar", - }, - { - name: "contains quote key", - path: `$.a'b`, - src: `a'b: 10`, - expected: uint64(10), - }, - { - name: "escaped quote", - path: `$.'alice\'s age'`, - src: `alice's age: 10`, - expected: uint64(10), - }, - { - name: "directly use white space", - path: `$.a b`, - src: `a b: 10`, - expected: uint64(10), - }, - { - name: "empty quoted key", - path: `$.''`, - src: `a: 10`, - failure: true, - }, - { - name: "unterminated quote", - path: `$.'abcd`, - src: `abcd: 10`, - failure: true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - path, err := yaml.PathString(test.path) - if test.failure { - if err == nil { - t.Fatal("expected error") - } - return - } else { - if err != nil { - t.Fatalf("%+v", err) - } - } - file, err := parser.ParseBytes([]byte(test.src), 0) - if err != nil { - t.Fatal(err) - } - var v interface{} - if err := path.Read(file, &v); err != nil { - t.Fatalf("%+v", err) - } - if v != test.expected { - t.Fatalf("failed to get value. expected:[%v] but got:[%v]", test.expected, v) - } - }) - } -} - -func TestPath_Invalid(t *testing.T) { - tests := []struct { - path string - src string - }{ - { - path: "$.wrong", - src: "foo: bar", - }, - } - for _, test := range tests { - path, err := yaml.PathString(test.path) - if err != nil { - t.Fatal(err) - } - t.Run("path.Read", func(t *testing.T) { - file, err := parser.ParseBytes([]byte(test.src), 0) - if err != nil { - t.Fatal(err) - } - var v interface{} - err = path.Read(file, &v) - if err == nil { - t.Fatal("expected error") - } - if !yaml.IsNotFoundNodeError(err) { - t.Fatalf("unexpected error %s", err) - } - }) - t.Run("path.ReadNode", func(t *testing.T) { - file, err := parser.ParseBytes([]byte(test.src), 0) - if err != nil { - t.Fatal(err) - } - _, err = path.ReadNode(file) - if err == nil { - t.Fatal("expected error") - } - if !yaml.IsNotFoundNodeError(err) { - t.Fatalf("unexpected error %s", err) - } - }) - } -} - -func TestPath_Merge(t *testing.T) { - tests := []struct { - path string - dst string - src string - expected string - }{ - { - path: "$.c", - dst: ` -a: 1 -b: 2 -c: - d: 3 - e: 4 -`, - src: ` -f: 5 -g: 6 -`, - expected: ` -a: 1 -b: 2 -c: - d: 3 - e: 4 - f: 5 - g: 6 -`, - }, - { - path: "$.a.b", - dst: ` -a: - b: - - 1 - - 2 -`, - src: ` -- 3 -- map: - - 4 - - 5 -`, - expected: ` -a: - b: - - 1 - - 2 - - 3 - - map: - - 4 - - 5 -`, - }, - } - for _, test := range tests { - t.Run(test.path, func(t *testing.T) { - path, err := yaml.PathString(test.path) - if err != nil { - t.Fatalf("%+v", err) - } - t.Run("FromReader", func(t *testing.T) { - file, err := parser.ParseBytes([]byte(test.dst), 0) - if err != nil { - t.Fatalf("%+v", err) - } - if err := path.MergeFromReader(file, strings.NewReader(test.src)); err != nil { - t.Fatalf("%+v", err) - } - actual := "\n" + file.String() - if test.expected != actual { - t.Fatalf("expected: %q. but got %q", test.expected, actual) - } - }) - t.Run("FromFile", func(t *testing.T) { - file, err := parser.ParseBytes([]byte(test.dst), 0) - if err != nil { - t.Fatalf("%+v", err) - } - src, err := parser.ParseBytes([]byte(test.src), 0) - if err != nil { - t.Fatalf("%+v", err) - } - if err := path.MergeFromFile(file, src); err != nil { - t.Fatalf("%+v", err) - } - actual := "\n" + file.String() - if test.expected != actual { - t.Fatalf("expected: %q. but got %q", test.expected, actual) - } - }) - t.Run("FromNode", func(t *testing.T) { - file, err := parser.ParseBytes([]byte(test.dst), 0) - if err != nil { - t.Fatalf("%+v", err) - } - src, err := parser.ParseBytes([]byte(test.src), 0) - if err != nil { - t.Fatalf("%+v", err) - } - if len(src.Docs) == 0 { - t.Fatalf("failed to parse") - } - if err := path.MergeFromNode(file, src.Docs[0]); err != nil { - t.Fatalf("%+v", err) - } - actual := "\n" + file.String() - if test.expected != actual { - t.Fatalf("expected: %q. but got %q", test.expected, actual) - } - }) - }) - } -} - -func TestPath_Replace(t *testing.T) { - tests := []struct { - path string - dst string - src string - expected string - }{ - { - path: "$.a", - dst: ` -a: 1 -b: 2 -`, - src: `3`, - expected: ` -a: 3 -b: 2 -`, - }, - { - path: "$.b", - dst: ` -b: 1 -c: 2 -`, - src: ` -d: e -f: - g: h - i: j -`, - expected: ` -b: - d: e - f: - g: h - i: j -c: 2 -`, - }, - { - path: "$.a.b[0]", - dst: ` -a: - b: - - hello -c: 2 -`, - src: `world`, - expected: ` -a: - b: - - world -c: 2 -`, - }, - - { - path: "$.books[*].author", - dst: ` -books: - - name: book_a - author: none - - name: book_b - author: none -pictures: - - name: picture_a - author: none - - name: picture_b - author: none -building: - author: none -`, - src: `ken`, - expected: ` -books: - - name: book_a - author: ken - - name: book_b - author: ken -pictures: - - name: picture_a - author: none - - name: picture_b - author: none -building: - author: none -`, - }, - { - path: "$..author", - dst: ` -books: - - name: book_a - author: none - - name: book_b - author: none -pictures: - - name: picture_a - author: none - - name: picture_b - author: none -building: - author: none -`, - src: `ken`, - expected: ` -books: - - name: book_a - author: ken - - name: book_b - author: ken -pictures: - - name: picture_a - author: ken - - name: picture_b - author: ken -building: - author: ken -`, - }, - } - for _, test := range tests { - t.Run(test.path, func(t *testing.T) { - path, err := yaml.PathString(test.path) - if err != nil { - t.Fatalf("%+v", err) - } - t.Run("WithReader", func(t *testing.T) { - file, err := parser.ParseBytes([]byte(test.dst), 0) - if err != nil { - t.Fatalf("%+v", err) - } - if err := path.ReplaceWithReader(file, strings.NewReader(test.src)); err != nil { - t.Fatalf("%+v", err) - } - actual := "\n" + file.String() - if test.expected != actual { - t.Fatalf("expected: %q. but got %q", test.expected, actual) - } - }) - t.Run("WithFile", func(t *testing.T) { - file, err := parser.ParseBytes([]byte(test.dst), 0) - if err != nil { - t.Fatalf("%+v", err) - } - src, err := parser.ParseBytes([]byte(test.src), 0) - if err != nil { - t.Fatalf("%+v", err) - } - if err := path.ReplaceWithFile(file, src); err != nil { - t.Fatalf("%+v", err) - } - actual := "\n" + file.String() - if test.expected != actual { - t.Fatalf("expected: %q. but got %q", test.expected, actual) - } - }) - t.Run("WithNode", func(t *testing.T) { - file, err := parser.ParseBytes([]byte(test.dst), 0) - if err != nil { - t.Fatalf("%+v", err) - } - src, err := parser.ParseBytes([]byte(test.src), 0) - if err != nil { - t.Fatalf("%+v", err) - } - if len(src.Docs) == 0 { - t.Fatalf("failed to parse") - } - if err := path.ReplaceWithNode(file, src.Docs[0]); err != nil { - t.Fatalf("%+v", err) - } - actual := "\n" + file.String() - if test.expected != actual { - t.Fatalf("expected: %q. but got %q", test.expected, actual) - } - }) - }) - } -} - -func ExamplePath_AnnotateSource() { - yml := ` -a: 1 -b: "hello" -` - var v struct { - A int - B string - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - panic(err) - } - if v.A != 2 { - // output error with YAML source - path, err := yaml.PathString("$.a") - if err != nil { - log.Fatal(err) - } - source, err := path.AnnotateSource([]byte(yml), false) - if err != nil { - log.Fatal(err) - } - fmt.Printf("a value expected 2 but actual %d:\n%s\n", v.A, string(source)) - } - // OUTPUT: - // a value expected 2 but actual 1: - // > 2 | a: 1 - // ^ - // 3 | b: "hello" -} - -func ExamplePath_AnnotateSourceWithComment() { - yml := ` -# This is my document -doc: - # This comment should be line 3 - map: - # And below should be line 5 - - value1 - - value2 - other: value3 - ` - path, err := yaml.PathString("$.doc.map[0]") - if err != nil { - log.Fatal(err) - } - msg, err := path.AnnotateSource([]byte(yml), false) - if err != nil { - log.Fatal(err) - } - fmt.Println(string(msg)) - // OUTPUT: - // 4 | # This comment should be line 3 - // 5 | map: - // 6 | # And below should be line 5 - // > 7 | - value1 - // ^ - // 8 | - value2 - // 9 | other: value3 - // 10 | -} - -func ExamplePath_PathString() { - yml := ` -store: - book: - - author: john - price: 10 - - author: ken - price: 12 - bicycle: - color: red - price: 19.95 -` - path, err := yaml.PathString("$.store.book[*].author") - if err != nil { - log.Fatal(err) - } - var authors []string - if err := path.Read(strings.NewReader(yml), &authors); err != nil { - log.Fatal(err) - } - fmt.Println(authors) - // OUTPUT: - // [john ken] -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/printer/printer.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/printer/printer.go deleted file mode 100644 index d5e25dc..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/printer/printer.go +++ /dev/null @@ -1,352 +0,0 @@ -package printer - -import ( - "fmt" - "math" - "strings" - - "github.com/fatih/color" - "github.com/goccy/go-yaml/ast" - "github.com/goccy/go-yaml/token" -) - -// Property additional property set for each the token -type Property struct { - Prefix string - Suffix string -} - -// PrintFunc returns property instance -type PrintFunc func() *Property - -// Printer create text from token collection or ast -type Printer struct { - LineNumber bool - LineNumberFormat func(num int) string - MapKey PrintFunc - Anchor PrintFunc - Alias PrintFunc - Bool PrintFunc - String PrintFunc - Number PrintFunc -} - -func defaultLineNumberFormat(num int) string { - return fmt.Sprintf("%2d | ", num) -} - -func (p *Printer) property(tk *token.Token) *Property { - prop := &Property{} - switch tk.PreviousType() { - case token.AnchorType: - if p.Anchor != nil { - return p.Anchor() - } - return prop - case token.AliasType: - if p.Alias != nil { - return p.Alias() - } - return prop - } - switch tk.NextType() { - case token.MappingValueType: - if p.MapKey != nil { - return p.MapKey() - } - return prop - } - switch tk.Type { - case token.BoolType: - if p.Bool != nil { - return p.Bool() - } - return prop - case token.AnchorType: - if p.Anchor != nil { - return p.Anchor() - } - return prop - case token.AliasType: - if p.Anchor != nil { - return p.Alias() - } - return prop - case token.StringType, token.SingleQuoteType, token.DoubleQuoteType: - if p.String != nil { - return p.String() - } - return prop - case token.IntegerType, token.FloatType: - if p.Number != nil { - return p.Number() - } - return prop - default: - } - return prop -} - -// PrintTokens create text from token collection -func (p *Printer) PrintTokens(tokens token.Tokens) string { - if len(tokens) == 0 { - return "" - } - if p.LineNumber { - if p.LineNumberFormat == nil { - p.LineNumberFormat = defaultLineNumberFormat - } - } - texts := []string{} - lineNumber := tokens[0].Position.Line - for _, tk := range tokens { - lines := strings.Split(tk.Origin, "\n") - prop := p.property(tk) - header := "" - if p.LineNumber { - header = p.LineNumberFormat(lineNumber) - } - if len(lines) == 1 { - line := prop.Prefix + lines[0] + prop.Suffix - if len(texts) == 0 { - texts = append(texts, header+line) - lineNumber++ - } else { - text := texts[len(texts)-1] - texts[len(texts)-1] = text + line - } - } else { - for idx, src := range lines { - if p.LineNumber { - header = p.LineNumberFormat(lineNumber) - } - line := prop.Prefix + src + prop.Suffix - if idx == 0 { - if len(texts) == 0 { - texts = append(texts, header+line) - lineNumber++ - } else { - text := texts[len(texts)-1] - texts[len(texts)-1] = text + line - } - } else { - texts = append(texts, fmt.Sprintf("%s%s", header, line)) - lineNumber++ - } - } - } - } - return strings.Join(texts, "\n") -} - -// PrintNode create text from ast.Node -func (p *Printer) PrintNode(node ast.Node) []byte { - return []byte(fmt.Sprintf("%+v\n", node)) -} - -const escape = "\x1b" - -func format(attr color.Attribute) string { - return fmt.Sprintf("%s[%dm", escape, attr) -} - -func (p *Printer) setDefaultColorSet() { - p.Bool = func() *Property { - return &Property{ - Prefix: format(color.FgHiMagenta), - Suffix: format(color.Reset), - } - } - p.Number = func() *Property { - return &Property{ - Prefix: format(color.FgHiMagenta), - Suffix: format(color.Reset), - } - } - p.MapKey = func() *Property { - return &Property{ - Prefix: format(color.FgHiCyan), - Suffix: format(color.Reset), - } - } - p.Anchor = func() *Property { - return &Property{ - Prefix: format(color.FgHiYellow), - Suffix: format(color.Reset), - } - } - p.Alias = func() *Property { - return &Property{ - Prefix: format(color.FgHiYellow), - Suffix: format(color.Reset), - } - } - p.String = func() *Property { - return &Property{ - Prefix: format(color.FgHiGreen), - Suffix: format(color.Reset), - } - } -} - -func (p *Printer) PrintErrorMessage(msg string, isColored bool) string { - if isColored { - return fmt.Sprintf("%s%s%s", - format(color.FgHiRed), - msg, - format(color.Reset), - ) - } - return msg -} - -func (p *Printer) removeLeftSideNewLineChar(src string) string { - return strings.TrimLeft(strings.TrimLeft(strings.TrimLeft(src, "\r"), "\n"), "\r\n") -} - -func (p *Printer) removeRightSideNewLineChar(src string) string { - return strings.TrimRight(strings.TrimRight(strings.TrimRight(src, "\r"), "\n"), "\r\n") -} - -func (p *Printer) removeRightSideWhiteSpaceChar(src string) string { - return p.removeRightSideNewLineChar(strings.TrimRight(src, " ")) -} - -func (p *Printer) newLineCount(s string) int { - src := []rune(s) - size := len(src) - cnt := 0 - for i := 0; i < size; i++ { - c := src[i] - switch c { - case '\r': - if i+1 < size && src[i+1] == '\n' { - i++ - } - cnt++ - case '\n': - cnt++ - } - } - return cnt -} - -func (p *Printer) isNewLineLastChar(s string) bool { - for i := len(s) - 1; i > 0; i-- { - c := s[i] - switch c { - case ' ': - continue - case '\n', '\r': - return true - } - break - } - return false -} - -func (p *Printer) printBeforeTokens(tk *token.Token, minLine, extLine int) token.Tokens { - for { - if tk.Prev == nil { - break - } - if tk.Prev.Position.Line < minLine { - break - } - tk = tk.Prev - } - minTk := tk.Clone() - if minTk.Prev != nil { - // add white spaces to minTk by prev token - prev := minTk.Prev - whiteSpaceLen := len(prev.Origin) - len(strings.TrimRight(prev.Origin, " ")) - minTk.Origin = strings.Repeat(" ", whiteSpaceLen) + minTk.Origin - } - minTk.Origin = p.removeLeftSideNewLineChar(minTk.Origin) - tokens := token.Tokens{minTk} - tk = minTk.Next - for tk != nil && tk.Position.Line <= extLine { - clonedTk := tk.Clone() - tokens.Add(clonedTk) - tk = clonedTk.Next - } - lastTk := tokens[len(tokens)-1] - trimmedOrigin := p.removeRightSideWhiteSpaceChar(lastTk.Origin) - suffix := lastTk.Origin[len(trimmedOrigin):] - lastTk.Origin = trimmedOrigin - - if lastTk.Next != nil && len(suffix) > 1 { - next := lastTk.Next.Clone() - // add suffix to header of next token - if suffix[0] == '\n' || suffix[0] == '\r' { - suffix = suffix[1:] - } - next.Origin = suffix + next.Origin - lastTk.Next = next - } - return tokens -} - -func (p *Printer) printAfterTokens(tk *token.Token, maxLine int) token.Tokens { - tokens := token.Tokens{} - if tk == nil { - return tokens - } - if tk.Position.Line > maxLine { - return tokens - } - minTk := tk.Clone() - minTk.Origin = p.removeLeftSideNewLineChar(minTk.Origin) - tokens.Add(minTk) - tk = minTk.Next - for tk != nil && tk.Position.Line <= maxLine { - clonedTk := tk.Clone() - tokens.Add(clonedTk) - tk = clonedTk.Next - } - return tokens -} - -func (p *Printer) setupErrorTokenFormat(annotateLine int, isColored bool) { - prefix := func(annotateLine, num int) string { - if annotateLine == num { - return fmt.Sprintf("> %2d | ", num) - } - return fmt.Sprintf(" %2d | ", num) - } - p.LineNumber = true - p.LineNumberFormat = func(num int) string { - if isColored { - fn := color.New(color.Bold, color.FgHiWhite).SprintFunc() - return fn(prefix(annotateLine, num)) - } - return prefix(annotateLine, num) - } - if isColored { - p.setDefaultColorSet() - } -} - -func (p *Printer) PrintErrorToken(tk *token.Token, isColored bool) string { - errToken := tk - curLine := tk.Position.Line - curExtLine := curLine + p.newLineCount(p.removeLeftSideNewLineChar(tk.Origin)) - if p.isNewLineLastChar(tk.Origin) { - // if last character ( exclude white space ) is new line character, ignore it. - curExtLine-- - } - - minLine := int(math.Max(float64(curLine-3), 1)) - maxLine := curExtLine + 3 - p.setupErrorTokenFormat(curLine, isColored) - - beforeTokens := p.printBeforeTokens(tk, minLine, curExtLine) - lastTk := beforeTokens[len(beforeTokens)-1] - afterTokens := p.printAfterTokens(lastTk.Next, maxLine) - - beforeSource := p.PrintTokens(beforeTokens) - prefixSpaceNum := len(fmt.Sprintf(" %2d | ", curLine)) - annotateLine := strings.Repeat(" ", prefixSpaceNum+errToken.Position.Column-1) + "^" - afterSource := p.PrintTokens(afterTokens) - return fmt.Sprintf("%s\n%s\n%s", beforeSource, annotateLine, afterSource) -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/printer/printer_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/printer/printer_test.go deleted file mode 100644 index 2afa74f..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/printer/printer_test.go +++ /dev/null @@ -1,223 +0,0 @@ -package printer_test - -import ( - "fmt" - "testing" - - "github.com/goccy/go-yaml/lexer" - "github.com/goccy/go-yaml/printer" -) - -func Test_Printer(t *testing.T) { - yml := `--- -text: aaaa -text2: aaaa - bbbb - cccc - dddd - eeee -text3: ffff - gggg - hhhh - iiii - jjjj -bool: true -number: 10 -anchor: &x 1 -alias: *x -` - t.Run("print starting from tokens[3]", func(t *testing.T) { - tokens := lexer.Tokenize(yml) - var p printer.Printer - actual := "\n" + p.PrintErrorToken(tokens[3], false) - expect := ` - 1 | --- -> 2 | text: aaaa - ^ - 3 | text2: aaaa - 4 | bbbb - 5 | cccc - 6 | dddd - 7 | eeee - 8 | ` - if actual != expect { - t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expect, actual) - } - }) - t.Run("print starting from tokens[4]", func(t *testing.T) { - tokens := lexer.Tokenize(yml) - var p printer.Printer - actual := "\n" + p.PrintErrorToken(tokens[4], false) - expect := ` - 1 | --- - 2 | text: aaaa -> 3 | text2: aaaa - 4 | bbbb - 5 | cccc - 6 | dddd - 7 | eeee - ^ -` - if actual != expect { - t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expect, actual) - } - }) - t.Run("print starting from tokens[6]", func(t *testing.T) { - tokens := lexer.Tokenize(yml) - var p printer.Printer - actual := "\n" + p.PrintErrorToken(tokens[6], false) - expect := ` - 1 | --- - 2 | text: aaaa -> 3 | text2: aaaa - 4 | bbbb - 5 | cccc - 6 | dddd - 7 | eeee - ^ - 8 | text3: ffff - 9 | gggg - 10 | hhhh - 11 | iiii - 12 | jjjj - 13 | ` - if actual != expect { - t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expect, actual) - } - }) - t.Run("print error token with document header", func(t *testing.T) { - tokens := lexer.Tokenize(`--- -a: - b: - c: - d: e - f: g - h: i - ---- -`) - expect := ` - 3 | b: - 4 | c: - 5 | d: e -> 6 | f: g - ^ - 7 | h: i - 8 | - 9 | ---` - var p printer.Printer - actual := "\n" + p.PrintErrorToken(tokens[12], false) - if actual != expect { - t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expect, actual) - } - }) - t.Run("output with color", func(t *testing.T) { - t.Run("token6", func(t *testing.T) { - tokens := lexer.Tokenize(yml) - var p printer.Printer - t.Logf("\n%s", p.PrintErrorToken(tokens[6], true)) - }) - t.Run("token9", func(t *testing.T) { - tokens := lexer.Tokenize(yml) - var p printer.Printer - t.Logf("\n%s", p.PrintErrorToken(tokens[9], true)) - }) - t.Run("token12", func(t *testing.T) { - tokens := lexer.Tokenize(yml) - var p printer.Printer - t.Logf("\n%s", p.PrintErrorToken(tokens[12], true)) - }) - }) - t.Run("print error message", func(t *testing.T) { - var p printer.Printer - src := "message" - msg := p.PrintErrorMessage(src, false) - if msg != src { - t.Fatal("unexpected result") - } - p.PrintErrorMessage(src, true) - }) -} - -func TestPrinter_Anchor(t *testing.T) { - expected := ` -anchor: &x 1 -alias: *x` - tokens := lexer.Tokenize(expected) - var p printer.Printer - got := p.PrintTokens(tokens) - if expected != got { - t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expected, got) - } -} - -func Test_Printer_Multiline(t *testing.T) { - yml := ` -text1: 'aaaa - bbbb - cccc' -text2: "ffff - gggg - hhhh" -text3: hello -` - tc := []struct { - token int - want string - }{ - { - token: 2, - want: ` -> 2 | text1: 'aaaa - 3 | bbbb - 4 | cccc' - ^ - 5 | text2: "ffff - 6 | gggg - 7 | hhhh"`, - }, - {token: 3, - want: ` - 2 | text1: 'aaaa - 3 | bbbb - 4 | cccc' -> 5 | text2: "ffff - 6 | gggg - 7 | hhhh" - ^ - 8 | text3: hello`, - }, - {token: 5, - want: ` - 2 | text1: 'aaaa - 3 | bbbb - 4 | cccc' -> 5 | text2: "ffff - 6 | gggg - 7 | hhhh" - ^ - 8 | text3: hello`, - }, - {token: 6, - want: ` - 5 | text2: "ffff - 6 | gggg - 7 | hhhh" -> 8 | text3: hello - ^ -`, - }, - } - for _, tt := range tc { - name := fmt.Sprintf("print starting from tokens[%d]", tt.token) - t.Run(name, func(t *testing.T) { - tokens := lexer.Tokenize(yml) - var p printer.Printer - got := "\n" + p.PrintErrorToken(tokens[tt.token], false) - want := tt.want - if got != want { - t.Fatalf("PrintErrorToken() got: %s\n want:%s\n", want, got) - } - }) - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/scanner/context.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/scanner/context.go deleted file mode 100644 index 09d0a2d..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/scanner/context.go +++ /dev/null @@ -1,229 +0,0 @@ -package scanner - -import ( - "sync" - - "github.com/goccy/go-yaml/token" -) - -const whitespace = ' ' - -// Context context at scanning -type Context struct { - idx int - size int - notSpaceCharPos int - notSpaceOrgCharPos int - src []rune - buf []rune - obuf []rune - tokens token.Tokens - isRawFolded bool - isLiteral bool - isFolded bool - isSingleLine bool - literalOpt string -} - -var ( - ctxPool = sync.Pool{ - New: func() interface{} { - return createContext() - }, - } -) - -func createContext() *Context { - return &Context{ - idx: 0, - tokens: token.Tokens{}, - isSingleLine: true, - } -} - -func newContext(src []rune) *Context { - ctx := ctxPool.Get().(*Context) - ctx.reset(src) - return ctx -} - -func (c *Context) release() { - ctxPool.Put(c) -} - -func (c *Context) reset(src []rune) { - c.idx = 0 - c.size = len(src) - c.src = src - c.tokens = c.tokens[:0] - c.resetBuffer() - c.isRawFolded = false - c.isSingleLine = true - c.isLiteral = false - c.isFolded = false - c.literalOpt = "" -} - -func (c *Context) resetBuffer() { - c.buf = c.buf[:0] - c.obuf = c.obuf[:0] - c.notSpaceCharPos = 0 - c.notSpaceOrgCharPos = 0 -} - -func (c *Context) isSaveIndentMode() bool { - return c.isLiteral || c.isFolded || c.isRawFolded -} - -func (c *Context) breakLiteral() { - c.isLiteral = false - c.isRawFolded = false - c.isFolded = false - c.literalOpt = "" -} - -func (c *Context) addToken(tk *token.Token) { - if tk == nil { - return - } - c.tokens = append(c.tokens, tk) -} - -func (c *Context) addBuf(r rune) { - if len(c.buf) == 0 && r == ' ' { - return - } - c.buf = append(c.buf, r) - if r != ' ' && r != '\t' { - c.notSpaceCharPos = len(c.buf) - } -} - -func (c *Context) addOriginBuf(r rune) { - c.obuf = append(c.obuf, r) - if r != ' ' && r != '\t' { - c.notSpaceOrgCharPos = len(c.obuf) - } -} - -func (c *Context) removeRightSpaceFromBuf() int { - trimmedBuf := c.obuf[:c.notSpaceOrgCharPos] - buflen := len(trimmedBuf) - diff := len(c.obuf) - buflen - if diff > 0 { - c.obuf = c.obuf[:buflen] - c.buf = c.bufferedSrc() - } - return diff -} - -func (c *Context) isDocument() bool { - return c.isLiteral || c.isFolded || c.isRawFolded -} - -func (c *Context) isEOS() bool { - return len(c.src)-1 <= c.idx -} - -func (c *Context) isNextEOS() bool { - return len(c.src)-1 <= c.idx+1 -} - -func (c *Context) next() bool { - return c.idx < c.size -} - -func (c *Context) source(s, e int) string { - return string(c.src[s:e]) -} - -func (c *Context) previousChar() rune { - if c.idx > 0 { - return c.src[c.idx-1] - } - return rune(0) -} - -func (c *Context) currentChar() rune { - if c.size > c.idx { - return c.src[c.idx] - } - return rune(0) -} - -func (c *Context) currentCharWithSkipWhitespace() rune { - idx := c.idx - for c.size > idx { - ch := c.src[idx] - if ch != whitespace { - return ch - } - idx++ - } - return rune(0) -} - -func (c *Context) nextChar() rune { - if c.size > c.idx+1 { - return c.src[c.idx+1] - } - return rune(0) -} - -func (c *Context) repeatNum(r rune) int { - cnt := 0 - for i := c.idx; i < c.size; i++ { - if c.src[i] == r { - cnt++ - } else { - break - } - } - return cnt -} - -func (c *Context) progress(num int) { - c.idx += num -} - -func (c *Context) nextPos() int { - return c.idx + 1 -} - -func (c *Context) existsBuffer() bool { - return len(c.bufferedSrc()) != 0 -} - -func (c *Context) bufferedSrc() []rune { - src := c.buf[:c.notSpaceCharPos] - if len(src) > 0 && src[len(src)-1] == '\n' && c.isDocument() && c.literalOpt == "-" { - // remove end '\n' character - src = src[:len(src)-1] - } - return src -} - -func (c *Context) bufferedToken(pos *token.Position) *token.Token { - if c.idx == 0 { - return nil - } - source := c.bufferedSrc() - if len(source) == 0 { - return nil - } - var tk *token.Token - if c.isDocument() { - tk = token.String(string(source), string(c.obuf), pos) - } else { - tk = token.New(string(source), string(c.obuf), pos) - } - c.resetBuffer() - return tk -} - -func (c *Context) lastToken() *token.Token { - if len(c.tokens) != 0 { - return c.tokens[len(c.tokens)-1] - } - return nil -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/scanner/scanner.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/scanner/scanner.go deleted file mode 100644 index ce9c665..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/scanner/scanner.go +++ /dev/null @@ -1,903 +0,0 @@ -package scanner - -import ( - "io" - "strings" - - "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" -) - -// IndentState state for indent -type IndentState int - -const ( - // IndentStateEqual equals previous indent - IndentStateEqual IndentState = iota - // IndentStateUp more indent than previous - IndentStateUp - // IndentStateDown less indent than previous - IndentStateDown - // IndentStateKeep uses not indent token - IndentStateKeep -) - -// Scanner holds the scanner's internal state while processing a given text. -// It can be allocated as part of another data structure but must be initialized via Init before use. -type Scanner struct { - source []rune - sourcePos int - sourceSize int - line int - column int - offset int - prevIndentLevel int - prevIndentNum int - prevIndentColumn int - docStartColumn int - indentLevel int - indentNum int - isFirstCharAtLine bool - isAnchor bool - startedFlowSequenceNum int - startedFlowMapNum int - indentState IndentState - savedPos *token.Position -} - -func (s *Scanner) pos() *token.Position { - return &token.Position{ - Line: s.line, - Column: s.column, - Offset: s.offset, - IndentNum: s.indentNum, - IndentLevel: s.indentLevel, - } -} - -func (s *Scanner) bufferedToken(ctx *Context) *token.Token { - if s.savedPos != nil { - tk := ctx.bufferedToken(s.savedPos) - s.savedPos = nil - return tk - } - line := s.line - column := s.column - len(ctx.buf) - level := s.indentLevel - if ctx.isSaveIndentMode() { - line -= s.newLineCount(ctx.buf) - column = strings.Index(string(ctx.obuf), string(ctx.buf)) + 1 - // Since we are in a literal, folded or raw folded - // we can use the indent level from the last token. - last := ctx.lastToken() - if last != nil { // The last token should never be nil here. - level = last.Position.IndentLevel + 1 - } - } - return ctx.bufferedToken(&token.Position{ - Line: line, - Column: column, - Offset: s.offset - len(ctx.buf), - IndentNum: s.indentNum, - IndentLevel: level, - }) -} - -func (s *Scanner) progressColumn(ctx *Context, num int) { - s.column += num - s.offset += num - ctx.progress(num) -} - -func (s *Scanner) progressLine(ctx *Context) { - s.column = 1 - s.line++ - s.offset++ - s.indentNum = 0 - s.isFirstCharAtLine = true - s.isAnchor = false - ctx.progress(1) -} - -func (s *Scanner) isNeededKeepPreviousIndentNum(ctx *Context, c rune) bool { - if !s.isChangedToIndentStateUp() { - return false - } - if ctx.isDocument() { - return true - } - if c == '-' && ctx.existsBuffer() { - return true - } - return false -} - -func (s *Scanner) isNewLineChar(c rune) bool { - if c == '\n' { - return true - } - if c == '\r' { - return true - } - return false -} - -func (s *Scanner) newLineCount(src []rune) int { - size := len(src) - cnt := 0 - for i := 0; i < size; i++ { - c := src[i] - switch c { - case '\r': - if i+1 < size && src[i+1] == '\n' { - i++ - } - cnt++ - case '\n': - cnt++ - } - } - return cnt -} - -func (s *Scanner) updateIndentState(ctx *Context) { - indentNumBasedIndentState := s.indentState - if s.prevIndentNum < s.indentNum { - s.indentLevel = s.prevIndentLevel + 1 - indentNumBasedIndentState = IndentStateUp - } else if s.prevIndentNum == s.indentNum { - s.indentLevel = s.prevIndentLevel - indentNumBasedIndentState = IndentStateEqual - } else { - indentNumBasedIndentState = IndentStateDown - if s.prevIndentLevel > 0 { - s.indentLevel = s.prevIndentLevel - 1 - } - } - - if s.prevIndentColumn > 0 { - if s.prevIndentColumn < s.column { - s.indentState = IndentStateUp - } else if s.prevIndentColumn != s.column || indentNumBasedIndentState != IndentStateEqual { - // The following case ( current position is 'd' ), some variables becomes like here - // - prevIndentColumn: 1 of 'a' - // - indentNumBasedIndentState: IndentStateDown because d's indentNum(1) is less than c's indentNum(3). - // Therefore, s.prevIndentColumn(1) == s.column(1) is true, but we want to treat this as IndentStateDown. - // So, we look also current indentState value by the above prevIndentNum based logic, and determins finally indentState. - // --- - // a: - // b - // c - // d: e - // ^ - s.indentState = IndentStateDown - } else { - s.indentState = IndentStateEqual - } - } else { - s.indentState = indentNumBasedIndentState - } -} - -func (s *Scanner) updateIndent(ctx *Context, c rune) { - if s.isFirstCharAtLine && s.isNewLineChar(c) && ctx.isDocument() { - return - } - if s.isFirstCharAtLine && c == ' ' { - s.indentNum++ - return - } - if !s.isFirstCharAtLine { - s.indentState = IndentStateKeep - return - } - s.updateIndentState(ctx) - s.isFirstCharAtLine = false - if s.isNeededKeepPreviousIndentNum(ctx, c) { - return - } - if s.indentState != IndentStateUp { - s.prevIndentColumn = 0 - } - s.prevIndentNum = s.indentNum - s.prevIndentLevel = s.indentLevel -} - -func (s *Scanner) isChangedToIndentStateDown() bool { - return s.indentState == IndentStateDown -} - -func (s *Scanner) isChangedToIndentStateUp() bool { - return s.indentState == IndentStateUp -} - -func (s *Scanner) isChangedToIndentStateEqual() bool { - return s.indentState == IndentStateEqual -} - -func (s *Scanner) addBufferedTokenIfExists(ctx *Context) { - ctx.addToken(s.bufferedToken(ctx)) -} - -func (s *Scanner) breakLiteral(ctx *Context) { - s.docStartColumn = 0 - ctx.breakLiteral() -} - -func (s *Scanner) scanSingleQuote(ctx *Context) (tk *token.Token, pos int) { - ctx.addOriginBuf('\'') - srcpos := s.pos() - startIndex := ctx.idx + 1 - src := ctx.src - size := len(src) - value := []rune{} - isFirstLineChar := false - isNewLine := false - for idx := startIndex; idx < size; idx++ { - if !isNewLine { - s.progressColumn(ctx, 1) - } else { - isNewLine = false - } - c := src[idx] - pos = idx + 1 - ctx.addOriginBuf(c) - if s.isNewLineChar(c) { - value = append(value, ' ') - isFirstLineChar = true - isNewLine = true - s.progressLine(ctx) - continue - } else if c == ' ' && isFirstLineChar { - continue - } else if c != '\'' { - value = append(value, c) - isFirstLineChar = false - continue - } - if idx+1 < len(ctx.src) && ctx.src[idx+1] == '\'' { - // '' handle as ' character - value = append(value, c) - ctx.addOriginBuf(c) - idx++ - continue - } - s.progressColumn(ctx, 1) - tk = token.SingleQuote(string(value), string(ctx.obuf), srcpos) - pos = idx - startIndex + 1 - return - } - return -} - -func hexToInt(b rune) int { - if b >= 'A' && b <= 'F' { - return int(b) - 'A' + 10 - } - if b >= 'a' && b <= 'f' { - return int(b) - 'a' + 10 - } - return int(b) - '0' -} - -func hexRunesToInt(b []rune) int { - sum := 0 - for i := 0; i < len(b); i++ { - sum += hexToInt(b[i]) << (uint(len(b)-i-1) * 4) - } - return sum -} - -func (s *Scanner) scanDoubleQuote(ctx *Context) (tk *token.Token, pos int) { - ctx.addOriginBuf('"') - srcpos := s.pos() - startIndex := ctx.idx + 1 - src := ctx.src - size := len(src) - value := []rune{} - isFirstLineChar := false - isNewLine := false - for idx := startIndex; idx < size; idx++ { - if !isNewLine { - s.progressColumn(ctx, 1) - } else { - isNewLine = false - } - c := src[idx] - pos = idx + 1 - ctx.addOriginBuf(c) - if s.isNewLineChar(c) { - value = append(value, ' ') - isFirstLineChar = true - isNewLine = true - s.progressLine(ctx) - continue - } else if c == ' ' && isFirstLineChar { - continue - } else if c == '\\' { - isFirstLineChar = false - if idx+1 < size { - nextChar := src[idx+1] - switch nextChar { - case 'b': - ctx.addOriginBuf(nextChar) - value = append(value, '\b') - idx++ - continue - case 'e': - ctx.addOriginBuf(nextChar) - value = append(value, '\x1B') - idx++ - continue - case 'f': - ctx.addOriginBuf(nextChar) - value = append(value, '\f') - idx++ - continue - case 'n': - ctx.addOriginBuf(nextChar) - value = append(value, '\n') - idx++ - continue - case 'v': - ctx.addOriginBuf(nextChar) - value = append(value, '\v') - idx++ - continue - case 'L': // LS (#x2028) - ctx.addOriginBuf(nextChar) - value = append(value, []rune{'\xE2', '\x80', '\xA8'}...) - idx++ - continue - case 'N': // NEL (#x85) - ctx.addOriginBuf(nextChar) - value = append(value, []rune{'\xC2', '\x85'}...) - idx++ - continue - case 'P': // PS (#x2029) - ctx.addOriginBuf(nextChar) - value = append(value, []rune{'\xE2', '\x80', '\xA9'}...) - idx++ - continue - case '_': // #xA0 - ctx.addOriginBuf(nextChar) - value = append(value, []rune{'\xC2', '\xA0'}...) - idx++ - continue - case '"': - ctx.addOriginBuf(nextChar) - value = append(value, nextChar) - idx++ - continue - case 'x': - if idx+3 >= size { - // TODO: need to return error - //err = xerrors.New("invalid escape character \\x") - return - } - codeNum := hexRunesToInt(src[idx+2 : idx+4]) - value = append(value, rune(codeNum)) - idx += 3 - continue - case 'u': - if idx+5 >= size { - // TODO: need to return error - //err = xerrors.New("invalid escape character \\u") - return - } - codeNum := hexRunesToInt(src[idx+2 : idx+6]) - value = append(value, rune(codeNum)) - idx += 5 - continue - case 'U': - if idx+9 >= size { - // TODO: need to return error - //err = xerrors.New("invalid escape character \\U") - return - } - codeNum := hexRunesToInt(src[idx+2 : idx+10]) - value = append(value, rune(codeNum)) - idx += 9 - continue - case '\\': - ctx.addOriginBuf(nextChar) - idx++ - } - } - value = append(value, c) - continue - } else if c != '"' { - value = append(value, c) - isFirstLineChar = false - continue - } - s.progressColumn(ctx, 1) - tk = token.DoubleQuote(string(value), string(ctx.obuf), srcpos) - pos = idx - startIndex + 1 - return - } - return -} - -func (s *Scanner) scanQuote(ctx *Context, ch rune) (tk *token.Token, pos int) { - if ch == '\'' { - return s.scanSingleQuote(ctx) - } - return s.scanDoubleQuote(ctx) -} - -func (s *Scanner) isMergeKey(ctx *Context) bool { - if ctx.repeatNum('<') != 2 { - return false - } - src := ctx.src - size := len(src) - for idx := ctx.idx + 2; idx < size; idx++ { - c := src[idx] - if c == ' ' { - continue - } - if c != ':' { - return false - } - if idx+1 < size { - nc := src[idx+1] - if nc == ' ' || s.isNewLineChar(nc) { - return true - } - } - } - return false -} - -func (s *Scanner) scanTag(ctx *Context) (tk *token.Token, pos int) { - ctx.addOriginBuf('!') - ctx.progress(1) // skip '!' character - for idx, c := range ctx.src[ctx.idx:] { - pos = idx + 1 - ctx.addOriginBuf(c) - switch c { - case ' ', '\n', '\r': - value := ctx.source(ctx.idx-1, ctx.idx+idx) - tk = token.Tag(value, string(ctx.obuf), s.pos()) - pos = len([]rune(value)) - return - } - } - return -} - -func (s *Scanner) scanComment(ctx *Context) (tk *token.Token, pos int) { - ctx.addOriginBuf('#') - ctx.progress(1) // skip '#' character - for idx, c := range ctx.src[ctx.idx:] { - pos = idx + 1 - ctx.addOriginBuf(c) - switch c { - case '\n', '\r': - if ctx.previousChar() == '\\' { - continue - } - value := ctx.source(ctx.idx, ctx.idx+idx) - tk = token.Comment(value, string(ctx.obuf), s.pos()) - pos = len([]rune(value)) + 1 - return - } - } - // document ends with comment. - value := string(ctx.src[ctx.idx:]) - tk = token.Comment(value, string(ctx.obuf), s.pos()) - pos = len([]rune(value)) + 1 - return -} - -func trimCommentFromLiteralOpt(text string) (string, error) { - idx := strings.Index(text, "#") - if idx < 0 { - return text, nil - } - if idx == 0 { - return "", xerrors.New("invalid literal header") - } - return text[:idx-1], nil -} - -func (s *Scanner) scanLiteral(ctx *Context, c rune) { - ctx.addOriginBuf(c) - if ctx.isEOS() { - if ctx.isLiteral { - ctx.addBuf(c) - } - value := ctx.bufferedSrc() - ctx.addToken(token.String(string(value), string(ctx.obuf), s.pos())) - ctx.resetBuffer() - s.progressColumn(ctx, 1) - } else if s.isNewLineChar(c) { - if ctx.isLiteral { - ctx.addBuf(c) - } else { - ctx.addBuf(' ') - } - s.progressLine(ctx) - } else if s.isFirstCharAtLine && c == ' ' { - if 0 < s.docStartColumn && s.docStartColumn <= s.column { - ctx.addBuf(c) - } - s.progressColumn(ctx, 1) - } else { - if s.docStartColumn == 0 { - s.docStartColumn = s.column - } - ctx.addBuf(c) - s.progressColumn(ctx, 1) - } -} - -func (s *Scanner) scanLiteralHeader(ctx *Context) (pos int, err error) { - header := ctx.currentChar() - ctx.addOriginBuf(header) - ctx.progress(1) // skip '|' or '>' character - for idx, c := range ctx.src[ctx.idx:] { - pos = idx - ctx.addOriginBuf(c) - switch c { - case '\n', '\r': - value := ctx.source(ctx.idx, ctx.idx+idx) - opt := strings.TrimRight(value, " ") - orgOptLen := len(opt) - opt, err = trimCommentFromLiteralOpt(opt) - if err != nil { - return - } - switch opt { - case "", "+", "-", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": - hasComment := len(opt) < orgOptLen - if header == '|' { - if hasComment { - commentLen := orgOptLen - len(opt) - headerPos := strings.Index(string(ctx.obuf), "|") - litBuf := ctx.obuf[:len(ctx.obuf)-commentLen-headerPos] - commentBuf := ctx.obuf[len(litBuf):] - ctx.addToken(token.Literal("|"+opt, string(litBuf), s.pos())) - s.column += len(litBuf) - s.offset += len(litBuf) - commentHeader := strings.Index(value, "#") - ctx.addToken(token.Comment(string(value[commentHeader+1:]), string(commentBuf), s.pos())) - } else { - ctx.addToken(token.Literal("|"+opt, string(ctx.obuf), s.pos())) - } - ctx.isLiteral = true - } else if header == '>' { - if hasComment { - commentLen := orgOptLen - len(opt) - headerPos := strings.Index(string(ctx.obuf), ">") - foldedBuf := ctx.obuf[:len(ctx.obuf)-commentLen-headerPos] - commentBuf := ctx.obuf[len(foldedBuf):] - ctx.addToken(token.Folded(">"+opt, string(foldedBuf), s.pos())) - s.column += len(foldedBuf) - s.offset += len(foldedBuf) - commentHeader := strings.Index(value, "#") - ctx.addToken(token.Comment(string(value[commentHeader+1:]), string(commentBuf), s.pos())) - } else { - ctx.addToken(token.Folded(">"+opt, string(ctx.obuf), s.pos())) - } - ctx.isFolded = true - } - s.indentState = IndentStateKeep - ctx.resetBuffer() - ctx.literalOpt = opt - return - } - break - } - } - err = xerrors.New("invalid literal header") - return -} - -func (s *Scanner) scanNewLine(ctx *Context, c rune) { - if len(ctx.buf) > 0 && s.savedPos == nil { - s.savedPos = s.pos() - s.savedPos.Column -= len(ctx.bufferedSrc()) - } - - // if the following case, origin buffer has unnecessary two spaces. - // So, `removeRightSpaceFromOriginBuf` remove them, also fix column number too. - // --- - // a:[space][space] - // b: c - removedNum := ctx.removeRightSpaceFromBuf() - if removedNum > 0 { - s.column -= removedNum - s.offset -= removedNum - if s.savedPos != nil { - s.savedPos.Column -= removedNum - } - } - - if ctx.isEOS() { - s.addBufferedTokenIfExists(ctx) - } else if s.isAnchor { - s.addBufferedTokenIfExists(ctx) - } - ctx.addBuf(' ') - ctx.addOriginBuf(c) - ctx.isSingleLine = false - s.progressLine(ctx) -} - -func (s *Scanner) scan(ctx *Context) (pos int) { - for ctx.next() { - pos = ctx.nextPos() - c := ctx.currentChar() - s.updateIndent(ctx, c) - if ctx.isDocument() { - if s.isChangedToIndentStateEqual() || - s.isChangedToIndentStateDown() { - s.addBufferedTokenIfExists(ctx) - s.breakLiteral(ctx) - } else { - s.scanLiteral(ctx, c) - continue - } - } else if s.isChangedToIndentStateDown() { - s.addBufferedTokenIfExists(ctx) - } else if s.isChangedToIndentStateEqual() { - // if first character is new line character, buffer expect to raw folded literal - if len(ctx.obuf) > 0 && s.newLineCount(ctx.obuf) <= 1 { - // doesn't raw folded literal - s.addBufferedTokenIfExists(ctx) - } - } - switch c { - case '{': - if !ctx.existsBuffer() { - ctx.addOriginBuf(c) - ctx.addToken(token.MappingStart(string(ctx.obuf), s.pos())) - s.startedFlowMapNum++ - s.progressColumn(ctx, 1) - return - } - case '}': - if !ctx.existsBuffer() || s.startedFlowMapNum > 0 { - ctx.addToken(s.bufferedToken(ctx)) - ctx.addOriginBuf(c) - ctx.addToken(token.MappingEnd(string(ctx.obuf), s.pos())) - s.startedFlowMapNum-- - s.progressColumn(ctx, 1) - return - } - case '.': - if s.indentNum == 0 && s.column == 1 && ctx.repeatNum('.') == 3 { - ctx.addToken(token.DocumentEnd(string(ctx.obuf)+"...", s.pos())) - s.progressColumn(ctx, 3) - pos += 2 - return - } - case '<': - if s.isMergeKey(ctx) { - s.prevIndentColumn = s.column - ctx.addToken(token.MergeKey(string(ctx.obuf)+"<<", s.pos())) - s.progressColumn(ctx, 1) - pos++ - return - } - case '-': - if s.indentNum == 0 && s.column == 1 && ctx.repeatNum('-') == 3 { - s.addBufferedTokenIfExists(ctx) - ctx.addToken(token.DocumentHeader(string(ctx.obuf)+"---", s.pos())) - s.progressColumn(ctx, 3) - pos += 2 - return - } - if ctx.existsBuffer() && s.isChangedToIndentStateUp() { - // raw folded - ctx.isRawFolded = true - ctx.addBuf(c) - ctx.addOriginBuf(c) - s.progressColumn(ctx, 1) - continue - } - if ctx.existsBuffer() { - // '-' is literal - ctx.addBuf(c) - ctx.addOriginBuf(c) - s.progressColumn(ctx, 1) - continue - } - nc := ctx.nextChar() - if nc == ' ' || s.isNewLineChar(nc) { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - tk := token.SequenceEntry(string(ctx.obuf), s.pos()) - s.prevIndentColumn = tk.Position.Column - ctx.addToken(tk) - s.progressColumn(ctx, 1) - return - } - case '[': - if !ctx.existsBuffer() { - ctx.addOriginBuf(c) - ctx.addToken(token.SequenceStart(string(ctx.obuf), s.pos())) - s.startedFlowSequenceNum++ - s.progressColumn(ctx, 1) - return - } - case ']': - if !ctx.existsBuffer() || s.startedFlowSequenceNum > 0 { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - ctx.addToken(token.SequenceEnd(string(ctx.obuf), s.pos())) - s.startedFlowSequenceNum-- - s.progressColumn(ctx, 1) - return - } - case ',': - if s.startedFlowSequenceNum > 0 || s.startedFlowMapNum > 0 { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - ctx.addToken(token.CollectEntry(string(ctx.obuf), s.pos())) - s.progressColumn(ctx, 1) - return - } - case ':': - nc := ctx.nextChar() - if s.startedFlowMapNum > 0 || nc == ' ' || s.isNewLineChar(nc) || ctx.isNextEOS() { - // mapping value - tk := s.bufferedToken(ctx) - if tk != nil { - s.prevIndentColumn = tk.Position.Column - ctx.addToken(tk) - } else if tk := ctx.lastToken(); tk != nil { - // If the map key is quote, the buffer does not exist because it has already been cut into tokens. - // Therefore, we need to check the last token. - if tk.Indicator == token.QuotedScalarIndicator { - s.prevIndentColumn = tk.Position.Column - } - } - ctx.addToken(token.MappingValue(s.pos())) - s.progressColumn(ctx, 1) - return - } - case '|', '>': - if !ctx.existsBuffer() { - progress, err := s.scanLiteralHeader(ctx) - if err != nil { - // TODO: returns syntax error object - return - } - s.progressColumn(ctx, progress) - s.progressLine(ctx) - continue - } - case '!': - if !ctx.existsBuffer() { - token, progress := s.scanTag(ctx) - ctx.addToken(token) - s.progressColumn(ctx, progress) - if c := ctx.previousChar(); s.isNewLineChar(c) { - s.progressLine(ctx) - } - pos += progress - return - } - case '%': - if !ctx.existsBuffer() && s.indentNum == 0 { - ctx.addToken(token.Directive(string(ctx.obuf)+"%", s.pos())) - s.progressColumn(ctx, 1) - return - } - case '?': - nc := ctx.nextChar() - if !ctx.existsBuffer() && nc == ' ' { - ctx.addToken(token.MappingKey(s.pos())) - s.progressColumn(ctx, 1) - return - } - case '&': - if !ctx.existsBuffer() { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - ctx.addToken(token.Anchor(string(ctx.obuf), s.pos())) - s.progressColumn(ctx, 1) - s.isAnchor = true - return - } - case '*': - if !ctx.existsBuffer() { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - ctx.addToken(token.Alias(string(ctx.obuf), s.pos())) - s.progressColumn(ctx, 1) - return - } - case '#': - if !ctx.existsBuffer() || ctx.previousChar() == ' ' { - s.addBufferedTokenIfExists(ctx) - token, progress := s.scanComment(ctx) - ctx.addToken(token) - s.progressColumn(ctx, progress) - s.progressLine(ctx) - pos += progress - return - } - case '\'', '"': - if !ctx.existsBuffer() { - token, progress := s.scanQuote(ctx, c) - ctx.addToken(token) - pos += progress - // If the non-whitespace character immediately following the quote is ':', the quote should be treated as a map key. - // Therefore, do not return and continue processing as a normal map key. - if ctx.currentCharWithSkipWhitespace() == ':' { - continue - } - return - } - case '\r', '\n': - // There is no problem that we ignore CR which followed by LF and normalize it to LF, because of following YAML1.2 spec. - // > Line breaks inside scalar content must be normalized by the YAML processor. Each such line break must be parsed into a single line feed character. - // > Outside scalar content, YAML allows any line break to be used to terminate lines. - // > -- https://yaml.org/spec/1.2/spec.html - if c == '\r' && ctx.nextChar() == '\n' { - ctx.addOriginBuf('\r') - ctx.progress(1) - c = '\n' - } - s.scanNewLine(ctx, c) - continue - case ' ': - if ctx.isSaveIndentMode() || (!s.isAnchor && !s.isFirstCharAtLine) { - ctx.addBuf(c) - ctx.addOriginBuf(c) - s.progressColumn(ctx, 1) - continue - } - if s.isFirstCharAtLine { - s.progressColumn(ctx, 1) - ctx.addOriginBuf(c) - continue - } - s.addBufferedTokenIfExists(ctx) - pos-- // to rescan white space at next scanning for adding white space to next buffer. - s.isAnchor = false - return - } - ctx.addBuf(c) - ctx.addOriginBuf(c) - s.progressColumn(ctx, 1) - } - s.addBufferedTokenIfExists(ctx) - return -} - -// Init prepares the scanner s to tokenize the text src by setting the scanner at the beginning of src. -func (s *Scanner) Init(text string) { - src := []rune(text) - s.source = src - s.sourcePos = 0 - s.sourceSize = len(src) - s.line = 1 - s.column = 1 - s.offset = 1 - s.prevIndentLevel = 0 - s.prevIndentNum = 0 - s.prevIndentColumn = 0 - s.indentLevel = 0 - s.indentNum = 0 - s.isFirstCharAtLine = true -} - -// Scan scans the next token and returns the token collection. The source end is indicated by io.EOF. -func (s *Scanner) Scan() (token.Tokens, error) { - if s.sourcePos >= s.sourceSize { - return nil, io.EOF - } - ctx := newContext(s.source[s.sourcePos:]) - defer ctx.release() - progress := s.scan(ctx) - s.sourcePos += progress - var tokens token.Tokens - tokens = append(tokens, ctx.tokens...) - return tokens, nil -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/stdlib_quote.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/stdlib_quote.go deleted file mode 100644 index be50ae6..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/stdlib_quote.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copied and trimmed down from https://github.com/golang/go/blob/e3769299cd3484e018e0e2a6e1b95c2b18ce4f41/src/strconv/quote.go -// We want to use the standard library's private "quoteWith" function rather than write our own so that we get robust unicode support. -// Every private function called by quoteWith was copied. -// There are 2 modifications to simplify the code: -// 1. The unicode.IsPrint function was substituted for the custom implementation of IsPrint -// 2. All code paths reachable only when ASCIIonly or grphicOnly are set to true were removed. - -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package yaml - -import ( - "unicode" - "unicode/utf8" -) - -const ( - lowerhex = "0123456789abcdef" -) - -func quoteWith(s string, quote byte) string { - return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote)) -} - -func appendQuotedWith(buf []byte, s string, quote byte) []byte { - // Often called with big strings, so preallocate. If there's quoting, - // this is conservative but still helps a lot. - if cap(buf)-len(buf) < len(s) { - nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1) - copy(nBuf, buf) - buf = nBuf - } - buf = append(buf, quote) - for width := 0; len(s) > 0; s = s[width:] { - r := rune(s[0]) - width = 1 - if r >= utf8.RuneSelf { - r, width = utf8.DecodeRuneInString(s) - } - if width == 1 && r == utf8.RuneError { - buf = append(buf, `\x`...) - buf = append(buf, lowerhex[s[0]>>4]) - buf = append(buf, lowerhex[s[0]&0xF]) - continue - } - buf = appendEscapedRune(buf, r, quote) - } - buf = append(buf, quote) - return buf -} - -func appendEscapedRune(buf []byte, r rune, quote byte) []byte { - var runeTmp [utf8.UTFMax]byte - if r == rune(quote) || r == '\\' { // always backslashed - buf = append(buf, '\\') - buf = append(buf, byte(r)) - return buf - } - if unicode.IsPrint(r) { - n := utf8.EncodeRune(runeTmp[:], r) - buf = append(buf, runeTmp[:n]...) - return buf - } - switch r { - case '\a': - buf = append(buf, `\a`...) - case '\b': - buf = append(buf, `\b`...) - case '\f': - buf = append(buf, `\f`...) - case '\n': - buf = append(buf, `\n`...) - case '\r': - buf = append(buf, `\r`...) - case '\t': - buf = append(buf, `\t`...) - case '\v': - buf = append(buf, `\v`...) - default: - switch { - case r < ' ': - buf = append(buf, `\x`...) - buf = append(buf, lowerhex[byte(r)>>4]) - buf = append(buf, lowerhex[byte(r)&0xF]) - case r > utf8.MaxRune: - r = 0xFFFD - fallthrough - case r < 0x10000: - buf = append(buf, `\u`...) - for s := 12; s >= 0; s -= 4 { - buf = append(buf, lowerhex[r>>uint(s)&0xF]) - } - default: - buf = append(buf, `\U`...) - for s := 28; s >= 0; s -= 4 { - buf = append(buf, lowerhex[r>>uint(s)&0xF]) - } - } - } - return buf -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/struct.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/struct.go deleted file mode 100644 index a3da8dd..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/struct.go +++ /dev/null @@ -1,130 +0,0 @@ -package yaml - -import ( - "reflect" - "strings" - - "golang.org/x/xerrors" -) - -const ( - // StructTagName tag keyword for Marshal/Unmarshal - StructTagName = "yaml" -) - -// StructField information for each the field in structure -type StructField struct { - FieldName string - RenderName string - AnchorName string - AliasName string - IsAutoAnchor bool - IsAutoAlias bool - IsOmitEmpty bool - IsFlow bool - IsInline bool -} - -func getTag(field reflect.StructField) string { - // If struct tag `yaml` exist, use that. If no `yaml` - // exists, but `json` does, use that and try the best to - // adhere to its rules - tag := field.Tag.Get(StructTagName) - if tag == "" { - tag = field.Tag.Get(`json`) - } - return tag -} - -func structField(field reflect.StructField) *StructField { - tag := getTag(field) - fieldName := strings.ToLower(field.Name) - options := strings.Split(tag, ",") - if len(options) > 0 { - if options[0] != "" { - fieldName = options[0] - } - } - structField := &StructField{ - FieldName: field.Name, - RenderName: fieldName, - } - if len(options) > 1 { - for _, opt := range options[1:] { - switch { - case opt == "omitempty": - structField.IsOmitEmpty = true - case opt == "flow": - structField.IsFlow = true - case opt == "inline": - structField.IsInline = true - case strings.HasPrefix(opt, "anchor"): - anchor := strings.Split(opt, "=") - if len(anchor) > 1 { - structField.AnchorName = anchor[1] - } else { - structField.IsAutoAnchor = true - } - case strings.HasPrefix(opt, "alias"): - alias := strings.Split(opt, "=") - if len(alias) > 1 { - structField.AliasName = alias[1] - } else { - structField.IsAutoAlias = true - } - default: - } - } - } - return structField -} - -func isIgnoredStructField(field reflect.StructField) bool { - if field.PkgPath != "" && !field.Anonymous { - // private field - return true - } - tag := getTag(field) - if tag == "-" { - return true - } - return false -} - -type StructFieldMap map[string]*StructField - -func (m StructFieldMap) isIncludedRenderName(name string) bool { - for _, v := range m { - if !v.IsInline && v.RenderName == name { - return true - } - } - return false -} - -func (m StructFieldMap) hasMergeProperty() bool { - for _, v := range m { - if v.IsOmitEmpty && v.IsInline && v.IsAutoAlias { - return true - } - } - return false -} - -func structFieldMap(structType reflect.Type) (StructFieldMap, error) { - structFieldMap := StructFieldMap{} - renderNameMap := map[string]struct{}{} - for i := 0; i < structType.NumField(); i++ { - field := structType.Field(i) - if isIgnoredStructField(field) { - continue - } - structField := structField(field) - if _, exists := renderNameMap[structField.RenderName]; exists { - return nil, xerrors.Errorf("duplicated struct field name %s", structField.RenderName) - } - structFieldMap[structField.FieldName] = structField - renderNameMap[structField.RenderName] = struct{}{} - } - return structFieldMap, nil -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/testdata/anchor.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/testdata/anchor.yml deleted file mode 100644 index c016c7f..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/testdata/anchor.yml +++ /dev/null @@ -1,3 +0,0 @@ -a: &a - b: 1 - c: hello diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/token/token.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/token/token.go deleted file mode 100644 index 182f4be..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/token/token.go +++ /dev/null @@ -1,1070 +0,0 @@ -package token - -import ( - "fmt" - "strings" -) - -// Character type for character -type Character byte - -const ( - // SequenceEntryCharacter character for sequence entry - SequenceEntryCharacter Character = '-' - // MappingKeyCharacter character for mapping key - MappingKeyCharacter Character = '?' - // MappingValueCharacter character for mapping value - MappingValueCharacter Character = ':' - // CollectEntryCharacter character for collect entry - CollectEntryCharacter Character = ',' - // SequenceStartCharacter character for sequence start - SequenceStartCharacter Character = '[' - // SequenceEndCharacter character for sequence end - SequenceEndCharacter Character = ']' - // MappingStartCharacter character for mapping start - MappingStartCharacter Character = '{' - // MappingEndCharacter character for mapping end - MappingEndCharacter Character = '}' - // CommentCharacter character for comment - CommentCharacter Character = '#' - // AnchorCharacter character for anchor - AnchorCharacter Character = '&' - // AliasCharacter character for alias - AliasCharacter Character = '*' - // TagCharacter character for tag - TagCharacter Character = '!' - // LiteralCharacter character for literal - LiteralCharacter Character = '|' - // FoldedCharacter character for folded - FoldedCharacter Character = '>' - // SingleQuoteCharacter character for single quote - SingleQuoteCharacter Character = '\'' - // DoubleQuoteCharacter character for double quote - DoubleQuoteCharacter Character = '"' - // DirectiveCharacter character for directive - DirectiveCharacter Character = '%' - // SpaceCharacter character for space - SpaceCharacter Character = ' ' - // LineBreakCharacter character for line break - LineBreakCharacter Character = '\n' -) - -// Type type identifier for token -type Type int - -const ( - // UnknownType reserve for invalid type - UnknownType Type = iota - // DocumentHeaderType type for DocumentHeader token - DocumentHeaderType - // DocumentEndType type for DocumentEnd token - DocumentEndType - // SequenceEntryType type for SequenceEntry token - SequenceEntryType - // MappingKeyType type for MappingKey token - MappingKeyType - // MappingValueType type for MappingValue token - MappingValueType - // MergeKeyType type for MergeKey token - MergeKeyType - // CollectEntryType type for CollectEntry token - CollectEntryType - // SequenceStartType type for SequenceStart token - SequenceStartType - // SequenceEndType type for SequenceEnd token - SequenceEndType - // MappingStartType type for MappingStart token - MappingStartType - // MappingEndType type for MappingEnd token - MappingEndType - // CommentType type for Comment token - CommentType - // AnchorType type for Anchor token - AnchorType - // AliasType type for Alias token - AliasType - // TagType type for Tag token - TagType - // LiteralType type for Literal token - LiteralType - // FoldedType type for Folded token - FoldedType - // SingleQuoteType type for SingleQuote token - SingleQuoteType - // DoubleQuoteType type for DoubleQuote token - DoubleQuoteType - // DirectiveType type for Directive token - DirectiveType - // SpaceType type for Space token - SpaceType - // NullType type for Null token - NullType - // InfinityType type for Infinity token - InfinityType - // NanType type for Nan token - NanType - // IntegerType type for Integer token - IntegerType - // BinaryIntegerType type for BinaryInteger token - BinaryIntegerType - // OctetIntegerType type for OctetInteger token - OctetIntegerType - // HexIntegerType type for HexInteger token - HexIntegerType - // FloatType type for Float token - FloatType - // StringType type for String token - StringType - // BoolType type for Bool token - BoolType -) - -// String type identifier to text -func (t Type) String() string { - switch t { - case UnknownType: - return "Unknown" - case DocumentHeaderType: - return "DocumentHeader" - case DocumentEndType: - return "DocumentEnd" - case SequenceEntryType: - return "SequenceEntry" - case MappingKeyType: - return "MappingKey" - case MappingValueType: - return "MappingValue" - case MergeKeyType: - return "MergeKey" - case CollectEntryType: - return "CollectEntry" - case SequenceStartType: - return "SequenceStart" - case SequenceEndType: - return "SequenceEnd" - case MappingStartType: - return "MappingStart" - case MappingEndType: - return "MappingEnd" - case CommentType: - return "Comment" - case AnchorType: - return "Anchor" - case AliasType: - return "Alias" - case TagType: - return "Tag" - case LiteralType: - return "Literal" - case FoldedType: - return "Folded" - case SingleQuoteType: - return "SingleQuote" - case DoubleQuoteType: - return "DoubleQuote" - case DirectiveType: - return "Directive" - case SpaceType: - return "Space" - case StringType: - return "String" - case BoolType: - return "Bool" - case IntegerType: - return "Integer" - case BinaryIntegerType: - return "BinaryInteger" - case OctetIntegerType: - return "OctetInteger" - case HexIntegerType: - return "HexInteger" - case FloatType: - return "Float" - case NullType: - return "Null" - case InfinityType: - return "Infinity" - case NanType: - return "Nan" - } - return "" -} - -// CharacterType type for character category -type CharacterType int - -const ( - // CharacterTypeIndicator type of indicator character - CharacterTypeIndicator CharacterType = iota - // CharacterTypeWhiteSpace type of white space character - CharacterTypeWhiteSpace - // CharacterTypeMiscellaneous type of miscellaneous character - CharacterTypeMiscellaneous - // CharacterTypeEscaped type of escaped character - CharacterTypeEscaped -) - -// String character type identifier to text -func (c CharacterType) String() string { - switch c { - case CharacterTypeIndicator: - return "Indicator" - case CharacterTypeWhiteSpace: - return "WhiteSpcae" - case CharacterTypeMiscellaneous: - return "Miscellaneous" - case CharacterTypeEscaped: - return "Escaped" - } - return "" -} - -// Indicator type for indicator -type Indicator int - -const ( - // NotIndicator not indicator - NotIndicator Indicator = iota - // BlockStructureIndicator indicator for block structure ( '-', '?', ':' ) - BlockStructureIndicator - // FlowCollectionIndicator indicator for flow collection ( '[', ']', '{', '}', ',' ) - FlowCollectionIndicator - // CommentIndicator indicator for comment ( '#' ) - CommentIndicator - // NodePropertyIndicator indicator for node property ( '!', '&', '*' ) - NodePropertyIndicator - // BlockScalarIndicator indicator for block scalar ( '|', '>' ) - BlockScalarIndicator - // QuotedScalarIndicator indicator for quoted scalar ( ''', '"' ) - QuotedScalarIndicator - // DirectiveIndicator indicator for directive ( '%' ) - DirectiveIndicator - // InvalidUseOfReservedIndicator indicator for invalid use of reserved keyword ( '@', '`' ) - InvalidUseOfReservedIndicator -) - -// String indicator to text -func (i Indicator) String() string { - switch i { - case NotIndicator: - return "NotIndicator" - case BlockStructureIndicator: - return "BlockStructure" - case FlowCollectionIndicator: - return "FlowCollection" - case CommentIndicator: - return "Comment" - case NodePropertyIndicator: - return "NodeProperty" - case BlockScalarIndicator: - return "BlockScalar" - case QuotedScalarIndicator: - return "QuotedScalar" - case DirectiveIndicator: - return "Directive" - case InvalidUseOfReservedIndicator: - return "InvalidUseOfReserved" - } - return "" -} - -var ( - reservedNullKeywords = []string{ - "null", - "Null", - "NULL", - "~", - } - reservedBoolKeywords = []string{ - "true", - "True", - "TRUE", - "false", - "False", - "FALSE", - } - // For compatibility with other YAML 1.1 parsers - // Note that we use these solely for encoding the bool value with quotes. - // go-yaml should not treat these as reserved keywords at parsing time. - // as go-yaml is supposed to be compliant only with YAML 1.2. - reservedLegacyBoolKeywords = []string{ - "y", - "Y", - "yes", - "Yes", - "YES", - "n", - "N", - "no", - "No", - "NO", - "on", - "On", - "ON", - "off", - "Off", - "OFF", - } - reservedInfKeywords = []string{ - ".inf", - ".Inf", - ".INF", - "-.inf", - "-.Inf", - "-.INF", - } - reservedNanKeywords = []string{ - ".nan", - ".NaN", - ".NAN", - } - reservedKeywordMap = map[string]func(string, string, *Position) *Token{} - // reservedEncKeywordMap contains is the keyword map used at encoding time. - // This is supposed to be a superset of reservedKeywordMap, - // and used to quote legacy keywords present in YAML 1.1 or lesser for compatibility reasons, - // even though this library is supposed to be YAML 1.2-compliant. - reservedEncKeywordMap = map[string]func(string, string, *Position) *Token{} -) - -func reservedKeywordToken(typ Type, value, org string, pos *Position) *Token { - return &Token{ - Type: typ, - CharacterType: CharacterTypeMiscellaneous, - Indicator: NotIndicator, - Value: value, - Origin: org, - Position: pos, - } -} - -func init() { - for _, keyword := range reservedNullKeywords { - reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { - return reservedKeywordToken(NullType, value, org, pos) - } - } - for _, keyword := range reservedBoolKeywords { - f := func(value, org string, pos *Position) *Token { - return reservedKeywordToken(BoolType, value, org, pos) - } - reservedKeywordMap[keyword] = f - reservedEncKeywordMap[keyword] = f - } - for _, keyword := range reservedLegacyBoolKeywords { - reservedEncKeywordMap[keyword] = func(value, org string, pos *Position) *Token { - return reservedKeywordToken(BoolType, value, org, pos) - } - } - for _, keyword := range reservedInfKeywords { - reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { - return reservedKeywordToken(InfinityType, value, org, pos) - } - } - for _, keyword := range reservedNanKeywords { - reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { - return reservedKeywordToken(NanType, value, org, pos) - } - } -} - -// ReservedTagKeyword type of reserved tag keyword -type ReservedTagKeyword string - -const ( - // IntegerTag `!!int` tag - IntegerTag ReservedTagKeyword = "!!int" - // FloatTag `!!float` tag - FloatTag ReservedTagKeyword = "!!float" - // NullTag `!!null` tag - NullTag ReservedTagKeyword = "!!null" - // SequenceTag `!!seq` tag - SequenceTag ReservedTagKeyword = "!!seq" - // MappingTag `!!map` tag - MappingTag ReservedTagKeyword = "!!map" - // StringTag `!!str` tag - StringTag ReservedTagKeyword = "!!str" - // BinaryTag `!!binary` tag - BinaryTag ReservedTagKeyword = "!!binary" - // OrderedMapTag `!!omap` tag - OrderedMapTag ReservedTagKeyword = "!!omap" - // SetTag `!!set` tag - SetTag ReservedTagKeyword = "!!set" - // TimestampTag `!!timestamp` tag - TimestampTag ReservedTagKeyword = "!!timestamp" -) - -var ( - // ReservedTagKeywordMap map for reserved tag keywords - ReservedTagKeywordMap = map[ReservedTagKeyword]func(string, string, *Position) *Token{ - IntegerTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - FloatTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - NullTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - SequenceTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - MappingTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - StringTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - BinaryTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - OrderedMapTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - SetTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - TimestampTag: func(value, org string, pos *Position) *Token { - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } - }, - } -) - -type numType int - -const ( - numTypeNone numType = iota - numTypeBinary - numTypeOctet - numTypeHex - numTypeFloat -) - -type numStat struct { - isNum bool - typ numType -} - -func getNumberStat(str string) *numStat { - stat := &numStat{} - if str == "" { - return stat - } - if str == "-" || str == "." || str == "+" || str == "_" { - return stat - } - if str[0] == '_' { - return stat - } - dotFound := false - isNegative := false - isExponent := false - if str[0] == '-' { - isNegative = true - } - for idx, c := range str { - switch c { - case 'x': - if (isNegative && idx == 2) || (!isNegative && idx == 1) { - continue - } - case 'o': - if (isNegative && idx == 2) || (!isNegative && idx == 1) { - continue - } - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - continue - case 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F': - if (len(str) > 2 && str[0] == '0' && str[1] == 'x') || - (len(str) > 3 && isNegative && str[1] == '0' && str[2] == 'x') { - // hex number - continue - } - if c == 'b' && ((isNegative && idx == 2) || (!isNegative && idx == 1)) { - // binary number - continue - } - if (c == 'e' || c == 'E') && dotFound { - // exponent - isExponent = true - continue - } - case '.': - if dotFound { - // multiple dot - return stat - } - dotFound = true - continue - case '-': - if idx == 0 || isExponent { - continue - } - case '+': - if idx == 0 || isExponent { - continue - } - case '_': - continue - } - return stat - } - stat.isNum = true - switch { - case dotFound: - stat.typ = numTypeFloat - case strings.HasPrefix(str, "0b") || strings.HasPrefix(str, "-0b"): - stat.typ = numTypeBinary - case strings.HasPrefix(str, "0x") || strings.HasPrefix(str, "-0x"): - stat.typ = numTypeHex - case strings.HasPrefix(str, "0o") || strings.HasPrefix(str, "-0o"): - stat.typ = numTypeOctet - case (len(str) > 1 && str[0] == '0') || (len(str) > 1 && str[0] == '-' && str[1] == '0'): - stat.typ = numTypeOctet - } - return stat -} - -func looksLikeTimeValue(value string) bool { - for i, c := range value { - switch c { - case ':', '1', '2', '3', '4', '5', '6', '7', '8', '9': - continue - case '0': - if i == 0 { - return false - } - continue - } - return false - } - return true -} - -// IsNeedQuoted whether need quote for passed string or not -func IsNeedQuoted(value string) bool { - if value == "" { - return true - } - if _, exists := reservedEncKeywordMap[value]; exists { - return true - } - if stat := getNumberStat(value); stat.isNum { - return true - } - first := value[0] - switch first { - case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"', '@': - return true - } - last := value[len(value)-1] - switch last { - case ':': - return true - } - if looksLikeTimeValue(value) { - return true - } - for i, c := range value { - switch c { - case '#', '\\': - return true - case ':': - if i+1 < len(value) && value[i+1] == ' ' { - return true - } - } - } - return false -} - -// LiteralBlockHeader detect literal block scalar header -func LiteralBlockHeader(value string) string { - lbc := DetectLineBreakCharacter(value) - - switch { - case !strings.Contains(value, lbc): - return "" - case strings.HasSuffix(value, fmt.Sprintf("%s%s", lbc, lbc)): - return "|+" - case strings.HasSuffix(value, lbc): - return "|" - default: - return "|-" - } -} - -// New create reserved keyword token or number token and other string token -func New(value string, org string, pos *Position) *Token { - fn := reservedKeywordMap[value] - if fn != nil { - return fn(value, org, pos) - } - if stat := getNumberStat(value); stat.isNum { - tk := &Token{ - Type: IntegerType, - CharacterType: CharacterTypeMiscellaneous, - Indicator: NotIndicator, - Value: value, - Origin: org, - Position: pos, - } - switch stat.typ { - case numTypeFloat: - tk.Type = FloatType - case numTypeBinary: - tk.Type = BinaryIntegerType - case numTypeOctet: - tk.Type = OctetIntegerType - case numTypeHex: - tk.Type = HexIntegerType - } - return tk - } - return String(value, org, pos) -} - -// Position type for position in YAML document -type Position struct { - Line int - Column int - Offset int - IndentNum int - IndentLevel int -} - -// String position to text -func (p *Position) String() string { - return fmt.Sprintf("[level:%d,line:%d,column:%d,offset:%d]", p.IndentLevel, p.Line, p.Column, p.Offset) -} - -// Token type for token -type Token struct { - Type Type - CharacterType CharacterType - Indicator Indicator - Value string - Origin string - Position *Position - Next *Token - Prev *Token -} - -// PreviousType previous token type -func (t *Token) PreviousType() Type { - if t.Prev != nil { - return t.Prev.Type - } - return UnknownType -} - -// NextType next token type -func (t *Token) NextType() Type { - if t.Next != nil { - return t.Next.Type - } - return UnknownType -} - -// AddColumn append column number to current position of column -func (t *Token) AddColumn(col int) { - if t == nil { - return - } - t.Position.Column += col -} - -// Clone copy token ( preserve Prev/Next reference ) -func (t *Token) Clone() *Token { - if t == nil { - return nil - } - copied := *t - if t.Position != nil { - pos := *(t.Position) - copied.Position = &pos - } - return &copied -} - -// Tokens type of token collection -type Tokens []*Token - -func (t *Tokens) add(tk *Token) { - tokens := *t - if len(tokens) == 0 { - tokens = append(tokens, tk) - } else { - last := tokens[len(tokens)-1] - last.Next = tk - tk.Prev = last - tokens = append(tokens, tk) - } - *t = tokens -} - -// Add append new some tokens -func (t *Tokens) Add(tks ...*Token) { - for _, tk := range tks { - t.add(tk) - } -} - -// Dump dump all token structures for debugging -func (t Tokens) Dump() { - for _, tk := range t { - fmt.Printf("- %+v\n", tk) - } -} - -// String create token for String -func String(value string, org string, pos *Position) *Token { - return &Token{ - Type: StringType, - CharacterType: CharacterTypeMiscellaneous, - Indicator: NotIndicator, - Value: value, - Origin: org, - Position: pos, - } -} - -// SequenceEntry create token for SequenceEntry -func SequenceEntry(org string, pos *Position) *Token { - return &Token{ - Type: SequenceEntryType, - CharacterType: CharacterTypeIndicator, - Indicator: BlockStructureIndicator, - Value: string(SequenceEntryCharacter), - Origin: org, - Position: pos, - } -} - -// MappingKey create token for MappingKey -func MappingKey(pos *Position) *Token { - return &Token{ - Type: MappingKeyType, - CharacterType: CharacterTypeIndicator, - Indicator: BlockStructureIndicator, - Value: string(MappingKeyCharacter), - Origin: string(MappingKeyCharacter), - Position: pos, - } -} - -// MappingValue create token for MappingValue -func MappingValue(pos *Position) *Token { - return &Token{ - Type: MappingValueType, - CharacterType: CharacterTypeIndicator, - Indicator: BlockStructureIndicator, - Value: string(MappingValueCharacter), - Origin: string(MappingValueCharacter), - Position: pos, - } -} - -// CollectEntry create token for CollectEntry -func CollectEntry(org string, pos *Position) *Token { - return &Token{ - Type: CollectEntryType, - CharacterType: CharacterTypeIndicator, - Indicator: FlowCollectionIndicator, - Value: string(CollectEntryCharacter), - Origin: org, - Position: pos, - } -} - -// SequenceStart create token for SequenceStart -func SequenceStart(org string, pos *Position) *Token { - return &Token{ - Type: SequenceStartType, - CharacterType: CharacterTypeIndicator, - Indicator: FlowCollectionIndicator, - Value: string(SequenceStartCharacter), - Origin: org, - Position: pos, - } -} - -// SequenceEnd create token for SequenceEnd -func SequenceEnd(org string, pos *Position) *Token { - return &Token{ - Type: SequenceEndType, - CharacterType: CharacterTypeIndicator, - Indicator: FlowCollectionIndicator, - Value: string(SequenceEndCharacter), - Origin: org, - Position: pos, - } -} - -// MappingStart create token for MappingStart -func MappingStart(org string, pos *Position) *Token { - return &Token{ - Type: MappingStartType, - CharacterType: CharacterTypeIndicator, - Indicator: FlowCollectionIndicator, - Value: string(MappingStartCharacter), - Origin: org, - Position: pos, - } -} - -// MappingEnd create token for MappingEnd -func MappingEnd(org string, pos *Position) *Token { - return &Token{ - Type: MappingEndType, - CharacterType: CharacterTypeIndicator, - Indicator: FlowCollectionIndicator, - Value: string(MappingEndCharacter), - Origin: org, - Position: pos, - } -} - -// Comment create token for Comment -func Comment(value string, org string, pos *Position) *Token { - return &Token{ - Type: CommentType, - CharacterType: CharacterTypeIndicator, - Indicator: CommentIndicator, - Value: value, - Origin: org, - Position: pos, - } -} - -// Anchor create token for Anchor -func Anchor(org string, pos *Position) *Token { - return &Token{ - Type: AnchorType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: string(AnchorCharacter), - Origin: org, - Position: pos, - } -} - -// Alias create token for Alias -func Alias(org string, pos *Position) *Token { - return &Token{ - Type: AliasType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: string(AliasCharacter), - Origin: org, - Position: pos, - } -} - -// Tag create token for Tag -func Tag(value string, org string, pos *Position) *Token { - fn := ReservedTagKeywordMap[ReservedTagKeyword(value)] - if fn != nil { - return fn(value, org, pos) - } - return &Token{ - Type: TagType, - CharacterType: CharacterTypeIndicator, - Indicator: NodePropertyIndicator, - Value: value, - Origin: org, - Position: pos, - } -} - -// Literal create token for Literal -func Literal(value string, org string, pos *Position) *Token { - return &Token{ - Type: LiteralType, - CharacterType: CharacterTypeIndicator, - Indicator: BlockScalarIndicator, - Value: value, - Origin: org, - Position: pos, - } -} - -// Folded create token for Folded -func Folded(value string, org string, pos *Position) *Token { - return &Token{ - Type: FoldedType, - CharacterType: CharacterTypeIndicator, - Indicator: BlockScalarIndicator, - Value: value, - Origin: org, - Position: pos, - } -} - -// SingleQuote create token for SingleQuote -func SingleQuote(value string, org string, pos *Position) *Token { - return &Token{ - Type: SingleQuoteType, - CharacterType: CharacterTypeIndicator, - Indicator: QuotedScalarIndicator, - Value: value, - Origin: org, - Position: pos, - } -} - -// DoubleQuote create token for DoubleQuote -func DoubleQuote(value string, org string, pos *Position) *Token { - return &Token{ - Type: DoubleQuoteType, - CharacterType: CharacterTypeIndicator, - Indicator: QuotedScalarIndicator, - Value: value, - Origin: org, - Position: pos, - } -} - -// Directive create token for Directive -func Directive(org string, pos *Position) *Token { - return &Token{ - Type: DirectiveType, - CharacterType: CharacterTypeIndicator, - Indicator: DirectiveIndicator, - Value: string(DirectiveCharacter), - Origin: org, - Position: pos, - } -} - -// Space create token for Space -func Space(pos *Position) *Token { - return &Token{ - Type: SpaceType, - CharacterType: CharacterTypeWhiteSpace, - Indicator: NotIndicator, - Value: string(SpaceCharacter), - Origin: string(SpaceCharacter), - Position: pos, - } -} - -// MergeKey create token for MergeKey -func MergeKey(org string, pos *Position) *Token { - return &Token{ - Type: MergeKeyType, - CharacterType: CharacterTypeMiscellaneous, - Indicator: NotIndicator, - Value: "<<", - Origin: org, - Position: pos, - } -} - -// DocumentHeader create token for DocumentHeader -func DocumentHeader(org string, pos *Position) *Token { - return &Token{ - Type: DocumentHeaderType, - CharacterType: CharacterTypeMiscellaneous, - Indicator: NotIndicator, - Value: "---", - Origin: org, - Position: pos, - } -} - -// DocumentEnd create token for DocumentEnd -func DocumentEnd(org string, pos *Position) *Token { - return &Token{ - Type: DocumentEndType, - CharacterType: CharacterTypeMiscellaneous, - Indicator: NotIndicator, - Value: "...", - Origin: org, - Position: pos, - } -} - -// DetectLineBreakCharacter detect line break character in only one inside scalar content scope. -func DetectLineBreakCharacter(src string) string { - nc := strings.Count(src, "\n") - rc := strings.Count(src, "\r") - rnc := strings.Count(src, "\r\n") - switch { - case nc == rnc && rc == rnc: - return "\r\n" - case rc > nc: - return "\r" - default: - return "\n" - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/token/token_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/token/token_test.go deleted file mode 100644 index 4f5764f..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/token/token_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package token_test - -import ( - "testing" - - "github.com/goccy/go-yaml/token" -) - -func TestToken(t *testing.T) { - pos := &token.Position{} - tokens := token.Tokens{ - token.SequenceEntry("-", pos), - token.MappingKey(pos), - token.MappingValue(pos), - token.CollectEntry(",", pos), - token.SequenceStart("[", pos), - token.SequenceEnd("]", pos), - token.MappingStart("{", pos), - token.MappingEnd("}", pos), - token.Comment("#", "#", pos), - token.Anchor("&", pos), - token.Alias("*", pos), - token.Literal("|", "|", pos), - token.Folded(">", ">", pos), - token.SingleQuote("'", "'", pos), - token.DoubleQuote(`"`, `"`, pos), - token.Directive("%", pos), - token.Space(pos), - token.MergeKey("<<", pos), - token.DocumentHeader("---", pos), - token.DocumentEnd("...", pos), - token.New("1", "1", pos), - token.New("3.14", "3.14", pos), - token.New("-0b101010", "-0b101010", pos), - token.New("0xA", "0xA", pos), - token.New("685.230_15e+03", "685.230_15e+03", pos), - token.New("02472256", "02472256", pos), - token.New("0o2472256", "0o2472256", pos), - token.New("", "", pos), - token.New("_1", "_1", pos), - token.New("1.1.1.1", "1.1.1.1", pos), - token.New("+", "+", pos), - token.New("-", "-", pos), - token.New("_", "_", pos), - token.New("~", "~", pos), - token.New("true", "true", pos), - token.New("false", "false", pos), - token.New(".nan", ".nan", pos), - token.New(".inf", ".inf", pos), - token.New("-.inf", "-.inf", pos), - token.New("null", "null", pos), - token.Tag("!!null", "!!null", pos), - token.Tag("!!map", "!!map", pos), - token.Tag("!!str", "!!str", pos), - token.Tag("!!seq", "!!seq", pos), - token.Tag("!!binary", "!!binary", pos), - token.Tag("!!omap", "!!omap", pos), - token.Tag("!!set", "!!set", pos), - token.Tag("!!int", "!!int", pos), - token.Tag("!!float", "!!float", pos), - token.Tag("!hoge", "!hoge", pos), - } - tokens.Dump() - tokens.Add(token.New("hoge", "hoge", pos)) - if tokens[len(tokens)-1].PreviousType() != token.TagType { - t.Fatal("invalid previous token type") - } - if tokens[0].PreviousType() != token.UnknownType { - t.Fatal("invalid previous token type") - } - if tokens[len(tokens)-2].NextType() != token.StringType { - t.Fatal("invalid next token type") - } - if tokens[len(tokens)-1].NextType() != token.UnknownType { - t.Fatal("invalid next token type") - } -} - -func TestIsNeedQuoted(t *testing.T) { - needQuotedTests := []string{ - "", - "true", - "1.234", - "1:1", - "hoge # comment", - "\\0", - "#a b", - "*a b", - "&a b", - "{a b", - "}a b", - "[a b", - "]a b", - ",a b", - "!a b", - "|a b", - ">a b", - ">a b", - "%a b", - `'a b`, - `"a b`, - "a:", - "a: b", - "y", - "Y", - "yes", - "Yes", - "YES", - "n", - "N", - "no", - "No", - "NO", - "on", - "On", - "ON", - "off", - "Off", - "OFF", - "@test", - } - for i, test := range needQuotedTests { - if !token.IsNeedQuoted(test) { - t.Fatalf("%d: failed to quoted judge for %s", i, test) - } - } - notNeedQuotedTests := []string{ - "Hello World", - } - for i, test := range notNeedQuotedTests { - if token.IsNeedQuoted(test) { - t.Fatalf("%d: failed to quoted judge for %s", i, test) - } - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/validate.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/validate.go deleted file mode 100644 index 20a2d6d..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/validate.go +++ /dev/null @@ -1,13 +0,0 @@ -package yaml - -// StructValidator need to implement Struct method only -// ( see https://pkg.go.dev/github.com/go-playground/validator/v10#Validate.Struct ) -type StructValidator interface { - Struct(interface{}) error -} - -// FieldError need to implement StructField method only -// ( see https://pkg.go.dev/github.com/go-playground/validator/v10#FieldError ) -type FieldError interface { - StructField() string -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/validate_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/validate_test.go deleted file mode 100644 index 265deb8..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/validate_test.go +++ /dev/null @@ -1,198 +0,0 @@ -package yaml_test - -import ( - "strings" - "testing" - - "github.com/go-playground/validator/v10" - "github.com/goccy/go-yaml" -) - -func TestStructValidator(t *testing.T) { - type Inner struct { - Required string `validate:"required"` - Lt10 int `validate:"lt=10"` - } - - cases := []struct { - TestName string - YAMLContent string - ExpectedErr string - Instance interface{} - }{ - { - TestName: "Test Simple Validation", - YAMLContent: `--- -- name: john - age: 20 -- name: tom - age: -1 -- name: ken - age: 10`, - ExpectedErr: `[5:8] Key: 'Age' Error:Field validation for 'Age' failed on the 'gte' tag - 2 | - name: john - 3 | age: 20 - 4 | - name: tom -> 5 | age: -1 - ^ - 6 | - name: ken - 7 | age: 10`, - Instance: &[]struct { - Name string `yaml:"name" validate:"required"` - Age int `yaml:"age" validate:"gte=0,lt=120"` - }{}, - }, - { - TestName: "Test Missing Required Field", - YAMLContent: `--- -- name: john - age: 20 -- age: 10`, - ExpectedErr: `[4:1] Key: 'Name' Error:Field validation for 'Name' failed on the 'required' tag - 1 | --- - 2 | - name: john - 3 | age: 20 -> 4 | - age: 10 - ^ -`, - Instance: &[]struct { - Name string `yaml:"name" validate:"required"` - Age int `yaml:"age" validate:"gte=0,lt=120"` - }{}, - }, - { - TestName: "Test Nested Validation Missing Internal Required", - YAMLContent: `--- -name: john -age: 10 -addr: - number: seven`, - ExpectedErr: `[4:5] Key: 'State' Error:Field validation for 'State' failed on the 'required' tag - 1 | --- - 2 | name: john - 3 | age: 10 -> 4 | addr: - ^ - 5 | number: seven`, - Instance: &struct { - Name string `yaml:"name" validate:"required"` - Age int `yaml:"age" validate:"gte=0,lt=120"` - Addr struct { - Number string `yaml:"number" validate:"required"` - State string `yaml:"state" validate:"required"` - } `yaml:"addr"` - }{}, - }, - { - TestName: "Test nested Validation with unknown field", - YAMLContent: `--- -name: john -age: 20 -addr: - number: seven - state: washington - error: error -`, - ExpectedErr: `[7:3] unknown field "error" - 4 | addr: - 5 | number: seven - 6 | state: washington -> 7 | error: error - ^ -`, - Instance: &struct { - Name string `yaml:"name" validate:"required"` - Age int `yaml:"age" validate:"gte=0,lt=120"` - Addr *struct { - Number string `yaml:"number" validate:"required"` - State string `yaml:"state" validate:"required"` - } `yaml:"addr" validate:"required"` - }{}, - }, - { - TestName: "Test Validation with wrong field type", - YAMLContent: `--- -name: myDocument -roles: - name: myRole - permissions: - - hello - - how - - are - - you - `, - ExpectedErr: `[4:7] mapping was used where sequence is expected - 1 | --- - 2 | name: myDocument - 3 | roles: -> 4 | name: myRole - ^ - 5 | permissions: - 6 | - hello - 7 | - how - 8 | `, - Instance: &struct { - Name string `yaml:"name"` - Roles []struct { - Name string `yaml:"name"` - Permissions []string `yaml:"permissions"` - } `yaml:"roles"` - }{}, - }, - { - TestName: "Test inline validation missing required", - YAMLContent: `--- -name: john -age: 20 -`, - ExpectedErr: `Key: 'Inner.Required' Error:Field validation for 'Required' failed on the 'required' tag`, - Instance: &struct { - Name string `yaml:"name" validate:"required"` - Age int `yaml:"age" validate:"gte=0,lt=120"` - Inner `yaml:",inline"` - }{}, - }, - { - TestName: "Test inline validation field error", - YAMLContent: `--- -name: john -age: 20 -required: present -lt10: 20 -`, - ExpectedErr: `[5:7] Key: 'Inner.Lt10' Error:Field validation for 'Lt10' failed on the 'lt' tag - 2 | name: john - 3 | age: 20 - 4 | required: present -> 5 | lt10: 20 - ^ -`, - Instance: &struct { - Name string `yaml:"name" validate:"required"` - Age int `yaml:"age" validate:"gte=0,lt=120"` - Inner `yaml:",inline"` - }{}, - }, - } - - for _, tc := range cases { - tc := tc // NOTE: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables - t.Run(tc.TestName, func(t *testing.T) { - validate := validator.New() - dec := yaml.NewDecoder( - strings.NewReader(tc.YAMLContent), - yaml.Validator(validate), - yaml.Strict(), - ) - err := dec.Decode(tc.Instance) - switch { - case tc.ExpectedErr != "" && err == nil: - t.Fatal("expected error") - case tc.ExpectedErr == "" && err != nil: - t.Fatalf("unexpected error: %v", err) - case tc.ExpectedErr != "" && tc.ExpectedErr != err.Error(): - t.Fatalf("expected `%s` but actual `%s`", tc.ExpectedErr, err.Error()) - } - }) - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/yaml.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/yaml.go deleted file mode 100644 index 2e541d8..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/yaml.go +++ /dev/null @@ -1,290 +0,0 @@ -package yaml - -import ( - "bytes" - "context" - "io" - "reflect" - "sync" - - "github.com/goccy/go-yaml/ast" - "github.com/goccy/go-yaml/internal/errors" - "golang.org/x/xerrors" -) - -// BytesMarshaler interface may be implemented by types to customize their -// behavior when being marshaled into a YAML document. The returned value -// is marshaled in place of the original value implementing Marshaler. -// -// If an error is returned by MarshalYAML, the marshaling procedure stops -// and returns with the provided error. -type BytesMarshaler interface { - MarshalYAML() ([]byte, error) -} - -// BytesMarshalerContext interface use BytesMarshaler with context.Context. -type BytesMarshalerContext interface { - MarshalYAML(context.Context) ([]byte, error) -} - -// InterfaceMarshaler interface has MarshalYAML compatible with github.com/go-yaml/yaml package. -type InterfaceMarshaler interface { - MarshalYAML() (interface{}, error) -} - -// InterfaceMarshalerContext interface use InterfaceMarshaler with context.Context. -type InterfaceMarshalerContext interface { - MarshalYAML(context.Context) (interface{}, error) -} - -// BytesUnmarshaler interface may be implemented by types to customize their -// behavior when being unmarshaled from a YAML document. -type BytesUnmarshaler interface { - UnmarshalYAML([]byte) error -} - -// BytesUnmarshalerContext interface use BytesUnmarshaler with context.Context. -type BytesUnmarshalerContext interface { - UnmarshalYAML(context.Context, []byte) error -} - -// InterfaceUnmarshaler interface has UnmarshalYAML compatible with github.com/go-yaml/yaml package. -type InterfaceUnmarshaler interface { - UnmarshalYAML(func(interface{}) error) error -} - -// InterfaceUnmarshalerContext interface use InterfaceUnmarshaler with context.Context. -type InterfaceUnmarshalerContext interface { - UnmarshalYAML(context.Context, func(interface{}) error) error -} - -// MapItem is an item in a MapSlice. -type MapItem struct { - Key, Value interface{} -} - -// MapSlice encodes and decodes as a YAML map. -// The order of keys is preserved when encoding and decoding. -type MapSlice []MapItem - -// ToMap convert to map[interface{}]interface{}. -func (s MapSlice) ToMap() map[interface{}]interface{} { - v := map[interface{}]interface{}{} - for _, item := range s { - v[item.Key] = item.Value - } - return v -} - -// Marshal serializes the value provided into a YAML document. The structure -// of the generated document will reflect the structure of the value itself. -// Maps and pointers (to struct, string, int, etc) are accepted as the in value. -// -// Struct fields are only marshalled if they are exported (have an upper case -// first letter), and are marshalled using the field name lowercased as the -// default key. Custom keys may be defined via the "yaml" name in the field -// tag: the content preceding the first comma is used as the key, and the -// following comma-separated options are used to tweak the marshalling process. -// Conflicting names result in a runtime error. -// -// The field tag format accepted is: -// -// `(...) yaml:"[][,[,]]" (...)` -// -// The following flags are currently supported: -// -// omitempty Only include the field if it's not set to the zero -// value for the type or to empty slices or maps. -// Zero valued structs will be omitted if all their public -// fields are zero, unless they implement an IsZero -// method (see the IsZeroer interface type), in which -// case the field will be included if that method returns true. -// -// flow Marshal using a flow style (useful for structs, -// sequences and maps). -// -// inline Inline the field, which must be a struct or a map, -// causing all of its fields or keys to be processed as if -// they were part of the outer struct. For maps, keys must -// not conflict with the yaml keys of other struct fields. -// -// anchor Marshal with anchor. If want to define anchor name explicitly, use anchor=name style. -// Otherwise, if used 'anchor' name only, used the field name lowercased as the anchor name -// -// alias Marshal with alias. If want to define alias name explicitly, use alias=name style. -// Otherwise, If omitted alias name and the field type is pointer type, -// assigned anchor name automatically from same pointer address. -// -// In addition, if the key is "-", the field is ignored. -// -// For example: -// -// type T struct { -// F int `yaml:"a,omitempty"` -// B int -// } -// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" -// yaml.Marshal(&T{F: 1}) // Returns "a: 1\nb: 0\n" -// -func Marshal(v interface{}) ([]byte, error) { - return MarshalWithOptions(v) -} - -// MarshalWithOptions serializes the value provided into a YAML document with EncodeOptions. -func MarshalWithOptions(v interface{}, opts ...EncodeOption) ([]byte, error) { - return MarshalContext(context.Background(), v, opts...) -} - -// MarshalContext serializes the value provided into a YAML document with context.Context and EncodeOptions. -func MarshalContext(ctx context.Context, v interface{}, opts ...EncodeOption) ([]byte, error) { - var buf bytes.Buffer - if err := NewEncoder(&buf, opts...).EncodeContext(ctx, v); err != nil { - return nil, errors.Wrapf(err, "failed to marshal") - } - return buf.Bytes(), nil -} - -// ValueToNode convert from value to ast.Node. -func ValueToNode(v interface{}, opts ...EncodeOption) (ast.Node, error) { - var buf bytes.Buffer - node, err := NewEncoder(&buf, opts...).EncodeToNode(v) - if err != nil { - return nil, errors.Wrapf(err, "failed to convert value to node") - } - return node, nil -} - -// Unmarshal decodes the first document found within the in byte slice -// and assigns decoded values into the out value. -// -// Struct fields are only unmarshalled if they are exported (have an -// upper case first letter), and are unmarshalled using the field name -// lowercased as the default key. Custom keys may be defined via the -// "yaml" name in the field tag: the content preceding the first comma -// is used as the key, and the following comma-separated options are -// used to tweak the marshalling process (see Marshal). -// Conflicting names result in a runtime error. -// -// For example: -// -// type T struct { -// F int `yaml:"a,omitempty"` -// B int -// } -// var t T -// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) -// -// See the documentation of Marshal for the format of tags and a list of -// supported tag options. -// -func Unmarshal(data []byte, v interface{}) error { - return UnmarshalWithOptions(data, v) -} - -// UnmarshalWithOptions decodes with DecodeOptions the first document found within the in byte slice -// and assigns decoded values into the out value. -func UnmarshalWithOptions(data []byte, v interface{}, opts ...DecodeOption) error { - return UnmarshalContext(context.Background(), data, v, opts...) -} - -// UnmarshalContext decodes with context.Context and DecodeOptions. -func UnmarshalContext(ctx context.Context, data []byte, v interface{}, opts ...DecodeOption) error { - dec := NewDecoder(bytes.NewBuffer(data), opts...) - if err := dec.DecodeContext(ctx, v); err != nil { - if err == io.EOF { - return nil - } - return errors.Wrapf(err, "failed to unmarshal") - } - return nil -} - -// NodeToValue converts node to the value pointed to by v. -func NodeToValue(node ast.Node, v interface{}, opts ...DecodeOption) error { - var buf bytes.Buffer - if err := NewDecoder(&buf, opts...).DecodeFromNode(node, v); err != nil { - return errors.Wrapf(err, "failed to convert node to value") - } - return nil -} - -// FormatError is a utility function that takes advantage of the metadata -// stored in the errors returned by this package's parser. -// -// If the second argument `colored` is true, the error message is colorized. -// If the third argument `inclSource` is true, the error message will -// contain snippets of the YAML source that was used. -func FormatError(e error, colored, inclSource bool) string { - var pp errors.PrettyPrinter - if xerrors.As(e, &pp) { - var buf bytes.Buffer - pp.PrettyPrint(&errors.Sink{&buf}, colored, inclSource) - return buf.String() - } - - return e.Error() -} - -// YAMLToJSON convert YAML bytes to JSON. -func YAMLToJSON(bytes []byte) ([]byte, error) { - var v interface{} - if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil { - return nil, errors.Wrapf(err, "failed to unmarshal") - } - out, err := MarshalWithOptions(v, JSON()) - if err != nil { - return nil, errors.Wrapf(err, "failed to marshal with json option") - } - return out, nil -} - -// JSONToYAML convert JSON bytes to YAML. -func JSONToYAML(bytes []byte) ([]byte, error) { - var v interface{} - if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil { - return nil, errors.Wrapf(err, "failed to unmarshal from json bytes") - } - out, err := Marshal(v) - if err != nil { - return nil, errors.Wrapf(err, "failed to marshal") - } - return out, nil -} - -var ( - globalCustomMarshalerMu sync.Mutex - globalCustomUnmarshalerMu sync.Mutex - globalCustomMarshalerMap = map[reflect.Type]func(interface{}) ([]byte, error){} - globalCustomUnmarshalerMap = map[reflect.Type]func(interface{}, []byte) error{} -) - -// RegisterCustomMarshaler overrides any encoding process for the type specified in generics. -// If you want to switch the behavior for each encoder, use `CustomMarshaler` defined as EncodeOption. -// -// NOTE: If type T implements MarshalYAML for pointer receiver, the type specified in RegisterCustomMarshaler must be *T. -// If RegisterCustomMarshaler and CustomMarshaler of EncodeOption are specified for the same type, -// the CustomMarshaler specified in EncodeOption takes precedence. -func RegisterCustomMarshaler[T any](marshaler func(T) ([]byte, error)) { - globalCustomMarshalerMu.Lock() - defer globalCustomMarshalerMu.Unlock() - - var typ T - globalCustomMarshalerMap[reflect.TypeOf(typ)] = func(v interface{}) ([]byte, error) { - return marshaler(v.(T)) - } -} - -// RegisterCustomUnmarshaler overrides any decoding process for the type specified in generics. -// If you want to switch the behavior for each decoder, use `CustomUnmarshaler` defined as DecodeOption. -// -// NOTE: If RegisterCustomUnmarshaler and CustomUnmarshaler of DecodeOption are specified for the same type, -// the CustomUnmarshaler specified in DecodeOption takes precedence. -func RegisterCustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) { - globalCustomUnmarshalerMu.Lock() - defer globalCustomUnmarshalerMu.Unlock() - - var typ *T - globalCustomUnmarshalerMap[reflect.TypeOf(typ)] = func(v interface{}, b []byte) error { - return unmarshaler(v.(*T), b) - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/yaml_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/yaml_test.go deleted file mode 100644 index 5828629..0000000 --- a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.0/yaml_test.go +++ /dev/null @@ -1,1195 +0,0 @@ -package yaml_test - -import ( - "bytes" - "testing" - - "github.com/google/go-cmp/cmp" - "golang.org/x/xerrors" - - "github.com/goccy/go-yaml" - "github.com/goccy/go-yaml/ast" -) - -func TestMarshal(t *testing.T) { - var v struct { - A int - B string - } - v.A = 1 - v.B = "hello" - bytes, err := yaml.Marshal(v) - if err != nil { - t.Fatalf("%+v", err) - } - if string(bytes) != "a: 1\nb: hello\n" { - t.Fatal("failed to marshal") - } -} - -func TestUnmarshal(t *testing.T) { - yml := ` -%YAML 1.2 ---- -a: 1 -b: c -` - var v struct { - A int - B string - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatalf("%+v", err) - } -} - -type marshalTest struct{} - -func (t *marshalTest) MarshalYAML() ([]byte, error) { - return yaml.Marshal(yaml.MapSlice{ - { - "a", 1, - }, - { - "b", "hello", - }, - { - "c", true, - }, - { - "d", map[string]string{"x": "y"}, - }, - }) -} - -type marshalTest2 struct{} - -func (t *marshalTest2) MarshalYAML() (interface{}, error) { - return yaml.MapSlice{ - { - "a", 2, - }, - { - "b", "world", - }, - { - "c", true, - }, - }, nil -} - -func TestMarshalYAML(t *testing.T) { - var v struct { - A *marshalTest - B *marshalTest2 - } - v.A = &marshalTest{} - v.B = &marshalTest2{} - bytes, err := yaml.Marshal(v) - if err != nil { - t.Fatalf("failed to Marshal: %+v", err) - } - expect := ` -a: - a: 1 - b: hello - c: true - d: - x: "y" -b: - a: 2 - b: world - c: true -` - actual := "\n" + string(bytes) - if expect != actual { - t.Fatalf("failed to MarshalYAML expect:[%s], actual:[%s]", expect, actual) - } -} - -type unmarshalTest struct { - a int - b string - c bool -} - -func (t *unmarshalTest) UnmarshalYAML(b []byte) error { - if t.a != 0 { - return xerrors.New("unexpected field value to a") - } - if t.b != "" { - return xerrors.New("unexpected field value to b") - } - if t.c { - return xerrors.New("unexpected field value to c") - } - var v struct { - A int - B string - C bool - } - if err := yaml.Unmarshal(b, &v); err != nil { - return err - } - t.a = v.A - t.b = v.B - t.c = v.C - return nil -} - -type unmarshalTest2 struct { - a int - b string - c bool -} - -func (t *unmarshalTest2) UnmarshalYAML(unmarshal func(interface{}) error) error { - var v struct { - A int - B string - C bool - } - if t.a != 0 { - return xerrors.New("unexpected field value to a") - } - if t.b != "" { - return xerrors.New("unexpected field value to b") - } - if t.c { - return xerrors.New("unexpected field value to c") - } - if err := unmarshal(&v); err != nil { - return err - } - t.a = v.A - t.b = v.B - t.c = v.C - return nil -} - -func TestUnmarshalYAML(t *testing.T) { - yml := ` -a: - a: 1 - b: hello - c: true -b: - a: 2 - b: world - c: true -` - var v struct { - A *unmarshalTest - B *unmarshalTest2 - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatalf("failed to Unmarshal: %+v", err) - } - if v.A == nil { - t.Fatal("failed to UnmarshalYAML") - } - if v.A.a != 1 { - t.Fatal("failed to UnmarshalYAML") - } - if v.A.b != "hello" { - t.Fatal("failed to UnmarshalYAML") - } - if !v.A.c { - t.Fatal("failed to UnmarshalYAML") - } - if v.B == nil { - t.Fatal("failed to UnmarshalYAML") - } - if v.B.a != 2 { - t.Fatal("failed to UnmarshalYAML") - } - if v.B.b != "world" { - t.Fatal("failed to UnmarshalYAML") - } - if !v.B.c { - t.Fatal("failed to UnmarshalYAML") - } -} - -type ObjectMap map[string]*Object -type ObjectDecl struct { - Name string `yaml:"-"` - *Object `yaml:",inline,anchor"` -} - -func (m ObjectMap) MarshalYAML() (interface{}, error) { - newMap := map[string]*ObjectDecl{} - for k, v := range m { - newMap[k] = &ObjectDecl{Name: k, Object: v} - } - return newMap, nil -} - -type rootObject struct { - Single ObjectMap `yaml:"single"` - Collection map[string][]*Object `yaml:"collection"` -} - -type Object struct { - *Object `yaml:",omitempty,inline,alias"` - MapValue map[string]interface{} `yaml:",omitempty,inline"` -} - -func TestInlineAnchorAndAlias(t *testing.T) { - yml := `--- -single: - default: &default - id: 1 - name: john - user_1: &user_1 - id: 1 - name: ken - user_2: &user_2 - <<: *default - id: 2 -collection: - defaults: - - *default - - <<: *default - - <<: *default - id: 2 - users: - - <<: *user_1 - - <<: *user_2 - - <<: *user_1 - id: 3 - - <<: *user_1 - id: 4 - - <<: *user_1 - id: 5 -` - var v rootObject - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatal(err) - } - opt := yaml.MarshalAnchor(func(anchor *ast.AnchorNode, value interface{}) error { - if o, ok := value.(*ObjectDecl); ok { - return anchor.SetName(o.Name) - } - return nil - }) - var buf bytes.Buffer - if err := yaml.NewEncoder(&buf, opt).Encode(v); err != nil { - t.Fatalf("%+v", err) - } - actual := "---\n" + buf.String() - if yml != actual { - t.Fatalf("failed to marshal: expected:[%s] actual:[%s]", yml, actual) - } -} - -func TestMapSlice_Map(t *testing.T) { - yml := ` -a: b -c: d -` - var v yaml.MapSlice - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatal(err) - } - m := v.ToMap() - if len(m) != 2 { - t.Fatal("failed to convert MapSlice to map") - } - if m["a"] != "b" { - t.Fatal("failed to convert MapSlice to map") - } - if m["c"] != "d" { - t.Fatal("failed to convert MapSlice to map") - } -} - -func TestMarshalWithModifiedAnchorAlias(t *testing.T) { - yml := ` -a: &a 1 -b: *a -` - var v struct { - A *int `yaml:"a,anchor"` - B *int `yaml:"b,alias"` - } - if err := yaml.Unmarshal([]byte(yml), &v); err != nil { - t.Fatal(err) - } - node, err := yaml.ValueToNode(v) - if err != nil { - t.Fatal(err) - } - anchors := ast.Filter(ast.AnchorType, node) - if len(anchors) != 1 { - t.Fatal("failed to filter node") - } - anchor := anchors[0].(*ast.AnchorNode) - if err := anchor.SetName("b"); err != nil { - t.Fatal(err) - } - aliases := ast.Filter(ast.AliasType, node) - if len(anchors) != 1 { - t.Fatal("failed to filter node") - } - alias := aliases[0].(*ast.AliasNode) - if err := alias.SetName("b"); err != nil { - t.Fatal(err) - } - - expected := ` -a: &b 1 -b: *b` - - actual := "\n" + node.String() - if expected != actual { - t.Fatalf("failed to marshal: expected:[%q] but got [%q]", expected, actual) - } -} - -func Test_YAMLToJSON(t *testing.T) { - yml := ` -foo: - bar: - - a - - b - - c -a: 1 -` - actual, err := yaml.YAMLToJSON([]byte(yml)) - if err != nil { - t.Fatal(err) - } - expected := `{"foo": {"bar": ["a", "b", "c"]}, "a": 1}` - if expected+"\n" != string(actual) { - t.Fatalf("failed to convert yaml to json: expected [%q] but got [%q]", expected, actual) - } -} - -func Test_JSONToYAML(t *testing.T) { - json := `{"foo": {"bar": ["a", "b", "c"]}, "a": 1}` - expected := ` -foo: - bar: - - a - - b - - c -a: 1 -` - actual, err := yaml.JSONToYAML([]byte(json)) - if err != nil { - t.Fatal(err) - } - if expected != "\n"+string(actual) { - t.Fatalf("failed to convert json to yaml: expected [%q] but got [%q]", expected, actual) - } -} - -func Test_WithCommentOption(t *testing.T) { - t.Run("line comment", func(t *testing.T) { - v := struct { - Foo string `yaml:"foo"` - Bar map[string]interface{} `yaml:"bar"` - Baz struct { - X int `yaml:"x"` - } `yaml:"baz"` - }{ - Foo: "aaa", - Bar: map[string]interface{}{"bbb": "ccc"}, - Baz: struct { - X int `yaml:"x"` - }{X: 10}, - } - b, err := yaml.MarshalWithOptions(v, yaml.WithComment( - yaml.CommentMap{ - "$.foo": []*yaml.Comment{yaml.LineComment("foo comment")}, - "$.bar": []*yaml.Comment{yaml.LineComment("bar comment")}, - "$.bar.bbb": []*yaml.Comment{yaml.LineComment("bbb comment")}, - "$.baz.x": []*yaml.Comment{yaml.LineComment("x comment")}, - }, - )) - if err != nil { - t.Fatal(err) - } - expected := ` -foo: aaa #foo comment -bar: #bar comment - bbb: ccc #bbb comment -baz: - x: 10 #x comment -` - actual := "\n" + string(b) - if expected != actual { - t.Fatalf("expected:%s but got %s", expected, actual) - } - }) - t.Run("line comment2", func(t *testing.T) { - v := struct { - Foo map[string]interface{} `yaml:"foo"` - }{ - Foo: map[string]interface{}{ - "bar": map[string]interface{}{ - "baz": true, - }, - }, - } - b, err := yaml.MarshalWithOptions(v, yaml.WithComment( - yaml.CommentMap{ - "$.foo.bar": []*yaml.Comment{yaml.HeadComment(" bar head comment"), yaml.LineComment(" bar line comment")}, - "$.foo.bar.baz": []*yaml.Comment{yaml.LineComment(" baz line comment")}, - }, - )) - if err != nil { - t.Fatal(err) - } - expected := ` -foo: - # bar head comment - bar: # bar line comment - baz: true # baz line comment -` - actual := "\n" + string(b) - if expected != actual { - t.Fatalf("expected:%s but got %s", expected, actual) - } - }) - t.Run("single head comment", func(t *testing.T) { - v := struct { - Foo string `yaml:"foo"` - Bar map[string]interface{} `yaml:"bar"` - Baz struct { - X int `yaml:"x"` - } `yaml:"baz"` - }{ - Foo: "aaa", - Bar: map[string]interface{}{"bbb": "ccc"}, - Baz: struct { - X int `yaml:"x"` - }{X: 10}, - } - - b, err := yaml.MarshalWithOptions(v, yaml.WithComment( - yaml.CommentMap{ - "$.foo": []*yaml.Comment{yaml.HeadComment("foo comment")}, - "$.bar": []*yaml.Comment{yaml.HeadComment("bar comment")}, - "$.bar.bbb": []*yaml.Comment{yaml.HeadComment("bbb comment")}, - "$.baz.x": []*yaml.Comment{yaml.HeadComment("x comment")}, - }, - )) - if err != nil { - t.Fatal(err) - } - expected := ` -#foo comment -foo: aaa -#bar comment -bar: - #bbb comment - bbb: ccc -baz: - #x comment - x: 10 -` - actual := "\n" + string(b) - if expected != actual { - t.Fatalf("expected:%s but got %s", expected, actual) - } - }) - - t.Run("multiple head comment", func(t *testing.T) { - v := struct { - Foo string `yaml:"foo"` - Bar map[string]interface{} `yaml:"bar"` - Baz struct { - X int `yaml:"x"` - } `yaml:"baz"` - }{ - Foo: "aaa", - Bar: map[string]interface{}{"bbb": "ccc"}, - Baz: struct { - X int `yaml:"x"` - }{X: 10}, - } - - b, err := yaml.MarshalWithOptions(v, yaml.WithComment( - yaml.CommentMap{ - "$.foo": []*yaml.Comment{ - yaml.HeadComment( - "foo comment", - "foo comment2", - ), - }, - "$.bar": []*yaml.Comment{ - yaml.HeadComment( - "bar comment", - "bar comment2", - ), - }, - "$.bar.bbb": []*yaml.Comment{ - yaml.HeadComment( - "bbb comment", - "bbb comment2", - ), - }, - "$.baz.x": []*yaml.Comment{ - yaml.HeadComment( - "x comment", - "x comment2", - ), - }, - }, - )) - if err != nil { - t.Fatal(err) - } - expected := ` -#foo comment -#foo comment2 -foo: aaa -#bar comment -#bar comment2 -bar: - #bbb comment - #bbb comment2 - bbb: ccc -baz: - #x comment - #x comment2 - x: 10 -` - actual := "\n" + string(b) - if expected != actual { - t.Fatalf("expected:%s but got %s", expected, actual) - } - }) - t.Run("foot comment", func(t *testing.T) { - v := struct { - Bar map[string]interface{} `yaml:"bar"` - Baz []int `yaml:"baz"` - }{ - Bar: map[string]interface{}{"bbb": "ccc"}, - Baz: []int{1, 2}, - } - - b, err := yaml.MarshalWithOptions(v, yaml.IndentSequence(true), yaml.WithComment( - yaml.CommentMap{ - "$.bar.bbb": []*yaml.Comment{yaml.FootComment("ccc: ddd")}, - "$.baz[1]": []*yaml.Comment{yaml.FootComment("- 3")}, - "$.baz": []*yaml.Comment{yaml.FootComment(" foot comment", "foot comment2")}, - }, - )) - if err != nil { - t.Fatal(err) - } - expected := ` -bar: - bbb: ccc - #ccc: ddd -baz: - - 1 - - 2 - #- 3 -# foot comment -#foot comment2 -` - actual := "\n" + string(b) - if expected != actual { - t.Fatalf("expected:%s but got %s", expected, actual) - } - }) - - t.Run("combination", func(t *testing.T) { - v := struct { - Foo map[string]interface{} `yaml:"foo"` - O map[string]interface{} `yaml:"o"` - T map[string]bool `yaml:"t"` - Bar map[string]interface{} `yaml:"bar"` - Baz []int `yaml:"baz"` - Hoge map[string]interface{} `yaml:"hoge"` - }{ - Foo: map[string]interface{}{ - "a": map[string]interface{}{ - "b": map[string]interface{}{ - "c": "d", - }, - }, - }, - O: map[string]interface{}{ - "p": map[string]interface{}{ - "q": map[string]interface{}{ - "r": "s", - }, - }, - }, - T: map[string]bool{ - "u": true, - }, - Bar: map[string]interface{}{"bbb": "ccc"}, - Baz: []int{1, 2}, - Hoge: map[string]interface{}{ - "moga": true, - }, - } - - b, err := yaml.MarshalWithOptions(v, yaml.IndentSequence(true), yaml.WithComment( - yaml.CommentMap{ - "$.foo": []*yaml.Comment{ - yaml.HeadComment(" foo head comment", " foo head comment2"), - yaml.LineComment(" foo line comment"), - }, - "$.foo.a": []*yaml.Comment{ - yaml.HeadComment(" a head comment"), - yaml.LineComment(" a line comment"), - }, - "$.foo.a.b": []*yaml.Comment{ - yaml.HeadComment(" b head comment"), - yaml.LineComment(" b line comment"), - }, - "$.foo.a.b.c": []*yaml.Comment{ - yaml.LineComment(" c line comment"), - }, - "$.o": []*yaml.Comment{ - yaml.LineComment(" o line comment"), - }, - "$.o.p": []*yaml.Comment{ - yaml.HeadComment(" p head comment", " p head comment2"), - yaml.LineComment(" p line comment"), - }, - "$.o.p.q": []*yaml.Comment{ - yaml.HeadComment(" q head comment", " q head comment2"), - yaml.LineComment(" q line comment"), - }, - "$.o.p.q.r": []*yaml.Comment{ - yaml.LineComment(" r line comment"), - }, - "$.t.u": []*yaml.Comment{ - yaml.LineComment(" u line comment"), - }, - "$.bar": []*yaml.Comment{ - yaml.HeadComment(" bar head comment"), - yaml.LineComment(" bar line comment"), - }, - "$.bar.bbb": []*yaml.Comment{ - yaml.HeadComment(" bbb head comment"), - yaml.LineComment(" bbb line comment"), - yaml.FootComment(" bbb foot comment"), - }, - "$.baz[0]": []*yaml.Comment{ - yaml.HeadComment(" sequence head comment"), - yaml.LineComment(" sequence line comment"), - }, - "$.baz[1]": []*yaml.Comment{ - yaml.HeadComment(" sequence head comment2"), - yaml.LineComment(" sequence line comment2"), - yaml.FootComment(" sequence foot comment"), - }, - "$.baz": []*yaml.Comment{ - yaml.HeadComment(" baz head comment", " baz head comment2"), - yaml.LineComment(" baz line comment"), - yaml.FootComment(" baz foot comment"), - }, - "$.hoge.moga": []*yaml.Comment{ - yaml.LineComment(" moga line comment"), - yaml.FootComment(" moga foot comment"), - }, - }, - )) - if err != nil { - t.Fatal(err) - } - expected := ` -# foo head comment -# foo head comment2 -foo: # foo line comment - # a head comment - a: # a line comment - # b head comment - b: # b line comment - c: d # c line comment -o: # o line comment - # p head comment - # p head comment2 - p: # p line comment - # q head comment - # q head comment2 - q: # q line comment - r: s # r line comment -t: - u: true # u line comment -# bar head comment -bar: # bar line comment - # bbb head comment - bbb: ccc # bbb line comment - # bbb foot comment -# baz head comment -# baz head comment2 -baz: # baz line comment - # sequence head comment - - 1 # sequence line comment - # sequence head comment2 - - 2 # sequence line comment2 - # sequence foot comment -# baz foot comment -hoge: - moga: true # moga line comment - # moga foot comment -` - actual := "\n" + string(b) - if expected != actual { - t.Fatalf("expected:%s but got %s", expected, actual) - } - }) - -} - -func Test_CommentToMapOption(t *testing.T) { - t.Run("line comment", func(t *testing.T) { - yml := ` -foo: aaa #foo comment -bar: #bar comment - bbb: ccc #bbb comment -baz: - x: 10 #x comment -` - var ( - v interface{} - cm = yaml.CommentMap{} - ) - if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { - t.Fatal(err) - } - expected := []struct { - path string - comments []*yaml.Comment - }{ - { - path: "$.foo", - comments: []*yaml.Comment{yaml.LineComment("foo comment")}, - }, - { - path: "$.bar", - comments: []*yaml.Comment{yaml.LineComment("bar comment")}, - }, - { - path: "$.bar.bbb", - comments: []*yaml.Comment{yaml.LineComment("bbb comment")}, - }, - { - path: "$.baz.x", - comments: []*yaml.Comment{yaml.LineComment("x comment")}, - }, - } - for _, exp := range expected { - comments := cm[exp.path] - if comments == nil { - t.Fatalf("failed to get path %s", exp.path) - } - if diff := cmp.Diff(exp.comments, comments); diff != "" { - t.Errorf("(-got, +want)\n%s", diff) - } - } - }) - t.Run("line comment2", func(t *testing.T) { - yml := ` -foo: - bar: baz # comment` - var ( - v interface{} - cm = yaml.CommentMap{} - ) - if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { - t.Fatal(err) - } - expected := []struct { - path string - comments []*yaml.Comment - }{ - { - path: "$.foo.bar", - comments: []*yaml.Comment{yaml.LineComment(" comment")}, - }, - } - for _, exp := range expected { - comments := cm[exp.path] - if comments == nil { - t.Fatalf("failed to get path %s", exp.path) - } - if diff := cmp.Diff(exp.comments, comments); diff != "" { - t.Errorf("(-got, +want)\n%s", diff) - } - } - }) - - t.Run("single head comment", func(t *testing.T) { - yml := ` -#foo comment -foo: aaa -#bar comment -bar: - #bbb comment - bbb: ccc -baz: - #x comment - x: 10 -` - var ( - v interface{} - cm = yaml.CommentMap{} - ) - if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { - t.Fatal(err) - } - expected := []struct { - path string - comments []*yaml.Comment - }{ - { - path: "$.foo", - comments: []*yaml.Comment{yaml.HeadComment("foo comment")}, - }, - { - path: "$.bar", - comments: []*yaml.Comment{yaml.HeadComment("bar comment")}, - }, - { - path: "$.bar.bbb", - comments: []*yaml.Comment{yaml.HeadComment("bbb comment")}, - }, - { - path: "$.baz.x", - comments: []*yaml.Comment{yaml.HeadComment("x comment")}, - }, - } - for _, exp := range expected { - comments := cm[exp.path] - if comments == nil { - t.Fatalf("failed to get path %s", exp.path) - } - if diff := cmp.Diff(exp.comments, comments); diff != "" { - t.Errorf("(-got, +want)\n%s", diff) - } - } - }) - t.Run("multiple head comments", func(t *testing.T) { - yml := ` -#foo comment -#foo comment2 -foo: aaa -#bar comment -#bar comment2 -bar: - #bbb comment - #bbb comment2 - bbb: ccc -baz: - #x comment - #x comment2 - x: 10 -` - var ( - v interface{} - cm = yaml.CommentMap{} - ) - if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { - t.Fatal(err) - } - expected := []struct { - path string - comments []*yaml.Comment - }{ - { - path: "$.foo", - comments: []*yaml.Comment{ - yaml.HeadComment( - "foo comment", - "foo comment2", - ), - }, - }, - { - path: "$.bar", - comments: []*yaml.Comment{ - yaml.HeadComment( - "bar comment", - "bar comment2", - ), - }, - }, - { - path: "$.bar.bbb", - comments: []*yaml.Comment{ - yaml.HeadComment( - "bbb comment", - "bbb comment2", - ), - }, - }, - { - path: "$.baz.x", - comments: []*yaml.Comment{ - yaml.HeadComment( - "x comment", - "x comment2", - ), - }, - }, - } - for _, exp := range expected { - comments := cm[exp.path] - if comments == nil { - t.Fatalf("failed to get path %s", exp.path) - } - if diff := cmp.Diff(exp.comments, comments); diff != "" { - t.Errorf("(-got, +want)\n%s", diff) - } - } - }) - t.Run("foot comment", func(t *testing.T) { - yml := ` -bar: - bbb: ccc - #ccc: ddd -baz: - - 1 - - 2 - #- 3 - # foot comment -#foot comment2 -` - var ( - v interface{} - cm = yaml.CommentMap{} - ) - if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { - t.Fatal(err) - } - expected := []struct { - path string - comments []*yaml.Comment - }{ - { - path: "$.bar.bbb", - comments: []*yaml.Comment{yaml.FootComment("ccc: ddd")}, - }, - { - path: "$.baz[1]", - comments: []*yaml.Comment{yaml.FootComment("- 3")}, - }, - { - path: "$.baz", - comments: []*yaml.Comment{yaml.FootComment(" foot comment", "foot comment2")}, - }, - } - for _, exp := range expected { - comments := cm[exp.path] - if comments == nil { - t.Fatalf("failed to get path %s", exp.path) - } - if diff := cmp.Diff(exp.comments, comments); diff != "" { - t.Errorf("(-got, +want)\n%s", diff) - } - } - }) - t.Run("combination", func(t *testing.T) { - yml := ` -# foo head comment -# foo head comment2 -foo: # foo line comment - # a head comment - a: # a line comment - # b head comment - b: # b line comment - c: d # c line comment -o: # o line comment - # p head comment - # p head comment2 - p: # p line comment - # q head comment - # q head comment2 - q: # q line comment - r: s # r line comment -t: - u: true # u line comment -# bar head comment -bar: # bar line comment - # bbb head comment - bbb: ccc # bbb line comment - # bbb foot comment -# baz head comment -# baz head comment2 -baz: # baz line comment - # sequence head comment - - 1 # sequence line comment - # sequence head comment2 - - 2 # sequence line comment2 - # sequence foot comment -hoge: - moga: true # moga line comment - # moga foot comment -# hoge foot comment -` - var ( - v interface{} - cm = yaml.CommentMap{} - ) - if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { - t.Fatal(err) - } - expected := []struct { - path string - comments []*yaml.Comment - }{ - { - path: "$.foo", - comments: []*yaml.Comment{ - yaml.HeadComment(" foo head comment", " foo head comment2"), - yaml.LineComment(" foo line comment"), - }, - }, - { - path: "$.foo.a", - comments: []*yaml.Comment{ - yaml.HeadComment(" a head comment"), - yaml.LineComment(" a line comment"), - }, - }, - { - path: "$.foo.a.b", - comments: []*yaml.Comment{ - yaml.HeadComment(" b head comment"), - yaml.LineComment(" b line comment"), - }, - }, - { - path: "$.foo.a.b.c", - comments: []*yaml.Comment{ - yaml.LineComment(" c line comment"), - }, - }, - { - path: "$.o", - comments: []*yaml.Comment{ - yaml.LineComment(" o line comment"), - }, - }, - { - path: "$.o.p", - comments: []*yaml.Comment{ - yaml.HeadComment(" p head comment", " p head comment2"), - yaml.LineComment(" p line comment"), - }, - }, - { - path: "$.o.p.q", - comments: []*yaml.Comment{ - yaml.HeadComment(" q head comment", " q head comment2"), - yaml.LineComment(" q line comment"), - }, - }, - { - path: "$.o.p.q.r", - comments: []*yaml.Comment{ - yaml.LineComment(" r line comment"), - }, - }, - { - path: "$.t.u", - comments: []*yaml.Comment{ - yaml.LineComment(" u line comment"), - }, - }, - { - path: "$.bar", - comments: []*yaml.Comment{ - yaml.HeadComment(" bar head comment"), - yaml.LineComment(" bar line comment"), - }, - }, - { - path: "$.bar.bbb", - comments: []*yaml.Comment{ - yaml.HeadComment(" bbb head comment"), - yaml.LineComment(" bbb line comment"), - yaml.FootComment(" bbb foot comment"), - }, - }, - { - path: "$.baz[0]", - comments: []*yaml.Comment{ - yaml.HeadComment(" sequence head comment"), - yaml.LineComment(" sequence line comment"), - }, - }, - { - path: "$.baz[1]", - comments: []*yaml.Comment{ - yaml.HeadComment(" sequence head comment2"), - yaml.LineComment(" sequence line comment2"), - yaml.FootComment(" sequence foot comment"), - }, - }, - { - path: "$.baz", - comments: []*yaml.Comment{ - yaml.HeadComment(" baz head comment", " baz head comment2"), - yaml.LineComment(" baz line comment"), - }, - }, - { - path: "$.hoge", - comments: []*yaml.Comment{ - yaml.FootComment(" hoge foot comment"), - }, - }, - { - path: "$.hoge.moga", - comments: []*yaml.Comment{ - yaml.LineComment(" moga line comment"), - yaml.FootComment(" moga foot comment"), - }, - }, - } - for _, exp := range expected { - comments := cm[exp.path] - if comments == nil { - t.Fatalf("failed to get path %s", exp.path) - } - if diff := cmp.Diff(exp.comments, comments); diff != "" { - t.Errorf("%s: (-got, +want)\n%s", exp.path, diff) - } - } - }) -} - -func TestRegisterCustomMarshaler(t *testing.T) { - type T struct { - Foo []byte `yaml:"foo"` - } - yaml.RegisterCustomMarshaler[T](func(_ T) ([]byte, error) { - return []byte(`"override"`), nil - }) - b, err := yaml.Marshal(&T{Foo: []byte("bar")}) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(b, []byte("\"override\"\n")) { - t.Fatalf("failed to register custom marshaler. got: %q", b) - } -} - -func TestRegisterCustomUnmarshaler(t *testing.T) { - type T struct { - Foo []byte `yaml:"foo"` - } - yaml.RegisterCustomUnmarshaler[T](func(v *T, _ []byte) error { - v.Foo = []byte("override") - return nil - }) - var v T - if err := yaml.Unmarshal([]byte(`"foo: "bar"`), &v); err != nil { - t.Fatal(err) - } - if !bytes.Equal(v.Foo, []byte("override")) { - t.Fatalf("failed to decode. got %q", v.Foo) - } -} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.codecov.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.codecov.yml new file mode 100644 index 0000000..8364eea --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.codecov.yml @@ -0,0 +1,31 @@ +codecov: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + + status: + project: + default: + target: 75% + threshold: 2% + patch: off + changes: no + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "header,diff" + behavior: default + require_changes: no + +ignore: + - ast diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/FUNDING.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/FUNDING.yml new file mode 100644 index 0000000..ab4b632 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [goccy] diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/ISSUE_TEMPLATE/bug_report.md b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1aad475 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** + +Please provide a minimum yaml content that can be reproduced. +We are more than happy to use [Go Playground](https://go.dev/play) + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Version Variables** + - Go version: [e.g. 1.21 ] + - go-yaml's Version: [e.g. v1.11.1 ] + +**Additional context** +Add any other context about the problem here. diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/ISSUE_TEMPLATE/feature_request.md b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..e301d68 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: feature request +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/pull_request_template.md b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/pull_request_template.md new file mode 100644 index 0000000..a1d6c8e --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/pull_request_template.md @@ -0,0 +1,4 @@ +Before submitting your PR, please confirm the following. + +- [ ] Describe the purpose for which you created this PR. +- [ ] Create test code that corresponds to the modification \ No newline at end of file diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/workflows/go.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/workflows/go.yml new file mode 100644 index 0000000..647c692 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/.github/workflows/go.yml @@ -0,0 +1,79 @@ +name: Go +on: + push: + branches: + - master + pull_request: +jobs: + race-test: + name: Test with -race + strategy: + matrix: + os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] + go-version: [ "1.19", "1.20", "1.21" ] + runs-on: ${{ matrix.os }} + steps: + - name: checkout + uses: actions/checkout@v4 + - name: setup Go ${{ matrix.go-version }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + - name: test + run: | + make test + + i386-test: + name: Test in i386 + strategy: + matrix: + os: [ "ubuntu-latest", "windows-latest" ] + go-version: [ "1.19", "1.20", "1.21" ] + runs-on: ${{ matrix.os }} + env: + GOARCH: "386" + steps: + - name: checkout + uses: actions/checkout@v4 + - name: setup Go ${{ matrix.go-version }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + - name: test + run: | + make simple-test + + ycat: + name: ycat + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v4 + - name: setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.21" + - name: build + run: | + make ycat/build + - name: run + run: | + ./ycat .github/workflows/go.yml + + coverage: + name: Coverage + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v4 + - name: setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.21" + - name: measure coverage + run: | + make cover + - uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true + verbose: true diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/CHANGELOG.md b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/CHANGELOG.md new file mode 100644 index 0000000..c8f820d --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/CHANGELOG.md @@ -0,0 +1,186 @@ +# 1.11.2 - 2023-09-15 + +### Fix bugs + +- Fix quoted comments ( #370 ) +- Fix handle of space at start or last ( #376 ) +- Fix sequence with comment ( #390 ) + +# 1.11.1 - 2023-09-14 + +### Fix bugs + +- Handle `\r` in a double-quoted string the same as `\n` ( #372 ) +- Replace loop with n.Values = append(n.Values, target.Values...) ( #380 ) +- Skip encoding an inline field if it is null ( #386 ) +- Fix comment parsing with null value ( #388 ) + +# 1.11.0 - 2023-04-03 + +### Features + +- Supports dynamically switch encode and decode processing for a given type + +# 1.10.1 - 2023-03-28 + +### Features + +- Quote YAML 1.1 bools at encoding time for compatibility with other legacy parsers +- Add support of 32-bit architecture + +### Fix bugs + +- Don't trim all space characters in block style sequence +- Support strings starting with `@` + +# 1.10.0 - 2023-03-01 + +### Fix bugs + +Reversible conversion of comments was not working in various cases, which has been corrected. +**Breaking Change** exists in the comment map interface. However, if you are dealing with CommentMap directly, there is no problem. + + +# 1.9.8 - 2022-12-19 + +### Fix feature + +- Append new line at the end of file ( #329 ) + +### Fix bugs + +- Fix custom marshaler ( #333, #334 ) +- Fix behavior when struct fields conflicted( #335 ) +- Fix position calculation for literal, folded and raw folded strings ( #330 ) + +# 1.9.7 - 2022-12-03 + +### Fix bugs + +- Fix handling of quoted map key ( #328 ) +- Fix resusing process of scanning context ( #322 ) + +## v1.9.6 - 2022-10-26 + +### New Features + +- Introduce MapKeyNode interface to limit node types for map key ( #312 ) + +### Fix bugs + +- Quote strings with special characters in flow mode ( #270 ) +- typeError implements PrettyPrinter interface ( #280 ) +- Fix incorrect const type ( #284 ) +- Fix large literals type inference on 32 bits ( #293 ) +- Fix UTF-8 characters ( #294 ) +- Fix decoding of unknown aliases ( #317 ) +- Fix stream encoder for insert a separator between each encoded document ( #318 ) + +### Update + +- Update golang.org/x/sys ( #289 ) +- Update Go version in CI ( #295 ) +- Add test cases for missing keys to struct literals ( #300 ) + +## v1.9.5 - 2022-01-12 + +### New Features + +* Add UseSingleQuote option ( #265 ) + +### Fix bugs + +* Preserve defaults while decoding nested structs ( #260 ) +* Fix minor typo in decodeInit error ( #264 ) +* Handle empty sequence entries ( #275 ) +* Fix encoding of sequence with multiline string ( #276 ) +* Fix encoding of BytesMarshaler type ( #277 ) +* Fix indentState logic for multi-line value ( #278 ) + +## v1.9.4 - 2021-10-12 + +### Fix bugs + +* Keep prev/next reference between tokens containing comments when filtering comment tokens ( #257 ) +* Supports escaping reserved keywords in PathBuilder ( #258 ) + +## v1.9.3 - 2021-09-07 + +### New Features + +* Support encoding and decoding `time.Duration` fields ( #246 ) +* Allow reserved characters for key name in YAMLPath ( #251 ) +* Support getting YAMLPath from ast.Node ( #252 ) +* Support CommentToMap option ( #253 ) + +### Fix bugs + +* Fix encoding nested sequences with `yaml.IndentSequence` ( #241 ) +* Fix error reporting on inline structs in strict mode ( #244, #245 ) +* Fix encoding of large floats ( #247 ) + +### Improve workflow + +* Migrate CI from CircleCI to GitHub Action ( #249 ) +* Add workflow for ycat ( #250 ) + +## v1.9.2 - 2021-07-26 + +### Support WithComment option ( #238 ) + +`yaml.WithComment` is a option for encoding with comment. +The position where you want to add a comment is represented by YAMLPath, and it is the key of `yaml.CommentMap`. +Also, you can select `Head` comment or `Line` comment as the comment type. + +## v1.9.1 - 2021-07-20 + +### Fix DecodeFromNode ( #237 ) + +- Fix YAML handling where anchor exists + +## v1.9.0 - 2021-07-19 + +### New features + +- Support encoding of comment node ( #233 ) +- Support `yaml.NodeToValue(ast.Node, interface{}, ...DecodeOption) error` ( #236 ) + - Can convert a AST node to a value directly + +### Fix decoder for comment + +- Fix parsing of literal with comment ( #234 ) + +### Rename API ( #235 ) + +- Rename `MarshalWithContext` to `MarshalContext` +- Rename `UnmarshalWithContext` to `UnmarshalContext` + +## v1.8.10 - 2021-07-02 + +### Fixed bugs + +- Fix searching anchor by alias name ( #212 ) +- Fixing Issue 186, scanner should account for newline characters when processing multi-line text. Without this source annotations line/column number (for this and all subsequent tokens) is inconsistent with plain text editors. e.g. https://github.com/goccy/go-yaml/issues/186. This addresses the issue specifically for single and double quote text only. ( #210 ) +- Add error for unterminated flow mapping node ( #213 ) +- Handle missing required field validation ( #221 ) +- Nicely format unexpected node type errors ( #229 ) +- Support to encode map which has defined type key ( #231 ) + +### New features + +- Support sequence indentation by EncodeOption ( #232 ) + +## v1.8.9 - 2021-03-01 + +### Fixed bugs + +- Fix origin buffer for DocumentHeader and DocumentEnd and Directive +- Fix origin buffer for anchor value +- Fix syntax error about map value +- Fix parsing MergeKey ('<<') characters +- Fix encoding of float value +- Fix incorrect column annotation when single or double quotes are used + +### New features + +- Support to encode/decode of ast.Node directly diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/LICENSE b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/LICENSE new file mode 100644 index 0000000..04485ce --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Masaaki Goshima + +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. diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/Makefile b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/Makefile new file mode 100644 index 0000000..1b1d923 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/Makefile @@ -0,0 +1,19 @@ +.PHONY: test +test: + go test -v -race ./... + +.PHONY: simple-test +simple-test: + go test -v ./... + +.PHONY: cover +cover: + go test -coverprofile=cover.out ./... + +.PHONY: cover-html +cover-html: cover + go tool cover -html=cover.out + +.PHONY: ycat/build +ycat/build: + go build -o ycat ./cmd/ycat diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/README.md b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/README.md new file mode 100644 index 0000000..9452349 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/README.md @@ -0,0 +1,370 @@ +# YAML support for the Go language + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/goccy/go-yaml)](https://pkg.go.dev/github.com/goccy/go-yaml) +![Go](https://github.com/goccy/go-yaml/workflows/Go/badge.svg) +[![codecov](https://codecov.io/gh/goccy/go-yaml/branch/master/graph/badge.svg)](https://codecov.io/gh/goccy/go-yaml) +[![Go Report Card](https://goreportcard.com/badge/github.com/goccy/go-yaml)](https://goreportcard.com/report/github.com/goccy/go-yaml) + + + +# Why a new library? + +As of this writing, there already exists a de facto standard library for YAML processing for Go: [https://github.com/go-yaml/yaml](https://github.com/go-yaml/yaml). However we feel that some features are lacking, namely: + +- Pretty format for error notifications +- Direct manipulation of YAML abstract syntax tree +- Support for `Anchor` and `Alias` when marshaling +- Allow referencing elements declared in another file via anchors + +# Features + +- Pretty format for error notifications +- Supports `Scanner` or `Lexer` or `Parser` as public API +- Supports `Anchor` and `Alias` to Marshaler +- Allow referencing elements declared in another file via anchors +- Extract value or AST by YAMLPath ( YAMLPath is like a JSONPath ) + +# Installation + +```sh +go get -u github.com/goccy/go-yaml +``` + +# Synopsis + +## 1. Simple Encode/Decode + +Has an interface like `go-yaml/yaml` using `reflect` + +```go +var v struct { + A int + B string +} +v.A = 1 +v.B = "hello" +bytes, err := yaml.Marshal(v) +if err != nil { + //... +} +fmt.Println(string(bytes)) // "a: 1\nb: hello\n" +``` + +```go + yml := ` +%YAML 1.2 +--- +a: 1 +b: c +` +var v struct { + A int + B string +} +if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + //... +} +``` + +To control marshal/unmarshal behavior, you can use the `yaml` tag. + +```go + yml := `--- +foo: 1 +bar: c +` +var v struct { + A int `yaml:"foo"` + B string `yaml:"bar"` +} +if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + //... +} +``` + +For convenience, we also accept the `json` tag. Note that not all options from +the `json` tag will have significance when parsing YAML documents. If both +tags exist, `yaml` tag will take precedence. + +```go + yml := `--- +foo: 1 +bar: c +` +var v struct { + A int `json:"foo"` + B string `json:"bar"` +} +if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + //... +} +``` + +For custom marshal/unmarshaling, implement either `Bytes` or `Interface` variant of marshaler/unmarshaler. The difference is that while `BytesMarshaler`/`BytesUnmarshaler` behaves like [`encoding/json`](https://pkg.go.dev/encoding/json) and `InterfaceMarshaler`/`InterfaceUnmarshaler` behaves like [`gopkg.in/yaml.v2`](https://pkg.go.dev/gopkg.in/yaml.v2). + +Semantically both are the same, but they differ in performance. Because indentation matters in YAML, you cannot simply accept a valid YAML fragment from a Marshaler, and expect it to work when it is attached to the parent container's serialized form. Therefore when we receive use the `BytesMarshaler`, which returns `[]byte`, we must decode it once to figure out how to make it work in the given context. If you use the `InterfaceMarshaler`, we can skip the decoding. + +If you are repeatedly marshaling complex objects, the latter is always better +performance wise. But if you are, for example, just providing a choice between +a config file format that is read only once, the former is probably easier to +code. + +## 2. Reference elements declared in another file + +`testdata` directory contains `anchor.yml` file: + +```shell +├── testdata +   └── anchor.yml +``` + +And `anchor.yml` is defined as follows: + +```yaml +a: &a + b: 1 + c: hello +``` + +Then, if `yaml.ReferenceDirs("testdata")` option is passed to `yaml.Decoder`, + `Decoder` tries to find the anchor definition from YAML files the under `testdata` directory. + +```go +buf := bytes.NewBufferString("a: *a\n") +dec := yaml.NewDecoder(buf, yaml.ReferenceDirs("testdata")) +var v struct { + A struct { + B int + C string + } +} +if err := dec.Decode(&v); err != nil { + //... +} +fmt.Printf("%+v\n", v) // {A:{B:1 C:hello}} +``` + +## 3. Encode with `Anchor` and `Alias` + +### 3.1. Explicitly declared `Anchor` name and `Alias` name + +If you want to use `anchor` or `alias`, you can define it as a struct tag. + +```go +type T struct { + A int + B string +} +var v struct { + C *T `yaml:"c,anchor=x"` + D *T `yaml:"d,alias=x"` +} +v.C = &T{A: 1, B: "hello"} +v.D = v.C +bytes, err := yaml.Marshal(v) +if err != nil { + panic(err) +} +fmt.Println(string(bytes)) +/* +c: &x + a: 1 + b: hello +d: *x +*/ +``` + +### 3.2. Implicitly declared `Anchor` and `Alias` names + +If you do not explicitly declare the anchor name, the default behavior is to +use the equivalent of `strings.ToLower($FieldName)` as the name of the anchor. + +If you do not explicitly declare the alias name AND the value is a pointer +to another element, we look up the anchor name by finding out which anchor +field the value is assigned to by looking up its pointer address. + +```go +type T struct { + I int + S string +} +var v struct { + A *T `yaml:"a,anchor"` + B *T `yaml:"b,anchor"` + C *T `yaml:"c,alias"` + D *T `yaml:"d,alias"` +} +v.A = &T{I: 1, S: "hello"} +v.B = &T{I: 2, S: "world"} +v.C = v.A // C has same pointer address to A +v.D = v.B // D has same pointer address to B +bytes, err := yaml.Marshal(v) +if err != nil { + //... +} +fmt.Println(string(bytes)) +/* +a: &a + i: 1 + s: hello +b: &b + i: 2 + s: world +c: *a +d: *b +*/ +``` + +### 3.3 MergeKey and Alias + +Merge key and alias ( `<<: *alias` ) can be used by embedding a structure with the `inline,alias` tag. + +```go +type Person struct { + *Person `yaml:",omitempty,inline,alias"` // embed Person type for default value + Name string `yaml:",omitempty"` + Age int `yaml:",omitempty"` +} +defaultPerson := &Person{ + Name: "John Smith", + Age: 20, +} +people := []*Person{ + { + Person: defaultPerson, // assign default value + Name: "Ken", // override Name property + Age: 10, // override Age property + }, + { + Person: defaultPerson, // assign default value only + }, +} +var doc struct { + Default *Person `yaml:"default,anchor"` + People []*Person `yaml:"people"` +} +doc.Default = defaultPerson +doc.People = people +bytes, err := yaml.Marshal(doc) +if err != nil { + //... +} +fmt.Println(string(bytes)) +/* +default: &default + name: John Smith + age: 20 +people: +- <<: *default + name: Ken + age: 10 +- <<: *default +*/ +``` + +## 4. Pretty Formatted Errors + +Error values produced during parsing have two extra features over regular +error values. + +First, by default, they contain extra information on the location of the error +from the source YAML document, to make it easier to find the error location. + +Second, the error messages can optionally be colorized. + +If you would like to control exactly how the output looks like, consider +using `yaml.FormatError`, which accepts two boolean values to +control turning these features on or off. + + + +## 5. Use YAMLPath + +```go +yml := ` +store: + book: + - author: john + price: 10 + - author: ken + price: 12 + bicycle: + color: red + price: 19.95 +` +path, err := yaml.PathString("$.store.book[*].author") +if err != nil { + //... +} +var authors []string +if err := path.Read(strings.NewReader(yml), &authors); err != nil { + //... +} +fmt.Println(authors) +// [john ken] +``` + +### 5.1 Print customized error with YAML source code + +```go +package main + +import ( + "fmt" + + "github.com/goccy/go-yaml" +) + +func main() { + yml := ` +a: 1 +b: "hello" +` + var v struct { + A int + B string + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + panic(err) + } + if v.A != 2 { + // output error with YAML source + path, err := yaml.PathString("$.a") + if err != nil { + panic(err) + } + source, err := path.AnnotateSource([]byte(yml), true) + if err != nil { + panic(err) + } + fmt.Printf("a value expected 2 but actual %d:\n%s\n", v.A, string(source)) + } +} +``` + +output result is the following: + + + + +# Tools + +## ycat + +print yaml file with color + +ycat + +### Installation + +```sh +go install github.com/goccy/go-yaml/cmd/ycat@latest +``` + +# Looking for Sponsors + +I'm looking for sponsors this library. This library is being developed as a personal project in my spare time. If you want a quick response or problem resolution when using this library in your project, please register as a [sponsor](https://github.com/sponsors/goccy). I will cooperate as much as possible. Of course, this library is developed as an MIT license, so you can use it freely for free. + +# License + +MIT diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/ast/ast.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/ast/ast.go new file mode 100644 index 0000000..b4d5ec4 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/ast/ast.go @@ -0,0 +1,2115 @@ +package ast + +import ( + "fmt" + "io" + "math" + "strconv" + "strings" + + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +var ( + ErrInvalidTokenType = xerrors.New("invalid token type") + ErrInvalidAnchorName = xerrors.New("invalid anchor name") + ErrInvalidAliasName = xerrors.New("invalid alias name") +) + +// NodeType type identifier of node +type NodeType int + +const ( + // UnknownNodeType type identifier for default + UnknownNodeType NodeType = iota + // DocumentType type identifier for document node + DocumentType + // NullType type identifier for null node + NullType + // BoolType type identifier for boolean node + BoolType + // IntegerType type identifier for integer node + IntegerType + // FloatType type identifier for float node + FloatType + // InfinityType type identifier for infinity node + InfinityType + // NanType type identifier for nan node + NanType + // StringType type identifier for string node + StringType + // MergeKeyType type identifier for merge key node + MergeKeyType + // LiteralType type identifier for literal node + LiteralType + // MappingType type identifier for mapping node + MappingType + // MappingKeyType type identifier for mapping key node + MappingKeyType + // MappingValueType type identifier for mapping value node + MappingValueType + // SequenceType type identifier for sequence node + SequenceType + // AnchorType type identifier for anchor node + AnchorType + // AliasType type identifier for alias node + AliasType + // DirectiveType type identifier for directive node + DirectiveType + // TagType type identifier for tag node + TagType + // CommentType type identifier for comment node + CommentType + // CommentGroupType type identifier for comment group node + CommentGroupType +) + +// String node type identifier to text +func (t NodeType) String() string { + switch t { + case UnknownNodeType: + return "UnknownNode" + case DocumentType: + return "Document" + case NullType: + return "Null" + case BoolType: + return "Bool" + case IntegerType: + return "Integer" + case FloatType: + return "Float" + case InfinityType: + return "Infinity" + case NanType: + return "Nan" + case StringType: + return "String" + case MergeKeyType: + return "MergeKey" + case LiteralType: + return "Literal" + case MappingType: + return "Mapping" + case MappingKeyType: + return "MappingKey" + case MappingValueType: + return "MappingValue" + case SequenceType: + return "Sequence" + case AnchorType: + return "Anchor" + case AliasType: + return "Alias" + case DirectiveType: + return "Directive" + case TagType: + return "Tag" + case CommentType: + return "Comment" + case CommentGroupType: + return "CommentGroup" + } + return "" +} + +// String node type identifier to YAML Structure name +// based on https://yaml.org/spec/1.2/spec.html +func (t NodeType) YAMLName() string { + switch t { + case UnknownNodeType: + return "unknown" + case DocumentType: + return "document" + case NullType: + return "null" + case BoolType: + return "boolean" + case IntegerType: + return "int" + case FloatType: + return "float" + case InfinityType: + return "inf" + case NanType: + return "nan" + case StringType: + return "string" + case MergeKeyType: + return "merge key" + case LiteralType: + return "scalar" + case MappingType: + return "mapping" + case MappingKeyType: + return "key" + case MappingValueType: + return "value" + case SequenceType: + return "sequence" + case AnchorType: + return "anchor" + case AliasType: + return "alias" + case DirectiveType: + return "directive" + case TagType: + return "tag" + case CommentType: + return "comment" + case CommentGroupType: + return "comment" + } + return "" +} + +// Node type of node +type Node interface { + io.Reader + // String node to text + String() string + // GetToken returns token instance + GetToken() *token.Token + // Type returns type of node + Type() NodeType + // AddColumn add column number to child nodes recursively + AddColumn(int) + // SetComment set comment token to node + SetComment(*CommentGroupNode) error + // Comment returns comment token instance + GetComment() *CommentGroupNode + // GetPath returns YAMLPath for the current node + GetPath() string + // SetPath set YAMLPath for the current node + SetPath(string) + // MarshalYAML + MarshalYAML() ([]byte, error) + // already read length + readLen() int + // append read length + addReadLen(int) + // clean read length + clearLen() +} + +// MapKeyNode type for map key node +type MapKeyNode interface { + Node + // String node to text without comment + stringWithoutComment() string +} + +// ScalarNode type for scalar node +type ScalarNode interface { + MapKeyNode + GetValue() interface{} +} + +type BaseNode struct { + Path string + Comment *CommentGroupNode + read int +} + +func addCommentString(base string, node *CommentGroupNode) string { + return fmt.Sprintf("%s %s", base, node.String()) +} + +func (n *BaseNode) readLen() int { + return n.read +} + +func (n *BaseNode) clearLen() { + n.read = 0 +} + +func (n *BaseNode) addReadLen(len int) { + n.read += len +} + +// GetPath returns YAMLPath for the current node. +func (n *BaseNode) GetPath() string { + if n == nil { + return "" + } + return n.Path +} + +// SetPath set YAMLPath for the current node. +func (n *BaseNode) SetPath(path string) { + if n == nil { + return + } + n.Path = path +} + +// GetComment returns comment token instance +func (n *BaseNode) GetComment() *CommentGroupNode { + return n.Comment +} + +// SetComment set comment token +func (n *BaseNode) SetComment(node *CommentGroupNode) error { + n.Comment = node + return nil +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func readNode(p []byte, node Node) (int, error) { + s := node.String() + readLen := node.readLen() + remain := len(s) - readLen + if remain == 0 { + node.clearLen() + return 0, io.EOF + } + size := min(remain, len(p)) + for idx, b := range []byte(s[readLen : readLen+size]) { + p[idx] = byte(b) + } + node.addReadLen(size) + return size, nil +} + +// Null create node for null value +func Null(tk *token.Token) *NullNode { + return &NullNode{ + BaseNode: &BaseNode{}, + Token: tk, + } +} + +// Bool create node for boolean value +func Bool(tk *token.Token) *BoolNode { + b, _ := strconv.ParseBool(tk.Value) + return &BoolNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: b, + } +} + +// Integer create node for integer value +func Integer(tk *token.Token) *IntegerNode { + value := removeUnderScoreFromNumber(tk.Value) + switch tk.Type { + case token.BinaryIntegerType: + // skip two characters because binary token starts with '0b' + skipCharacterNum := 2 + negativePrefix := "" + if value[0] == '-' { + skipCharacterNum++ + negativePrefix = "-" + } + if len(negativePrefix) > 0 { + i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 2, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + i, _ := strconv.ParseUint(negativePrefix+value[skipCharacterNum:], 2, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + case token.OctetIntegerType: + // octet token starts with '0o' or '-0o' or '0' or '-0' + skipCharacterNum := 1 + negativePrefix := "" + if value[0] == '-' { + skipCharacterNum++ + if len(value) > 2 && value[2] == 'o' { + skipCharacterNum++ + } + negativePrefix = "-" + } else { + if value[1] == 'o' { + skipCharacterNum++ + } + } + if len(negativePrefix) > 0 { + i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 8, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + i, _ := strconv.ParseUint(value[skipCharacterNum:], 8, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + case token.HexIntegerType: + // hex token starts with '0x' or '-0x' + skipCharacterNum := 2 + negativePrefix := "" + if value[0] == '-' { + skipCharacterNum++ + negativePrefix = "-" + } + if len(negativePrefix) > 0 { + i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 16, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + i, _ := strconv.ParseUint(value[skipCharacterNum:], 16, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + if value[0] == '-' || value[0] == '+' { + i, _ := strconv.ParseInt(value, 10, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + i, _ := strconv.ParseUint(value, 10, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } +} + +// Float create node for float value +func Float(tk *token.Token) *FloatNode { + f, _ := strconv.ParseFloat(removeUnderScoreFromNumber(tk.Value), 64) + return &FloatNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: f, + } +} + +// Infinity create node for .inf or -.inf value +func Infinity(tk *token.Token) *InfinityNode { + node := &InfinityNode{ + BaseNode: &BaseNode{}, + Token: tk, + } + switch tk.Value { + case ".inf", ".Inf", ".INF": + node.Value = math.Inf(0) + case "-.inf", "-.Inf", "-.INF": + node.Value = math.Inf(-1) + } + return node +} + +// Nan create node for .nan value +func Nan(tk *token.Token) *NanNode { + return &NanNode{ + BaseNode: &BaseNode{}, + Token: tk, + } +} + +// String create node for string value +func String(tk *token.Token) *StringNode { + return &StringNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: tk.Value, + } +} + +// Comment create node for comment +func Comment(tk *token.Token) *CommentNode { + return &CommentNode{ + BaseNode: &BaseNode{}, + Token: tk, + } +} + +func CommentGroup(comments []*token.Token) *CommentGroupNode { + nodes := []*CommentNode{} + for _, comment := range comments { + nodes = append(nodes, Comment(comment)) + } + return &CommentGroupNode{ + BaseNode: &BaseNode{}, + Comments: nodes, + } +} + +// MergeKey create node for merge key ( << ) +func MergeKey(tk *token.Token) *MergeKeyNode { + return &MergeKeyNode{ + BaseNode: &BaseNode{}, + Token: tk, + } +} + +// Mapping create node for map +func Mapping(tk *token.Token, isFlowStyle bool, values ...*MappingValueNode) *MappingNode { + node := &MappingNode{ + BaseNode: &BaseNode{}, + Start: tk, + IsFlowStyle: isFlowStyle, + Values: []*MappingValueNode{}, + } + node.Values = append(node.Values, values...) + return node +} + +// MappingValue create node for mapping value +func MappingValue(tk *token.Token, key MapKeyNode, value Node) *MappingValueNode { + return &MappingValueNode{ + BaseNode: &BaseNode{}, + Start: tk, + Key: key, + Value: value, + } +} + +// MappingKey create node for map key ( '?' ). +func MappingKey(tk *token.Token) *MappingKeyNode { + return &MappingKeyNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +// Sequence create node for sequence +func Sequence(tk *token.Token, isFlowStyle bool) *SequenceNode { + return &SequenceNode{ + BaseNode: &BaseNode{}, + Start: tk, + IsFlowStyle: isFlowStyle, + Values: []Node{}, + } +} + +func Anchor(tk *token.Token) *AnchorNode { + return &AnchorNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +func Alias(tk *token.Token) *AliasNode { + return &AliasNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +func Document(tk *token.Token, body Node) *DocumentNode { + return &DocumentNode{ + BaseNode: &BaseNode{}, + Start: tk, + Body: body, + } +} + +func Directive(tk *token.Token) *DirectiveNode { + return &DirectiveNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +func Literal(tk *token.Token) *LiteralNode { + return &LiteralNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +func Tag(tk *token.Token) *TagNode { + return &TagNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +// File contains all documents in YAML file +type File struct { + Name string + Docs []*DocumentNode +} + +// Read implements (io.Reader).Read +func (f *File) Read(p []byte) (int, error) { + for _, doc := range f.Docs { + n, err := doc.Read(p) + if err == io.EOF { + continue + } + return n, nil + } + return 0, io.EOF +} + +// String all documents to text +func (f *File) String() string { + docs := []string{} + for _, doc := range f.Docs { + docs = append(docs, doc.String()) + } + if len(docs) > 0 { + return strings.Join(docs, "\n") + "\n" + } else { + return "" + } +} + +// DocumentNode type of Document +type DocumentNode struct { + *BaseNode + Start *token.Token // position of DocumentHeader ( `---` ) + End *token.Token // position of DocumentEnd ( `...` ) + Body Node +} + +// Read implements (io.Reader).Read +func (d *DocumentNode) Read(p []byte) (int, error) { + return readNode(p, d) +} + +// Type returns DocumentNodeType +func (d *DocumentNode) Type() NodeType { return DocumentType } + +// GetToken returns token instance +func (d *DocumentNode) GetToken() *token.Token { + return d.Body.GetToken() +} + +// AddColumn add column number to child nodes recursively +func (d *DocumentNode) AddColumn(col int) { + if d.Body != nil { + d.Body.AddColumn(col) + } +} + +// String document to text +func (d *DocumentNode) String() string { + doc := []string{} + if d.Start != nil { + doc = append(doc, d.Start.Value) + } + doc = append(doc, d.Body.String()) + if d.End != nil { + doc = append(doc, d.End.Value) + } + return strings.Join(doc, "\n") +} + +// MarshalYAML encodes to a YAML text +func (d *DocumentNode) MarshalYAML() ([]byte, error) { + return []byte(d.String()), nil +} + +func removeUnderScoreFromNumber(num string) string { + return strings.ReplaceAll(num, "_", "") +} + +// NullNode type of null node +type NullNode struct { + *BaseNode + Token *token.Token +} + +// Read implements (io.Reader).Read +func (n *NullNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns NullType +func (n *NullNode) Type() NodeType { return NullType } + +// GetToken returns token instance +func (n *NullNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *NullNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns nil value +func (n *NullNode) GetValue() interface{} { + return nil +} + +// String returns `null` text +func (n *NullNode) String() string { + if n.Comment != nil { + return addCommentString("null", n.Comment) + } + return n.stringWithoutComment() +} + +func (n *NullNode) stringWithoutComment() string { + return "null" +} + +// MarshalYAML encodes to a YAML text +func (n *NullNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// IntegerNode type of integer node +type IntegerNode struct { + *BaseNode + Token *token.Token + Value interface{} // int64 or uint64 value +} + +// Read implements (io.Reader).Read +func (n *IntegerNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns IntegerType +func (n *IntegerNode) Type() NodeType { return IntegerType } + +// GetToken returns token instance +func (n *IntegerNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *IntegerNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns int64 value +func (n *IntegerNode) GetValue() interface{} { + return n.Value +} + +// String int64 to text +func (n *IntegerNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *IntegerNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *IntegerNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// FloatNode type of float node +type FloatNode struct { + *BaseNode + Token *token.Token + Precision int + Value float64 +} + +// Read implements (io.Reader).Read +func (n *FloatNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns FloatType +func (n *FloatNode) Type() NodeType { return FloatType } + +// GetToken returns token instance +func (n *FloatNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *FloatNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns float64 value +func (n *FloatNode) GetValue() interface{} { + return n.Value +} + +// String float64 to text +func (n *FloatNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *FloatNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *FloatNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// StringNode type of string node +type StringNode struct { + *BaseNode + Token *token.Token + Value string +} + +// Read implements (io.Reader).Read +func (n *StringNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns StringType +func (n *StringNode) Type() NodeType { return StringType } + +// GetToken returns token instance +func (n *StringNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *StringNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns string value +func (n *StringNode) GetValue() interface{} { + return n.Value +} + +// escapeSingleQuote escapes s to a single quoted scalar. +// https://yaml.org/spec/1.2.2/#732-single-quoted-style +func escapeSingleQuote(s string) string { + var sb strings.Builder + growLen := len(s) + // s includes also one ' from the doubled pair + 2 + // opening and closing ' + strings.Count(s, "'") // ' added by ReplaceAll + sb.Grow(growLen) + sb.WriteString("'") + sb.WriteString(strings.ReplaceAll(s, "'", "''")) + sb.WriteString("'") + return sb.String() +} + +// String string value to text with quote or literal header if required +func (n *StringNode) String() string { + switch n.Token.Type { + case token.SingleQuoteType: + quoted := escapeSingleQuote(n.Value) + if n.Comment != nil { + return addCommentString(quoted, n.Comment) + } + return quoted + case token.DoubleQuoteType: + quoted := strconv.Quote(n.Value) + if n.Comment != nil { + return addCommentString(quoted, n.Comment) + } + return quoted + } + + lbc := token.DetectLineBreakCharacter(n.Value) + if strings.Contains(n.Value, lbc) { + // This block assumes that the line breaks in this inside scalar content and the Outside scalar content are the same. + // It works mostly, but inconsistencies occur if line break characters are mixed. + header := token.LiteralBlockHeader(n.Value) + space := strings.Repeat(" ", n.Token.Position.Column-1) + values := []string{} + for _, v := range strings.Split(n.Value, lbc) { + values = append(values, fmt.Sprintf("%s %s", space, v)) + } + block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space)) + return fmt.Sprintf("%s%s%s", header, lbc, block) + } else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') { + return fmt.Sprintf(`'%s'`, n.Value) + } + if n.Comment != nil { + return addCommentString(n.Value, n.Comment) + } + return n.Value +} + +func (n *StringNode) stringWithoutComment() string { + switch n.Token.Type { + case token.SingleQuoteType: + quoted := fmt.Sprintf(`'%s'`, n.Value) + return quoted + case token.DoubleQuoteType: + quoted := strconv.Quote(n.Value) + return quoted + } + + lbc := token.DetectLineBreakCharacter(n.Value) + if strings.Contains(n.Value, lbc) { + // This block assumes that the line breaks in this inside scalar content and the Outside scalar content are the same. + // It works mostly, but inconsistencies occur if line break characters are mixed. + header := token.LiteralBlockHeader(n.Value) + space := strings.Repeat(" ", n.Token.Position.Column-1) + values := []string{} + for _, v := range strings.Split(n.Value, lbc) { + values = append(values, fmt.Sprintf("%s %s", space, v)) + } + block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space)) + return fmt.Sprintf("%s%s%s", header, lbc, block) + } else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') { + return fmt.Sprintf(`'%s'`, n.Value) + } + return n.Value +} + +// MarshalYAML encodes to a YAML text +func (n *StringNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// LiteralNode type of literal node +type LiteralNode struct { + *BaseNode + Start *token.Token + Value *StringNode +} + +// Read implements (io.Reader).Read +func (n *LiteralNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns LiteralType +func (n *LiteralNode) Type() NodeType { return LiteralType } + +// GetToken returns token instance +func (n *LiteralNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *LiteralNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// GetValue returns string value +func (n *LiteralNode) GetValue() interface{} { + return n.String() +} + +// String literal to text +func (n *LiteralNode) String() string { + origin := n.Value.GetToken().Origin + lit := strings.TrimRight(strings.TrimRight(origin, " "), "\n") + if n.Comment != nil { + return fmt.Sprintf("%s %s\n%s", n.Start.Value, n.Comment.String(), lit) + } + return fmt.Sprintf("%s\n%s", n.Start.Value, lit) +} + +func (n *LiteralNode) stringWithoutComment() string { + return n.String() +} + +// MarshalYAML encodes to a YAML text +func (n *LiteralNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// MergeKeyNode type of merge key node +type MergeKeyNode struct { + *BaseNode + Token *token.Token +} + +// Read implements (io.Reader).Read +func (n *MergeKeyNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns MergeKeyType +func (n *MergeKeyNode) Type() NodeType { return MergeKeyType } + +// GetToken returns token instance +func (n *MergeKeyNode) GetToken() *token.Token { + return n.Token +} + +// GetValue returns '<<' value +func (n *MergeKeyNode) GetValue() interface{} { + return n.Token.Value +} + +// String returns '<<' value +func (n *MergeKeyNode) String() string { + return n.stringWithoutComment() +} + +func (n *MergeKeyNode) stringWithoutComment() string { + return n.Token.Value +} + +// AddColumn add column number to child nodes recursively +func (n *MergeKeyNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// MarshalYAML encodes to a YAML text +func (n *MergeKeyNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// BoolNode type of boolean node +type BoolNode struct { + *BaseNode + Token *token.Token + Value bool +} + +// Read implements (io.Reader).Read +func (n *BoolNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns BoolType +func (n *BoolNode) Type() NodeType { return BoolType } + +// GetToken returns token instance +func (n *BoolNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *BoolNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns boolean value +func (n *BoolNode) GetValue() interface{} { + return n.Value +} + +// String boolean to text +func (n *BoolNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *BoolNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *BoolNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// InfinityNode type of infinity node +type InfinityNode struct { + *BaseNode + Token *token.Token + Value float64 +} + +// Read implements (io.Reader).Read +func (n *InfinityNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns InfinityType +func (n *InfinityNode) Type() NodeType { return InfinityType } + +// GetToken returns token instance +func (n *InfinityNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *InfinityNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns math.Inf(0) or math.Inf(-1) +func (n *InfinityNode) GetValue() interface{} { + return n.Value +} + +// String infinity to text +func (n *InfinityNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *InfinityNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *InfinityNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// NanNode type of nan node +type NanNode struct { + *BaseNode + Token *token.Token +} + +// Read implements (io.Reader).Read +func (n *NanNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns NanType +func (n *NanNode) Type() NodeType { return NanType } + +// GetToken returns token instance +func (n *NanNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *NanNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns math.NaN() +func (n *NanNode) GetValue() interface{} { + return math.NaN() +} + +// String returns .nan +func (n *NanNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *NanNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *NanNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// MapNode interface of MappingValueNode / MappingNode +type MapNode interface { + MapRange() *MapNodeIter +} + +// MapNodeIter is an iterator for ranging over a MapNode +type MapNodeIter struct { + values []*MappingValueNode + idx int +} + +const ( + startRangeIndex = -1 +) + +// Next advances the map iterator and reports whether there is another entry. +// It returns false when the iterator is exhausted. +func (m *MapNodeIter) Next() bool { + m.idx++ + next := m.idx < len(m.values) + return next +} + +// Key returns the key of the iterator's current map node entry. +func (m *MapNodeIter) Key() MapKeyNode { + return m.values[m.idx].Key +} + +// Value returns the value of the iterator's current map node entry. +func (m *MapNodeIter) Value() Node { + return m.values[m.idx].Value +} + +// MappingNode type of mapping node +type MappingNode struct { + *BaseNode + Start *token.Token + End *token.Token + IsFlowStyle bool + Values []*MappingValueNode + FootComment *CommentGroupNode +} + +func (n *MappingNode) startPos() *token.Position { + if len(n.Values) == 0 { + return n.Start.Position + } + return n.Values[0].Key.GetToken().Position +} + +// Merge merge key/value of map. +func (n *MappingNode) Merge(target *MappingNode) { + keyToMapValueMap := map[string]*MappingValueNode{} + for _, value := range n.Values { + key := value.Key.String() + keyToMapValueMap[key] = value + } + column := n.startPos().Column - target.startPos().Column + target.AddColumn(column) + for _, value := range target.Values { + mapValue, exists := keyToMapValueMap[value.Key.String()] + if exists { + mapValue.Value = value.Value + } else { + n.Values = append(n.Values, value) + } + } +} + +// SetIsFlowStyle set value to IsFlowStyle field recursively. +func (n *MappingNode) SetIsFlowStyle(isFlow bool) { + n.IsFlowStyle = isFlow + for _, value := range n.Values { + value.SetIsFlowStyle(isFlow) + } +} + +// Read implements (io.Reader).Read +func (n *MappingNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns MappingType +func (n *MappingNode) Type() NodeType { return MappingType } + +// GetToken returns token instance +func (n *MappingNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *MappingNode) AddColumn(col int) { + n.Start.AddColumn(col) + n.End.AddColumn(col) + for _, value := range n.Values { + value.AddColumn(col) + } +} + +func (n *MappingNode) flowStyleString(commentMode bool) string { + values := []string{} + for _, value := range n.Values { + values = append(values, strings.TrimLeft(value.String(), " ")) + } + mapText := fmt.Sprintf("{%s}", strings.Join(values, ", ")) + if commentMode && n.Comment != nil { + return addCommentString(mapText, n.Comment) + } + return mapText +} + +func (n *MappingNode) blockStyleString(commentMode bool) string { + values := []string{} + for _, value := range n.Values { + values = append(values, value.String()) + } + mapText := strings.Join(values, "\n") + if commentMode && n.Comment != nil { + value := values[0] + var spaceNum int + for i := 0; i < len(value); i++ { + if value[i] != ' ' { + break + } + spaceNum++ + } + comment := n.Comment.StringWithSpace(spaceNum) + return fmt.Sprintf("%s\n%s", comment, mapText) + } + return mapText +} + +// String mapping values to text +func (n *MappingNode) String() string { + if len(n.Values) == 0 { + if n.Comment != nil { + return addCommentString("{}", n.Comment) + } + return "{}" + } + + commentMode := true + if n.IsFlowStyle || len(n.Values) == 0 { + return n.flowStyleString(commentMode) + } + return n.blockStyleString(commentMode) +} + +// MapRange implements MapNode protocol +func (n *MappingNode) MapRange() *MapNodeIter { + return &MapNodeIter{ + idx: startRangeIndex, + values: n.Values, + } +} + +// MarshalYAML encodes to a YAML text +func (n *MappingNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// MappingKeyNode type of tag node +type MappingKeyNode struct { + *BaseNode + Start *token.Token + Value Node +} + +// Read implements (io.Reader).Read +func (n *MappingKeyNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns MappingKeyType +func (n *MappingKeyNode) Type() NodeType { return MappingKeyType } + +// GetToken returns token instance +func (n *MappingKeyNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *MappingKeyNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String tag to text +func (n *MappingKeyNode) String() string { + return n.stringWithoutComment() +} + +func (n *MappingKeyNode) stringWithoutComment() string { + return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String()) +} + +// MarshalYAML encodes to a YAML text +func (n *MappingKeyNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// MappingValueNode type of mapping value +type MappingValueNode struct { + *BaseNode + Start *token.Token + Key MapKeyNode + Value Node + FootComment *CommentGroupNode +} + +// Replace replace value node. +func (n *MappingValueNode) Replace(value Node) error { + column := n.Value.GetToken().Position.Column - value.GetToken().Position.Column + value.AddColumn(column) + n.Value = value + return nil +} + +// Read implements (io.Reader).Read +func (n *MappingValueNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns MappingValueType +func (n *MappingValueNode) Type() NodeType { return MappingValueType } + +// GetToken returns token instance +func (n *MappingValueNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *MappingValueNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Key != nil { + n.Key.AddColumn(col) + } + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// SetIsFlowStyle set value to IsFlowStyle field recursively. +func (n *MappingValueNode) SetIsFlowStyle(isFlow bool) { + switch value := n.Value.(type) { + case *MappingNode: + value.SetIsFlowStyle(isFlow) + case *MappingValueNode: + value.SetIsFlowStyle(isFlow) + case *SequenceNode: + value.SetIsFlowStyle(isFlow) + } +} + +// String mapping value to text +func (n *MappingValueNode) String() string { + var text string + if n.Comment != nil { + text = fmt.Sprintf( + "%s\n%s", + n.Comment.StringWithSpace(n.Key.GetToken().Position.Column-1), + n.toString(), + ) + } else { + text = n.toString() + } + if n.FootComment != nil { + text += fmt.Sprintf("\n%s", n.FootComment.StringWithSpace(n.Key.GetToken().Position.Column-1)) + } + return text +} + +func (n *MappingValueNode) toString() string { + space := strings.Repeat(" ", n.Key.GetToken().Position.Column-1) + keyIndentLevel := n.Key.GetToken().Position.IndentLevel + valueIndentLevel := n.Value.GetToken().Position.IndentLevel + keyComment := n.Key.GetComment() + if _, ok := n.Value.(ScalarNode); ok { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } else if keyIndentLevel < valueIndentLevel { + if keyComment != nil { + return fmt.Sprintf( + "%s%s: %s\n%s", + space, + n.Key.stringWithoutComment(), + keyComment.String(), + n.Value.String(), + ) + } + return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String()) + } else if m, ok := n.Value.(*MappingNode); ok && (m.IsFlowStyle || len(m.Values) == 0) { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } else if s, ok := n.Value.(*SequenceNode); ok && (s.IsFlowStyle || len(s.Values) == 0) { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } else if _, ok := n.Value.(*AnchorNode); ok { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } else if _, ok := n.Value.(*AliasNode); ok { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } + if keyComment != nil { + return fmt.Sprintf( + "%s%s: %s\n%s", + space, + n.Key.stringWithoutComment(), + keyComment.String(), + n.Value.String(), + ) + } + if m, ok := n.Value.(*MappingNode); ok && m.Comment != nil { + return fmt.Sprintf( + "%s%s: %s", + space, + n.Key.String(), + strings.TrimLeft(n.Value.String(), " "), + ) + } + return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String()) +} + +// MapRange implements MapNode protocol +func (n *MappingValueNode) MapRange() *MapNodeIter { + return &MapNodeIter{ + idx: startRangeIndex, + values: []*MappingValueNode{n}, + } +} + +// MarshalYAML encodes to a YAML text +func (n *MappingValueNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// ArrayNode interface of SequenceNode +type ArrayNode interface { + ArrayRange() *ArrayNodeIter +} + +// ArrayNodeIter is an iterator for ranging over a ArrayNode +type ArrayNodeIter struct { + values []Node + idx int +} + +// Next advances the array iterator and reports whether there is another entry. +// It returns false when the iterator is exhausted. +func (m *ArrayNodeIter) Next() bool { + m.idx++ + next := m.idx < len(m.values) + return next +} + +// Value returns the value of the iterator's current array entry. +func (m *ArrayNodeIter) Value() Node { + return m.values[m.idx] +} + +// Len returns length of array +func (m *ArrayNodeIter) Len() int { + return len(m.values) +} + +// SequenceNode type of sequence node +type SequenceNode struct { + *BaseNode + Start *token.Token + End *token.Token + IsFlowStyle bool + Values []Node + ValueHeadComments []*CommentGroupNode + FootComment *CommentGroupNode +} + +// Replace replace value node. +func (n *SequenceNode) Replace(idx int, value Node) error { + if len(n.Values) <= idx { + return xerrors.Errorf( + "invalid index for sequence: sequence length is %d, but specified %d index", + len(n.Values), idx, + ) + } + column := n.Values[idx].GetToken().Position.Column - value.GetToken().Position.Column + value.AddColumn(column) + n.Values[idx] = value + return nil +} + +// Merge merge sequence value. +func (n *SequenceNode) Merge(target *SequenceNode) { + column := n.Start.Position.Column - target.Start.Position.Column + target.AddColumn(column) + n.Values = append(n.Values, target.Values...) +} + +// SetIsFlowStyle set value to IsFlowStyle field recursively. +func (n *SequenceNode) SetIsFlowStyle(isFlow bool) { + n.IsFlowStyle = isFlow + for _, value := range n.Values { + switch value := value.(type) { + case *MappingNode: + value.SetIsFlowStyle(isFlow) + case *MappingValueNode: + value.SetIsFlowStyle(isFlow) + case *SequenceNode: + value.SetIsFlowStyle(isFlow) + } + } +} + +// Read implements (io.Reader).Read +func (n *SequenceNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns SequenceType +func (n *SequenceNode) Type() NodeType { return SequenceType } + +// GetToken returns token instance +func (n *SequenceNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *SequenceNode) AddColumn(col int) { + n.Start.AddColumn(col) + n.End.AddColumn(col) + for _, value := range n.Values { + value.AddColumn(col) + } +} + +func (n *SequenceNode) flowStyleString() string { + values := []string{} + for _, value := range n.Values { + values = append(values, value.String()) + } + return fmt.Sprintf("[%s]", strings.Join(values, ", ")) +} + +func (n *SequenceNode) blockStyleString() string { + space := strings.Repeat(" ", n.Start.Position.Column-1) + values := []string{} + if n.Comment != nil { + values = append(values, n.Comment.StringWithSpace(n.Start.Position.Column-1)) + } + + for idx, value := range n.Values { + valueStr := value.String() + splittedValues := strings.Split(valueStr, "\n") + trimmedFirstValue := strings.TrimLeft(splittedValues[0], " ") + diffLength := len(splittedValues[0]) - len(trimmedFirstValue) + if len(splittedValues) > 1 && value.Type() == StringType || value.Type() == LiteralType { + // If multi-line string, the space characters for indent have already been added, so delete them. + prefix := space + " " + for i := 1; i < len(splittedValues); i++ { + splittedValues[i] = strings.TrimPrefix(splittedValues[i], prefix) + } + } + newValues := []string{trimmedFirstValue} + for i := 1; i < len(splittedValues); i++ { + if len(splittedValues[i]) <= diffLength { + // this line is \n or white space only + newValues = append(newValues, "") + continue + } + trimmed := splittedValues[i][diffLength:] + newValues = append(newValues, fmt.Sprintf("%s %s", space, trimmed)) + } + newValue := strings.Join(newValues, "\n") + if len(n.ValueHeadComments) == len(n.Values) && n.ValueHeadComments[idx] != nil { + values = append(values, n.ValueHeadComments[idx].StringWithSpace(n.Start.Position.Column-1)) + } + values = append(values, fmt.Sprintf("%s- %s", space, newValue)) + } + if n.FootComment != nil { + values = append(values, n.FootComment.StringWithSpace(n.Start.Position.Column-1)) + } + return strings.Join(values, "\n") +} + +// String sequence to text +func (n *SequenceNode) String() string { + if n.IsFlowStyle || len(n.Values) == 0 { + return n.flowStyleString() + } + return n.blockStyleString() +} + +// ArrayRange implements ArrayNode protocol +func (n *SequenceNode) ArrayRange() *ArrayNodeIter { + return &ArrayNodeIter{ + idx: startRangeIndex, + values: n.Values, + } +} + +// MarshalYAML encodes to a YAML text +func (n *SequenceNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// AnchorNode type of anchor node +type AnchorNode struct { + *BaseNode + Start *token.Token + Name Node + Value Node +} + +func (n *AnchorNode) SetName(name string) error { + if n.Name == nil { + return ErrInvalidAnchorName + } + s, ok := n.Name.(*StringNode) + if !ok { + return ErrInvalidAnchorName + } + s.Value = name + return nil +} + +// Read implements (io.Reader).Read +func (n *AnchorNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns AnchorType +func (n *AnchorNode) Type() NodeType { return AnchorType } + +// GetToken returns token instance +func (n *AnchorNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *AnchorNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Name != nil { + n.Name.AddColumn(col) + } + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String anchor to text +func (n *AnchorNode) String() string { + value := n.Value.String() + if len(strings.Split(value, "\n")) > 1 { + return fmt.Sprintf("&%s\n%s", n.Name.String(), value) + } else if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle { + return fmt.Sprintf("&%s\n%s", n.Name.String(), value) + } else if m, ok := n.Value.(*MappingNode); ok && !m.IsFlowStyle { + return fmt.Sprintf("&%s\n%s", n.Name.String(), value) + } + return fmt.Sprintf("&%s %s", n.Name.String(), value) +} + +// MarshalYAML encodes to a YAML text +func (n *AnchorNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// AliasNode type of alias node +type AliasNode struct { + *BaseNode + Start *token.Token + Value Node +} + +func (n *AliasNode) SetName(name string) error { + if n.Value == nil { + return ErrInvalidAliasName + } + s, ok := n.Value.(*StringNode) + if !ok { + return ErrInvalidAliasName + } + s.Value = name + return nil +} + +// Read implements (io.Reader).Read +func (n *AliasNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns AliasType +func (n *AliasNode) Type() NodeType { return AliasType } + +// GetToken returns token instance +func (n *AliasNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *AliasNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String alias to text +func (n *AliasNode) String() string { + return fmt.Sprintf("*%s", n.Value.String()) +} + +// MarshalYAML encodes to a YAML text +func (n *AliasNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// DirectiveNode type of directive node +type DirectiveNode struct { + *BaseNode + Start *token.Token + Value Node +} + +// Read implements (io.Reader).Read +func (n *DirectiveNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns DirectiveType +func (n *DirectiveNode) Type() NodeType { return DirectiveType } + +// GetToken returns token instance +func (n *DirectiveNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *DirectiveNode) AddColumn(col int) { + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String directive to text +func (n *DirectiveNode) String() string { + return fmt.Sprintf("%s%s", n.Start.Value, n.Value.String()) +} + +// MarshalYAML encodes to a YAML text +func (n *DirectiveNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// TagNode type of tag node +type TagNode struct { + *BaseNode + Start *token.Token + Value Node +} + +// Read implements (io.Reader).Read +func (n *TagNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns TagType +func (n *TagNode) Type() NodeType { return TagType } + +// GetToken returns token instance +func (n *TagNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *TagNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String tag to text +func (n *TagNode) String() string { + return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String()) +} + +// MarshalYAML encodes to a YAML text +func (n *TagNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// CommentNode type of comment node +type CommentNode struct { + *BaseNode + Token *token.Token +} + +// Read implements (io.Reader).Read +func (n *CommentNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns TagType +func (n *CommentNode) Type() NodeType { return CommentType } + +// GetToken returns token instance +func (n *CommentNode) GetToken() *token.Token { return n.Token } + +// AddColumn add column number to child nodes recursively +func (n *CommentNode) AddColumn(col int) { + if n.Token == nil { + return + } + n.Token.AddColumn(col) +} + +// String comment to text +func (n *CommentNode) String() string { + return fmt.Sprintf("#%s", n.Token.Value) +} + +// MarshalYAML encodes to a YAML text +func (n *CommentNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// CommentGroupNode type of comment node +type CommentGroupNode struct { + *BaseNode + Comments []*CommentNode +} + +// Read implements (io.Reader).Read +func (n *CommentGroupNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns TagType +func (n *CommentGroupNode) Type() NodeType { return CommentType } + +// GetToken returns token instance +func (n *CommentGroupNode) GetToken() *token.Token { + if len(n.Comments) > 0 { + return n.Comments[0].Token + } + return nil +} + +// AddColumn add column number to child nodes recursively +func (n *CommentGroupNode) AddColumn(col int) { + for _, comment := range n.Comments { + comment.AddColumn(col) + } +} + +// String comment to text +func (n *CommentGroupNode) String() string { + values := []string{} + for _, comment := range n.Comments { + values = append(values, comment.String()) + } + return strings.Join(values, "\n") +} + +func (n *CommentGroupNode) StringWithSpace(col int) string { + values := []string{} + space := strings.Repeat(" ", col) + for _, comment := range n.Comments { + values = append(values, space+comment.String()) + } + return strings.Join(values, "\n") + +} + +// MarshalYAML encodes to a YAML text +func (n *CommentGroupNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// Visitor has Visit method that is invokded for each node encountered by Walk. +// If the result visitor w is not nil, Walk visits each of the children of node with the visitor w, +// followed by a call of w.Visit(nil). +type Visitor interface { + Visit(Node) Visitor +} + +// Walk traverses an AST in depth-first order: It starts by calling v.Visit(node); node must not be nil. +// If the visitor w returned by v.Visit(node) is not nil, +// Walk is invoked recursively with visitor w for each of the non-nil children of node, +// followed by a call of w.Visit(nil). +func Walk(v Visitor, node Node) { + if v = v.Visit(node); v == nil { + return + } + + switch n := node.(type) { + case *CommentNode: + case *NullNode: + walkComment(v, n.BaseNode) + case *IntegerNode: + walkComment(v, n.BaseNode) + case *FloatNode: + walkComment(v, n.BaseNode) + case *StringNode: + walkComment(v, n.BaseNode) + case *MergeKeyNode: + walkComment(v, n.BaseNode) + case *BoolNode: + walkComment(v, n.BaseNode) + case *InfinityNode: + walkComment(v, n.BaseNode) + case *NanNode: + walkComment(v, n.BaseNode) + case *LiteralNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + case *DirectiveNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + case *TagNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + case *DocumentNode: + walkComment(v, n.BaseNode) + Walk(v, n.Body) + case *MappingNode: + walkComment(v, n.BaseNode) + for _, value := range n.Values { + Walk(v, value) + } + case *MappingKeyNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + case *MappingValueNode: + walkComment(v, n.BaseNode) + Walk(v, n.Key) + Walk(v, n.Value) + case *SequenceNode: + walkComment(v, n.BaseNode) + for _, value := range n.Values { + Walk(v, value) + } + case *AnchorNode: + walkComment(v, n.BaseNode) + Walk(v, n.Name) + Walk(v, n.Value) + case *AliasNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + } +} + +func walkComment(v Visitor, base *BaseNode) { + if base == nil { + return + } + if base.Comment == nil { + return + } + Walk(v, base.Comment) +} + +type filterWalker struct { + typ NodeType + results []Node +} + +func (v *filterWalker) Visit(n Node) Visitor { + if v.typ == n.Type() { + v.results = append(v.results, n) + } + return v +} + +type parentFinder struct { + target Node +} + +func (f *parentFinder) walk(parent, node Node) Node { + if f.target == node { + return parent + } + switch n := node.(type) { + case *CommentNode: + return nil + case *NullNode: + return nil + case *IntegerNode: + return nil + case *FloatNode: + return nil + case *StringNode: + return nil + case *MergeKeyNode: + return nil + case *BoolNode: + return nil + case *InfinityNode: + return nil + case *NanNode: + return nil + case *LiteralNode: + return f.walk(node, n.Value) + case *DirectiveNode: + return f.walk(node, n.Value) + case *TagNode: + return f.walk(node, n.Value) + case *DocumentNode: + return f.walk(node, n.Body) + case *MappingNode: + for _, value := range n.Values { + if found := f.walk(node, value); found != nil { + return found + } + } + case *MappingKeyNode: + return f.walk(node, n.Value) + case *MappingValueNode: + if found := f.walk(node, n.Key); found != nil { + return found + } + return f.walk(node, n.Value) + case *SequenceNode: + for _, value := range n.Values { + if found := f.walk(node, value); found != nil { + return found + } + } + case *AnchorNode: + if found := f.walk(node, n.Name); found != nil { + return found + } + return f.walk(node, n.Value) + case *AliasNode: + return f.walk(node, n.Value) + } + return nil +} + +// Parent get parent node from child node. +func Parent(root, child Node) Node { + finder := &parentFinder{target: child} + return finder.walk(root, root) +} + +// Filter returns a list of nodes that match the given type. +func Filter(typ NodeType, node Node) []Node { + walker := &filterWalker{typ: typ} + Walk(walker, node) + return walker.results +} + +// FilterFile returns a list of nodes that match the given type. +func FilterFile(typ NodeType, file *File) []Node { + results := []Node{} + for _, doc := range file.Docs { + walker := &filterWalker{typ: typ} + Walk(walker, doc) + results = append(results, walker.results...) + } + return results +} + +type ErrInvalidMergeType struct { + dst Node + src Node +} + +func (e *ErrInvalidMergeType) Error() string { + return fmt.Sprintf("cannot merge %s into %s", e.src.Type(), e.dst.Type()) +} + +// Merge merge document, map, sequence node. +func Merge(dst Node, src Node) error { + if doc, ok := src.(*DocumentNode); ok { + src = doc.Body + } + err := &ErrInvalidMergeType{dst: dst, src: src} + switch dst.Type() { + case DocumentType: + node := dst.(*DocumentNode) + return Merge(node.Body, src) + case MappingType: + node := dst.(*MappingNode) + target, ok := src.(*MappingNode) + if !ok { + return err + } + node.Merge(target) + return nil + case SequenceType: + node := dst.(*SequenceNode) + target, ok := src.(*SequenceNode) + if !ok { + return err + } + node.Merge(target) + return nil + } + return err +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/ast/ast_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/ast/ast_test.go new file mode 100644 index 0000000..e7022b6 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/ast/ast_test.go @@ -0,0 +1,36 @@ +package ast + +import ( + "testing" + + "github.com/goccy/go-yaml/token" +) + +func TestEscapeSingleQuote(t *testing.T) { + expected := `'Victor''s victory'` + got := escapeSingleQuote("Victor's victory") + if got != expected { + t.Fatalf("expected:%s\ngot:%s", expected, got) + } +} + +func TestReadNode(t *testing.T) { + t.Run("utf-8", func(t *testing.T) { + value := "éɛทᛞ⠻チ▓🦄" + node := &StringNode{ + BaseNode: &BaseNode{}, + Token: &token.Token{}, + Value: value, + } + expectedSize := len(value) + gotBuffer := make([]byte, expectedSize) + expectedBuffer := []byte(value) + gotSize, _ := readNode(gotBuffer, node) + if gotSize != expectedSize { + t.Fatalf("expected size:%d\ngot:%d", expectedSize, gotSize) + } + if string(gotBuffer) != string(expectedBuffer) { + t.Fatalf("expected buffer:%s\ngot:%s", expectedBuffer, gotBuffer) + } + }) +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/cmd/ycat/ycat.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/cmd/ycat/ycat.go new file mode 100644 index 0000000..c70cb3b --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/cmd/ycat/ycat.go @@ -0,0 +1,83 @@ +package main + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + + "github.com/fatih/color" + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/lexer" + "github.com/goccy/go-yaml/printer" + "github.com/mattn/go-colorable" +) + +const escape = "\x1b" + +func format(attr color.Attribute) string { + return fmt.Sprintf("%s[%dm", escape, attr) +} + +func _main(args []string) error { + if len(args) < 2 { + return errors.New("ycat: usage: ycat file.yml") + } + filename := args[1] + bytes, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + tokens := lexer.Tokenize(string(bytes)) + var p printer.Printer + p.LineNumber = true + p.LineNumberFormat = func(num int) string { + fn := color.New(color.Bold, color.FgHiWhite).SprintFunc() + return fn(fmt.Sprintf("%2d | ", num)) + } + p.Bool = func() *printer.Property { + return &printer.Property{ + Prefix: format(color.FgHiMagenta), + Suffix: format(color.Reset), + } + } + p.Number = func() *printer.Property { + return &printer.Property{ + Prefix: format(color.FgHiMagenta), + Suffix: format(color.Reset), + } + } + p.MapKey = func() *printer.Property { + return &printer.Property{ + Prefix: format(color.FgHiCyan), + Suffix: format(color.Reset), + } + } + p.Anchor = func() *printer.Property { + return &printer.Property{ + Prefix: format(color.FgHiYellow), + Suffix: format(color.Reset), + } + } + p.Alias = func() *printer.Property { + return &printer.Property{ + Prefix: format(color.FgHiYellow), + Suffix: format(color.Reset), + } + } + p.String = func() *printer.Property { + return &printer.Property{ + Prefix: format(color.FgHiGreen), + Suffix: format(color.Reset), + } + } + writer := colorable.NewColorableStdout() + writer.Write([]byte(p.PrintTokens(tokens) + "\n")) + return nil +} + +func main() { + if err := _main(os.Args); err != nil { + fmt.Printf("%v\n", yaml.FormatError(err, true, true)) + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/decode.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/decode.go new file mode 100644 index 0000000..d3dbabc --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/decode.go @@ -0,0 +1,1753 @@ +package yaml + +import ( + "bytes" + "context" + "encoding" + "encoding/base64" + "fmt" + "io" + "io/ioutil" + "math" + "os" + "path/filepath" + "reflect" + "sort" + "strconv" + "time" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/parser" + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +// Decoder reads and decodes YAML values from an input stream. +type Decoder struct { + reader io.Reader + referenceReaders []io.Reader + anchorNodeMap map[string]ast.Node + anchorValueMap map[string]reflect.Value + customUnmarshalerMap map[reflect.Type]func(interface{}, []byte) error + toCommentMap CommentMap + opts []DecodeOption + referenceFiles []string + referenceDirs []string + isRecursiveDir bool + isResolvedReference bool + validator StructValidator + disallowUnknownField bool + disallowDuplicateKey bool + useOrderedMap bool + useJSONUnmarshaler bool + parsedFile *ast.File + streamIndex int +} + +// NewDecoder returns a new decoder that reads from r. +func NewDecoder(r io.Reader, opts ...DecodeOption) *Decoder { + return &Decoder{ + reader: r, + anchorNodeMap: map[string]ast.Node{}, + anchorValueMap: map[string]reflect.Value{}, + customUnmarshalerMap: map[reflect.Type]func(interface{}, []byte) error{}, + opts: opts, + referenceReaders: []io.Reader{}, + referenceFiles: []string{}, + referenceDirs: []string{}, + isRecursiveDir: false, + isResolvedReference: false, + disallowUnknownField: false, + disallowDuplicateKey: false, + useOrderedMap: false, + } +} + +func (d *Decoder) castToFloat(v interface{}) interface{} { + switch vv := v.(type) { + case int: + return float64(vv) + case int8: + return float64(vv) + case int16: + return float64(vv) + case int32: + return float64(vv) + case int64: + return float64(vv) + case uint: + return float64(vv) + case uint8: + return float64(vv) + case uint16: + return float64(vv) + case uint32: + return float64(vv) + case uint64: + return float64(vv) + case float32: + return float64(vv) + case float64: + return vv + case string: + // if error occurred, return zero value + f, _ := strconv.ParseFloat(vv, 64) + return f + } + return 0 +} + +func (d *Decoder) mergeValueNode(value ast.Node) ast.Node { + if value.Type() == ast.AliasType { + aliasNode := value.(*ast.AliasNode) + aliasName := aliasNode.Value.GetToken().Value + return d.anchorNodeMap[aliasName] + } + return value +} + +func (d *Decoder) mapKeyNodeToString(node ast.MapKeyNode) string { + key := d.nodeToValue(node) + if key == nil { + return "null" + } + if k, ok := key.(string); ok { + return k + } + return fmt.Sprint(key) +} + +func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) { + d.setPathToCommentMap(node) + switch n := node.(type) { + case *ast.MappingValueNode: + if n.Key.Type() == ast.MergeKeyType { + d.setToMapValue(d.mergeValueNode(n.Value), m) + } else { + key := d.mapKeyNodeToString(n.Key) + m[key] = d.nodeToValue(n.Value) + } + case *ast.MappingNode: + for _, value := range n.Values { + d.setToMapValue(value, m) + } + case *ast.AnchorNode: + anchorName := n.Name.GetToken().Value + d.anchorNodeMap[anchorName] = n.Value + } +} + +func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) { + switch n := node.(type) { + case *ast.MappingValueNode: + if n.Key.Type() == ast.MergeKeyType { + d.setToOrderedMapValue(d.mergeValueNode(n.Value), m) + } else { + key := d.mapKeyNodeToString(n.Key) + *m = append(*m, MapItem{Key: key, Value: d.nodeToValue(n.Value)}) + } + case *ast.MappingNode: + for _, value := range n.Values { + d.setToOrderedMapValue(value, m) + } + } +} + +func (d *Decoder) setPathToCommentMap(node ast.Node) { + if d.toCommentMap == nil { + return + } + d.addHeadOrLineCommentToMap(node) + d.addFootCommentToMap(node) +} + +func (d *Decoder) addHeadOrLineCommentToMap(node ast.Node) { + sequence, ok := node.(*ast.SequenceNode) + if ok { + d.addSequenceNodeCommentToMap(sequence) + return + } + commentGroup := node.GetComment() + if commentGroup == nil { + return + } + texts := []string{} + targetLine := node.GetToken().Position.Line + minCommentLine := math.MaxInt + for _, comment := range commentGroup.Comments { + if minCommentLine > comment.Token.Position.Line { + minCommentLine = comment.Token.Position.Line + } + texts = append(texts, comment.Token.Value) + } + if len(texts) == 0 { + return + } + commentPath := node.GetPath() + if minCommentLine < targetLine { + d.addCommentToMap(commentPath, HeadComment(texts...)) + } else { + d.addCommentToMap(commentPath, LineComment(texts[0])) + } +} + +func (d *Decoder) addSequenceNodeCommentToMap(node *ast.SequenceNode) { + if len(node.ValueHeadComments) != 0 { + for idx, headComment := range node.ValueHeadComments { + if headComment == nil { + continue + } + texts := make([]string, 0, len(headComment.Comments)) + for _, comment := range headComment.Comments { + texts = append(texts, comment.Token.Value) + } + if len(texts) != 0 { + d.addCommentToMap(node.Values[idx].GetPath(), HeadComment(texts...)) + } + } + } + firstElemHeadComment := node.GetComment() + if firstElemHeadComment != nil { + texts := make([]string, 0, len(firstElemHeadComment.Comments)) + for _, comment := range firstElemHeadComment.Comments { + texts = append(texts, comment.Token.Value) + } + if len(texts) != 0 { + d.addCommentToMap(node.Values[0].GetPath(), HeadComment(texts...)) + } + } +} + +func (d *Decoder) addFootCommentToMap(node ast.Node) { + var ( + footComment *ast.CommentGroupNode + footCommentPath string = node.GetPath() + ) + switch n := node.(type) { + case *ast.SequenceNode: + if len(n.Values) != 0 { + footCommentPath = n.Values[len(n.Values)-1].GetPath() + } + footComment = n.FootComment + case *ast.MappingNode: + footComment = n.FootComment + case *ast.MappingValueNode: + footComment = n.FootComment + } + if footComment == nil { + return + } + var texts []string + for _, comment := range footComment.Comments { + texts = append(texts, comment.Token.Value) + } + if len(texts) != 0 { + d.addCommentToMap(footCommentPath, FootComment(texts...)) + } +} + +func (d *Decoder) addCommentToMap(path string, comment *Comment) { + for _, c := range d.toCommentMap[path] { + if c.Position == comment.Position { + // already added same comment + return + } + } + d.toCommentMap[path] = append(d.toCommentMap[path], comment) + sort.Slice(d.toCommentMap[path], func(i, j int) bool { + return d.toCommentMap[path][i].Position < d.toCommentMap[path][j].Position + }) +} + +func (d *Decoder) nodeToValue(node ast.Node) interface{} { + d.setPathToCommentMap(node) + switch n := node.(type) { + case *ast.NullNode: + return nil + case *ast.StringNode: + return n.GetValue() + case *ast.IntegerNode: + return n.GetValue() + case *ast.FloatNode: + return n.GetValue() + case *ast.BoolNode: + return n.GetValue() + case *ast.InfinityNode: + return n.GetValue() + case *ast.NanNode: + return n.GetValue() + case *ast.TagNode: + switch token.ReservedTagKeyword(n.Start.Value) { + case token.TimestampTag: + t, _ := d.castToTime(n.Value) + return t + case token.IntegerTag: + i, _ := strconv.Atoi(fmt.Sprint(d.nodeToValue(n.Value))) + return i + case token.FloatTag: + return d.castToFloat(d.nodeToValue(n.Value)) + case token.NullTag: + return nil + case token.BinaryTag: + b, _ := base64.StdEncoding.DecodeString(d.nodeToValue(n.Value).(string)) + return b + case token.StringTag: + return d.nodeToValue(n.Value) + case token.MappingTag: + return d.nodeToValue(n.Value) + } + case *ast.AnchorNode: + anchorName := n.Name.GetToken().Value + anchorValue := d.nodeToValue(n.Value) + d.anchorNodeMap[anchorName] = n.Value + return anchorValue + case *ast.AliasNode: + aliasName := n.Value.GetToken().Value + node := d.anchorNodeMap[aliasName] + return d.nodeToValue(node) + case *ast.LiteralNode: + return n.Value.GetValue() + case *ast.MappingKeyNode: + return d.nodeToValue(n.Value) + case *ast.MappingValueNode: + if n.Key.Type() == ast.MergeKeyType { + value := d.mergeValueNode(n.Value) + if d.useOrderedMap { + m := MapSlice{} + d.setToOrderedMapValue(value, &m) + return m + } + m := map[string]interface{}{} + d.setToMapValue(value, m) + return m + } + key := d.mapKeyNodeToString(n.Key) + if d.useOrderedMap { + return MapSlice{{Key: key, Value: d.nodeToValue(n.Value)}} + } + return map[string]interface{}{ + key: d.nodeToValue(n.Value), + } + case *ast.MappingNode: + if d.useOrderedMap { + m := make(MapSlice, 0, len(n.Values)) + for _, value := range n.Values { + d.setToOrderedMapValue(value, &m) + } + return m + } + m := make(map[string]interface{}, len(n.Values)) + for _, value := range n.Values { + d.setToMapValue(value, m) + } + return m + case *ast.SequenceNode: + v := make([]interface{}, 0, len(n.Values)) + for _, value := range n.Values { + v = append(v, d.nodeToValue(value)) + } + return v + } + return nil +} + +func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) { + switch n := node.(type) { + case *ast.MappingNode: + for idx, v := range n.Values { + value, err := d.resolveAlias(v) + if err != nil { + return nil, err + } + n.Values[idx] = value.(*ast.MappingValueNode) + } + case *ast.TagNode: + value, err := d.resolveAlias(n.Value) + if err != nil { + return nil, err + } + n.Value = value + case *ast.MappingKeyNode: + value, err := d.resolveAlias(n.Value) + if err != nil { + return nil, err + } + n.Value = value + case *ast.MappingValueNode: + if n.Key.Type() == ast.MergeKeyType && n.Value.Type() == ast.AliasType { + value, err := d.resolveAlias(n.Value) + if err != nil { + return nil, err + } + keyColumn := n.Key.GetToken().Position.Column + requiredColumn := keyColumn + 2 + value.AddColumn(requiredColumn) + n.Value = value + } else { + key, err := d.resolveAlias(n.Key) + if err != nil { + return nil, err + } + n.Key = key.(ast.MapKeyNode) + value, err := d.resolveAlias(n.Value) + if err != nil { + return nil, err + } + n.Value = value + } + case *ast.SequenceNode: + for idx, v := range n.Values { + value, err := d.resolveAlias(v) + if err != nil { + return nil, err + } + n.Values[idx] = value + } + case *ast.AliasNode: + aliasName := n.Value.GetToken().Value + node := d.anchorNodeMap[aliasName] + if node == nil { + return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) + } + return d.resolveAlias(node) + } + return node, nil +} + +func (d *Decoder) getMapNode(node ast.Node) (ast.MapNode, error) { + if _, ok := node.(*ast.NullNode); ok { + return nil, nil + } + if anchor, ok := node.(*ast.AnchorNode); ok { + mapNode, ok := anchor.Value.(ast.MapNode) + if ok { + return mapNode, nil + } + return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.MappingType, node.GetToken()) + } + if alias, ok := node.(*ast.AliasNode); ok { + aliasName := alias.Value.GetToken().Value + node := d.anchorNodeMap[aliasName] + if node == nil { + return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) + } + mapNode, ok := node.(ast.MapNode) + if ok { + return mapNode, nil + } + return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) + } + mapNode, ok := node.(ast.MapNode) + if !ok { + return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) + } + return mapNode, nil +} + +func (d *Decoder) getArrayNode(node ast.Node) (ast.ArrayNode, error) { + if _, ok := node.(*ast.NullNode); ok { + return nil, nil + } + if anchor, ok := node.(*ast.AnchorNode); ok { + arrayNode, ok := anchor.Value.(ast.ArrayNode) + if ok { + return arrayNode, nil + } + + return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.SequenceType, node.GetToken()) + } + if alias, ok := node.(*ast.AliasNode); ok { + aliasName := alias.Value.GetToken().Value + node := d.anchorNodeMap[aliasName] + if node == nil { + return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) + } + arrayNode, ok := node.(ast.ArrayNode) + if ok { + return arrayNode, nil + } + return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) + } + arrayNode, ok := node.(ast.ArrayNode) + if !ok { + return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) + } + return arrayNode, nil +} + +func (d *Decoder) fileToNode(f *ast.File) ast.Node { + for _, doc := range f.Docs { + if v := d.nodeToValue(doc.Body); v != nil { + return doc.Body + } + } + return nil +} + +func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) (reflect.Value, error) { + if typ.Kind() != reflect.String { + if !v.Type().ConvertibleTo(typ) { + return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken()) + } + return v.Convert(typ), nil + } + // cast value to string + switch v.Type().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return reflect.ValueOf(fmt.Sprint(v.Int())), nil + case reflect.Float32, reflect.Float64: + return reflect.ValueOf(fmt.Sprint(v.Float())), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return reflect.ValueOf(fmt.Sprint(v.Uint())), nil + case reflect.Bool: + return reflect.ValueOf(fmt.Sprint(v.Bool())), nil + } + if !v.Type().ConvertibleTo(typ) { + return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken()) + } + return v.Convert(typ), nil +} + +type overflowError struct { + dstType reflect.Type + srcNum string +} + +func (e *overflowError) Error() string { + return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.srcNum, e.dstType) +} + +func errOverflow(dstType reflect.Type, num string) *overflowError { + return &overflowError{dstType: dstType, srcNum: num} +} + +func errTypeMismatch(dstType, srcType reflect.Type, token *token.Token) *errors.TypeError { + return &errors.TypeError{DstType: dstType, SrcType: srcType, Token: token} +} + +type unknownFieldError struct { + err error +} + +func (e *unknownFieldError) Error() string { + return e.err.Error() +} + +func errUnknownField(msg string, tk *token.Token) *unknownFieldError { + return &unknownFieldError{err: errors.ErrSyntax(msg, tk)} +} + +func errUnexpectedNodeType(actual, expected ast.NodeType, tk *token.Token) error { + return errors.ErrSyntax(fmt.Sprintf("%s was used where %s is expected", actual.YAMLName(), expected.YAMLName()), tk) +} + +type duplicateKeyError struct { + err error +} + +func (e *duplicateKeyError) Error() string { + return e.err.Error() +} + +func errDuplicateKey(msg string, tk *token.Token) *duplicateKeyError { + return &duplicateKeyError{err: errors.ErrSyntax(msg, tk)} +} + +func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[string]ast.Node) error { + if structType.Kind() == reflect.Ptr { + structType = structType.Elem() + } + structFieldMap, err := structFieldMap(structType) + if err != nil { + return errors.Wrapf(err, "failed to create struct field map") + } + + for j := 0; j < structType.NumField(); j++ { + field := structType.Field(j) + if isIgnoredStructField(field) { + continue + } + + structField, exists := structFieldMap[field.Name] + if !exists { + continue + } + + if structField.IsInline { + d.deleteStructKeys(field.Type, unknownFields) + } else { + delete(unknownFields, structField.RenderName) + } + } + return nil +} + +func (d *Decoder) lastNode(node ast.Node) ast.Node { + switch n := node.(type) { + case *ast.MappingNode: + if len(n.Values) > 0 { + return d.lastNode(n.Values[len(n.Values)-1]) + } + case *ast.MappingValueNode: + return d.lastNode(n.Value) + case *ast.SequenceNode: + if len(n.Values) > 0 { + return d.lastNode(n.Values[len(n.Values)-1]) + } + } + return node +} + +func (d *Decoder) unmarshalableDocument(node ast.Node) ([]byte, error) { + var err error + node, err = d.resolveAlias(node) + if err != nil { + return nil, err + } + doc := node.String() + last := d.lastNode(node) + if last != nil && last.Type() == ast.LiteralType { + doc += "\n" + } + return []byte(doc), nil +} + +func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool, error) { + var err error + node, err = d.resolveAlias(node) + if err != nil { + return nil, false, err + } + if node.Type() == ast.AnchorType { + node = node.(*ast.AnchorNode).Value + } + switch n := node.(type) { + case *ast.StringNode: + return []byte(n.Value), true, nil + case *ast.LiteralNode: + return []byte(n.Value.GetToken().Value), true, nil + default: + scalar, ok := n.(ast.ScalarNode) + if ok { + return []byte(fmt.Sprint(scalar.GetValue())), true, nil + } + } + return nil, false, nil +} + +type jsonUnmarshaler interface { + UnmarshalJSON([]byte) error +} + +func (d *Decoder) existsTypeInCustomUnmarshalerMap(t reflect.Type) bool { + if _, exists := d.customUnmarshalerMap[t]; exists { + return true + } + + globalCustomUnmarshalerMu.Lock() + defer globalCustomUnmarshalerMu.Unlock() + if _, exists := globalCustomUnmarshalerMap[t]; exists { + return true + } + return false +} + +func (d *Decoder) unmarshalerFromCustomUnmarshalerMap(t reflect.Type) (func(interface{}, []byte) error, bool) { + if unmarshaler, exists := d.customUnmarshalerMap[t]; exists { + return unmarshaler, exists + } + + globalCustomUnmarshalerMu.Lock() + defer globalCustomUnmarshalerMu.Unlock() + if unmarshaler, exists := globalCustomUnmarshalerMap[t]; exists { + return unmarshaler, exists + } + return nil, false +} + +func (d *Decoder) canDecodeByUnmarshaler(dst reflect.Value) bool { + ptrValue := dst.Addr() + if d.existsTypeInCustomUnmarshalerMap(ptrValue.Type()) { + return true + } + iface := ptrValue.Interface() + switch iface.(type) { + case BytesUnmarshalerContext: + return true + case BytesUnmarshaler: + return true + case InterfaceUnmarshalerContext: + return true + case InterfaceUnmarshaler: + return true + case *time.Time: + return true + case *time.Duration: + return true + case encoding.TextUnmarshaler: + return true + case jsonUnmarshaler: + return d.useJSONUnmarshaler + } + return false +} + +func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, src ast.Node) error { + ptrValue := dst.Addr() + if unmarshaler, exists := d.unmarshalerFromCustomUnmarshalerMap(ptrValue.Type()); exists { + b, err := d.unmarshalableDocument(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + if err := unmarshaler(ptrValue.Interface(), b); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + iface := ptrValue.Interface() + + if unmarshaler, ok := iface.(BytesUnmarshalerContext); ok { + b, err := d.unmarshalableDocument(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + if err := unmarshaler.UnmarshalYAML(ctx, b); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + + if unmarshaler, ok := iface.(BytesUnmarshaler); ok { + b, err := d.unmarshalableDocument(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + if err := unmarshaler.UnmarshalYAML(b); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + + if unmarshaler, ok := iface.(InterfaceUnmarshalerContext); ok { + if err := unmarshaler.UnmarshalYAML(ctx, func(v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Ptr { + return errors.ErrDecodeRequiredPointerType + } + if err := d.decodeValue(ctx, rv.Elem(), src); err != nil { + return errors.Wrapf(err, "failed to decode value") + } + return nil + }); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + + if unmarshaler, ok := iface.(InterfaceUnmarshaler); ok { + if err := unmarshaler.UnmarshalYAML(func(v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Ptr { + return errors.ErrDecodeRequiredPointerType + } + if err := d.decodeValue(ctx, rv.Elem(), src); err != nil { + return errors.Wrapf(err, "failed to decode value") + } + return nil + }); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + + if _, ok := iface.(*time.Time); ok { + return d.decodeTime(ctx, dst, src) + } + + if _, ok := iface.(*time.Duration); ok { + return d.decodeDuration(ctx, dst, src) + } + + if unmarshaler, isText := iface.(encoding.TextUnmarshaler); isText { + b, ok, err := d.unmarshalableText(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalText") + } + if ok { + if err := unmarshaler.UnmarshalText(b); err != nil { + return errors.Wrapf(err, "failed to UnmarshalText") + } + return nil + } + } + + if d.useJSONUnmarshaler { + if unmarshaler, ok := iface.(jsonUnmarshaler); ok { + b, err := d.unmarshalableDocument(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalJSON") + } + jsonBytes, err := YAMLToJSON(b) + if err != nil { + return errors.Wrapf(err, "failed to convert yaml to json") + } + jsonBytes = bytes.TrimRight(jsonBytes, "\n") + if err := unmarshaler.UnmarshalJSON(jsonBytes); err != nil { + return errors.Wrapf(err, "failed to UnmarshalJSON") + } + return nil + } + } + + return xerrors.Errorf("does not implemented Unmarshaler") +} + +var ( + astNodeType = reflect.TypeOf((*ast.Node)(nil)).Elem() +) + +func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.Node) error { + if src.Type() == ast.AnchorType { + anchorName := src.(*ast.AnchorNode).Name.GetToken().Value + if _, exists := d.anchorValueMap[anchorName]; !exists { + d.anchorValueMap[anchorName] = dst + } + } + if d.canDecodeByUnmarshaler(dst) { + if err := d.decodeByUnmarshaler(ctx, dst, src); err != nil { + return errors.Wrapf(err, "failed to decode by unmarshaler") + } + return nil + } + valueType := dst.Type() + switch valueType.Kind() { + case reflect.Ptr: + if dst.IsNil() { + return nil + } + if src.Type() == ast.NullType { + // set nil value to pointer + dst.Set(reflect.Zero(valueType)) + return nil + } + v := d.createDecodableValue(dst.Type()) + if err := d.decodeValue(ctx, v, src); err != nil { + return errors.Wrapf(err, "failed to decode ptr value") + } + dst.Set(d.castToAssignableValue(v, dst.Type())) + case reflect.Interface: + if dst.Type() == astNodeType { + dst.Set(reflect.ValueOf(src)) + return nil + } + v := reflect.ValueOf(d.nodeToValue(src)) + if v.IsValid() { + dst.Set(v) + } + case reflect.Map: + return d.decodeMap(ctx, dst, src) + case reflect.Array: + return d.decodeArray(ctx, dst, src) + case reflect.Slice: + if mapSlice, ok := dst.Addr().Interface().(*MapSlice); ok { + return d.decodeMapSlice(ctx, mapSlice, src) + } + return d.decodeSlice(ctx, dst, src) + case reflect.Struct: + if mapItem, ok := dst.Addr().Interface().(*MapItem); ok { + return d.decodeMapItem(ctx, mapItem, src) + } + return d.decodeStruct(ctx, dst, src) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v := d.nodeToValue(src) + switch vv := v.(type) { + case int64: + if !dst.OverflowInt(vv) { + dst.SetInt(vv) + return nil + } + case uint64: + if vv <= math.MaxInt64 && !dst.OverflowInt(int64(vv)) { + dst.SetInt(int64(vv)) + return nil + } + case float64: + if vv <= math.MaxInt64 && !dst.OverflowInt(int64(vv)) { + dst.SetInt(int64(vv)) + return nil + } + default: + return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) + } + return errOverflow(valueType, fmt.Sprint(v)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v := d.nodeToValue(src) + switch vv := v.(type) { + case int64: + if 0 <= vv && !dst.OverflowUint(uint64(vv)) { + dst.SetUint(uint64(vv)) + return nil + } + case uint64: + if !dst.OverflowUint(vv) { + dst.SetUint(vv) + return nil + } + case float64: + if 0 <= vv && vv <= math.MaxUint64 && !dst.OverflowUint(uint64(vv)) { + dst.SetUint(uint64(vv)) + return nil + } + default: + return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) + } + return errOverflow(valueType, fmt.Sprint(v)) + } + v := reflect.ValueOf(d.nodeToValue(src)) + if v.IsValid() { + convertedValue, err := d.convertValue(v, dst.Type(), src) + if err != nil { + return errors.Wrapf(err, "failed to convert value") + } + dst.Set(convertedValue) + } + return nil +} + +func (d *Decoder) createDecodableValue(typ reflect.Type) reflect.Value { + for { + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + continue + } + break + } + return reflect.New(typ).Elem() +} + +func (d *Decoder) castToAssignableValue(value reflect.Value, target reflect.Type) reflect.Value { + if target.Kind() != reflect.Ptr { + return value + } + maxTryCount := 5 + tryCount := 0 + for { + if tryCount > maxTryCount { + return value + } + if value.Type().AssignableTo(target) { + break + } + value = value.Addr() + tryCount++ + } + return value +} + +func (d *Decoder) createDecodedNewValue( + ctx context.Context, typ reflect.Type, defaultVal reflect.Value, node ast.Node, +) (reflect.Value, error) { + if node.Type() == ast.AliasType { + aliasName := node.(*ast.AliasNode).Value.GetToken().Value + newValue := d.anchorValueMap[aliasName] + if newValue.IsValid() { + return newValue, nil + } + } + if node.Type() == ast.NullType { + return reflect.Zero(typ), nil + } + newValue := d.createDecodableValue(typ) + for defaultVal.Kind() == reflect.Ptr { + defaultVal = defaultVal.Elem() + } + if defaultVal.IsValid() && defaultVal.Type().AssignableTo(newValue.Type()) { + newValue.Set(defaultVal) + } + if err := d.decodeValue(ctx, newValue, node); err != nil { + return newValue, errors.Wrapf(err, "failed to decode value") + } + return newValue, nil +} + +func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValueNode func(*ast.MapNodeIter) ast.Node) (map[string]ast.Node, error) { + mapNode, err := d.getMapNode(node) + if err != nil { + return nil, errors.Wrapf(err, "failed to get map node") + } + keyMap := map[string]struct{}{} + keyToNodeMap := map[string]ast.Node{} + if mapNode == nil { + return keyToNodeMap, nil + } + mapIter := mapNode.MapRange() + for mapIter.Next() { + keyNode := mapIter.Key() + if keyNode.Type() == ast.MergeKeyType { + if ignoreMergeKey { + continue + } + mergeMap, err := d.keyToNodeMap(mapIter.Value(), ignoreMergeKey, getKeyOrValueNode) + if err != nil { + return nil, errors.Wrapf(err, "failed to get keyToNodeMap by MergeKey node") + } + for k, v := range mergeMap { + if err := d.validateDuplicateKey(keyMap, k, v); err != nil { + return nil, errors.Wrapf(err, "invalid struct key") + } + keyToNodeMap[k] = v + } + } else { + key, ok := d.nodeToValue(keyNode).(string) + if !ok { + return nil, errors.Wrapf(err, "failed to decode map key") + } + if err := d.validateDuplicateKey(keyMap, key, keyNode); err != nil { + return nil, errors.Wrapf(err, "invalid struct key") + } + keyToNodeMap[key] = getKeyOrValueNode(mapIter) + } + } + return keyToNodeMap, nil +} + +func (d *Decoder) keyToKeyNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { + m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Key() }) + if err != nil { + return nil, errors.Wrapf(err, "failed to get keyToNodeMap") + } + return m, nil +} + +func (d *Decoder) keyToValueNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { + m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Value() }) + if err != nil { + return nil, errors.Wrapf(err, "failed to get keyToNodeMap") + } + return m, nil +} + +func (d *Decoder) setDefaultValueIfConflicted(v reflect.Value, fieldMap StructFieldMap) error { + typ := v.Type() + if typ.Kind() != reflect.Struct { + return nil + } + embeddedStructFieldMap, err := structFieldMap(typ) + if err != nil { + return errors.Wrapf(err, "failed to get struct field map by embedded type") + } + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + if isIgnoredStructField(field) { + continue + } + structField := embeddedStructFieldMap[field.Name] + if !fieldMap.isIncludedRenderName(structField.RenderName) { + continue + } + // if declared same key name, set default value + fieldValue := v.Field(i) + if fieldValue.CanSet() { + fieldValue.Set(reflect.Zero(fieldValue.Type())) + } + } + return nil +} + +// This is a subset of the formats allowed by the regular expression +// defined at http://yaml.org/type/timestamp.html. +var allowedTimestampFormats = []string{ + "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. + "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". + "2006-1-2 15:4:5.999999999", // space separated with no time zone + "2006-1-2", // date only +} + +func (d *Decoder) castToTime(src ast.Node) (time.Time, error) { + if src == nil { + return time.Time{}, nil + } + v := d.nodeToValue(src) + if t, ok := v.(time.Time); ok { + return t, nil + } + s, ok := v.(string) + if !ok { + return time.Time{}, errTypeMismatch(reflect.TypeOf(time.Time{}), reflect.TypeOf(v), src.GetToken()) + } + for _, format := range allowedTimestampFormats { + t, err := time.Parse(format, s) + if err != nil { + // invalid format + continue + } + return t, nil + } + return time.Time{}, nil +} + +func (d *Decoder) decodeTime(ctx context.Context, dst reflect.Value, src ast.Node) error { + t, err := d.castToTime(src) + if err != nil { + return errors.Wrapf(err, "failed to convert to time") + } + dst.Set(reflect.ValueOf(t)) + return nil +} + +func (d *Decoder) castToDuration(src ast.Node) (time.Duration, error) { + if src == nil { + return 0, nil + } + v := d.nodeToValue(src) + if t, ok := v.(time.Duration); ok { + return t, nil + } + s, ok := v.(string) + if !ok { + return 0, errTypeMismatch(reflect.TypeOf(time.Duration(0)), reflect.TypeOf(v), src.GetToken()) + } + t, err := time.ParseDuration(s) + if err != nil { + return 0, errors.Wrapf(err, "failed to parse duration") + } + return t, nil +} + +func (d *Decoder) decodeDuration(ctx context.Context, dst reflect.Value, src ast.Node) error { + t, err := d.castToDuration(src) + if err != nil { + return errors.Wrapf(err, "failed to convert to duration") + } + dst.Set(reflect.ValueOf(t)) + return nil +} + +// getMergeAliasName support single alias only +func (d *Decoder) getMergeAliasName(src ast.Node) string { + mapNode, err := d.getMapNode(src) + if err != nil { + return "" + } + if mapNode == nil { + return "" + } + mapIter := mapNode.MapRange() + for mapIter.Next() { + key := mapIter.Key() + value := mapIter.Value() + if key.Type() == ast.MergeKeyType && value.Type() == ast.AliasType { + return value.(*ast.AliasNode).Value.GetToken().Value + } + } + return "" +} + +func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.Node) error { + if src == nil { + return nil + } + structType := dst.Type() + srcValue := reflect.ValueOf(src) + srcType := srcValue.Type() + if srcType.Kind() == reflect.Ptr { + srcType = srcType.Elem() + srcValue = srcValue.Elem() + } + if structType == srcType { + // dst value implements ast.Node + dst.Set(srcValue) + return nil + } + structFieldMap, err := structFieldMap(structType) + if err != nil { + return errors.Wrapf(err, "failed to create struct field map") + } + ignoreMergeKey := structFieldMap.hasMergeProperty() + keyToNodeMap, err := d.keyToValueNodeMap(src, ignoreMergeKey) + if err != nil { + return errors.Wrapf(err, "failed to get keyToValueNodeMap") + } + var unknownFields map[string]ast.Node + if d.disallowUnknownField { + unknownFields, err = d.keyToKeyNodeMap(src, ignoreMergeKey) + if err != nil { + return errors.Wrapf(err, "failed to get keyToKeyNodeMap") + } + } + + aliasName := d.getMergeAliasName(src) + var foundErr error + + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if isIgnoredStructField(field) { + continue + } + structField := structFieldMap[field.Name] + if structField.IsInline { + fieldValue := dst.FieldByName(field.Name) + if structField.IsAutoAlias { + if aliasName != "" { + newFieldValue := d.anchorValueMap[aliasName] + if newFieldValue.IsValid() { + fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) + } + } + continue + } + if !fieldValue.CanSet() { + return xerrors.Errorf("cannot set embedded type as unexported field %s.%s", field.PkgPath, field.Name) + } + if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType { + // set nil value to pointer + fieldValue.Set(reflect.Zero(fieldValue.Type())) + continue + } + mapNode := ast.Mapping(nil, false) + for k, v := range keyToNodeMap { + key := &ast.StringNode{BaseNode: &ast.BaseNode{}, Value: k} + mapNode.Values = append(mapNode.Values, ast.MappingValue(nil, key, v)) + } + newFieldValue, err := d.createDecodedNewValue(ctx, fieldValue.Type(), fieldValue, mapNode) + if d.disallowUnknownField { + if err := d.deleteStructKeys(fieldValue.Type(), unknownFields); err != nil { + return errors.Wrapf(err, "cannot delete struct keys") + } + } + + if err != nil { + if foundErr != nil { + continue + } + var te *errors.TypeError + if xerrors.As(err, &te) { + if te.StructFieldName != nil { + fieldName := fmt.Sprintf("%s.%s", structType.Name(), *te.StructFieldName) + te.StructFieldName = &fieldName + } else { + fieldName := fmt.Sprintf("%s.%s", structType.Name(), field.Name) + te.StructFieldName = &fieldName + } + foundErr = te + continue + } else { + foundErr = err + } + continue + } + d.setDefaultValueIfConflicted(newFieldValue, structFieldMap) + fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) + continue + } + v, exists := keyToNodeMap[structField.RenderName] + if !exists { + continue + } + delete(unknownFields, structField.RenderName) + fieldValue := dst.FieldByName(field.Name) + if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType { + // set nil value to pointer + fieldValue.Set(reflect.Zero(fieldValue.Type())) + continue + } + newFieldValue, err := d.createDecodedNewValue(ctx, fieldValue.Type(), fieldValue, v) + if err != nil { + if foundErr != nil { + continue + } + var te *errors.TypeError + if xerrors.As(err, &te) { + fieldName := fmt.Sprintf("%s.%s", structType.Name(), field.Name) + te.StructFieldName = &fieldName + foundErr = te + } else { + foundErr = err + } + continue + } + fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) + } + if foundErr != nil { + return errors.Wrapf(foundErr, "failed to decode value") + } + + // Ignore unknown fields when parsing an inline struct (recognized by a nil token). + // Unknown fields are expected (they could be fields from the parent struct). + if len(unknownFields) != 0 && d.disallowUnknownField && src.GetToken() != nil { + for key, node := range unknownFields { + return errUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken()) + } + } + + if d.validator != nil { + if err := d.validator.Struct(dst.Interface()); err != nil { + ev := reflect.ValueOf(err) + if ev.Type().Kind() == reflect.Slice { + for i := 0; i < ev.Len(); i++ { + fieldErr, ok := ev.Index(i).Interface().(FieldError) + if !ok { + continue + } + fieldName := fieldErr.StructField() + structField, exists := structFieldMap[fieldName] + if !exists { + continue + } + node, exists := keyToNodeMap[structField.RenderName] + if exists { + // TODO: to make FieldError message cutomizable + return errors.ErrSyntax(fmt.Sprintf("%s", err), node.GetToken()) + } else if t := src.GetToken(); t != nil && t.Prev != nil && t.Prev.Prev != nil { + // A missing required field will not be in the keyToNodeMap + // the error needs to be associated with the parent of the source node + return errors.ErrSyntax(fmt.Sprintf("%s", err), t.Prev.Prev) + } + } + } + return err + } + } + return nil +} + +func (d *Decoder) decodeArray(ctx context.Context, dst reflect.Value, src ast.Node) error { + arrayNode, err := d.getArrayNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get array node") + } + if arrayNode == nil { + return nil + } + iter := arrayNode.ArrayRange() + arrayValue := reflect.New(dst.Type()).Elem() + arrayType := dst.Type() + elemType := arrayType.Elem() + idx := 0 + + var foundErr error + for iter.Next() { + v := iter.Value() + if elemType.Kind() == reflect.Ptr && v.Type() == ast.NullType { + // set nil value to pointer + arrayValue.Index(idx).Set(reflect.Zero(elemType)) + } else { + dstValue, err := d.createDecodedNewValue(ctx, elemType, reflect.Value{}, v) + if err != nil { + if foundErr == nil { + foundErr = err + } + continue + } else { + arrayValue.Index(idx).Set(d.castToAssignableValue(dstValue, elemType)) + } + } + idx++ + } + dst.Set(arrayValue) + if foundErr != nil { + return errors.Wrapf(foundErr, "failed to decode value") + } + return nil +} + +func (d *Decoder) decodeSlice(ctx context.Context, dst reflect.Value, src ast.Node) error { + arrayNode, err := d.getArrayNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get array node") + } + if arrayNode == nil { + return nil + } + iter := arrayNode.ArrayRange() + sliceType := dst.Type() + sliceValue := reflect.MakeSlice(sliceType, 0, iter.Len()) + elemType := sliceType.Elem() + + var foundErr error + for iter.Next() { + v := iter.Value() + if elemType.Kind() == reflect.Ptr && v.Type() == ast.NullType { + // set nil value to pointer + sliceValue = reflect.Append(sliceValue, reflect.Zero(elemType)) + continue + } + dstValue, err := d.createDecodedNewValue(ctx, elemType, reflect.Value{}, v) + if err != nil { + if foundErr == nil { + foundErr = err + } + continue + } + sliceValue = reflect.Append(sliceValue, d.castToAssignableValue(dstValue, elemType)) + } + dst.Set(sliceValue) + if foundErr != nil { + return errors.Wrapf(foundErr, "failed to decode value") + } + return nil +} + +func (d *Decoder) decodeMapItem(ctx context.Context, dst *MapItem, src ast.Node) error { + mapNode, err := d.getMapNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get map node") + } + if mapNode == nil { + return nil + } + mapIter := mapNode.MapRange() + if !mapIter.Next() { + return nil + } + key := mapIter.Key() + value := mapIter.Value() + if key.Type() == ast.MergeKeyType { + if err := d.decodeMapItem(ctx, dst, value); err != nil { + return errors.Wrapf(err, "failed to decode map with merge key") + } + return nil + } + *dst = MapItem{ + Key: d.nodeToValue(key), + Value: d.nodeToValue(value), + } + return nil +} + +func (d *Decoder) validateDuplicateKey(keyMap map[string]struct{}, key interface{}, keyNode ast.Node) error { + k, ok := key.(string) + if !ok { + return nil + } + if d.disallowDuplicateKey { + if _, exists := keyMap[k]; exists { + return errDuplicateKey(fmt.Sprintf(`duplicate key "%s"`, k), keyNode.GetToken()) + } + } + keyMap[k] = struct{}{} + return nil +} + +func (d *Decoder) decodeMapSlice(ctx context.Context, dst *MapSlice, src ast.Node) error { + mapNode, err := d.getMapNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get map node") + } + if mapNode == nil { + return nil + } + mapSlice := MapSlice{} + mapIter := mapNode.MapRange() + keyMap := map[string]struct{}{} + for mapIter.Next() { + key := mapIter.Key() + value := mapIter.Value() + if key.Type() == ast.MergeKeyType { + var m MapSlice + if err := d.decodeMapSlice(ctx, &m, value); err != nil { + return errors.Wrapf(err, "failed to decode map with merge key") + } + for _, v := range m { + if err := d.validateDuplicateKey(keyMap, v.Key, value); err != nil { + return errors.Wrapf(err, "invalid map key") + } + mapSlice = append(mapSlice, v) + } + continue + } + k := d.nodeToValue(key) + if err := d.validateDuplicateKey(keyMap, k, key); err != nil { + return errors.Wrapf(err, "invalid map key") + } + mapSlice = append(mapSlice, MapItem{ + Key: k, + Value: d.nodeToValue(value), + }) + } + *dst = mapSlice + return nil +} + +func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node) error { + mapNode, err := d.getMapNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get map node") + } + if mapNode == nil { + return nil + } + mapType := dst.Type() + mapValue := reflect.MakeMap(mapType) + keyType := mapValue.Type().Key() + valueType := mapValue.Type().Elem() + mapIter := mapNode.MapRange() + keyMap := map[string]struct{}{} + var foundErr error + for mapIter.Next() { + key := mapIter.Key() + value := mapIter.Value() + if key.Type() == ast.MergeKeyType { + if err := d.decodeMap(ctx, dst, value); err != nil { + return errors.Wrapf(err, "failed to decode map with merge key") + } + iter := dst.MapRange() + for iter.Next() { + if err := d.validateDuplicateKey(keyMap, iter.Key(), value); err != nil { + return errors.Wrapf(err, "invalid map key") + } + mapValue.SetMapIndex(iter.Key(), iter.Value()) + } + continue + } + k := reflect.ValueOf(d.nodeToValue(key)) + if k.IsValid() && k.Type().ConvertibleTo(keyType) { + k = k.Convert(keyType) + } + if k.IsValid() { + if err := d.validateDuplicateKey(keyMap, k.Interface(), key); err != nil { + return errors.Wrapf(err, "invalid map key") + } + } + if valueType.Kind() == reflect.Ptr && value.Type() == ast.NullType { + // set nil value to pointer + mapValue.SetMapIndex(k, reflect.Zero(valueType)) + continue + } + dstValue, err := d.createDecodedNewValue(ctx, valueType, reflect.Value{}, value) + if err != nil { + if foundErr == nil { + foundErr = err + } + } + if !k.IsValid() { + // expect nil key + mapValue.SetMapIndex(d.createDecodableValue(keyType), d.castToAssignableValue(dstValue, valueType)) + continue + } + mapValue.SetMapIndex(k, d.castToAssignableValue(dstValue, valueType)) + } + dst.Set(mapValue) + if foundErr != nil { + return errors.Wrapf(foundErr, "failed to decode value") + } + return nil +} + +func (d *Decoder) fileToReader(file string) (io.Reader, error) { + reader, err := os.Open(file) + if err != nil { + return nil, errors.Wrapf(err, "failed to open file") + } + return reader, nil +} + +func (d *Decoder) isYAMLFile(file string) bool { + ext := filepath.Ext(file) + if ext == ".yml" { + return true + } + if ext == ".yaml" { + return true + } + return false +} + +func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) { + pattern := fmt.Sprintf("%s/*", dir) + matches, err := filepath.Glob(pattern) + if err != nil { + return nil, errors.Wrapf(err, "failed to get files by %s", pattern) + } + readers := []io.Reader{} + for _, match := range matches { + if !d.isYAMLFile(match) { + continue + } + reader, err := d.fileToReader(match) + if err != nil { + return nil, errors.Wrapf(err, "failed to get reader") + } + readers = append(readers, reader) + } + return readers, nil +} + +func (d *Decoder) readersUnderDirRecursive(dir string) ([]io.Reader, error) { + readers := []io.Reader{} + if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if !d.isYAMLFile(path) { + return nil + } + reader, err := d.fileToReader(path) + if err != nil { + return errors.Wrapf(err, "failed to get reader") + } + readers = append(readers, reader) + return nil + }); err != nil { + return nil, errors.Wrapf(err, "interrupt walk in %s", dir) + } + return readers, nil +} + +func (d *Decoder) resolveReference() error { + for _, opt := range d.opts { + if err := opt(d); err != nil { + return errors.Wrapf(err, "failed to exec option") + } + } + for _, file := range d.referenceFiles { + reader, err := d.fileToReader(file) + if err != nil { + return errors.Wrapf(err, "failed to get reader") + } + d.referenceReaders = append(d.referenceReaders, reader) + } + for _, dir := range d.referenceDirs { + if !d.isRecursiveDir { + readers, err := d.readersUnderDir(dir) + if err != nil { + return errors.Wrapf(err, "failed to get readers from under the %s", dir) + } + d.referenceReaders = append(d.referenceReaders, readers...) + } else { + readers, err := d.readersUnderDirRecursive(dir) + if err != nil { + return errors.Wrapf(err, "failed to get readers from under the %s", dir) + } + d.referenceReaders = append(d.referenceReaders, readers...) + } + } + for _, reader := range d.referenceReaders { + bytes, err := ioutil.ReadAll(reader) + if err != nil { + return errors.Wrapf(err, "failed to read buffer") + } + + // assign new anchor definition to anchorMap + if _, err := d.parse(bytes); err != nil { + return errors.Wrapf(err, "failed to decode") + } + } + d.isResolvedReference = true + return nil +} + +func (d *Decoder) parse(bytes []byte) (*ast.File, error) { + var parseMode parser.Mode + if d.toCommentMap != nil { + parseMode = parser.ParseComments + } + f, err := parser.ParseBytes(bytes, parseMode) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse yaml") + } + normalizedFile := &ast.File{} + for _, doc := range f.Docs { + // try to decode ast.Node to value and map anchor value to anchorMap + if v := d.nodeToValue(doc.Body); v != nil { + normalizedFile.Docs = append(normalizedFile.Docs, doc) + } + } + return normalizedFile, nil +} + +func (d *Decoder) isInitialized() bool { + return d.parsedFile != nil +} + +func (d *Decoder) decodeInit() error { + if !d.isResolvedReference { + if err := d.resolveReference(); err != nil { + return errors.Wrapf(err, "failed to resolve reference") + } + } + var buf bytes.Buffer + if _, err := io.Copy(&buf, d.reader); err != nil { + return errors.Wrapf(err, "failed to copy from reader") + } + file, err := d.parse(buf.Bytes()) + if err != nil { + return errors.Wrapf(err, "failed to decode") + } + d.parsedFile = file + return nil +} + +func (d *Decoder) decode(ctx context.Context, v reflect.Value) error { + if len(d.parsedFile.Docs) <= d.streamIndex { + return io.EOF + } + body := d.parsedFile.Docs[d.streamIndex].Body + if body == nil { + return nil + } + if err := d.decodeValue(ctx, v.Elem(), body); err != nil { + return errors.Wrapf(err, "failed to decode value") + } + d.streamIndex++ + return nil +} + +// Decode reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (d *Decoder) Decode(v interface{}) error { + return d.DecodeContext(context.Background(), v) +} + +// DecodeContext reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v with context.Context. +func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Ptr { + return errors.ErrDecodeRequiredPointerType + } + if d.isInitialized() { + if err := d.decode(ctx, rv); err != nil { + if err == io.EOF { + return err + } + return errors.Wrapf(err, "failed to decode") + } + return nil + } + if err := d.decodeInit(); err != nil { + return errors.Wrapf(err, "failed to decodeInit") + } + if err := d.decode(ctx, rv); err != nil { + if err == io.EOF { + return err + } + return errors.Wrapf(err, "failed to decode") + } + return nil +} + +// DecodeFromNode decodes node into the value pointed to by v. +func (d *Decoder) DecodeFromNode(node ast.Node, v interface{}) error { + return d.DecodeFromNodeContext(context.Background(), node, v) +} + +// DecodeFromNodeContext decodes node into the value pointed to by v with context.Context. +func (d *Decoder) DecodeFromNodeContext(ctx context.Context, node ast.Node, v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Ptr { + return errors.ErrDecodeRequiredPointerType + } + if !d.isInitialized() { + if err := d.decodeInit(); err != nil { + return errors.Wrapf(err, "failed to decodInit") + } + } + // resolve references to the anchor on the same file + d.nodeToValue(node) + if err := d.decodeValue(ctx, rv.Elem(), node); err != nil { + return errors.Wrapf(err, "failed to decode value") + } + return nil +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/decode_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/decode_test.go new file mode 100644 index 0000000..cabfd33 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/decode_test.go @@ -0,0 +1,2908 @@ +package yaml_test + +import ( + "bytes" + "context" + "fmt" + "io" + "log" + "math" + "net" + "reflect" + "strconv" + "strings" + "testing" + "time" + + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/parser" + "golang.org/x/xerrors" +) + +type Child struct { + B int + C int `yaml:"-"` +} + +func TestDecoder(t *testing.T) { + tests := []struct { + source string + value interface{} + }{ + { + "null\n", + (*struct{})(nil), + }, + { + "v: hi\n", + map[string]string{"v": "hi"}, + }, + { + "v: \"true\"\n", + map[string]string{"v": "true"}, + }, + { + "v: \"false\"\n", + map[string]string{"v": "false"}, + }, + { + "v: true\n", + map[string]interface{}{"v": true}, + }, + { + "v: true\n", + map[string]string{"v": "true"}, + }, + { + "v: 10\n", + map[string]string{"v": "10"}, + }, + { + "v: -10\n", + map[string]string{"v": "-10"}, + }, + { + "v: 1.234\n", + map[string]string{"v": "1.234"}, + }, + { + "v: \" foo\"\n", + map[string]string{"v": " foo"}, + }, + { + "v: \"foo \"\n", + map[string]string{"v": "foo "}, + }, + { + "v: \" foo \"\n", + map[string]string{"v": " foo "}, + }, + { + "v: false\n", + map[string]bool{"v": false}, + }, + { + "v: 10\n", + map[string]int{"v": 10}, + }, + { + "v: 10", + map[string]interface{}{"v": 10}, + }, + { + "v: 0b10", + map[string]interface{}{"v": 2}, + }, + { + "v: -0b101010", + map[string]interface{}{"v": -42}, + }, + { + "v: -0b1000000000000000000000000000000000000000000000000000000000000000", + map[string]interface{}{"v": int64(-9223372036854775808)}, + }, + { + "v: 0xA", + map[string]interface{}{"v": 10}, + }, + { + "v: .1", + map[string]interface{}{"v": 0.1}, + }, + { + "v: -.1", + map[string]interface{}{"v": -0.1}, + }, + { + "v: -10\n", + map[string]int{"v": -10}, + }, + { + "v: 4294967296\n", + map[string]int64{"v": int64(4294967296)}, + }, + { + "v: 0.1\n", + map[string]interface{}{"v": 0.1}, + }, + { + "v: 0.99\n", + map[string]float32{"v": 0.99}, + }, + { + "v: -0.1\n", + map[string]float64{"v": -0.1}, + }, + { + "v: 6.8523e+5", + map[string]interface{}{"v": 6.8523e+5}, + }, + { + "v: 685.230_15e+03", + map[string]interface{}{"v": 685.23015e+03}, + }, + { + "v: 685_230.15", + map[string]interface{}{"v": 685230.15}, + }, + { + "v: 685_230.15", + map[string]float64{"v": 685230.15}, + }, + { + "v: 685230", + map[string]interface{}{"v": 685230}, + }, + { + "v: +685_230", + map[string]interface{}{"v": 685230}, + }, + { + "v: 02472256", + map[string]interface{}{"v": 685230}, + }, + { + "v: 0x_0A_74_AE", + map[string]interface{}{"v": 685230}, + }, + { + "v: 0b1010_0111_0100_1010_1110", + map[string]interface{}{"v": 685230}, + }, + { + "v: +685_230", + map[string]int{"v": 685230}, + }, + + // Bools from spec + { + "v: True", + map[string]interface{}{"v": true}, + }, + { + "v: TRUE", + map[string]interface{}{"v": true}, + }, + { + "v: False", + map[string]interface{}{"v": false}, + }, + { + "v: FALSE", + map[string]interface{}{"v": false}, + }, + { + "v: y", + map[string]interface{}{"v": "y"}, // y or yes or Yes is string + }, + { + "v: NO", + map[string]interface{}{"v": "NO"}, // no or No or NO is string + }, + { + "v: on", + map[string]interface{}{"v": "on"}, // on is string + }, + + // Some cross type conversions + { + "v: 42", + map[string]uint{"v": 42}, + }, { + "v: 4294967296", + map[string]uint64{"v": uint64(4294967296)}, + }, + + // int + { + "v: 2147483647", + map[string]int{"v": math.MaxInt32}, + }, + { + "v: -2147483648", + map[string]int{"v": math.MinInt32}, + }, + + // int64 + { + "v: 9223372036854775807", + map[string]int64{"v": math.MaxInt64}, + }, + { + "v: 0b111111111111111111111111111111111111111111111111111111111111111", + map[string]int64{"v": math.MaxInt64}, + }, + { + "v: -9223372036854775808", + map[string]int64{"v": math.MinInt64}, + }, + { + "v: -0b111111111111111111111111111111111111111111111111111111111111111", + map[string]int64{"v": -math.MaxInt64}, + }, + + // uint + { + "v: 0", + map[string]uint{"v": 0}, + }, + { + "v: 4294967295", + map[string]uint{"v": math.MaxUint32}, + }, + + // uint64 + { + "v: 0", + map[string]uint{"v": 0}, + }, + { + "v: 18446744073709551615", + map[string]uint64{"v": math.MaxUint64}, + }, + { + "v: 0b1111111111111111111111111111111111111111111111111111111111111111", + map[string]uint64{"v": math.MaxUint64}, + }, + { + "v: 9223372036854775807", + map[string]uint64{"v": math.MaxInt64}, + }, + + // float32 + { + "v: 3.40282346638528859811704183484516925440e+38", + map[string]float32{"v": math.MaxFloat32}, + }, + { + "v: 1.401298464324817070923729583289916131280e-45", + map[string]float32{"v": math.SmallestNonzeroFloat32}, + }, + { + "v: 18446744073709551615", + map[string]float32{"v": float32(math.MaxUint64)}, + }, + { + "v: 18446744073709551616", + map[string]float32{"v": float32(math.MaxUint64 + 1)}, + }, + + // float64 + { + "v: 1.797693134862315708145274237317043567981e+308", + map[string]float64{"v": math.MaxFloat64}, + }, + { + "v: 4.940656458412465441765687928682213723651e-324", + map[string]float64{"v": math.SmallestNonzeroFloat64}, + }, + { + "v: 18446744073709551615", + map[string]float64{"v": float64(math.MaxUint64)}, + }, + { + "v: 18446744073709551616", + map[string]float64{"v": float64(math.MaxUint64 + 1)}, + }, + + // Timestamps + { + // Date only. + "v: 2015-01-01\n", + map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, + }, + { + // RFC3339 + "v: 2015-02-24T18:19:39.12Z\n", + map[string]time.Time{"v": time.Date(2015, 2, 24, 18, 19, 39, .12e9, time.UTC)}, + }, + { + // RFC3339 with short dates. + "v: 2015-2-3T3:4:5Z", + map[string]time.Time{"v": time.Date(2015, 2, 3, 3, 4, 5, 0, time.UTC)}, + }, + { + // ISO8601 lower case t + "v: 2015-02-24t18:19:39Z\n", + map[string]time.Time{"v": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)}, + }, + { + // space separate, no time zone + "v: 2015-02-24 18:19:39\n", + map[string]time.Time{"v": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)}, + }, + { + "v: 60s\n", + map[string]time.Duration{"v": time.Minute}, + }, + { + "v: -0.5h\n", + map[string]time.Duration{"v": -30 * time.Minute}, + }, + + // Single Quoted values. + { + `'1': '2'`, + map[interface{}]interface{}{"1": `2`}, + }, + { + `'1': '"2"'`, + map[interface{}]interface{}{"1": `"2"`}, + }, + { + `'1': ''''`, + map[interface{}]interface{}{"1": `'`}, + }, + { + `'1': '''2'''`, + map[interface{}]interface{}{"1": `'2'`}, + }, + { + `'1': 'B''z'`, + map[interface{}]interface{}{"1": `B'z`}, + }, + { + `'1': '\'`, + map[interface{}]interface{}{"1": `\`}, + }, + { + `'1': '\\'`, + map[interface{}]interface{}{"1": `\\`}, + }, + { + `'1': '\"2\"'`, + map[interface{}]interface{}{"1": `\"2\"`}, + }, + { + `'1': '\\"2\\"'`, + map[interface{}]interface{}{"1": `\\"2\\"`}, + }, + { + "'1': ' 1\n 2\n 3'", + map[interface{}]interface{}{"1": " 1 2 3"}, + }, + { + "'1': '\n 2\n 3'", + map[interface{}]interface{}{"1": " 2 3"}, + }, + + // Double Quoted values. + { + `"1": "2"`, + map[interface{}]interface{}{"1": `2`}, + }, + { + `"1": "\"2\""`, + map[interface{}]interface{}{"1": `"2"`}, + }, + { + `"1": "\""`, + map[interface{}]interface{}{"1": `"`}, + }, + { + `"1": "X\"z"`, + map[interface{}]interface{}{"1": `X"z`}, + }, + { + `"1": "\\"`, + map[interface{}]interface{}{"1": `\`}, + }, + { + `"1": "\\\\"`, + map[interface{}]interface{}{"1": `\\`}, + }, + { + `"1": "\\\"2\\\""`, + map[interface{}]interface{}{"1": `\"2\"`}, + }, + { + "'1': \" 1\n 2\n 3\"", + map[interface{}]interface{}{"1": " 1 2 3"}, + }, + { + "'1': \"\n 2\n 3\"", + map[interface{}]interface{}{"1": " 2 3"}, + }, + { + `"1": "a\x2Fb"`, + map[interface{}]interface{}{"1": `a/b`}, + }, + { + `"1": "a\u002Fb"`, + map[interface{}]interface{}{"1": `a/b`}, + }, + { + `"1": "a\x2Fb\u002Fc\U0000002Fd"`, + map[interface{}]interface{}{"1": `a/b/c/d`}, + }, + { + "'1': \"2\\n3\"", + map[interface{}]interface{}{"1": "2\n3"}, + }, + { + "'1': \"2\\r\\n3\"", + map[interface{}]interface{}{"1": "2\r\n3"}, + }, + + { + "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"}, + }, + + // Nulls + { + "v:", + map[string]interface{}{"v": nil}, + }, + { + "v: ~", + map[string]interface{}{"v": nil}, + }, + { + "~: null key", + map[interface{}]string{nil: "null key"}, + }, + { + "v:", + map[string]*bool{"v": nil}, + }, + { + "v: null", + map[string]*string{"v": nil}, + }, + { + "v: null", + map[string]string{"v": ""}, + }, + { + "v: null", + map[string]interface{}{"v": nil}, + }, + { + "v: Null", + map[string]interface{}{"v": nil}, + }, + { + "v: NULL", + map[string]interface{}{"v": nil}, + }, + { + "v: ~", + map[string]*string{"v": nil}, + }, + { + "v: ~", + map[string]string{"v": ""}, + }, + + { + "v: .inf\n", + map[string]interface{}{"v": math.Inf(0)}, + }, + { + "v: .Inf\n", + map[string]interface{}{"v": math.Inf(0)}, + }, + { + "v: .INF\n", + map[string]interface{}{"v": math.Inf(0)}, + }, + { + "v: -.inf\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.Inf(-1)}, + }, + { + "v: .nan\n", + map[string]interface{}{"v": math.NaN()}, + }, + { + "v: .NaN\n", + map[string]interface{}{"v": math.NaN()}, + }, + { + "v: .NAN\n", + map[string]interface{}{"v": math.NaN()}, + }, + + // 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}, + }, + { + "v: !!timestamp \"2015-01-01\"", + map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, + }, + { + "v: !!timestamp 2015-01-01", + map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, + }, + + // Flow sequence + { + "v: [A,B]", + map[string]interface{}{"v": []interface{}{"A", "B"}}, + }, + { + "v: [A,B,C,]", + map[string][]string{"v": []string{"A", "B", "C"}}, + }, + { + "v: [A,1,C]", + map[string][]string{"v": []string{"A", "1", "C"}}, + }, + { + "v: [A,1,C]", + map[string]interface{}{"v": []interface{}{"A", 1, "C"}}, + }, + + // Block sequence + { + "v:\n - A\n - B", + map[string]interface{}{"v": []interface{}{"A", "B"}}, + }, + { + "v:\n - A\n - B\n - C", + map[string][]string{"v": []string{"A", "B", "C"}}, + }, + { + "v:\n - A\n - 1\n - C", + map[string][]string{"v": []string{"A", "1", "C"}}, + }, + { + "v:\n - A\n - 1\n - C", + map[string]interface{}{"v": []interface{}{"A", 1, "C"}}, + }, + + // Map inside interface with no type hints. + { + "a: {b: c}", + map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, + }, + + { + "v: \"\"\n", + map[string]string{"v": ""}, + }, + { + "v:\n- A\n- B\n", + map[string][]string{"v": {"A", "B"}}, + }, + { + "a: '-'\n", + map[string]string{"a": "-"}, + }, + { + "123\n", + 123, + }, + { + "hello: world\n", + map[string]string{"hello": "world"}, + }, + { + "hello: world\r\n", + map[string]string{"hello": "world"}, + }, + { + "hello: world\rGo: Gopher", + map[string]string{"hello": "world", "Go": "Gopher"}, + }, + + // 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 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 `yaml:"a"` + }{1}, + }, + + { + "a: 1\n", + yaml.MapItem{Key: "a", Value: 1}, + }, + { + "a: 1\nb: 2\nc: 3\n", + yaml.MapSlice{ + {Key: "a", Value: 1}, + {Key: "b", Value: 2}, + {Key: "c", Value: 3}, + }, + }, + { + "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", + map[string]interface{}{ + "v": []interface{}{ + "A", + 1, + map[string][]int{ + "B": {2, 3}, + }, + }, + }, + }, + { + "a:\n b: c\n", + map[string]interface{}{ + "a": map[string]string{ + "b": "c", + }, + }, + }, + { + "a: {x: 1}\n", + map[string]map[string]int{ + "a": { + "x": 1, + }, + }, + }, + { + "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", + map[string]string{ + "t2": "2018-01-09T10:40:47Z", + "t4": "2098-01-09T10:40:47Z", + }, + }, + { + "a: [1, 2]\n", + map[string][]int{ + "a": {1, 2}, + }, + }, + { + "a: {b: c, d: e}\n", + map[string]interface{}{ + "a": map[string]string{ + "b": "c", + "d": "e", + }, + }, + }, + { + "a: 3s\n", + map[string]string{ + "a": "3s", + }, + }, + { + "a: \n", + map[string]string{"a": ""}, + }, + { + "a: \"1:1\"\n", + map[string]string{"a": "1:1"}, + }, + { + "a: 1.2.3.4\n", + map[string]string{"a": "1.2.3.4"}, + }, + { + "a: 'b: c'\n", + map[string]string{"a": "b: c"}, + }, + { + "a: 'Hello #comment'\n", + map[string]string{"a": "Hello #comment"}, + }, + { + "a: 100.5\n", + map[string]interface{}{ + "a": 100.5, + }, + }, + { + "a: \"\\0\"\n", + map[string]string{"a": "\\0"}, + }, + { + "b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n", + map[string]interface{}{ + "b": 2, + "a": 1, + "d": 4, + "c": 3, + "sub": map[string]int{ + "e": 5, + }, + }, + }, + { + " a : b \n", + map[string]string{"a": "b"}, + }, + { + "a: b # comment\nb: c\n", + map[string]string{ + "a": "b", + "b": "c", + }, + }, + { + "---\na: b\n", + map[string]string{"a": "b"}, + }, + { + "a: b\n...\n", + map[string]string{"a": "b"}, + }, + { + "%YAML 1.2\n---\n", + (*struct{})(nil), + }, + { + "---\n", + (*struct{})(nil), + }, + { + "...", + (*struct{})(nil), + }, + { + "v: go test ./...", + map[string]string{"v": "go test ./..."}, + }, + { + "v: echo ---", + map[string]string{"v": "echo ---"}, + }, + { + "v: |\n hello\n ...\n world\n", + map[string]string{"v": "hello\n...\nworld\n"}, + }, + { + "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)}, + }, + { + "v:\n- A\n- |-\n B\n C\n", + map[string][]string{ + "v": { + "A", "B\nC", + }, + }, + }, + { + "v:\n- A\n- |-\n B\n C\n\n\n", + map[string][]string{ + "v": { + "A", "B\nC", + }, + }, + }, + { + "v:\n- A\n- >-\n B\n C\n", + map[string][]string{ + "v": { + "A", "B C", + }, + }, + }, + { + "v:\n- A\n- >-\n B\n C\n\n\n", + map[string][]string{ + "v": { + "A", "B C", + }, + }, + }, + { + "a: b\nc: d\n", + struct { + A string + C string `yaml:"c"` + }{ + "b", "d", + }, + }, + { + "a: 1\nb: 2\n", + struct { + A int + B int `yaml:"-"` + }{ + 1, 0, + }, + }, + { + "a: 1\nb: 2\n", + struct { + A int + Child `yaml:",inline"` + }{ + 1, + Child{ + B: 2, + C: 0, + }, + }, + }, + + // 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\n", + struct { + A, B struct { + C int + } + }{struct{ C int }{1}, struct{ C int }{1}}, + }, + { + "a: &a [1, 2]\nb: *a\n", + struct{ B []int }{[]int{1, 2}}, + }, + + { + "tags:\n- hello-world\na: foo", + struct { + Tags []string + A string + }{Tags: []string{"hello-world"}, A: "foo"}, + }, + { + "", + (*struct{})(nil), + }, + { + "{}", struct{}{}, + }, + { + "v: /a/{b}", + map[string]string{"v": "/a/{b}"}, + }, + { + "v: 1[]{},!%?&*", + map[string]string{"v": "1[]{},!%?&*"}, + }, + { + "v: user's item", + map[string]string{"v": "user's item"}, + }, + { + "v: [1,[2,[3,[4,5],6],7],8]", + map[string]interface{}{ + "v": []interface{}{ + 1, + []interface{}{ + 2, + []interface{}{ + 3, + []int{4, 5}, + 6, + }, + 7, + }, + 8, + }, + }, + }, + { + "v: {a: {b: {c: {d: e},f: g},h: i},j: k}", + map[string]interface{}{ + "v": map[string]interface{}{ + "a": map[string]interface{}{ + "b": map[string]interface{}{ + "c": map[string]string{ + "d": "e", + }, + "f": "g", + }, + "h": "i", + }, + "j": "k", + }, + }, + }, + { + `--- +- a: + b: +- c: d +`, + []map[string]interface{}{ + { + "a": map[string]interface{}{ + "b": nil, + }, + }, + { + "c": "d", + }, + }, + }, + { + `--- +a: + b: +c: d +`, + map[string]interface{}{ + "a": map[string]interface{}{ + "b": nil, + }, + "c": "d", + }, + }, + { + `--- +a: +b: +c: +`, + map[string]interface{}{ + "a": nil, + "b": nil, + "c": nil, + }, + }, + { + `--- +a: go test ./... +b: +c: +`, + map[string]interface{}{ + "a": "go test ./...", + "b": nil, + "c": nil, + }, + }, + { + `--- +a: | + hello + ... + world +b: +c: +`, + map[string]interface{}{ + "a": "hello\n...\nworld\n", + "b": nil, + "c": nil, + }, + }, + + // Multi bytes + { + "v: あいうえお\nv2: かきくけこ", + map[string]string{"v": "あいうえお", "v2": "かきくけこ"}, + }, + } + for _, test := range tests { + t.Run(test.source, func(t *testing.T) { + buf := bytes.NewBufferString(test.source) + dec := yaml.NewDecoder(buf) + typ := reflect.ValueOf(test.value).Type() + value := reflect.New(typ) + if err := dec.Decode(value.Interface()); err != nil { + if err == io.EOF { + return + } + t.Fatalf("%s: %+v", test.source, err) + } + actual := fmt.Sprintf("%+v", value.Elem().Interface()) + expect := fmt.Sprintf("%+v", test.value) + if actual != expect { + t.Fatalf("failed to test [%s], actual=[%s], expect=[%s]", test.source, actual, expect) + } + }) + } +} + +func TestDecoder_TypeConversionError(t *testing.T) { + t.Run("type conversion for struct", func(t *testing.T) { + type T struct { + A int + B uint + C float32 + D bool + } + type U struct { + *T `yaml:",inline"` + } + t.Run("string to int", func(t *testing.T) { + var v T + err := yaml.Unmarshal([]byte(`a: str`), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal string into Go struct field T.A of type int" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + }) + t.Run("string to bool", func(t *testing.T) { + var v T + err := yaml.Unmarshal([]byte(`d: str`), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal string into Go struct field T.D of type bool" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + }) + t.Run("string to int at inline", func(t *testing.T) { + var v U + err := yaml.Unmarshal([]byte(`a: str`), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal string into Go struct field U.T.A of type int" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + }) + }) + t.Run("type conversion for array", func(t *testing.T) { + t.Run("string to int", func(t *testing.T) { + var v map[string][]int + err := yaml.Unmarshal([]byte(`v: [A,1,C]`), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal string into Go value of type int" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + if len(v) == 0 || len(v["v"]) == 0 { + t.Fatal("failed to decode value") + } + if v["v"][0] != 1 { + t.Fatal("failed to decode value") + } + }) + t.Run("string to int", func(t *testing.T) { + var v map[string][]int + err := yaml.Unmarshal([]byte("v:\n - A\n - 1\n - C"), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal string into Go value of type int" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + if len(v) == 0 || len(v["v"]) == 0 { + t.Fatal("failed to decode value") + } + if v["v"][0] != 1 { + t.Fatal("failed to decode value") + } + }) + }) + t.Run("overflow error", func(t *testing.T) { + t.Run("negative number to uint", func(t *testing.T) { + var v map[string]uint + err := yaml.Unmarshal([]byte("v: -42"), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal -42 into Go value of type uint ( overflow )" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + if v["v"] != 0 { + t.Fatal("failed to decode value") + } + }) + t.Run("negative number to uint64", func(t *testing.T) { + var v map[string]uint64 + err := yaml.Unmarshal([]byte("v: -4294967296"), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal -4294967296 into Go value of type uint64 ( overflow )" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + if v["v"] != 0 { + t.Fatal("failed to decode value") + } + }) + t.Run("larger number for int32", func(t *testing.T) { + var v map[string]int32 + err := yaml.Unmarshal([]byte("v: 4294967297"), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal 4294967297 into Go value of type int32 ( overflow )" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + if v["v"] != 0 { + t.Fatal("failed to decode value") + } + }) + t.Run("larger number for int8", func(t *testing.T) { + var v map[string]int8 + err := yaml.Unmarshal([]byte("v: 128"), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal 128 into Go value of type int8 ( overflow )" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + if v["v"] != 0 { + t.Fatal("failed to decode value") + } + }) + }) + t.Run("type conversion for time", func(t *testing.T) { + type T struct { + A time.Time + B time.Duration + } + t.Run("int to time", func(t *testing.T) { + var v T + err := yaml.Unmarshal([]byte(`a: 123`), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal uint64 into Go struct field T.A of type time.Time" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + }) + t.Run("string to duration", func(t *testing.T) { + var v T + err := yaml.Unmarshal([]byte(`b: str`), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := `time: invalid duration "str"` + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + }) + t.Run("int to duration", func(t *testing.T) { + var v T + err := yaml.Unmarshal([]byte(`b: 10`), &v) + if err == nil { + t.Fatal("expected to error") + } + msg := "cannot unmarshal uint64 into Go struct field T.B of type time.Duration" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg) + } + }) + }) +} + +func TestDecoder_AnchorReferenceDirs(t *testing.T) { + buf := bytes.NewBufferString("a: *a\n") + dec := yaml.NewDecoder(buf, yaml.ReferenceDirs("testdata")) + var v struct { + A struct { + B int + C string + } + } + if err := dec.Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + if v.A.B != 1 { + t.Fatal("failed to decode by reference dirs") + } + if v.A.C != "hello" { + t.Fatal("failed to decode by reference dirs") + } +} + +func TestDecoder_AnchorReferenceDirsRecursive(t *testing.T) { + buf := bytes.NewBufferString("a: *a\n") + dec := yaml.NewDecoder( + buf, + yaml.RecursiveDir(true), + yaml.ReferenceDirs("testdata"), + ) + var v struct { + A struct { + B int + C string + } + } + if err := dec.Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + if v.A.B != 1 { + t.Fatal("failed to decode by reference dirs") + } + if v.A.C != "hello" { + t.Fatal("failed to decode by reference dirs") + } +} + +func TestDecoder_AnchorFiles(t *testing.T) { + buf := bytes.NewBufferString("a: *a\n") + dec := yaml.NewDecoder(buf, yaml.ReferenceFiles("testdata/anchor.yml")) + var v struct { + A struct { + B int + C string + } + } + if err := dec.Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + if v.A.B != 1 { + t.Fatal("failed to decode by reference dirs") + } + if v.A.C != "hello" { + t.Fatal("failed to decode by reference dirs") + } +} + +func TestDecodeWithMergeKey(t *testing.T) { + yml := ` +a: &a + b: 1 + c: hello +items: +- <<: *a +- <<: *a + c: world +` + type Item struct { + B int + C string + } + type T struct { + Items []*Item + } + buf := bytes.NewBufferString(yml) + dec := yaml.NewDecoder(buf) + var v T + if err := dec.Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + if len(v.Items) != 2 { + t.Fatal("failed to decode with merge key") + } + if v.Items[0].B != 1 || v.Items[0].C != "hello" { + t.Fatal("failed to decode with merge key") + } + if v.Items[1].B != 1 || v.Items[1].C != "world" { + t.Fatal("failed to decode with merge key") + } + t.Run("decode with interface{}", func(t *testing.T) { + buf := bytes.NewBufferString(yml) + dec := yaml.NewDecoder(buf) + var v interface{} + if err := dec.Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + items := v.(map[string]interface{})["items"].([]interface{}) + if len(items) != 2 { + t.Fatal("failed to decode with merge key") + } + b0 := items[0].(map[string]interface{})["b"] + if _, ok := b0.(uint64); !ok { + t.Fatal("failed to decode with merge key") + } + if b0.(uint64) != 1 { + t.Fatal("failed to decode with merge key") + } + c0 := items[0].(map[string]interface{})["c"] + if _, ok := c0.(string); !ok { + t.Fatal("failed to decode with merge key") + } + if c0.(string) != "hello" { + t.Fatal("failed to decode with merge key") + } + b1 := items[1].(map[string]interface{})["b"] + if _, ok := b1.(uint64); !ok { + t.Fatal("failed to decode with merge key") + } + if b1.(uint64) != 1 { + t.Fatal("failed to decode with merge key") + } + c1 := items[1].(map[string]interface{})["c"] + if _, ok := c1.(string); !ok { + t.Fatal("failed to decode with merge key") + } + if c1.(string) != "world" { + t.Fatal("failed to decode with merge key") + } + }) + t.Run("decode with map", func(t *testing.T) { + var v struct { + Items []map[string]interface{} + } + buf := bytes.NewBufferString(yml) + dec := yaml.NewDecoder(buf) + if err := dec.Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + if len(v.Items) != 2 { + t.Fatal("failed to decode with merge key") + } + b0 := v.Items[0]["b"] + if _, ok := b0.(uint64); !ok { + t.Fatal("failed to decode with merge key") + } + if b0.(uint64) != 1 { + t.Fatal("failed to decode with merge key") + } + c0 := v.Items[0]["c"] + if _, ok := c0.(string); !ok { + t.Fatal("failed to decode with merge key") + } + if c0.(string) != "hello" { + t.Fatal("failed to decode with merge key") + } + b1 := v.Items[1]["b"] + if _, ok := b1.(uint64); !ok { + t.Fatal("failed to decode with merge key") + } + if b1.(uint64) != 1 { + t.Fatal("failed to decode with merge key") + } + c1 := v.Items[1]["c"] + if _, ok := c1.(string); !ok { + t.Fatal("failed to decode with merge key") + } + if c1.(string) != "world" { + t.Fatal("failed to decode with merge key") + } + }) +} + +func TestDecoder_Inline(t *testing.T) { + type Base struct { + A int + B string + } + yml := `--- +a: 1 +b: hello +c: true +` + var v struct { + *Base `yaml:",inline"` + C bool + } + if err := yaml.NewDecoder(strings.NewReader(yml)).Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + if v.A != 1 { + t.Fatal("failed to decode with inline key") + } + if v.B != "hello" { + t.Fatal("failed to decode with inline key") + } + if !v.C { + t.Fatal("failed to decode with inline key") + } + + t.Run("multiple inline with strict", func(t *testing.T) { + type Base struct { + A int + B string + } + type Base2 struct { + Base *Base `yaml:",inline"` + } + yml := `--- +a: 1 +b: hello +` + var v struct { + Base2 *Base2 `yaml:",inline"` + } + if err := yaml.NewDecoder(strings.NewReader(yml), yaml.Strict()).Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + if v.Base2.Base.A != 1 { + t.Fatal("failed to decode with inline key") + } + if v.Base2.Base.B != "hello" { + t.Fatal("failed to decode with inline key") + } + }) +} + +func TestDecoder_InlineAndConflictKey(t *testing.T) { + type Base struct { + A int + B string + } + yml := `--- +a: 1 +b: hello +c: true +` + var v struct { + *Base `yaml:",inline"` + A int + C bool + } + if err := yaml.NewDecoder(strings.NewReader(yml)).Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + if v.A != 1 { + t.Fatal("failed to decode with inline key") + } + if v.B != "hello" { + t.Fatal("failed to decode with inline key") + } + if !v.C { + t.Fatal("failed to decode with inline key") + } + if v.Base.A != 0 { + t.Fatal("failed to decode with inline key") + } +} + +func TestDecoder_InlineAndWrongTypeStrict(t *testing.T) { + type Base struct { + A int + B string + } + yml := `--- +a: notanint +b: hello +c: true +` + var v struct { + *Base `yaml:",inline"` + C bool + } + err := yaml.NewDecoder(strings.NewReader(yml), yaml.Strict()).Decode(&v) + if err == nil { + t.Fatalf("expected error") + } + + //TODO: properly check if errors are colored/have source + t.Logf("%s", err) + t.Logf("%s", yaml.FormatError(err, true, false)) + t.Logf("%s", yaml.FormatError(err, false, true)) + t.Logf("%s", yaml.FormatError(err, true, true)) +} + +func TestDecoder_InvalidCases(t *testing.T) { + const src = `--- +a: +- b + c: d +` + var v struct { + A []string + } + err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v) + if err == nil { + t.Fatalf("expected error") + } + + if err.Error() != yaml.FormatError(err, false, true) { + t.Logf("err.Error() = %s", err.Error()) + t.Logf("yaml.FormatError(err, false, true) = %s", yaml.FormatError(err, false, true)) + t.Fatal(`err.Error() should match yaml.FormatError(err, false, true)`) + } + + //TODO: properly check if errors are colored/have source + t.Logf("%s", err) + t.Logf("%s", yaml.FormatError(err, true, false)) + t.Logf("%s", yaml.FormatError(err, false, true)) + t.Logf("%s", yaml.FormatError(err, true, true)) +} + +func TestDecoder_JSONTags(t *testing.T) { + var v struct { + A string `json:"a_json"` // no YAML tag + B string `json:"b_json" yaml:"b_yaml"` // both tags + } + + const src = `--- +a_json: a_json_value +b_json: b_json_value +b_yaml: b_yaml_value +` + if err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v); err != nil { + t.Fatalf(`parsing should succeed: %s`, err) + } + + if v.A != "a_json_value" { + t.Fatalf("v.A should be `a_json_value`, got `%s`", v.A) + } + + if v.B != "b_yaml_value" { + t.Fatalf("v.B should be `b_yaml_value`, got `%s`", v.B) + } +} + +func TestDecoder_DisallowUnknownField(t *testing.T) { + t.Run("different level keys with same name", func(t *testing.T) { + var v struct { + C Child `yaml:"c"` + } + yml := `--- +b: 1 +c: + b: 1 +` + + err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField()).Decode(&v) + if err == nil { + t.Fatalf("error expected") + } + }) + t.Run("inline", func(t *testing.T) { + var v struct { + *Child `yaml:",inline"` + A string `yaml:"a"` + } + yml := `--- +a: a +b: 1 +` + + if err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField()).Decode(&v); err != nil { + t.Fatalf(`parsing should succeed: %s`, err) + } + if v.A != "a" { + t.Fatalf("v.A should be `a`, got `%s`", v.A) + } + if v.B != 1 { + t.Fatalf("v.B should be 1, got %d", v.B) + } + if v.C != 0 { + t.Fatalf("v.C should be 0, got %d", v.C) + } + }) + t.Run("list", func(t *testing.T) { + type C struct { + Child `yaml:",inline"` + } + + var v struct { + Children []C `yaml:"children"` + } + + yml := `--- +children: +- b: 1 +- b: 2 +` + + if err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowUnknownField()).Decode(&v); err != nil { + t.Fatalf(`parsing should succeed: %s`, err) + } + + if len(v.Children) != 2 { + t.Fatalf(`len(v.Children) should be 2, got %d`, len(v.Children)) + } + + if v.Children[0].B != 1 { + t.Fatalf(`v.Children[0].B should be 1, got %d`, v.Children[0].B) + } + + if v.Children[1].B != 2 { + t.Fatalf(`v.Children[1].B should be 2, got %d`, v.Children[1].B) + } + }) +} + +func TestDecoder_DisallowDuplicateKey(t *testing.T) { + yml := ` +a: b +a: c +` + expected := ` +[3:1] duplicate key "a" + 2 | a: b +> 3 | a: c + ^ +` + t.Run("map", func(t *testing.T) { + var v map[string]string + err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowDuplicateKey()).Decode(&v) + if err == nil { + t.Fatal("decoding should fail") + } + actual := "\n" + err.Error() + if expected != actual { + t.Fatalf("expected:[%s] actual:[%s]", expected, actual) + } + }) + t.Run("struct", func(t *testing.T) { + var v struct { + A string + } + err := yaml.NewDecoder(strings.NewReader(yml), yaml.DisallowDuplicateKey()).Decode(&v) + if err == nil { + t.Fatal("decoding should fail") + } + actual := "\n" + err.Error() + if expected != actual { + t.Fatalf("expected:[%s] actual:[%s]", expected, actual) + } + }) +} + +func TestDecoder_DefaultValues(t *testing.T) { + v := struct { + A string `yaml:"a"` + B string `yaml:"b"` + c string // private + D struct { + E string `yaml:"e"` + F struct { + G string `yaml:"g"` + } `yaml:"f"` + H struct { + I string `yaml:"i"` + } `yaml:",inline"` + } `yaml:"d"` + J struct { + K string `yaml:"k"` + L struct { + M string `yaml:"m"` + } `yaml:"l"` + N struct { + O string `yaml:"o"` + } `yaml:",inline"` + } `yaml:",inline"` + P struct { + Q string `yaml:"q"` + R struct { + S string `yaml:"s"` + } `yaml:"r"` + T struct { + U string `yaml:"u"` + } `yaml:",inline"` + } `yaml:"p"` + V struct { + W string `yaml:"w"` + X struct { + Y string `yaml:"y"` + } `yaml:"x"` + Z struct { + Ä string `yaml:"ä"` + } `yaml:",inline"` + } `yaml:",inline"` + }{ + B: "defaultBValue", + c: "defaultCValue", + } + + v.D.E = "defaultEValue" + v.D.F.G = "defaultGValue" + v.D.H.I = "defaultIValue" + v.J.K = "defaultKValue" + v.J.L.M = "defaultMValue" + v.J.N.O = "defaultOValue" + v.P.R.S = "defaultSValue" + v.P.T.U = "defaultUValue" + v.V.X.Y = "defaultYValue" + v.V.Z.Ä = "defaultÄValue" + + const src = `--- +a: a_value +p: + q: q_value +w: w_value +` + if err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v); err != nil { + t.Fatalf(`parsing should succeed: %s`, err) + } + if v.A != "a_value" { + t.Fatalf("v.A should be `a_value`, got `%s`", v.A) + } + + if v.B != "defaultBValue" { + t.Fatalf("v.B should be `defaultValue`, got `%s`", v.B) + } + + if v.c != "defaultCValue" { + t.Fatalf("v.c should be `defaultCValue`, got `%s`", v.c) + } + + if v.D.E != "defaultEValue" { + t.Fatalf("v.D.E should be `defaultEValue`, got `%s`", v.D.E) + } + + if v.D.F.G != "defaultGValue" { + t.Fatalf("v.D.F.G should be `defaultGValue`, got `%s`", v.D.F.G) + } + + if v.D.H.I != "defaultIValue" { + t.Fatalf("v.D.H.I should be `defaultIValue`, got `%s`", v.D.H.I) + } + + if v.J.K != "defaultKValue" { + t.Fatalf("v.J.K should be `defaultKValue`, got `%s`", v.J.K) + } + + if v.J.L.M != "defaultMValue" { + t.Fatalf("v.J.L.M should be `defaultMValue`, got `%s`", v.J.L.M) + } + + if v.J.N.O != "defaultOValue" { + t.Fatalf("v.J.N.O should be `defaultOValue`, got `%s`", v.J.N.O) + } + + if v.P.Q != "q_value" { + t.Fatalf("v.P.Q should be `q_value`, got `%s`", v.P.Q) + } + + if v.P.R.S != "defaultSValue" { + t.Fatalf("v.P.R.S should be `defaultSValue`, got `%s`", v.P.R.S) + } + + if v.P.T.U != "defaultUValue" { + t.Fatalf("v.P.T.U should be `defaultUValue`, got `%s`", v.P.T.U) + } + + if v.V.W != "w_value" { + t.Fatalf("v.V.W should be `w_value`, got `%s`", v.V.W) + } + + if v.V.X.Y != "defaultYValue" { + t.Fatalf("v.V.X.Y should be `defaultYValue`, got `%s`", v.V.X.Y) + } + + if v.V.Z.Ä != "defaultÄValue" { + t.Fatalf("v.V.Z.Ä should be `defaultÄValue`, got `%s`", v.V.Z.Ä) + } +} + +func Example_YAMLTags() { + yml := `--- +foo: 1 +bar: c +A: 2 +B: d +` + var v struct { + A int `yaml:"foo" json:"A"` + B string `yaml:"bar" json:"B"` + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + log.Fatal(err) + } + fmt.Println(v.A) + fmt.Println(v.B) + // OUTPUT: + // 1 + // c +} + +type useJSONUnmarshalerTest struct { + s string +} + +func (t *useJSONUnmarshalerTest) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + t.s = s + return nil +} + +func TestDecoder_UseJSONUnmarshaler(t *testing.T) { + var v useJSONUnmarshalerTest + if err := yaml.UnmarshalWithOptions([]byte(`"a"`), &v, yaml.UseJSONUnmarshaler()); err != nil { + t.Fatal(err) + } + if v.s != "a" { + t.Fatalf("unexpected decoded value: %s", v.s) + } +} + +func TestDecoder_CustomUnmarshaler(t *testing.T) { + t.Run("override struct type", func(t *testing.T) { + type T struct { + Foo string `yaml:"foo"` + } + src := []byte(`foo: "bar"`) + var v T + if err := yaml.UnmarshalWithOptions(src, &v, yaml.CustomUnmarshaler[T](func(dst *T, b []byte) error { + if !bytes.Equal(src, b) { + t.Fatalf("failed to get decode target buffer. expected %q but got %q", src, b) + } + var v T + if err := yaml.Unmarshal(b, &v); err != nil { + return err + } + if v.Foo != "bar" { + t.Fatal("failed to decode") + } + dst.Foo = "bazbaz" // assign another value to target + return nil + })); err != nil { + t.Fatal(err) + } + if v.Foo != "bazbaz" { + t.Fatalf("failed to switch to custom unmarshaler. got: %v", v.Foo) + } + }) + t.Run("override bytes type", func(t *testing.T) { + type T struct { + Foo []byte `yaml:"foo"` + } + src := []byte(`foo: "bar"`) + var v T + if err := yaml.UnmarshalWithOptions(src, &v, yaml.CustomUnmarshaler[[]byte](func(dst *[]byte, b []byte) error { + if !bytes.Equal(b, []byte(`"bar"`)) { + t.Fatalf("failed to get target buffer: %q", b) + } + *dst = []byte("bazbaz") + return nil + })); err != nil { + t.Fatal(err) + } + if !bytes.Equal(v.Foo, []byte("bazbaz")) { + t.Fatalf("failed to switch to custom unmarshaler. got: %q", v.Foo) + } + }) +} + +type unmarshalContext struct { + v int +} + +func (c *unmarshalContext) UnmarshalYAML(ctx context.Context, b []byte) error { + v, ok := ctx.Value("k").(int) + if !ok { + return fmt.Errorf("cannot get valid context") + } + if v != 1 { + return fmt.Errorf("cannot get valid context") + } + if string(b) != "1" { + return fmt.Errorf("cannot get valid bytes") + } + c.v = v + return nil +} + +func Test_UnmarshalerContext(t *testing.T) { + ctx := context.WithValue(context.Background(), "k", 1) + var v unmarshalContext + if err := yaml.UnmarshalContext(ctx, []byte(`1`), &v); err != nil { + t.Fatalf("%+v", err) + } + if v.v != 1 { + t.Fatal("cannot call UnmarshalYAML") + } +} + +func TestDecoder_DecodeFromNode(t *testing.T) { + t.Run("has reference", func(t *testing.T) { + str := ` +anchor: &map + text: hello +map: *map` + var buf bytes.Buffer + dec := yaml.NewDecoder(&buf) + f, err := parser.ParseBytes([]byte(str), 0) + if err != nil { + t.Fatalf("failed to parse: %s", err) + } + type T struct { + Map map[string]string + } + var v T + if err := dec.DecodeFromNode(f.Docs[0].Body, &v); err != nil { + t.Fatalf("failed to decode: %s", err) + } + actual := fmt.Sprintf("%+v", v) + expect := fmt.Sprintf("%+v", T{map[string]string{"text": "hello"}}) + if actual != expect { + t.Fatalf("actual=[%s], expect=[%s]", actual, expect) + } + }) + t.Run("with reference option", func(t *testing.T) { + anchor := strings.NewReader(` +map: &map + text: hello`) + var buf bytes.Buffer + dec := yaml.NewDecoder(&buf, yaml.ReferenceReaders(anchor)) + f, err := parser.ParseBytes([]byte("map: *map"), 0) + if err != nil { + t.Fatalf("failed to parse: %s", err) + } + type T struct { + Map map[string]string + } + var v T + if err := dec.DecodeFromNode(f.Docs[0].Body, &v); err != nil { + t.Fatalf("failed to decode: %s", err) + } + actual := fmt.Sprintf("%+v", v) + expect := fmt.Sprintf("%+v", T{map[string]string{"text": "hello"}}) + if actual != expect { + t.Fatalf("actual=[%s], expect=[%s]", actual, expect) + } + }) + t.Run("value is not pointer", func(t *testing.T) { + var buf bytes.Buffer + var v bool + err := yaml.NewDecoder(&buf).DecodeFromNode(nil, v) + if !xerrors.Is(err, errors.ErrDecodeRequiredPointerType) { + t.Fatalf("unexpected error: %s", err) + } + }) +} + +func Example_JSONTags() { + yml := `--- +foo: 1 +bar: c +` + var v struct { + A int `json:"foo"` + B string `json:"bar"` + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + log.Fatal(err) + } + fmt.Println(v.A) + fmt.Println(v.B) + // OUTPUT: + // 1 + // c +} + +func Example_DisallowUnknownField() { + var v struct { + A string `yaml:"simple"` + C string `yaml:"complicated"` + } + + const src = `--- +simple: string +complecated: string +` + err := yaml.NewDecoder(strings.NewReader(src), yaml.DisallowUnknownField()).Decode(&v) + fmt.Printf("%v\n", err) + + // OUTPUT: + // [3:1] unknown field "complecated" + // 1 | --- + // 2 | simple: string + // > 3 | complecated: string + // ^ +} + +func Example_Unmarshal_Node() { + f, err := parser.ParseBytes([]byte("text: node example"), 0) + if err != nil { + panic(err) + } + var v struct { + Text string `yaml:"text"` + } + if err := yaml.NodeToValue(f.Docs[0].Body, &v); err != nil { + panic(err) + } + fmt.Println(v.Text) + // OUTPUT: + // node example +} + +type unmarshalableYAMLStringValue string + +func (v *unmarshalableYAMLStringValue) UnmarshalYAML(b []byte) error { + var s string + if err := yaml.Unmarshal(b, &s); err != nil { + return err + } + *v = unmarshalableYAMLStringValue(s) + return nil +} + +type unmarshalableTextStringValue string + +func (v *unmarshalableTextStringValue) UnmarshalText(b []byte) error { + *v = unmarshalableTextStringValue(string(b)) + return nil +} + +type unmarshalableStringContainer struct { + A unmarshalableYAMLStringValue `yaml:"a"` + B unmarshalableTextStringValue `yaml:"b"` +} + +func TestUnmarshalableString(t *testing.T) { + t.Run("empty string", func(t *testing.T) { + t.Parallel() + yml := ` +a: "" +b: "" +` + var container unmarshalableStringContainer + if err := yaml.Unmarshal([]byte(yml), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.A != "" { + t.Fatalf("expected empty string, but %q is set", container.A) + } + if container.B != "" { + t.Fatalf("expected empty string, but %q is set", container.B) + } + }) + t.Run("filled string", func(t *testing.T) { + t.Parallel() + yml := ` +a: "aaa" +b: "bbb" +` + var container unmarshalableStringContainer + if err := yaml.Unmarshal([]byte(yml), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.A != "aaa" { + t.Fatalf("expected \"aaa\", but %q is set", container.A) + } + if container.B != "bbb" { + t.Fatalf("expected \"bbb\", but %q is set", container.B) + } + }) + t.Run("single-quoted string", func(t *testing.T) { + t.Parallel() + yml := ` +a: 'aaa' +b: 'bbb' +` + var container unmarshalableStringContainer + if err := yaml.Unmarshal([]byte(yml), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.A != "aaa" { + t.Fatalf("expected \"aaa\", but %q is set", container.A) + } + if container.B != "bbb" { + t.Fatalf("expected \"aaa\", but %q is set", container.B) + } + }) + t.Run("literal", func(t *testing.T) { + t.Parallel() + yml := ` +a: | + a + b + c +b: | + a + b + c +` + var container unmarshalableStringContainer + if err := yaml.Unmarshal([]byte(yml), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.A != "a\nb\nc\n" { + t.Fatalf("expected \"a\nb\nc\n\", but %q is set", container.A) + } + if container.B != "a\nb\nc\n" { + t.Fatalf("expected \"a\nb\nc\n\", but %q is set", container.B) + } + }) + t.Run("anchor/alias", func(t *testing.T) { + yml := ` +a: &x 1 +b: *x +c: &y hello +d: *y +` + var v struct { + A, B, C, D unmarshalableTextStringValue + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatal(err) + } + if v.A != "1" { + t.Fatal("failed to unmarshal") + } + if v.B != "1" { + t.Fatal("failed to unmarshal") + } + if v.C != "hello" { + t.Fatal("failed to unmarshal") + } + if v.D != "hello" { + t.Fatal("failed to unmarshal") + } + }) + t.Run("net.IP", func(t *testing.T) { + yml := ` +a: &a 127.0.0.1 +b: *a +` + var v struct { + A, B net.IP + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatal(err) + } + if v.A.String() != net.IPv4(127, 0, 0, 1).String() { + t.Fatal("failed to unmarshal") + } + if v.B.String() != net.IPv4(127, 0, 0, 1).String() { + t.Fatal("failed to unmarshal") + } + }) + t.Run("quoted map keys", func(t *testing.T) { + t.Parallel() + yml := ` +a: + "b" : 2 + 'c': true +` + var v struct { + A struct { + B int + C bool + } + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if v.A.B != 2 { + t.Fatalf("expected a.b to equal 2 but was %d", v.A.B) + } + if !v.A.C { + t.Fatal("expected a.c to be true but was false") + } + }) +} + +type unmarshalablePtrStringContainer struct { + V *string `yaml:"value"` +} + +func TestUnmarshalablePtrString(t *testing.T) { + t.Run("empty string", func(t *testing.T) { + t.Parallel() + var container unmarshalablePtrStringContainer + if err := yaml.Unmarshal([]byte(`value: ""`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.V == nil || *container.V != "" { + t.Fatalf("expected empty string, but %q is set", *container.V) + } + }) + + t.Run("null", func(t *testing.T) { + t.Parallel() + var container unmarshalablePtrStringContainer + if err := yaml.Unmarshal([]byte(`value: null`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.V != (*string)(nil) { + t.Fatalf("expected nil, but %q is set", *container.V) + } + }) +} + +type unmarshalableIntValue int + +func (v *unmarshalableIntValue) UnmarshalYAML(raw []byte) error { + i, err := strconv.Atoi(string(raw)) + if err != nil { + return err + } + *v = unmarshalableIntValue(i) + return nil +} + +type unmarshalableIntContainer struct { + V unmarshalableIntValue `yaml:"value"` +} + +func TestUnmarshalableInt(t *testing.T) { + t.Run("empty int", func(t *testing.T) { + t.Parallel() + var container unmarshalableIntContainer + if err := yaml.Unmarshal([]byte(``), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.V != 0 { + t.Fatalf("expected empty int, but %d is set", container.V) + } + }) + t.Run("filled int", func(t *testing.T) { + t.Parallel() + var container unmarshalableIntContainer + if err := yaml.Unmarshal([]byte(`value: 9`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.V != 9 { + t.Fatalf("expected 9, but %d is set", container.V) + } + }) + t.Run("filled number", func(t *testing.T) { + t.Parallel() + var container unmarshalableIntContainer + if err := yaml.Unmarshal([]byte(`value: 9`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.V != 9 { + t.Fatalf("expected 9, but %d is set", container.V) + } + }) +} + +type unmarshalablePtrIntContainer struct { + V *int `yaml:"value"` +} + +func TestUnmarshalablePtrInt(t *testing.T) { + t.Run("empty int", func(t *testing.T) { + t.Parallel() + var container unmarshalablePtrIntContainer + if err := yaml.Unmarshal([]byte(`value: 0`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.V == nil || *container.V != 0 { + t.Fatalf("expected 0, but %q is set", *container.V) + } + }) + + t.Run("null", func(t *testing.T) { + t.Parallel() + var container unmarshalablePtrIntContainer + if err := yaml.Unmarshal([]byte(`value: null`), &container); err != nil { + t.Fatalf("failed to unmarshal %v", err) + } + if container.V != (*int)(nil) { + t.Fatalf("expected nil, but %q is set", *container.V) + } + }) +} + +type literalContainer struct { + v string +} + +func (c *literalContainer) UnmarshalYAML(v []byte) error { + var lit string + if err := yaml.Unmarshal(v, &lit); err != nil { + return err + } + c.v = lit + return nil +} + +func TestDecode_Literal(t *testing.T) { + yml := `--- +value: | + { + "key": "value" + } +` + var v map[string]*literalContainer + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatalf("failed to unmarshal %+v", err) + } + if v["value"] == nil { + t.Fatal("failed to unmarshal literal with bytes unmarshaler") + } + if v["value"].v == "" { + t.Fatal("failed to unmarshal literal with bytes unmarshaler") + } +} + +func TestDecoder_UseOrderedMap(t *testing.T) { + yml := ` +a: b +c: d +e: + f: g + h: i +j: k +` + var v interface{} + if err := yaml.NewDecoder(strings.NewReader(yml), yaml.UseOrderedMap()).Decode(&v); err != nil { + t.Fatalf("%+v", err) + } + if _, ok := v.(yaml.MapSlice); !ok { + t.Fatalf("failed to convert to ordered map: %T", v) + } + bytes, err := yaml.Marshal(v) + if err != nil { + t.Fatalf("%+v", err) + } + if string(yml) != "\n"+string(bytes) { + t.Fatalf("expected:[%s] actual:[%s]", string(yml), "\n"+string(bytes)) + } +} + +func TestDecoder_Stream(t *testing.T) { + yml := ` +--- +a: b +c: d +--- +e: f +g: h +--- +i: j +k: l +` + dec := yaml.NewDecoder(strings.NewReader(yml)) + values := []map[string]string{} + for { + var v map[string]string + if err := dec.Decode(&v); err != nil { + if err == io.EOF { + break + } + t.Fatalf("%+v", err) + } + values = append(values, v) + } + if len(values) != 3 { + t.Fatal("failed to stream decoding") + } + if values[0]["a"] != "b" { + t.Fatal("failed to stream decoding") + } + if values[1]["e"] != "f" { + t.Fatal("failed to stream decoding") + } + if values[2]["i"] != "j" { + t.Fatal("failed to stream decoding") + } +} + +type unmarshalYAMLWithAliasString string + +func (v *unmarshalYAMLWithAliasString) UnmarshalYAML(b []byte) error { + var s string + if err := yaml.Unmarshal(b, &s); err != nil { + return err + } + *v = unmarshalYAMLWithAliasString(s) + return nil +} + +type unmarshalYAMLWithAliasMap map[string]interface{} + +func (v *unmarshalYAMLWithAliasMap) UnmarshalYAML(b []byte) error { + var m map[string]interface{} + if err := yaml.Unmarshal(b, &m); err != nil { + return err + } + *v = unmarshalYAMLWithAliasMap(m) + return nil +} + +func TestDecoder_UnmarshalYAMLWithAlias(t *testing.T) { + type value struct { + String unmarshalYAMLWithAliasString + Map unmarshalYAMLWithAliasMap + } + tests := []struct { + name string + yaml string + expectedValue value + err string + }{ + { + name: "ok", + yaml: ` +anchors: + w: &w "\"hello\" \"world\"" + map: &x + a: b + c: d + d: *w +string: *w +map: + <<: *x + e: f +`, + expectedValue: value{ + String: unmarshalYAMLWithAliasString(`"hello" "world"`), + Map: unmarshalYAMLWithAliasMap(map[string]interface{}{ + "a": "b", + "c": "d", + "d": `"hello" "world"`, + "e": "f", + }), + }, + }, + { + name: "unknown alias", + yaml: ` +anchors: + w: &w "\"hello\" \"world\"" + map: &x + a: b + c: d + d: *w +string: *y +map: + <<: *z + e: f +`, + err: "cannot find anchor by alias name y", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var v value + err := yaml.Unmarshal([]byte(test.yaml), &v) + + if test.err != "" { + if err == nil { + t.Fatal("expected to error") + } + if !strings.Contains(err.Error(), test.err) { + t.Fatalf("expected error message: %s to contain: %s", err.Error(), test.err) + } + } else { + if err != nil { + t.Fatalf("%+v", err) + } + if !reflect.DeepEqual(test.expectedValue, v) { + t.Fatalf("non matching values:\nexpected[%s]\ngot [%s]", test.expectedValue, v) + } + } + }) + } +} + +type unmarshalString string + +func (u *unmarshalString) UnmarshalYAML(b []byte) error { + *u = unmarshalString(string(b)) + return nil +} + +type unmarshalList struct { + v []map[string]unmarshalString +} + +func (u *unmarshalList) UnmarshalYAML(b []byte) error { + expected := ` + - b: c + d: | + hello + + hello + f: g + - h: i` + actual := "\n" + string(b) + if expected != actual { + return xerrors.Errorf("unexpected bytes: expected [%q] but got [%q]", expected, actual) + } + var v []map[string]unmarshalString + if err := yaml.Unmarshal(b, &v); err != nil { + return err + } + u.v = v + return nil +} + +func TestDecoder_UnmarshalBytesWithSeparatedList(t *testing.T) { + yml := ` +a: + - b: c + d: | + hello + + hello + f: g + - h: i +` + var v struct { + A unmarshalList + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatal(err) + } + if len(v.A.v) != 2 { + t.Fatalf("failed to unmarshal %+v", v) + } + if len(v.A.v[0]) != 3 { + t.Fatalf("failed to unmarshal %+v", v.A.v[0]) + } + if len(v.A.v[1]) != 1 { + t.Fatalf("failed to unmarshal %+v", v.A.v[1]) + } +} + +func TestDecoder_LiteralWithNewLine(t *testing.T) { + type A struct { + Node string `yaml:"b"` + LastNode string `yaml:"last"` + } + tests := []A{ + A{ + Node: "hello\nworld", + }, + A{ + Node: "hello\nworld\n", + }, + A{ + Node: "hello\nworld\n\n", + }, + A{ + LastNode: "hello\nworld", + }, + A{ + LastNode: "hello\nworld\n", + }, + A{ + LastNode: "hello\nworld\n\n", + }, + } + // struct(want) -> Marshal -> Unmarchal -> struct(got) + for _, want := range tests { + bytes, _ := yaml.Marshal(want) + got := A{} + if err := yaml.Unmarshal(bytes, &got); err != nil { + t.Fatal(err) + } + if want.Node != got.Node { + t.Fatalf("expected:%q but got %q", want.Node, got.Node) + } + if want.LastNode != got.LastNode { + t.Fatalf("expected:%q but got %q", want.LastNode, got.LastNode) + } + } +} + +func TestDecoder_TabCharacterAtRight(t *testing.T) { + yml := ` +- a: [2 , 2] + b: [2 , 2] + c: [2 , 2]` + var v []map[string][]int + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatal(err) + } + if len(v) != 1 { + t.Fatalf("failed to unmarshal %+v", v) + } + if len(v[0]) != 3 { + t.Fatalf("failed to unmarshal %+v", v) + } +} + +func TestDecoder_Canonical(t *testing.T) { + yml := ` +!!map { + ? !!str "explicit":!!str "entry", + ? !!str "implicit" : !!str "entry", + ? !!null "" : !!null "", +} +` + var v interface{} + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatalf("%+v", err) + } + m, ok := v.(map[string]interface{}) + if !ok { + t.Fatalf("failed to decode canonical yaml: %+v", v) + } + if m["explicit"] != "entry" { + t.Fatalf("failed to decode canonical yaml: %+v", m) + } + if m["implicit"] != "entry" { + t.Fatalf("failed to decode canonical yaml: %+v", m) + } + if m["null"] != nil { + t.Fatalf("failed to decode canonical yaml: %+v", m) + } +} + +func TestDecoder_DecodeFromFile(t *testing.T) { + yml := ` +a: b +c: d +` + file, err := parser.ParseBytes([]byte(yml), 0) + if err != nil { + t.Fatal(err) + } + var v map[string]string + if err := yaml.NewDecoder(file).Decode(&v); err != nil { + t.Fatal(err) + } + if len(v) != 2 { + t.Fatal("failed to decode from ast.File") + } + if v["a"] != "b" { + t.Fatal("failed to decode from ast.File") + } + if v["c"] != "d" { + t.Fatal("failed to decode from ast.File") + } +} + +func TestDecoder_DecodeWithNode(t *testing.T) { + t.Run("abstract node", func(t *testing.T) { + type T struct { + Text ast.Node `yaml:"text"` + } + var v T + if err := yaml.Unmarshal([]byte(`text: hello`), &v); err != nil { + t.Fatalf("%+v", err) + } + expected := "hello" + got := v.Text.String() + if expected != got { + t.Fatalf("failed to decode to ast.Node: expected %s but got %s", expected, got) + } + }) + t.Run("concrete node", func(t *testing.T) { + type T struct { + Text *ast.StringNode `yaml:"text"` + } + var v T + if err := yaml.Unmarshal([]byte(`text: hello`), &v); err != nil { + t.Fatalf("%+v", err) + } + expected := "hello" + got := v.Text.String() + if expected != got { + t.Fatalf("failed to decode to ast.Node: expected %s but got %s", expected, got) + } + }) +} + +func TestRoundtripAnchorAlias(t *testing.T) { + t.Run("irreversible", func(t *testing.T) { + type foo struct { + K1 string + K2 string + } + + type bar struct { + K1 string + K3 string + } + + type doc struct { + Foo foo + Bar bar + } + yml := ` +foo: + <<: &test-anchor + k1: "One" + k2: "Two" + +bar: + <<: *test-anchor + k3: "Three" +` + var v doc + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatalf("%+v", err) + } + bytes, err := yaml.Marshal(v) + if err != nil { + t.Fatalf("%+v", err) + } + expected := ` +foo: + k1: One + k2: Two +bar: + k1: One + k3: Three +` + got := "\n" + string(bytes) + if expected != got { + t.Fatalf("expected:[%s] but got [%s]", expected, got) + } + }) + t.Run("reversible", func(t *testing.T) { + type TestAnchor struct { + K1 string + } + type foo struct { + *TestAnchor `yaml:",inline,alias"` + K2 string + } + type bar struct { + *TestAnchor `yaml:",inline,alias"` + K3 string + } + type doc struct { + TestAnchor *TestAnchor `yaml:"test-anchor,anchor"` + Foo foo + Bar bar + } + yml := ` +test-anchor: &test-anchor + k1: One +foo: + <<: *test-anchor + k2: Two +bar: + <<: *test-anchor + k3: Three +` + var v doc + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatalf("%+v", err) + } + bytes, err := yaml.Marshal(v) + if err != nil { + t.Fatalf("%+v", err) + } + got := "\n" + string(bytes) + if yml != got { + t.Fatalf("expected:[%s] but got [%s]", yml, got) + } + }) +} + +func TestUnmarshalMapSliceParallel(t *testing.T) { + content := ` +steps: + req0: + desc: Get /users/1 + req: + /users/1: + get: nil + test: | + current.res.status == 200 + req1: + desc: Get /private + req: + /private: + get: nil + test: | + current.res.status == 403 + req2: + desc: Get /users + req: + /users: + get: nil + test: | + current.res.status == 200 +` + type mappedSteps struct { + Steps yaml.MapSlice `yaml:"steps,omitempty"` + } + for i := 0; i < 100; i++ { + t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) { + t.Parallel() + for i := 0; i < 10; i++ { + m := mappedSteps{ + Steps: yaml.MapSlice{}, + } + if err := yaml.Unmarshal([]byte(content), &m); err != nil { + t.Fatal(err) + } + for _, s := range m.Steps { + _, ok := s.Value.(map[string]interface{}) + if !ok { + t.Fatal("unexpected error") + } + } + } + }) + } +} + +func TestSameNameInineStruct(t *testing.T) { + type X struct { + X float64 `yaml:"x"` + } + + type T struct { + X `yaml:",inline"` + } + + var v T + if err := yaml.Unmarshal([]byte(`x: 0.7`), &v); err != nil { + t.Fatal(err) + } + if fmt.Sprint(v.X.X) != "0.7" { + t.Fatalf("failed to decode") + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/encode.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/encode.go new file mode 100644 index 0000000..3b9b298 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/encode.go @@ -0,0 +1,875 @@ +package yaml + +import ( + "context" + "encoding" + "fmt" + "io" + "math" + "reflect" + "sort" + "strconv" + "strings" + "time" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/parser" + "github.com/goccy/go-yaml/printer" + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +const ( + // DefaultIndentSpaces default number of space for indent + DefaultIndentSpaces = 2 +) + +// Encoder writes YAML values to an output stream. +type Encoder struct { + writer io.Writer + opts []EncodeOption + indent int + indentSequence bool + singleQuote bool + isFlowStyle bool + isJSONStyle bool + useJSONMarshaler bool + anchorCallback func(*ast.AnchorNode, interface{}) error + anchorPtrToNameMap map[uintptr]string + customMarshalerMap map[reflect.Type]func(interface{}) ([]byte, error) + useLiteralStyleIfMultiline bool + commentMap map[*Path][]*Comment + written bool + + line int + column int + offset int + indentNum int + indentLevel int +} + +// NewEncoder returns a new encoder that writes to w. +// The Encoder should be closed after use to flush all data to w. +func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder { + return &Encoder{ + writer: w, + opts: opts, + indent: DefaultIndentSpaces, + anchorPtrToNameMap: map[uintptr]string{}, + customMarshalerMap: map[reflect.Type]func(interface{}) ([]byte, error){}, + line: 1, + column: 1, + offset: 0, + } +} + +// Close closes the encoder by writing any remaining data. +// It does not write a stream terminating string "...". +func (e *Encoder) Close() error { + return nil +} + +// Encode writes the YAML encoding of v to the stream. +// If multiple items are encoded to the stream, +// the second and subsequent document will be preceded with a "---" document separator, +// but the first will not. +// +// See the documentation for Marshal for details about the conversion of Go values to YAML. +func (e *Encoder) Encode(v interface{}) error { + return e.EncodeContext(context.Background(), v) +} + +// EncodeContext writes the YAML encoding of v to the stream with context.Context. +func (e *Encoder) EncodeContext(ctx context.Context, v interface{}) error { + node, err := e.EncodeToNodeContext(ctx, v) + if err != nil { + return errors.Wrapf(err, "failed to encode to node") + } + if err := e.setCommentByCommentMap(node); err != nil { + return errors.Wrapf(err, "failed to set comment by comment map") + } + if !e.written { + e.written = true + } else { + // write document separator + e.writer.Write([]byte("---\n")) + } + var p printer.Printer + e.writer.Write(p.PrintNode(node)) + return nil +} + +// EncodeToNode convert v to ast.Node. +func (e *Encoder) EncodeToNode(v interface{}) (ast.Node, error) { + return e.EncodeToNodeContext(context.Background(), v) +} + +// EncodeToNodeContext convert v to ast.Node with context.Context. +func (e *Encoder) EncodeToNodeContext(ctx context.Context, v interface{}) (ast.Node, error) { + for _, opt := range e.opts { + if err := opt(e); err != nil { + return nil, errors.Wrapf(err, "failed to run option for encoder") + } + } + node, err := e.encodeValue(ctx, reflect.ValueOf(v), 1) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode value") + } + return node, nil +} + +func (e *Encoder) setCommentByCommentMap(node ast.Node) error { + if e.commentMap == nil { + return nil + } + for path, comments := range e.commentMap { + n, err := path.FilterNode(node) + if err != nil { + return errors.Wrapf(err, "failed to filter node") + } + if n == nil { + continue + } + for _, comment := range comments { + commentTokens := []*token.Token{} + for _, text := range comment.Texts { + commentTokens = append(commentTokens, token.New(text, text, nil)) + } + commentGroup := ast.CommentGroup(commentTokens) + switch comment.Position { + case CommentHeadPosition: + if err := e.setHeadComment(node, n, commentGroup); err != nil { + return errors.Wrapf(err, "failed to set head comment") + } + case CommentLinePosition: + if err := e.setLineComment(node, n, commentGroup); err != nil { + return errors.Wrapf(err, "failed to set line comment") + } + case CommentFootPosition: + if err := e.setFootComment(node, n, commentGroup); err != nil { + return errors.Wrapf(err, "failed to set foot comment") + } + default: + return ErrUnknownCommentPositionType + } + } + } + return nil +} + +func (e *Encoder) setHeadComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { + parent := ast.Parent(node, filtered) + if parent == nil { + return ErrUnsupportedHeadPositionType(node) + } + switch p := parent.(type) { + case *ast.MappingValueNode: + if err := p.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + case *ast.MappingNode: + if err := p.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + case *ast.SequenceNode: + if len(p.ValueHeadComments) == 0 { + p.ValueHeadComments = make([]*ast.CommentGroupNode, len(p.Values)) + } + var foundIdx int + for idx, v := range p.Values { + if v == filtered { + foundIdx = idx + break + } + } + p.ValueHeadComments[foundIdx] = comment + default: + return ErrUnsupportedHeadPositionType(node) + } + return nil +} + +func (e *Encoder) setLineComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { + switch filtered.(type) { + case *ast.MappingValueNode, *ast.SequenceNode: + // Line comment cannot be set for mapping value node. + // It should probably be set for the parent map node + if err := e.setLineCommentToParentMapNode(node, filtered, comment); err != nil { + return errors.Wrapf(err, "failed to set line comment to parent node") + } + default: + if err := filtered.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + } + return nil +} + +func (e *Encoder) setLineCommentToParentMapNode(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { + parent := ast.Parent(node, filtered) + if parent == nil { + return ErrUnsupportedLinePositionType(node) + } + switch p := parent.(type) { + case *ast.MappingValueNode: + if err := p.Key.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + case *ast.MappingNode: + if err := p.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + default: + return ErrUnsupportedLinePositionType(parent) + } + return nil +} + +func (e *Encoder) setFootComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { + parent := ast.Parent(node, filtered) + if parent == nil { + return ErrUnsupportedFootPositionType(node) + } + switch n := parent.(type) { + case *ast.MappingValueNode: + n.FootComment = comment + case *ast.MappingNode: + n.FootComment = comment + case *ast.SequenceNode: + n.FootComment = comment + default: + return ErrUnsupportedFootPositionType(n) + } + return nil +} + +func (e *Encoder) encodeDocument(doc []byte) (ast.Node, error) { + f, err := parser.ParseBytes(doc, 0) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse yaml") + } + for _, docNode := range f.Docs { + if docNode.Body != nil { + return docNode.Body, nil + } + } + return nil, nil +} + +func (e *Encoder) isInvalidValue(v reflect.Value) bool { + if !v.IsValid() { + return true + } + kind := v.Type().Kind() + if kind == reflect.Ptr && v.IsNil() { + return true + } + if kind == reflect.Interface && v.IsNil() { + return true + } + return false +} + +type jsonMarshaler interface { + MarshalJSON() ([]byte, error) +} + +func (e *Encoder) existsTypeInCustomMarshalerMap(t reflect.Type) bool { + if _, exists := e.customMarshalerMap[t]; exists { + return true + } + + globalCustomMarshalerMu.Lock() + defer globalCustomMarshalerMu.Unlock() + if _, exists := globalCustomMarshalerMap[t]; exists { + return true + } + return false +} + +func (e *Encoder) marshalerFromCustomMarshalerMap(t reflect.Type) (func(interface{}) ([]byte, error), bool) { + if marshaler, exists := e.customMarshalerMap[t]; exists { + return marshaler, exists + } + + globalCustomMarshalerMu.Lock() + defer globalCustomMarshalerMu.Unlock() + if marshaler, exists := globalCustomMarshalerMap[t]; exists { + return marshaler, exists + } + return nil, false +} + +func (e *Encoder) canEncodeByMarshaler(v reflect.Value) bool { + if !v.CanInterface() { + return false + } + if e.existsTypeInCustomMarshalerMap(v.Type()) { + return true + } + iface := v.Interface() + switch iface.(type) { + case BytesMarshalerContext: + return true + case BytesMarshaler: + return true + case InterfaceMarshalerContext: + return true + case InterfaceMarshaler: + return true + case time.Time: + return true + case time.Duration: + return true + case encoding.TextMarshaler: + return true + case jsonMarshaler: + return e.useJSONMarshaler + } + return false +} + +func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column int) (ast.Node, error) { + iface := v.Interface() + + if marshaler, exists := e.marshalerFromCustomMarshalerMap(v.Type()); exists { + doc, err := marshaler(iface) + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + + if marshaler, ok := iface.(BytesMarshalerContext); ok { + doc, err := marshaler.MarshalYAML(ctx) + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + + if marshaler, ok := iface.(BytesMarshaler); ok { + doc, err := marshaler.MarshalYAML() + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + + if marshaler, ok := iface.(InterfaceMarshalerContext); ok { + marshalV, err := marshaler.MarshalYAML(ctx) + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) + } + + if marshaler, ok := iface.(InterfaceMarshaler); ok { + marshalV, err := marshaler.MarshalYAML() + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) + } + + if t, ok := iface.(time.Time); ok { + return e.encodeTime(t, column), nil + } + + if t, ok := iface.(time.Duration); ok { + return e.encodeDuration(t, column), nil + } + + if marshaler, ok := iface.(encoding.TextMarshaler); ok { + doc, err := marshaler.MarshalText() + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalText") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + + if e.useJSONMarshaler { + if marshaler, ok := iface.(jsonMarshaler); ok { + jsonBytes, err := marshaler.MarshalJSON() + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalJSON") + } + doc, err := JSONToYAML(jsonBytes) + if err != nil { + return nil, errors.Wrapf(err, "failed to convert json to yaml") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + } + + return nil, xerrors.Errorf("does not implemented Marshaler") +} + +func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) (ast.Node, error) { + if e.isInvalidValue(v) { + return e.encodeNil(), nil + } + if e.canEncodeByMarshaler(v) { + node, err := e.encodeByMarshaler(ctx, v, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode by marshaler") + } + return node, nil + } + switch v.Type().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return e.encodeInt(v.Int()), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return e.encodeUint(v.Uint()), nil + case reflect.Float32: + return e.encodeFloat(v.Float(), 32), nil + case reflect.Float64: + return e.encodeFloat(v.Float(), 64), nil + case reflect.Ptr: + anchorName := e.anchorPtrToNameMap[v.Pointer()] + if anchorName != "" { + aliasName := anchorName + alias := ast.Alias(token.New("*", "*", e.pos(column))) + alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) + return alias, nil + } + return e.encodeValue(ctx, v.Elem(), column) + case reflect.Interface: + return e.encodeValue(ctx, v.Elem(), column) + case reflect.String: + return e.encodeString(v.String(), column), nil + case reflect.Bool: + return e.encodeBool(v.Bool()), nil + case reflect.Slice: + if mapSlice, ok := v.Interface().(MapSlice); ok { + return e.encodeMapSlice(ctx, mapSlice, column) + } + return e.encodeSlice(ctx, v) + case reflect.Array: + return e.encodeArray(ctx, v) + case reflect.Struct: + if v.CanInterface() { + if mapItem, ok := v.Interface().(MapItem); ok { + return e.encodeMapItem(ctx, mapItem, column) + } + if t, ok := v.Interface().(time.Time); ok { + return e.encodeTime(t, column), nil + } + } + return e.encodeStruct(ctx, v, column) + case reflect.Map: + return e.encodeMap(ctx, v, column), nil + default: + return nil, xerrors.Errorf("unknown value type %s", v.Type().String()) + } +} + +func (e *Encoder) pos(column int) *token.Position { + return &token.Position{ + Line: e.line, + Column: column, + Offset: e.offset, + IndentNum: e.indentNum, + IndentLevel: e.indentLevel, + } +} + +func (e *Encoder) encodeNil() *ast.NullNode { + value := "null" + return ast.Null(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) encodeInt(v int64) *ast.IntegerNode { + value := fmt.Sprint(v) + return ast.Integer(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) encodeUint(v uint64) *ast.IntegerNode { + value := fmt.Sprint(v) + return ast.Integer(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) encodeFloat(v float64, bitSize int) ast.Node { + if v == math.Inf(0) { + value := ".inf" + return ast.Infinity(token.New(value, value, e.pos(e.column))) + } else if v == math.Inf(-1) { + value := "-.inf" + return ast.Infinity(token.New(value, value, e.pos(e.column))) + } else if math.IsNaN(v) { + value := ".nan" + return ast.Nan(token.New(value, value, e.pos(e.column))) + } + value := strconv.FormatFloat(v, 'g', -1, bitSize) + if !strings.Contains(value, ".") && !strings.Contains(value, "e") { + // append x.0 suffix to keep float value context + value = fmt.Sprintf("%s.0", value) + } + return ast.Float(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) isNeedQuoted(v string) bool { + if e.isJSONStyle { + return true + } + if e.useLiteralStyleIfMultiline && strings.ContainsAny(v, "\n\r") { + return false + } + if e.isFlowStyle && strings.ContainsAny(v, `]},'"`) { + return true + } + if token.IsNeedQuoted(v) { + return true + } + return false +} + +func (e *Encoder) encodeString(v string, column int) *ast.StringNode { + if e.isNeedQuoted(v) { + if e.singleQuote { + v = quoteWith(v, '\'') + } else { + v = strconv.Quote(v) + } + } + return ast.String(token.New(v, v, e.pos(column))) +} + +func (e *Encoder) encodeBool(v bool) *ast.BoolNode { + value := fmt.Sprint(v) + return ast.Bool(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { + if e.indentSequence { + e.column += e.indent + } + column := e.column + sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) + for i := 0; i < value.Len(); i++ { + node, err := e.encodeValue(ctx, value.Index(i), column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode value for slice") + } + sequence.Values = append(sequence.Values, node) + } + if e.indentSequence { + e.column -= e.indent + } + return sequence, nil +} + +func (e *Encoder) encodeArray(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { + if e.indentSequence { + e.column += e.indent + } + column := e.column + sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) + for i := 0; i < value.Len(); i++ { + node, err := e.encodeValue(ctx, value.Index(i), column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode value for array") + } + sequence.Values = append(sequence.Values, node) + } + if e.indentSequence { + e.column -= e.indent + } + return sequence, nil +} + +func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) (*ast.MappingValueNode, error) { + k := reflect.ValueOf(item.Key) + v := reflect.ValueOf(item.Value) + value, err := e.encodeValue(ctx, v, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode MapItem") + } + if e.isMapNode(value) { + value.AddColumn(e.indent) + } + return ast.MappingValue( + token.New("", "", e.pos(column)), + e.encodeString(k.Interface().(string), column), + value, + ), nil +} + +func (e *Encoder) encodeMapSlice(ctx context.Context, value MapSlice, column int) (*ast.MappingNode, error) { + node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) + for _, item := range value { + value, err := e.encodeMapItem(ctx, item, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode MapItem for MapSlice") + } + node.Values = append(node.Values, value) + } + return node, nil +} + +func (e *Encoder) isMapNode(node ast.Node) bool { + _, ok := node.(ast.MapNode) + return ok +} + +func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) ast.Node { + node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) + keys := make([]interface{}, len(value.MapKeys())) + for i, k := range value.MapKeys() { + keys[i] = k.Interface() + } + sort.Slice(keys, func(i, j int) bool { + return fmt.Sprint(keys[i]) < fmt.Sprint(keys[j]) + }) + for _, key := range keys { + k := reflect.ValueOf(key) + v := value.MapIndex(k) + value, err := e.encodeValue(ctx, v, column) + if err != nil { + return nil + } + if e.isMapNode(value) { + value.AddColumn(e.indent) + } + node.Values = append(node.Values, ast.MappingValue( + nil, + e.encodeString(fmt.Sprint(key), column), + value, + )) + } + return node +} + +// IsZeroer is used to check whether an object is zero to determine +// whether it should be omitted when marshaling with the omitempty flag. +// One notable implementation is time.Time. +type IsZeroer interface { + IsZero() bool +} + +func (e *Encoder) isZeroValue(v reflect.Value) bool { + kind := v.Kind() + if z, ok := v.Interface().(IsZeroer); ok { + if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { + return true + } + return z.IsZero() + } + switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // private field + } + if !e.isZeroValue(v.Field(i)) { + return false + } + } + return true + } + return false +} + +func (e *Encoder) encodeTime(v time.Time, column int) *ast.StringNode { + value := v.Format(time.RFC3339Nano) + if e.isJSONStyle { + value = strconv.Quote(value) + } + return ast.String(token.New(value, value, e.pos(column))) +} + +func (e *Encoder) encodeDuration(v time.Duration, column int) *ast.StringNode { + value := v.String() + if e.isJSONStyle { + value = strconv.Quote(value) + } + return ast.String(token.New(value, value, e.pos(column))) +} + +func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue reflect.Value, column int) (*ast.AnchorNode, error) { + anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) + anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) + anchorNode.Value = value + if e.anchorCallback != nil { + if err := e.anchorCallback(anchorNode, fieldValue.Interface()); err != nil { + return nil, errors.Wrapf(err, "failed to marshal anchor") + } + if snode, ok := anchorNode.Name.(*ast.StringNode); ok { + anchorName = snode.Value + } + } + if fieldValue.Kind() == reflect.Ptr { + e.anchorPtrToNameMap[fieldValue.Pointer()] = anchorName + } + return anchorNode, nil +} + +func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column int) (ast.Node, error) { + node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) + structType := value.Type() + structFieldMap, err := structFieldMap(structType) + if err != nil { + return nil, errors.Wrapf(err, "failed to get struct field map") + } + hasInlineAnchorField := false + var inlineAnchorValue reflect.Value + for i := 0; i < value.NumField(); i++ { + field := structType.Field(i) + if isIgnoredStructField(field) { + continue + } + fieldValue := value.FieldByName(field.Name) + structField := structFieldMap[field.Name] + if structField.IsOmitEmpty && e.isZeroValue(fieldValue) { + // omit encoding + continue + } + ve := e + if !e.isFlowStyle && structField.IsFlow { + ve = &Encoder{} + *ve = *e + ve.isFlowStyle = true + } + value, err := ve.encodeValue(ctx, fieldValue, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode value") + } + if e.isMapNode(value) { + value.AddColumn(e.indent) + } + var key ast.MapKeyNode = e.encodeString(structField.RenderName, column) + switch { + case structField.AnchorName != "": + anchorNode, err := e.encodeAnchor(structField.AnchorName, value, fieldValue, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode anchor") + } + value = anchorNode + case structField.IsAutoAlias: + if fieldValue.Kind() != reflect.Ptr { + return nil, xerrors.Errorf( + "%s in struct is not pointer type. but required automatically alias detection", + structField.FieldName, + ) + } + anchorName := e.anchorPtrToNameMap[fieldValue.Pointer()] + if anchorName == "" { + return nil, xerrors.Errorf( + "cannot find anchor name from pointer address for automatically alias detection", + ) + } + aliasName := anchorName + alias := ast.Alias(token.New("*", "*", e.pos(column))) + alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) + value = alias + if structField.IsInline { + // if both used alias and inline, output `<<: *alias` + key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) + } + case structField.AliasName != "": + aliasName := structField.AliasName + alias := ast.Alias(token.New("*", "*", e.pos(column))) + alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) + value = alias + if structField.IsInline { + // if both used alias and inline, output `<<: *alias` + key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) + } + case structField.IsInline: + isAutoAnchor := structField.IsAutoAnchor + if !hasInlineAnchorField { + hasInlineAnchorField = isAutoAnchor + } + if isAutoAnchor { + inlineAnchorValue = fieldValue + } + mapNode, ok := value.(ast.MapNode) + if !ok { + // if an inline field is null, skip encoding it + if _, ok := value.(*ast.NullNode); ok { + continue + } + return nil, xerrors.Errorf("inline value is must be map or struct type") + } + mapIter := mapNode.MapRange() + for mapIter.Next() { + key := mapIter.Key() + value := mapIter.Value() + keyName := key.GetToken().Value + if structFieldMap.isIncludedRenderName(keyName) { + // if declared same key name, skip encoding this field + continue + } + key.AddColumn(-e.indent) + value.AddColumn(-e.indent) + node.Values = append(node.Values, ast.MappingValue(nil, key, value)) + } + continue + case structField.IsAutoAnchor: + anchorNode, err := e.encodeAnchor(structField.RenderName, value, fieldValue, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode anchor") + } + value = anchorNode + } + node.Values = append(node.Values, ast.MappingValue(nil, key, value)) + } + if hasInlineAnchorField { + node.AddColumn(e.indent) + anchorName := "anchor" + anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) + anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) + anchorNode.Value = node + if e.anchorCallback != nil { + if err := e.anchorCallback(anchorNode, value.Addr().Interface()); err != nil { + return nil, errors.Wrapf(err, "failed to marshal anchor") + } + if snode, ok := anchorNode.Name.(*ast.StringNode); ok { + anchorName = snode.Value + } + } + if inlineAnchorValue.Kind() == reflect.Ptr { + e.anchorPtrToNameMap[inlineAnchorValue.Pointer()] = anchorName + } + return anchorNode, nil + } + return node, nil +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/encode_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/encode_test.go new file mode 100644 index 0000000..3ff6f1c --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/encode_test.go @@ -0,0 +1,1632 @@ +package yaml_test + +import ( + "bytes" + "context" + "fmt" + "math" + "reflect" + "strconv" + "testing" + "time" + + "github.com/goccy/go-yaml/parser" + + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/ast" +) + +var zero = 0 +var emptyStr = "" + +func TestEncoder(t *testing.T) { + tests := []struct { + source string + value interface{} + options []yaml.EncodeOption + }{ + { + "null\n", + (*struct{})(nil), + nil, + }, + { + "v: hi\n", + map[string]string{"v": "hi"}, + nil, + }, + { + "v: \"true\"\n", + map[string]string{"v": "true"}, + nil, + }, + { + "v: \"false\"\n", + map[string]string{"v": "false"}, + nil, + }, + { + "v: true\n", + map[string]interface{}{"v": true}, + nil, + }, + { + "v: false\n", + map[string]bool{"v": false}, + nil, + }, + { + "v: 10\n", + map[string]int{"v": 10}, + nil, + }, + { + "v: -10\n", + map[string]int{"v": -10}, + nil, + }, + { + "v: 4294967296\n", + map[string]int64{"v": int64(4294967296)}, + nil, + }, + { + "v: 0.1\n", + map[string]interface{}{"v": 0.1}, + nil, + }, + { + "v: 0.99\n", + map[string]float32{"v": 0.99}, + nil, + }, + { + "v: 0.123456789\n", + map[string]float64{"v": 0.123456789}, + nil, + }, + { + "v: -0.1\n", + map[string]float64{"v": -0.1}, + nil, + }, + { + "v: 1.0\n", + map[string]float64{"v": 1.0}, + nil, + }, + { + "v: 1e+06\n", + map[string]float64{"v": 1000000}, + nil, + }, + { + "v: .inf\n", + map[string]interface{}{"v": math.Inf(0)}, + nil, + }, + { + "v: -.inf\n", + map[string]interface{}{"v": math.Inf(-1)}, + nil, + }, + { + "v: .nan\n", + map[string]interface{}{"v": math.NaN()}, + nil, + }, + { + "v: null\n", + map[string]interface{}{"v": nil}, + nil, + }, + { + "v: \"\"\n", + map[string]string{"v": ""}, + nil, + }, + { + "v:\n- A\n- B\n", + map[string][]string{"v": {"A", "B"}}, + nil, + }, + { + "v:\n - A\n - B\n", + map[string][]string{"v": {"A", "B"}}, + []yaml.EncodeOption{ + yaml.IndentSequence(true), + }, + }, + { + "v:\n- A\n- B\n", + map[string][2]string{"v": {"A", "B"}}, + nil, + }, + { + "v:\n - A\n - B\n", + map[string][2]string{"v": {"A", "B"}}, + []yaml.EncodeOption{ + yaml.IndentSequence(true), + }, + }, + { + "a: -\n", + map[string]string{"a": "-"}, + nil, + }, + { + "123\n", + 123, + nil, + }, + { + "hello: world\n", + map[string]string{"hello": "world"}, + nil, + }, + { + "hello: |\n hello\n world\n", + map[string]string{"hello": "hello\nworld\n"}, + nil, + }, + { + "hello: |-\n hello\n world\n", + map[string]string{"hello": "hello\nworld"}, + nil, + }, + { + "hello: |+\n hello\n world\n\n", + map[string]string{"hello": "hello\nworld\n\n"}, + nil, + }, + { + "hello:\n hello: |\n hello\n world\n", + map[string]map[string]string{"hello": {"hello": "hello\nworld\n"}}, + nil, + }, + { + "hello: |\r hello\r world\n", + map[string]string{"hello": "hello\rworld\r"}, + nil, + }, + { + "hello: |\r\n hello\r\n world\n", + map[string]string{"hello": "hello\r\nworld\r\n"}, + nil, + }, + { + "v: |-\n username: hello\n password: hello123\n", + map[string]interface{}{"v": "username: hello\npassword: hello123"}, + []yaml.EncodeOption{ + yaml.UseLiteralStyleIfMultiline(true), + }, + }, + { + "v: |-\n # comment\n username: hello\n password: hello123\n", + map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, + []yaml.EncodeOption{ + yaml.UseLiteralStyleIfMultiline(true), + }, + }, + { + "v: \"# comment\\nusername: hello\\npassword: hello123\"\n", + map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, + []yaml.EncodeOption{ + yaml.UseLiteralStyleIfMultiline(false), + }, + }, + { + "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", + map[string]interface{}{ + "v": []interface{}{ + "A", + 1, + map[string][]int{ + "B": {2, 3}, + }, + }, + }, + nil, + }, + { + "v:\n - A\n - 1\n - B:\n - 2\n - 3\n - 2\n", + map[string]interface{}{ + "v": []interface{}{ + "A", + 1, + map[string][]int{ + "B": {2, 3}, + }, + 2, + }, + }, + []yaml.EncodeOption{ + yaml.IndentSequence(true), + }, + }, + { + "a:\n b: c\n", + map[string]interface{}{ + "a": map[string]string{ + "b": "c", + }, + }, + nil, + }, + { + "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", + map[string]string{ + "t2": "2018-01-09T10:40:47Z", + "t4": "2098-01-09T10:40:47Z", + }, + nil, + }, + { + "a:\n b: c\n d: e\n", + map[string]interface{}{ + "a": map[string]string{ + "b": "c", + "d": "e", + }, + }, + nil, + }, + { + "a: 3s\n", + map[string]string{ + "a": "3s", + }, + nil, + }, + { + "a: \n", + map[string]string{"a": ""}, + nil, + }, + { + "a: \"1:1\"\n", + map[string]string{"a": "1:1"}, + nil, + }, + { + "a: 1.2.3.4\n", + map[string]string{"a": "1.2.3.4"}, + nil, + }, + { + "a: \"b: c\"\n", + map[string]string{"a": "b: c"}, + nil, + }, + { + "a: \"Hello #comment\"\n", + map[string]string{"a": "Hello #comment"}, + nil, + }, + { + "a: \" b\"\n", + map[string]string{"a": " b"}, + nil, + }, + { + "a: \"b \"\n", + map[string]string{"a": "b "}, + nil, + }, + { + "a: \" b \"\n", + map[string]string{"a": " b "}, + nil, + }, + { + "a: 100.5\n", + map[string]interface{}{ + "a": 100.5, + }, + nil, + }, + { + "a: \"\\\\0\"\n", + map[string]string{"a": "\\0"}, + nil, + }, + { + "a: 1\nb: 2\nc: 3\nd: 4\nsub:\n e: 5\n", + map[string]interface{}{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "sub": map[string]int{ + "e": 5, + }, + }, + nil, + }, + { + "a: 1\nb: []\n", + struct { + A int + B []string + }{ + 1, ([]string)(nil), + }, + nil, + }, + { + "a: 1\nb: []\n", + struct { + A int + B []string + }{ + 1, []string{}, + }, + nil, + }, + { + "a: {}\n", + struct { + A map[string]interface{} + }{ + map[string]interface{}{}, + }, + nil, + }, + { + "a: b\nc: d\n", + struct { + A string + C string `yaml:"c"` + }{ + "b", "d", + }, + nil, + }, + { + "a: 1\n", + struct { + A int + B int `yaml:"-"` + }{ + 1, 0, + }, + nil, + }, + { + "a: \"\"\n", + struct { + A string + }{ + "", + }, + nil, + }, + { + "a: null\n", + struct { + A *string + }{ + nil, + }, + nil, + }, + { + "a: \"\"\n", + struct { + A *string + }{ + &emptyStr, + }, + nil, + }, + { + "a: null\n", + struct { + A *int + }{ + nil, + }, + nil, + }, + { + "a: 0\n", + struct { + A *int + }{ + &zero, + }, + nil, + }, + + // Conditional flag + { + "a: 1\n", + struct { + A int `yaml:"a,omitempty"` + B int `yaml:"b,omitempty"` + }{1, 0}, + nil, + }, + { + "{}\n", + struct { + A int `yaml:"a,omitempty"` + B int `yaml:"b,omitempty"` + }{0, 0}, + nil, + }, + + { + "a:\n \"y\": \"\"\n", + struct { + A *struct { + X string `yaml:"x,omitempty"` + Y string + } + }{&struct { + X string `yaml:"x,omitempty"` + Y string + }{}}, + nil, + }, + + { + "a: {}\n", + struct { + A *struct { + X string `yaml:"x,omitempty"` + Y string `yaml:"y,omitempty"` + } + }{&struct { + X string `yaml:"x,omitempty"` + Y string `yaml:"y,omitempty"` + }{}}, + nil, + }, + + { + "a: {x: 1}\n", + struct { + A *struct{ X, y int } `yaml:"a,omitempty,flow"` + }{&struct{ X, y int }{1, 2}}, + nil, + }, + + { + "{}\n", + struct { + A *struct{ X, y int } `yaml:"a,omitempty,flow"` + }{nil}, + nil, + }, + + { + "a: {x: 0}\n", + struct { + A *struct{ X, y int } `yaml:"a,omitempty,flow"` + }{&struct{ X, y int }{}}, + nil, + }, + + { + "a: {x: 1}\n", + struct { + A struct{ X, y int } `yaml:"a,omitempty,flow"` + }{struct{ X, y int }{1, 2}}, + nil, + }, + { + "{}\n", + struct { + A struct{ X, y int } `yaml:"a,omitempty,flow"` + }{struct{ X, y int }{0, 1}}, + nil, + }, + { + "a: 1.0\n", + struct { + A float64 `yaml:"a,omitempty"` + B float64 `yaml:"b,omitempty"` + }{1, 0}, + nil, + }, + { + "a: 1\n", + struct { + A int + B []string `yaml:"b,omitempty"` + }{ + 1, []string{}, + }, + nil, + }, + + // Flow flag + { + "a: [1, 2]\n", + struct { + A []int `yaml:"a,flow"` + }{[]int{1, 2}}, + nil, + }, + { + "a: {b: c, d: e}\n", + &struct { + A map[string]string `yaml:"a,flow"` + }{map[string]string{"b": "c", "d": "e"}}, + nil, + }, + { + "a: {b: c, d: e}\n", + struct { + A struct { + B, D string + } `yaml:"a,flow"` + }{struct{ B, D string }{"c", "e"}}, + nil, + }, + // Quoting in flow mode + { + `a: [b, "c,d", e]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c,d", "e"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + { + `a: [b, "c]", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c]", "d"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + { + `a: [b, "c}", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c}", "d"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + { + `a: [b, "c\"", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", `c"`, "d"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + { + `a: [b, "c'", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c'", "d"}}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + // No quoting in non-flow mode + { + "a:\n- b\n- c,d\n- e\n", + struct { + A []string `yaml:"a"` + }{[]string{"b", "c,d", "e"}}, + nil, + }, + { + `a: [b, "c]", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c]", "d"}}, + nil, + }, + { + `a: [b, "c}", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c}", "d"}}, + nil, + }, + { + `a: [b, "c\"", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", `c"`, "d"}}, + nil, + }, + { + `a: [b, "c'", d]` + "\n", + struct { + A []string `yaml:"a,flow"` + }{[]string{"b", "c'", "d"}}, + nil, + }, + + // Multi bytes + { + "v: あいうえお\nv2: かきくけこ\n", + map[string]string{"v": "あいうえお", "v2": "かきくけこ"}, + nil, + }, + + // time value + { + "v: 0001-01-01T00:00:00Z\n", + map[string]time.Time{"v": time.Time{}}, + nil, + }, + { + "v: 0001-01-01T00:00:00Z\n", + map[string]*time.Time{"v": &time.Time{}}, + nil, + }, + { + "v: null\n", + map[string]*time.Time{"v": nil}, + nil, + }, + { + "v: 30s\n", + map[string]time.Duration{"v": 30 * time.Second}, + nil, + }, + // Quote style + { + `v: '\'a\'b'` + "\n", + map[string]string{"v": `'a'b`}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(true), + }, + }, + { + `v: "'a'b"` + "\n", + map[string]string{"v": `'a'b`}, + []yaml.EncodeOption{ + yaml.UseSingleQuote(false), + }, + }, + } + for _, test := range tests { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf, test.options...) + if err := enc.Encode(test.value); err != nil { + t.Fatalf("%+v", err) + } + if test.source != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", test.source, buf.String()) + } + } +} + +func TestEncodeStructIncludeMap(t *testing.T) { + type U struct { + M map[string]string + } + type T struct { + A U + } + bytes, err := yaml.Marshal(T{ + A: U{ + M: map[string]string{"x": "y"}, + }, + }) + if err != nil { + t.Fatalf("%+v", err) + } + expect := "a:\n m:\n x: \"y\"\n" + actual := string(bytes) + if actual != expect { + t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual) + } +} + +func TestEncodeDefinedTypeKeyMap(t *testing.T) { + type K string + type U struct { + M map[K]string + } + bytes, err := yaml.Marshal(U{ + M: map[K]string{K("x"): "y"}, + }) + if err != nil { + t.Fatalf("%+v", err) + } + expect := "m:\n x: \"y\"\n" + actual := string(bytes) + if actual != expect { + t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual) + } +} + +func TestEncodeWithAnchorAndAlias(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + type T struct { + A int + B string + } + var v struct { + A *T `yaml:"a,anchor=c"` + B *T `yaml:"b,alias=c"` + } + v.A = &T{A: 1, B: "hello"} + v.B = v.A + if err := enc.Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := "a: &c\n a: 1\n b: hello\nb: *c\n" + if expect != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) + } +} + +func TestEncodeWithAutoAlias(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + type T struct { + I int + S string + } + var v struct { + A *T `yaml:"a,anchor=a"` + B *T `yaml:"b,anchor=b"` + C *T `yaml:"c,alias"` + D *T `yaml:"d,alias"` + } + v.A = &T{I: 1, S: "hello"} + v.B = &T{I: 2, S: "world"} + v.C = v.A + v.D = v.B + if err := enc.Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := `a: &a + i: 1 + s: hello +b: &b + i: 2 + s: world +c: *a +d: *b +` + if expect != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) + } +} + +func TestEncodeWithImplicitAnchorAndAlias(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + type T struct { + I int + S string + } + var v struct { + A *T `yaml:"a,anchor"` + B *T `yaml:"b,anchor"` + C *T `yaml:"c,alias"` + D *T `yaml:"d,alias"` + } + v.A = &T{I: 1, S: "hello"} + v.B = &T{I: 2, S: "world"} + v.C = v.A + v.D = v.B + if err := enc.Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := `a: &a + i: 1 + s: hello +b: &b + i: 2 + s: world +c: *a +d: *b +` + if expect != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) + } +} + +func TestEncodeWithMerge(t *testing.T) { + type Person struct { + *Person `yaml:",omitempty,inline,alias"` + Name string `yaml:",omitempty"` + Age int `yaml:",omitempty"` + } + defaultPerson := &Person{ + Name: "John Smith", + Age: 20, + } + people := []*Person{ + { + Person: defaultPerson, + Name: "Ken", + Age: 10, + }, + { + Person: defaultPerson, + }, + } + var doc struct { + Default *Person `yaml:"default,anchor"` + People []*Person `yaml:"people"` + } + doc.Default = defaultPerson + doc.People = people + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + if err := enc.Encode(doc); err != nil { + t.Fatalf("%+v", err) + } + expect := `default: &default + name: John Smith + age: 20 +people: +- <<: *default + name: Ken + age: 10 +- <<: *default +` + if expect != buf.String() { + t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) + } +} + +func TestEncodeWithNestedYAML(t *testing.T) { + // Represents objects containing stringified YAML, and special chars + tests := []struct { + value interface{} + // If true, expects a different result between when using forced literal style or not + expectDifferent bool + }{ + { + value: map[string]interface{}{"v": "# comment\nname: hello\npassword: hello123\nspecial: \":ghost:\"\ntext: |\n nested multiline!"}, + expectDifferent: true, + }, + { + value: map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, + expectDifferent: true, + }, + { + value: map[string]interface{}{"v": "# comment\n"}, + expectDifferent: true, + }, + { + value: map[string]interface{}{"v": "\n"}, + }, + } + + for _, test := range tests { + yamlBytesForced, err := yaml.MarshalWithOptions(test.value, yaml.UseLiteralStyleIfMultiline(true)) + if err != nil { + t.Fatalf("%+v", err) + } + + // Convert it back for proper equality testing + var unmarshaled interface{} + + if err := yaml.Unmarshal(yamlBytesForced, &unmarshaled); err != nil { + t.Fatalf("%+v", err) + } + + if !reflect.DeepEqual(test.value, unmarshaled) { + t.Fatalf("expected %v(%T). but actual %v(%T)", test.value, test.value, unmarshaled, unmarshaled) + } + + if test.expectDifferent { + yamlBytesNotForced, err := yaml.MarshalWithOptions(test.value) + if err != nil { + t.Fatalf("%+v", err) + } + + if string(yamlBytesForced) == string(yamlBytesNotForced) { + t.Fatalf("expected different strings when force literal style is not enabled. forced: %s, not forced: %s", string(yamlBytesForced), string(yamlBytesNotForced)) + } + } + } +} + +func TestEncoder_Inline(t *testing.T) { + type base struct { + A int + B string + } + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + if err := enc.Encode(struct { + *base `yaml:",inline"` + C bool + }{ + base: &base{ + A: 1, + B: "hello", + }, + C: true, + }); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +a: 1 +b: hello +c: true +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_InlineAndConflictKey(t *testing.T) { + type base struct { + A int + B string + } + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + if err := enc.Encode(struct { + *base `yaml:",inline"` + A int // conflict + C bool + }{ + base: &base{ + A: 1, + B: "hello", + }, + A: 0, // default value + C: true, + }); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +b: hello +a: 0 +c: true +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_InlineNil(t *testing.T) { + type base struct { + A int + B string + } + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + if err := enc.Encode(struct { + *base `yaml:",inline"` + C bool + }{ + C: true, + }); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +c: true +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_Flow(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf, yaml.Flow(true)) + var v struct { + A int + B string + C struct { + D int + E string + } + F []int + } + v.A = 1 + v.B = "hello" + v.C.D = 3 + v.C.E = "world" + v.F = []int{1, 2} + if err := enc.Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +{a: 1, b: hello, c: {d: 3, e: world}, f: [1, 2]} +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_FlowRecursive(t *testing.T) { + var v struct { + M map[string][]int `yaml:",flow"` + } + v.M = map[string][]int{ + "test": []int{1, 2, 3}, + } + var buf bytes.Buffer + if err := yaml.NewEncoder(&buf).Encode(v); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +m: {test: [1, 2, 3]} +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_JSON(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf, yaml.JSON()) + type st struct { + I int8 + S string + F float32 + } + if err := enc.Encode(struct { + I int + U uint + S string + F float64 + Struct *st + Slice []int + Map map[string]interface{} + Time time.Time + Duration time.Duration + }{ + I: -10, + U: 10, + S: "hello", + F: 3.14, + Struct: &st{ + I: 2, + S: "world", + F: 1.23, + }, + Slice: []int{1, 2, 3, 4, 5}, + Map: map[string]interface{}{ + "a": 1, + "b": 1.23, + "c": "json", + }, + Time: time.Time{}, + Duration: 5 * time.Minute, + }); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +{"i": -10, "u": 10, "s": "hello", "f": 3.14, "struct": {"i": 2, "s": "world", "f": 1.23}, "slice": [1, 2, 3, 4, 5], "map": {"a": 1, "b": 1.23, "c": "json"}, "time": "0001-01-01T00:00:00Z", "duration": "5m0s"} +` + actual := "\n" + buf.String() + if expect != actual { + t.Fatalf("JSON style marshal error: expect=[%s] actual=[%s]", expect, actual) + } +} + +func TestEncoder_MarshalAnchor(t *testing.T) { + type Host struct { + Hostname string + Username string + Password string + } + type HostDecl struct { + Host *Host `yaml:",anchor"` + } + type Queue struct { + Name string `yaml:","` + *Host `yaml:",alias"` + } + var doc struct { + Hosts []*HostDecl `yaml:"hosts"` + Queues []*Queue `yaml:"queues"` + } + host1 := &Host{ + Hostname: "host1.example.com", + Username: "userA", + Password: "pass1", + } + host2 := &Host{ + Hostname: "host2.example.com", + Username: "userB", + Password: "pass2", + } + doc.Hosts = []*HostDecl{ + { + Host: host1, + }, + { + Host: host2, + }, + } + doc.Queues = []*Queue{ + { + Name: "queue", + Host: host1, + }, { + Name: "queue2", + Host: host2, + }, + } + hostIdx := 1 + opt := yaml.MarshalAnchor(func(anchor *ast.AnchorNode, value interface{}) error { + if _, ok := value.(*Host); ok { + nameNode := anchor.Name.(*ast.StringNode) + nameNode.Value = fmt.Sprintf("host%d", hostIdx) + hostIdx++ + } + return nil + }) + + var buf bytes.Buffer + if err := yaml.NewEncoder(&buf, opt).Encode(doc); err != nil { + t.Fatalf("%+v", err) + } + expect := ` +hosts: +- host: &host1 + hostname: host1.example.com + username: userA + password: pass1 +- host: &host2 + hostname: host2.example.com + username: userB + password: pass2 +queues: +- name: queue + host: *host1 +- name: queue2 + host: *host2 +` + if "\n"+buf.String() != expect { + t.Fatalf("unexpected output. %s", buf.String()) + } +} + +type useJSONMarshalerTest struct{} + +func (t useJSONMarshalerTest) MarshalJSON() ([]byte, error) { + return []byte(`{"a":[1, 2, 3]}`), nil +} + +func TestEncoder_UseJSONMarshaler(t *testing.T) { + got, err := yaml.MarshalWithOptions(useJSONMarshalerTest{}, yaml.UseJSONMarshaler()) + if err != nil { + t.Fatal(err) + } + expected := ` +a: +- 1 +- 2 +- 3 +` + if expected != "\n"+string(got) { + t.Fatalf("failed to use json marshaler. expected [%q] but got [%q]", expected, string(got)) + } +} + +func TestEncoder_CustomMarshaler(t *testing.T) { + t.Run("override struct type", func(t *testing.T) { + type T struct { + Foo string `yaml:"foo"` + } + b, err := yaml.MarshalWithOptions(&T{Foo: "bar"}, yaml.CustomMarshaler[T](func(v T) ([]byte, error) { + return []byte(`"override"`), nil + })) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(b, []byte("\"override\"\n")) { + t.Fatalf("failed to switch to custom marshaler. got: %q", b) + } + }) + t.Run("override bytes type", func(t *testing.T) { + type T struct { + Foo []byte `yaml:"foo"` + } + b, err := yaml.MarshalWithOptions(&T{Foo: []byte("bar")}, yaml.CustomMarshaler[[]byte](func(v []byte) ([]byte, error) { + if !bytes.Equal(v, []byte("bar")) { + t.Fatalf("failed to get src buffer: %q", v) + } + return []byte(`override`), nil + })) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(b, []byte("foo: override\n")) { + t.Fatalf("failed to switch to custom marshaler. got: %q", b) + } + }) +} + +func TestEncoder_MultipleDocuments(t *testing.T) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + if err := enc.Encode(1); err != nil { + t.Fatalf("failed to encode: %s", err) + } + if err := enc.Encode(2); err != nil { + t.Fatalf("failed to encode: %s", err) + } + if actual, expect := buf.String(), "1\n---\n2\n"; actual != expect { + t.Errorf("expect:\n%s\nactual\n%s\n", expect, actual) + } +} + +func Example_Marshal_Node() { + type T struct { + Text ast.Node `yaml:"text"` + } + stringNode, err := yaml.ValueToNode("node example") + if err != nil { + panic(err) + } + bytes, err := yaml.Marshal(T{Text: stringNode}) + if err != nil { + panic(err) + } + fmt.Println(string(bytes)) + // OUTPUT: + // text: node example +} + +func Example_Marshal_ExplicitAnchorAlias() { + type T struct { + A int + B string + } + var v struct { + C *T `yaml:"c,anchor=x"` + D *T `yaml:"d,alias=x"` + } + v.C = &T{A: 1, B: "hello"} + v.D = v.C + bytes, err := yaml.Marshal(v) + if err != nil { + panic(err) + } + fmt.Println(string(bytes)) + // OUTPUT: + // c: &x + // a: 1 + // b: hello + // d: *x +} + +func Example_Marshal_ImplicitAnchorAlias() { + type T struct { + I int + S string + } + var v struct { + A *T `yaml:"a,anchor"` + B *T `yaml:"b,anchor"` + C *T `yaml:"c,alias"` + D *T `yaml:"d,alias"` + } + v.A = &T{I: 1, S: "hello"} + v.B = &T{I: 2, S: "world"} + v.C = v.A // C has same pointer address to A + v.D = v.B // D has same pointer address to B + bytes, err := yaml.Marshal(v) + if err != nil { + panic(err) + } + fmt.Println(string(bytes)) + // OUTPUT: + // a: &a + // i: 1 + // s: hello + // b: &b + // i: 2 + // s: world + // c: *a + // d: *b +} + +type tMarshal []string + +func (t *tMarshal) MarshalYAML() ([]byte, error) { + var buf bytes.Buffer + buf.WriteString("tags:\n") + for i, v := range *t { + if i == 0 { + fmt.Fprintf(&buf, "- %s\n", v) + } else { + fmt.Fprintf(&buf, " %s\n", v) + } + } + return buf.Bytes(), nil +} +func Test_Marshaler(t *testing.T) { + const expected = `- hello-world +` + + // sanity check + var l []string + if err := yaml.Unmarshal([]byte(expected), &l); err != nil { + t.Fatalf("failed to parse string: %s", err) + } + + buf, err := yaml.Marshal(tMarshal{"hello-world"}) + if err != nil { + t.Fatalf("failed to marshal: %s", err) + } + + if string(buf) != expected { + t.Fatalf("expected '%s', got '%s'", expected, buf) + } + + t.Logf("%s", buf) +} + +type marshalContext struct{} + +func (c *marshalContext) MarshalYAML(ctx context.Context) ([]byte, error) { + v, ok := ctx.Value("k").(int) + if !ok { + return nil, fmt.Errorf("cannot get valid context") + } + if v != 1 { + return nil, fmt.Errorf("cannot get valid context") + } + return []byte("1"), nil +} + +func Test_MarshalerContext(t *testing.T) { + ctx := context.WithValue(context.Background(), "k", 1) + bytes, err := yaml.MarshalContext(ctx, &marshalContext{}) + if err != nil { + t.Fatalf("%+v", err) + } + if string(bytes) != "1\n" { + t.Fatalf("failed marshal: %q", string(bytes)) + } +} + +type SlowMarshaler struct { + A string + B int +} +type FastMarshaler struct { + A string + B int +} +type TextMarshaler int64 +type TextMarshalerContainer struct { + Field TextMarshaler `yaml:"field"` +} + +func (v SlowMarshaler) MarshalYAML() ([]byte, error) { + var buf bytes.Buffer + buf.WriteString("tags:\n") + buf.WriteString("- slow-marshaler\n") + buf.WriteString("a: " + v.A + "\n") + buf.WriteString("b: " + strconv.FormatInt(int64(v.B), 10) + "\n") + return buf.Bytes(), nil +} + +func (v FastMarshaler) MarshalYAML() (interface{}, error) { + return yaml.MapSlice{ + {"tags", []string{"fast-marshaler"}}, + {"a", v.A}, + {"b", v.B}, + }, nil +} + +func (t TextMarshaler) MarshalText() ([]byte, error) { + return []byte(strconv.FormatInt(int64(t), 8)), nil +} + +func Example_MarshalYAML() { + var slow SlowMarshaler + slow.A = "Hello slow poke" + slow.B = 100 + buf, err := yaml.Marshal(slow) + if err != nil { + panic(err.Error()) + } + + fmt.Println(string(buf)) + + var fast FastMarshaler + fast.A = "Hello speed demon" + fast.B = 100 + buf, err = yaml.Marshal(fast) + if err != nil { + panic(err.Error()) + } + + fmt.Println(string(buf)) + + text := TextMarshalerContainer{ + Field: 11, + } + buf, err = yaml.Marshal(text) + if err != nil { + panic(err.Error()) + } + + fmt.Println(string(buf)) + // OUTPUT: + // tags: + // - slow-marshaler + // a: Hello slow poke + // b: 100 + // + // tags: + // - fast-marshaler + // a: Hello speed demon + // b: 100 + // + // field: 13 +} + +func TestIssue356(t *testing.T) { + tests := map[string]struct { + in string + }{ + "content on first line": { + in: `args: + - | + + key: + nest1: something + nest2: + nest2a: b +`, + }, + "empty first line": { + in: `args: + - | + + key: + nest1: something + nest2: + nest2a: b +`, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + f, err := parser.ParseBytes([]byte(test.in), 0) + if err != nil { + t.Fatalf("parse: %v", err) + } + got := f.String() + if test.in != got { + t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", test.in, got) + } + }) + } +} + +func TestMarshalIndentWithMultipleText(t *testing.T) { + t.Run("depth1", func(t *testing.T) { + b, err := yaml.MarshalWithOptions(map[string]interface{}{ + "key": []string{`line1 +line2 +line3`}, + }, yaml.Indent(2)) + if err != nil { + t.Fatal(err) + } + got := string(b) + expected := `key: +- |- + line1 + line2 + line3 +` + if expected != got { + t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", expected, got) + } + }) + t.Run("depth2", func(t *testing.T) { + b, err := yaml.MarshalWithOptions(map[string]interface{}{ + "key": map[string]interface{}{ + "key2": []string{`line1 +line2 +line3`}, + }, + }, yaml.Indent(2)) + if err != nil { + t.Fatal(err) + } + got := string(b) + expected := `key: + key2: + - |- + line1 + line2 + line3 +` + if expected != got { + t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", expected, got) + } + }) +} + +type bytesMarshaler struct{} + +func (b *bytesMarshaler) MarshalYAML() ([]byte, error) { + return yaml.Marshal(map[string]interface{}{"d": "foo"}) +} + +func TestBytesMarshaler(t *testing.T) { + b, err := yaml.Marshal(map[string]interface{}{ + "a": map[string]interface{}{ + "b": map[string]interface{}{ + "c": &bytesMarshaler{}, + }, + }, + }) + if err != nil { + t.Fatal(err) + } + expected := ` +a: + b: + c: + d: foo +` + got := "\n" + string(b) + if expected != got { + t.Fatalf("failed to encode. expected %s but got %s", expected, got) + } +} + +type customMapSliceOneItemMarshaler struct{} + +func (m *customMapSliceOneItemMarshaler) MarshalYAML() ([]byte, error) { + var v yaml.MapSlice + v = append(v, yaml.MapItem{"a", "b"}) + return yaml.Marshal(v) +} + +type customMapSliceTwoItemMarshaler struct{} + +func (m *customMapSliceTwoItemMarshaler) MarshalYAML() ([]byte, error) { + var v yaml.MapSlice + v = append(v, yaml.MapItem{"a", "b"}) + v = append(v, yaml.MapItem{"b", "c"}) + return yaml.Marshal(v) +} + +func TestCustomMapSliceMarshaler(t *testing.T) { + type T struct { + A *customMapSliceOneItemMarshaler `yaml:"a"` + B *customMapSliceTwoItemMarshaler `yaml:"b"` + } + b, err := yaml.Marshal(&T{ + A: &customMapSliceOneItemMarshaler{}, + B: &customMapSliceTwoItemMarshaler{}, + }) + if err != nil { + t.Fatal(err) + } + expected := ` +a: + a: b +b: + a: b + b: c +` + got := "\n" + string(b) + if expected != got { + t.Fatalf("failed to encode. expected %s but got %s", expected, got) + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/error.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/error.go new file mode 100644 index 0000000..163dcc5 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/error.go @@ -0,0 +1,62 @@ +package yaml + +import ( + "github.com/goccy/go-yaml/ast" + "golang.org/x/xerrors" +) + +var ( + ErrInvalidQuery = xerrors.New("invalid query") + ErrInvalidPath = xerrors.New("invalid path instance") + ErrInvalidPathString = xerrors.New("invalid path string") + ErrNotFoundNode = xerrors.New("node not found") + ErrUnknownCommentPositionType = xerrors.New("unknown comment position type") + ErrInvalidCommentMapValue = xerrors.New("invalid comment map value. it must be not nil value") +) + +func ErrUnsupportedHeadPositionType(node ast.Node) error { + return xerrors.Errorf("unsupported comment head position for %s", node.Type()) +} + +func ErrUnsupportedLinePositionType(node ast.Node) error { + return xerrors.Errorf("unsupported comment line position for %s", node.Type()) +} + +func ErrUnsupportedFootPositionType(node ast.Node) error { + return xerrors.Errorf("unsupported comment foot position for %s", node.Type()) +} + +// IsInvalidQueryError whether err is ErrInvalidQuery or not. +func IsInvalidQueryError(err error) bool { + return xerrors.Is(err, ErrInvalidQuery) +} + +// IsInvalidPathError whether err is ErrInvalidPath or not. +func IsInvalidPathError(err error) bool { + return xerrors.Is(err, ErrInvalidPath) +} + +// IsInvalidPathStringError whether err is ErrInvalidPathString or not. +func IsInvalidPathStringError(err error) bool { + return xerrors.Is(err, ErrInvalidPathString) +} + +// IsNotFoundNodeError whether err is ErrNotFoundNode or not. +func IsNotFoundNodeError(err error) bool { + return xerrors.Is(err, ErrNotFoundNode) +} + +// IsInvalidTokenTypeError whether err is ast.ErrInvalidTokenType or not. +func IsInvalidTokenTypeError(err error) bool { + return xerrors.Is(err, ast.ErrInvalidTokenType) +} + +// IsInvalidAnchorNameError whether err is ast.ErrInvalidAnchorName or not. +func IsInvalidAnchorNameError(err error) bool { + return xerrors.Is(err, ast.ErrInvalidAnchorName) +} + +// IsInvalidAliasNameError whether err is ast.ErrInvalidAliasName or not. +func IsInvalidAliasNameError(err error) bool { + return xerrors.Is(err, ast.ErrInvalidAliasName) +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/go.mod b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/go.mod new file mode 100644 index 0000000..4550ff3 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/go.mod @@ -0,0 +1,20 @@ +module github.com/goccy/go-yaml + +go 1.19 + +require ( + github.com/fatih/color v1.10.0 + github.com/go-playground/validator/v10 v10.4.1 + github.com/google/go-cmp v0.5.9 + github.com/mattn/go-colorable v0.1.8 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 +) + +require ( + github.com/go-playground/locales v0.13.0 // indirect + github.com/go-playground/universal-translator v0.17.0 // indirect + github.com/leodido/go-urn v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/sys v0.6.0 // indirect +) diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/go.sum b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/go.sum new file mode 100644 index 0000000..7249df6 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/go.sum @@ -0,0 +1,44 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/internal/errors/error.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/internal/errors/error.go new file mode 100644 index 0000000..7f1ea9a --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/internal/errors/error.go @@ -0,0 +1,260 @@ +package errors + +import ( + "bytes" + "fmt" + "reflect" + + "github.com/goccy/go-yaml/printer" + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +const ( + defaultColorize = false + defaultIncludeSource = true +) + +var ( + ErrDecodeRequiredPointerType = xerrors.New("required pointer type value") +) + +// Wrapf wrap error for stack trace +func Wrapf(err error, msg string, args ...interface{}) error { + return &wrapError{ + baseError: &baseError{}, + err: xerrors.Errorf(msg, args...), + nextErr: err, + frame: xerrors.Caller(1), + } +} + +// ErrSyntax create syntax error instance with message and token +func ErrSyntax(msg string, tk *token.Token) *syntaxError { + return &syntaxError{ + baseError: &baseError{}, + msg: msg, + token: tk, + frame: xerrors.Caller(1), + } +} + +type baseError struct { + state fmt.State + verb rune +} + +func (e *baseError) Error() string { + return "" +} + +func (e *baseError) chainStateAndVerb(err error) { + wrapErr, ok := err.(*wrapError) + if ok { + wrapErr.state = e.state + wrapErr.verb = e.verb + } + syntaxErr, ok := err.(*syntaxError) + if ok { + syntaxErr.state = e.state + syntaxErr.verb = e.verb + } +} + +type wrapError struct { + *baseError + err error + nextErr error + frame xerrors.Frame +} + +type FormatErrorPrinter struct { + xerrors.Printer + Colored bool + InclSource bool +} + +func (e *wrapError) As(target interface{}) bool { + err := e.nextErr + for { + if wrapErr, ok := err.(*wrapError); ok { + err = wrapErr.nextErr + continue + } + break + } + return xerrors.As(err, target) +} + +func (e *wrapError) Unwrap() error { + return e.nextErr +} + +func (e *wrapError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { + return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +} + +func (e *wrapError) FormatError(p xerrors.Printer) error { + if _, ok := p.(*FormatErrorPrinter); !ok { + p = &FormatErrorPrinter{ + Printer: p, + Colored: defaultColorize, + InclSource: defaultIncludeSource, + } + } + if e.verb == 'v' && e.state.Flag('+') { + // print stack trace for debugging + p.Print(e.err, "\n") + e.frame.Format(p) + e.chainStateAndVerb(e.nextErr) + return e.nextErr + } + err := e.nextErr + for { + if wrapErr, ok := err.(*wrapError); ok { + err = wrapErr.nextErr + continue + } + break + } + e.chainStateAndVerb(err) + if fmtErr, ok := err.(xerrors.Formatter); ok { + fmtErr.FormatError(p) + } else { + p.Print(err) + } + return nil +} + +type wrapState struct { + org fmt.State +} + +func (s *wrapState) Write(b []byte) (n int, err error) { + return s.org.Write(b) +} + +func (s *wrapState) Width() (wid int, ok bool) { + return s.org.Width() +} + +func (s *wrapState) Precision() (prec int, ok bool) { + return s.org.Precision() +} + +func (s *wrapState) Flag(c int) bool { + // set true to 'printDetail' forced because when p.Detail() is false, xerrors.Printer no output any text + if c == '#' { + // ignore '#' keyword because xerrors.FormatError doesn't set true to printDetail. + // ( see https://github.com/golang/xerrors/blob/master/adaptor.go#L39-L43 ) + return false + } + return true +} + +func (e *wrapError) Format(state fmt.State, verb rune) { + e.state = state + e.verb = verb + xerrors.FormatError(e, &wrapState{org: state}, verb) +} + +func (e *wrapError) Error() string { + var buf bytes.Buffer + e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource) + return buf.String() +} + +type syntaxError struct { + *baseError + msg string + token *token.Token + frame xerrors.Frame +} + +func (e *syntaxError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { + return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +} + +func (e *syntaxError) FormatError(p xerrors.Printer) error { + var pp printer.Printer + + var colored, inclSource bool + if fep, ok := p.(*FormatErrorPrinter); ok { + colored = fep.Colored + inclSource = fep.InclSource + } + + pos := fmt.Sprintf("[%d:%d] ", e.token.Position.Line, e.token.Position.Column) + msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.msg), colored) + if inclSource { + msg += "\n" + pp.PrintErrorToken(e.token, colored) + } + p.Print(msg) + + if e.verb == 'v' && e.state.Flag('+') { + // %+v + // print stack trace for debugging + e.frame.Format(p) + } + return nil +} + +type PrettyPrinter interface { + PrettyPrint(xerrors.Printer, bool, bool) error +} +type Sink struct{ *bytes.Buffer } + +func (es *Sink) Print(args ...interface{}) { + fmt.Fprint(es.Buffer, args...) +} + +func (es *Sink) Printf(f string, args ...interface{}) { + fmt.Fprintf(es.Buffer, f, args...) +} + +func (es *Sink) Detail() bool { + return false +} + +func (e *syntaxError) Error() string { + var buf bytes.Buffer + e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource) + return buf.String() +} + +type TypeError struct { + DstType reflect.Type + SrcType reflect.Type + StructFieldName *string + Token *token.Token +} + +func (e *TypeError) Error() string { + if e.StructFieldName != nil { + return fmt.Sprintf("cannot unmarshal %s into Go struct field %s of type %s", e.SrcType, *e.StructFieldName, e.DstType) + } + return fmt.Sprintf("cannot unmarshal %s into Go value of type %s", e.SrcType, e.DstType) +} + +func (e *TypeError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { + return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +} + +func (e *TypeError) FormatError(p xerrors.Printer) error { + var pp printer.Printer + + var colored, inclSource bool + if fep, ok := p.(*FormatErrorPrinter); ok { + colored = fep.Colored + inclSource = fep.InclSource + } + + pos := fmt.Sprintf("[%d:%d] ", e.Token.Position.Line, e.Token.Position.Column) + msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.Error()), colored) + if inclSource { + msg += "\n" + pp.PrintErrorToken(e.Token, colored) + } + p.Print(msg) + + return nil +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/lexer/lexer.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/lexer/lexer.go new file mode 100644 index 0000000..3207f4f --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/lexer/lexer.go @@ -0,0 +1,23 @@ +package lexer + +import ( + "io" + + "github.com/goccy/go-yaml/scanner" + "github.com/goccy/go-yaml/token" +) + +// Tokenize split to token instances from string +func Tokenize(src string) token.Tokens { + var s scanner.Scanner + s.Init(src) + var tokens token.Tokens + for { + subTokens, err := s.Scan() + if err == io.EOF { + break + } + tokens.Add(subTokens...) + } + return tokens +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/lexer/lexer_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/lexer/lexer_test.go new file mode 100644 index 0000000..0d8f648 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/lexer/lexer_test.go @@ -0,0 +1,621 @@ +package lexer_test + +import ( + "sort" + "strings" + "testing" + + "github.com/goccy/go-yaml/lexer" + "github.com/goccy/go-yaml/token" +) + +func TestTokenize(t *testing.T) { + sources := []string{ + "null\n", + "{}\n", + "v: hi\n", + "v: \"true\"\n", + "v: \"false\"\n", + "v: true\n", + "v: false\n", + "v: 10\n", + "v: -10\n", + "v: 42\n", + "v: 4294967296\n", + "v: \"10\"\n", + "v: 0.1\n", + "v: 0.99\n", + "v: -0.1\n", + "v: .inf\n", + "v: -.inf\n", + "v: .nan\n", + "v: null\n", + "v: \"\"\n", + "v:\n- A\n- B\n", + "v:\n- A\n- |-\n B\n C\n", + "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", + "a:\n b: c\n", + "a: '-'\n", + "123\n", + "hello: world\n", + "a: null\n", + "a: {x: 1}\n", + "a: [1, 2]\n", + "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", + "a: {b: c, d: e}\n", + "a: 3s\n", + "a: \n", + "a: \"1:1\"\n", + "a: \"\\0\"\n", + "a: !!binary gIGC\n", + "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", + "b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n", + "a: 1.2.3.4\n", + "a: \"2015-02-24T18:19:39Z\"\n", + "a: 'b: c'\n", + "a: 'Hello #comment'\n", + "a: 100.5\n", + "a: bogus\n", + "\"a\": double quoted map key", + "'a': single quoted map key", + "a: \"double quoted\"\nb: \"value map\"", + "a: 'single quoted'\nb: 'value map'", + } + for _, src := range sources { + lexer.Tokenize(src).Dump() + } +} + +type testToken struct { + line int + column int + value string +} + +func TestSingleLineToken_ValueLineColumnPosition(t *testing.T) { + tests := []struct { + name string + src string + expect map[int]string // Column -> Value map. + }{ + { + name: "single quote, single value array", + src: "test: ['test']", + expect: map[int]string{ + 1: "test", + 5: ":", + 7: "[", + 8: "test", + 14: "]", + }, + }, + { + name: "double quote, single value array", + src: `test: ["test"]`, + expect: map[int]string{ + 1: "test", + 5: ":", + 7: "[", + 8: "test", + 14: "]", + }, + }, + { + name: "no quotes, single value array", + src: "test: [somevalue]", + expect: map[int]string{ + 1: "test", + 5: ":", + 7: "[", + 8: "somevalue", + 17: "]", + }, + }, + { + name: "single quote, multi value array", + src: "myarr: ['1','2','3', '444' , '55','66' , '77' ]", + expect: map[int]string{ + 1: "myarr", + 6: ":", + 8: "[", + 9: "1", + 12: ",", + 13: "2", + 16: ",", + 17: "3", + 20: ",", + 22: "444", + 28: ",", + 30: "55", + 34: ",", + 35: "66", + 40: ",", + 43: "77", + 49: "]", + }, + }, + { + name: "double quote, multi value array", + src: `myarr: ["1","2","3", "444" , "55","66" , "77" ]`, + expect: map[int]string{ + 1: "myarr", + 6: ":", + 8: "[", + 9: "1", + 12: ",", + 13: "2", + 16: ",", + 17: "3", + 20: ",", + 22: "444", + 28: ",", + 30: "55", + 34: ",", + 35: "66", + 40: ",", + 43: "77", + 49: "]", + }, + }, + { + name: "no quote, multi value array", + src: "numbers: [1, 5, 99,100, 3, 7 ]", + expect: map[int]string{ + 1: "numbers", + 8: ":", + 10: "[", + 11: "1", + 12: ",", + 14: "5", + 15: ",", + 17: "99", + 19: ",", + 20: "100", + 23: ",", + 25: "3", + 26: ",", + 28: "7", + 30: "]", + }, + }, + { + name: "double quotes, nested arrays", + src: `Strings: ["1",["2",["3"]]]`, + expect: map[int]string{ + 1: "Strings", + 8: ":", + 10: "[", + 11: "1", + 14: ",", + 15: "[", + 16: "2", + 19: ",", + 20: "[", + 21: "3", + 24: "]", + 25: "]", + 26: "]", + }, + }, + { + name: "mixed quotes, nested arrays", + src: `Values: [1,['2',"3",4,["5",6]]]`, + expect: map[int]string{ + 1: "Values", + 7: ":", + 9: "[", + 10: "1", + 11: ",", + 12: "[", + 13: "2", + 16: ",", + 17: "3", + 20: ",", + 21: "4", + 22: ",", + 23: "[", + 24: "5", + 27: ",", + 28: "6", + 29: "]", + 30: "]", + 31: "]", + }, + }, + { + name: "double quote, empty array", + src: `Empty: ["", ""]`, + expect: map[int]string{ + 1: "Empty", + 6: ":", + 8: "[", + 9: "", + 11: ",", + 13: "", + 15: "]", + }, + }, + { + name: "double quote key", + src: `"a": b`, + expect: map[int]string{ + 1: "a", + 4: ":", + 6: "b", + }, + }, + { + name: "single quote key", + src: `'a': b`, + expect: map[int]string{ + 1: "a", + 4: ":", + 6: "b", + }, + }, + { + name: "double quote key and value", + src: `"a": "b"`, + expect: map[int]string{ + 1: "a", + 4: ":", + 6: "b", + }, + }, + { + name: "single quote key and value", + src: `'a': 'b'`, + expect: map[int]string{ + 1: "a", + 4: ":", + 6: "b", + }, + }, + { + name: "double quote key, single quote value", + src: `"a": 'b'`, + expect: map[int]string{ + 1: "a", + 4: ":", + 6: "b", + }, + }, + { + name: "single quote key, double quote value", + src: `'a': "b"`, + expect: map[int]string{ + 1: "a", + 4: ":", + 6: "b", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := lexer.Tokenize(tc.src) + sort.Slice(got, func(i, j int) bool { + return got[i].Position.Column < got[j].Position.Column + }) + var expected []testToken + for k, v := range tc.expect { + tt := testToken{ + line: 1, + column: k, + value: v, + } + expected = append(expected, tt) + } + sort.Slice(expected, func(i, j int) bool { + return expected[i].column < expected[j].column + }) + if len(got) != len(expected) { + t.Errorf("Tokenize(%s) token count mismatch, expected:%d got:%d", tc.src, len(expected), len(got)) + } + for i, tok := range got { + if !tokenMatches(tok, expected[i]) { + t.Errorf("Tokenize(%s) expected:%+v got line:%d column:%d value:%s", tc.src, expected[i], tok.Position.Line, tok.Position.Column, tok.Value) + } + } + }) + } +} + +func tokenMatches(t *token.Token, e testToken) bool { + return t != nil && t.Position != nil && + t.Value == e.value && + t.Position.Line == e.line && + t.Position.Column == e.column +} + +func TestMultiLineToken_ValueLineColumnPosition(t *testing.T) { + tests := []struct { + name string + src string + expect []testToken + }{ + { + name: "double quote", + src: `one: "1 2 3 4 5" +two: "1 2 +3 4 +5" +three: "1 2 3 4 +5"`, + expect: []testToken{ + { + line: 1, + column: 1, + value: "one", + }, + { + line: 1, + column: 4, + value: ":", + }, + { + line: 1, + column: 6, + value: "1 2 3 4 5", + }, + { + line: 2, + column: 1, + value: "two", + }, + { + line: 2, + column: 4, + value: ":", + }, + { + line: 2, + column: 6, + value: "1 2 3 4 5", + }, + { + line: 5, + column: 1, + value: "three", + }, + { + line: 5, + column: 6, + value: ":", + }, + { + line: 5, + column: 8, + value: "1 2 3 4 5", + }, + }, + }, + { + name: "single quote in an array", + src: `arr: ['1', 'and +two'] +last: 'hello'`, + expect: []testToken{ + { + line: 1, + column: 1, + value: "arr", + }, + { + line: 1, + column: 4, + value: ":", + }, + { + line: 1, + column: 6, + value: "[", + }, + { + line: 1, + column: 7, + value: "1", + }, + { + line: 1, + column: 10, + value: ",", + }, + { + line: 1, + column: 12, + value: "and two", + }, + { + line: 2, + column: 5, + value: "]", + }, + { + line: 3, + column: 1, + value: "last", + }, + { + line: 3, + column: 5, + value: ":", + }, + { + line: 3, + column: 7, + value: "hello", + }, + }, + }, + { + name: "single quote and double quote", + src: `foo: "test + + + + +bar" +foo2: 'bar2'`, + expect: []testToken{ + { + line: 1, + column: 1, + value: "foo", + }, + { + line: 1, + column: 4, + value: ":", + }, + { + line: 1, + column: 6, + value: "test bar", + }, + { + line: 7, + column: 1, + value: "foo2", + }, + { + line: 7, + column: 5, + value: ":", + }, + { + line: 7, + column: 7, + value: "bar2", + }, + }, + }, + { + name: "single and double quote map keys", + src: `"a": test +'b': 1 +c: true`, + expect: []testToken{ + { + line: 1, + column: 1, + value: "a", + }, + { + line: 1, + column: 4, + value: ":", + }, + { + line: 1, + column: 6, + value: "test", + }, + { + line: 2, + column: 1, + value: "b", + }, + { + line: 2, + column: 4, + value: ":", + }, + { + line: 2, + column: 6, + value: "1", + }, + { + line: 3, + column: 1, + value: "c", + }, + { + line: 3, + column: 2, + value: ":", + }, + { + line: 3, + column: 4, + value: "true", + }, + }, + }, + { + name: "issue326", + src: `a: | + Text +b: 1`, + expect: []testToken{ + { + line: 1, + column: 1, + value: "a", + }, + { + line: 1, + column: 2, + value: ":", + }, + { + line: 1, + column: 4, + value: "|", + }, + { + line: 2, + column: 3, + value: "Text\n", + }, + { + line: 3, + column: 1, + value: "b", + }, + { + line: 3, + column: 2, + value: ":", + }, + { + line: 3, + column: 4, + value: "1", + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := lexer.Tokenize(tc.src) + sort.Slice(got, func(i, j int) bool { + // sort by line, then column + if got[i].Position.Line < got[j].Position.Line { + return true + } else if got[i].Position.Line == got[j].Position.Line { + return got[i].Position.Column < got[j].Position.Column + } + return false + }) + sort.Slice(tc.expect, func(i, j int) bool { + if tc.expect[i].line < tc.expect[j].line { + return true + } else if tc.expect[i].line == tc.expect[j].line { + return tc.expect[i].column < tc.expect[j].column + } + return false + }) + if len(got) != len(tc.expect) { + t.Errorf("Tokenize() token count mismatch, expected:%d got:%d", len(tc.expect), len(got)) + } + for i, tok := range got { + if !tokenMatches(tok, tc.expect[i]) { + t.Errorf("Tokenize() expected:%+v got line:%d column:%d value:%s", tc.expect[i], tok.Position.Line, tok.Position.Column, tok.Value) + } + } + }) + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/option.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/option.go new file mode 100644 index 0000000..eab5d43 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/option.go @@ -0,0 +1,278 @@ +package yaml + +import ( + "io" + "reflect" + + "github.com/goccy/go-yaml/ast" +) + +// DecodeOption functional option type for Decoder +type DecodeOption func(d *Decoder) error + +// ReferenceReaders pass to Decoder that reference to anchor defined by passed readers +func ReferenceReaders(readers ...io.Reader) DecodeOption { + return func(d *Decoder) error { + d.referenceReaders = append(d.referenceReaders, readers...) + return nil + } +} + +// ReferenceFiles pass to Decoder that reference to anchor defined by passed files +func ReferenceFiles(files ...string) DecodeOption { + return func(d *Decoder) error { + d.referenceFiles = files + return nil + } +} + +// ReferenceDirs pass to Decoder that reference to anchor defined by files under the passed dirs +func ReferenceDirs(dirs ...string) DecodeOption { + return func(d *Decoder) error { + d.referenceDirs = dirs + return nil + } +} + +// RecursiveDir search yaml file recursively from passed dirs by ReferenceDirs option +func RecursiveDir(isRecursive bool) DecodeOption { + return func(d *Decoder) error { + d.isRecursiveDir = isRecursive + return nil + } +} + +// Validator set StructValidator instance to Decoder +func Validator(v StructValidator) DecodeOption { + return func(d *Decoder) error { + d.validator = v + return nil + } +} + +// Strict enable DisallowUnknownField and DisallowDuplicateKey +func Strict() DecodeOption { + return func(d *Decoder) error { + d.disallowUnknownField = true + d.disallowDuplicateKey = true + return nil + } +} + +// DisallowUnknownField causes the Decoder to return an error when the destination +// is a struct and the input contains object keys which do not match any +// non-ignored, exported fields in the destination. +func DisallowUnknownField() DecodeOption { + return func(d *Decoder) error { + d.disallowUnknownField = true + return nil + } +} + +// DisallowDuplicateKey causes an error when mapping keys that are duplicates +func DisallowDuplicateKey() DecodeOption { + return func(d *Decoder) error { + d.disallowDuplicateKey = true + return nil + } +} + +// UseOrderedMap can be interpreted as a map, +// and uses MapSlice ( ordered map ) aggressively if there is no type specification +func UseOrderedMap() DecodeOption { + return func(d *Decoder) error { + d.useOrderedMap = true + return nil + } +} + +// UseJSONUnmarshaler if neither `BytesUnmarshaler` nor `InterfaceUnmarshaler` is implemented +// and `UnmashalJSON([]byte)error` is implemented, convert the argument from `YAML` to `JSON` and then call it. +func UseJSONUnmarshaler() DecodeOption { + return func(d *Decoder) error { + d.useJSONUnmarshaler = true + return nil + } +} + +// CustomUnmarshaler overrides any decoding process for the type specified in generics. +// +// NOTE: If RegisterCustomUnmarshaler and CustomUnmarshaler of DecodeOption are specified for the same type, +// the CustomUnmarshaler specified in DecodeOption takes precedence. +func CustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) DecodeOption { + return func(d *Decoder) error { + var typ *T + d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(v interface{}, b []byte) error { + return unmarshaler(v.(*T), b) + } + return nil + } +} + +// EncodeOption functional option type for Encoder +type EncodeOption func(e *Encoder) error + +// Indent change indent number +func Indent(spaces int) EncodeOption { + return func(e *Encoder) error { + e.indent = spaces + return nil + } +} + +// IndentSequence causes sequence values to be indented the same value as Indent +func IndentSequence(indent bool) EncodeOption { + return func(e *Encoder) error { + e.indentSequence = indent + return nil + } +} + +// UseSingleQuote determines if single or double quotes should be preferred for strings. +func UseSingleQuote(sq bool) EncodeOption { + return func(e *Encoder) error { + e.singleQuote = sq + return nil + } +} + +// Flow encoding by flow style +func Flow(isFlowStyle bool) EncodeOption { + return func(e *Encoder) error { + e.isFlowStyle = isFlowStyle + return nil + } +} + +// UseLiteralStyleIfMultiline causes encoding multiline strings with a literal syntax, +// no matter what characters they include +func UseLiteralStyleIfMultiline(useLiteralStyleIfMultiline bool) EncodeOption { + return func(e *Encoder) error { + e.useLiteralStyleIfMultiline = useLiteralStyleIfMultiline + return nil + } +} + +// JSON encode in JSON format +func JSON() EncodeOption { + return func(e *Encoder) error { + e.isJSONStyle = true + e.isFlowStyle = true + return nil + } +} + +// MarshalAnchor call back if encoder find an anchor during encoding +func MarshalAnchor(callback func(*ast.AnchorNode, interface{}) error) EncodeOption { + return func(e *Encoder) error { + e.anchorCallback = callback + return nil + } +} + +// UseJSONMarshaler if neither `BytesMarshaler` nor `InterfaceMarshaler` +// nor `encoding.TextMarshaler` is implemented and `MarshalJSON()([]byte, error)` is implemented, +// call `MarshalJSON` to convert the returned `JSON` to `YAML` for processing. +func UseJSONMarshaler() EncodeOption { + return func(e *Encoder) error { + e.useJSONMarshaler = true + return nil + } +} + +// CustomMarshaler overrides any encoding process for the type specified in generics. +// +// NOTE: If type T implements MarshalYAML for pointer receiver, the type specified in CustomMarshaler must be *T. +// If RegisterCustomMarshaler and CustomMarshaler of EncodeOption are specified for the same type, +// the CustomMarshaler specified in EncodeOption takes precedence. +func CustomMarshaler[T any](marshaler func(T) ([]byte, error)) EncodeOption { + return func(e *Encoder) error { + var typ T + e.customMarshalerMap[reflect.TypeOf(typ)] = func(v interface{}) ([]byte, error) { + return marshaler(v.(T)) + } + return nil + } +} + +// CommentPosition type of the position for comment. +type CommentPosition int + +const ( + CommentHeadPosition CommentPosition = CommentPosition(iota) + CommentLinePosition + CommentFootPosition +) + +func (p CommentPosition) String() string { + switch p { + case CommentHeadPosition: + return "Head" + case CommentLinePosition: + return "Line" + case CommentFootPosition: + return "Foot" + default: + return "" + } +} + +// LineComment create a one-line comment for CommentMap. +func LineComment(text string) *Comment { + return &Comment{ + Texts: []string{text}, + Position: CommentLinePosition, + } +} + +// HeadComment create a multiline comment for CommentMap. +func HeadComment(texts ...string) *Comment { + return &Comment{ + Texts: texts, + Position: CommentHeadPosition, + } +} + +// FootComment create a multiline comment for CommentMap. +func FootComment(texts ...string) *Comment { + return &Comment{ + Texts: texts, + Position: CommentFootPosition, + } +} + +// Comment raw data for comment. +type Comment struct { + Texts []string + Position CommentPosition +} + +// CommentMap map of the position of the comment and the comment information. +type CommentMap map[string][]*Comment + +// WithComment add a comment using the location and text information given in the CommentMap. +func WithComment(cm CommentMap) EncodeOption { + return func(e *Encoder) error { + commentMap := map[*Path][]*Comment{} + for k, v := range cm { + path, err := PathString(k) + if err != nil { + return err + } + commentMap[path] = v + } + e.commentMap = commentMap + return nil + } +} + +// CommentToMap apply the position and content of comments in a YAML document to a CommentMap. +func CommentToMap(cm CommentMap) DecodeOption { + return func(d *Decoder) error { + if cm == nil { + return ErrInvalidCommentMapValue + } + d.toCommentMap = cm + return nil + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/context.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/context.go new file mode 100644 index 0000000..42cc4f8 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/context.go @@ -0,0 +1,192 @@ +package parser + +import ( + "fmt" + "strings" + + "github.com/goccy/go-yaml/token" +) + +// context context at parsing +type context struct { + parent *context + idx int + size int + tokens token.Tokens + path string +} + +var pathSpecialChars = []string{ + "$", "*", ".", "[", "]", +} + +func containsPathSpecialChar(path string) bool { + for _, char := range pathSpecialChars { + if strings.Contains(path, char) { + return true + } + } + return false +} + +func normalizePath(path string) string { + if containsPathSpecialChar(path) { + return fmt.Sprintf("'%s'", path) + } + return path +} + +func (c *context) withChild(path string) *context { + ctx := c.copy() + path = normalizePath(path) + ctx.path += fmt.Sprintf(".%s", path) + return ctx +} + +func (c *context) withIndex(idx uint) *context { + ctx := c.copy() + ctx.path += fmt.Sprintf("[%d]", idx) + return ctx +} + +func (c *context) copy() *context { + return &context{ + parent: c, + idx: c.idx, + size: c.size, + tokens: append(token.Tokens{}, c.tokens...), + path: c.path, + } +} + +func (c *context) next() bool { + return c.idx < c.size +} + +func (c *context) previousToken() *token.Token { + if c.idx > 0 { + return c.tokens[c.idx-1] + } + return nil +} + +func (c *context) insertToken(idx int, tk *token.Token) { + if c.parent != nil { + c.parent.insertToken(idx, tk) + } + if c.size < idx { + return + } + if c.size == idx { + curToken := c.tokens[c.size-1] + tk.Next = curToken + curToken.Prev = tk + + c.tokens = append(c.tokens, tk) + c.size = len(c.tokens) + return + } + + curToken := c.tokens[idx] + tk.Next = curToken + curToken.Prev = tk + + c.tokens = append(c.tokens[:idx+1], c.tokens[idx:]...) + c.tokens[idx] = tk + c.size = len(c.tokens) +} + +func (c *context) currentToken() *token.Token { + if c.idx >= c.size { + return nil + } + return c.tokens[c.idx] +} + +func (c *context) nextToken() *token.Token { + if c.idx+1 >= c.size { + return nil + } + return c.tokens[c.idx+1] +} + +func (c *context) afterNextToken() *token.Token { + if c.idx+2 >= c.size { + return nil + } + return c.tokens[c.idx+2] +} + +func (c *context) nextNotCommentToken() *token.Token { + for i := c.idx + 1; i < c.size; i++ { + tk := c.tokens[i] + if tk.Type == token.CommentType { + continue + } + return tk + } + return nil +} + +func (c *context) afterNextNotCommentToken() *token.Token { + notCommentTokenCount := 0 + for i := c.idx + 1; i < c.size; i++ { + tk := c.tokens[i] + if tk.Type == token.CommentType { + continue + } + notCommentTokenCount++ + if notCommentTokenCount == 2 { + return tk + } + } + return nil +} + +func (c *context) isCurrentCommentToken() bool { + tk := c.currentToken() + if tk == nil { + return false + } + return tk.Type == token.CommentType +} + +func (c *context) progressIgnoreComment(num int) { + if c.parent != nil { + c.parent.progressIgnoreComment(num) + } + if c.size <= c.idx+num { + c.idx = c.size + } else { + c.idx += num + } +} + +func (c *context) progress(num int) { + if c.isCurrentCommentToken() { + return + } + c.progressIgnoreComment(num) +} + +func newContext(tokens token.Tokens, mode Mode) *context { + filteredTokens := []*token.Token{} + if mode&ParseComments != 0 { + filteredTokens = tokens + } else { + for _, tk := range tokens { + if tk.Type == token.CommentType { + continue + } + // keep prev/next reference between tokens containing comments + // https://github.com/goccy/go-yaml/issues/254 + filteredTokens = append(filteredTokens, tk) + } + } + return &context{ + idx: 0, + size: len(filteredTokens), + tokens: token.Tokens(filteredTokens), + path: "$", + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/parser.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/parser.go new file mode 100644 index 0000000..13ada50 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/parser.go @@ -0,0 +1,743 @@ +package parser + +import ( + "fmt" + "io/ioutil" + "strings" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/lexer" + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +type parser struct{} + +func (p *parser) parseMapping(ctx *context) (*ast.MappingNode, error) { + mapTk := ctx.currentToken() + node := ast.Mapping(mapTk, true) + node.SetPath(ctx.path) + ctx.progress(1) // skip MappingStart token + for ctx.next() { + tk := ctx.currentToken() + if tk.Type == token.MappingEndType { + node.End = tk + return node, nil + } else if tk.Type == token.CollectEntryType { + ctx.progress(1) + continue + } + + value, err := p.parseMappingValue(ctx) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse mapping value in mapping node") + } + mvnode, ok := value.(*ast.MappingValueNode) + if !ok { + return nil, errors.ErrSyntax("failed to parse flow mapping node", value.GetToken()) + } + node.Values = append(node.Values, mvnode) + ctx.progress(1) + } + return nil, errors.ErrSyntax("unterminated flow mapping", node.GetToken()) +} + +func (p *parser) parseSequence(ctx *context) (*ast.SequenceNode, error) { + node := ast.Sequence(ctx.currentToken(), true) + node.SetPath(ctx.path) + ctx.progress(1) // skip SequenceStart token + for ctx.next() { + tk := ctx.currentToken() + if tk.Type == token.SequenceEndType { + node.End = tk + break + } else if tk.Type == token.CollectEntryType { + ctx.progress(1) + continue + } + + value, err := p.parseToken(ctx.withIndex(uint(len(node.Values))), tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse sequence value in flow sequence node") + } + node.Values = append(node.Values, value) + ctx.progress(1) + } + return node, nil +} + +func (p *parser) parseTag(ctx *context) (*ast.TagNode, error) { + tagToken := ctx.currentToken() + node := ast.Tag(tagToken) + node.SetPath(ctx.path) + ctx.progress(1) // skip tag token + var ( + value ast.Node + err error + ) + switch token.ReservedTagKeyword(tagToken.Value) { + case token.MappingTag, + token.OrderedMapTag: + value, err = p.parseMapping(ctx) + case token.IntegerTag, + token.FloatTag, + token.StringTag, + token.BinaryTag, + token.TimestampTag, + token.NullTag: + typ := ctx.currentToken().Type + if typ == token.LiteralType || typ == token.FoldedType { + value, err = p.parseLiteral(ctx) + } else { + value = p.parseScalarValue(ctx.currentToken()) + } + case token.SequenceTag, + token.SetTag: + err = errors.ErrSyntax(fmt.Sprintf("sorry, currently not supported %s tag", tagToken.Value), tagToken) + default: + // custom tag + value, err = p.parseToken(ctx, ctx.currentToken()) + } + if err != nil { + return nil, errors.Wrapf(err, "failed to parse tag value") + } + node.Value = value + return node, nil +} + +func (p *parser) removeLeftSideNewLineCharacter(src string) string { + // CR or LF or CRLF + return strings.TrimLeft(strings.TrimLeft(strings.TrimLeft(src, "\r"), "\n"), "\r\n") +} + +func (p *parser) existsNewLineCharacter(src string) bool { + if strings.Index(src, "\n") > 0 { + return true + } + if strings.Index(src, "\r") > 0 { + return true + } + return false +} + +func (p *parser) validateMapKey(tk *token.Token) error { + if tk.Type != token.StringType { + return nil + } + origin := p.removeLeftSideNewLineCharacter(tk.Origin) + if p.existsNewLineCharacter(origin) { + return errors.ErrSyntax("unexpected key name", tk) + } + return nil +} + +func (p *parser) createNullToken(base *token.Token) *token.Token { + pos := *(base.Position) + pos.Column++ + return token.New("null", "null", &pos) +} + +func (p *parser) parseMapValue(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) { + node, err := p.createMapValueNode(ctx, key, colonToken) + if err != nil { + return nil, errors.Wrapf(err, "failed to create map value node") + } + if node != nil && node.GetPath() == "" { + node.SetPath(ctx.path) + } + return node, nil +} + +func (p *parser) createMapValueNode(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) { + tk := ctx.currentToken() + if tk == nil { + nullToken := p.createNullToken(colonToken) + ctx.insertToken(ctx.idx, nullToken) + return ast.Null(nullToken), nil + } + var comment *ast.CommentGroupNode + if tk.Type == token.CommentType { + comment = p.parseCommentOnly(ctx) + if comment != nil { + comment.SetPath(ctx.withChild(key.GetToken().Value).path) + } + tk = ctx.currentToken() + } + if tk.Position.Column == key.GetToken().Position.Column && tk.Type == token.StringType { + // in this case, + // ---- + // key: + // next + + nullToken := p.createNullToken(colonToken) + ctx.insertToken(ctx.idx, nullToken) + nullNode := ast.Null(nullToken) + + if comment != nil { + nullNode.SetComment(comment) + } else { + // If there is a comment, it is already bound to the key node, + // so remove the comment from the key to bind it to the null value. + keyComment := key.GetComment() + if keyComment != nil { + if err := key.SetComment(nil); err != nil { + return nil, err + } + nullNode.SetComment(keyComment) + } + } + return nullNode, nil + } + + if tk.Position.Column < key.GetToken().Position.Column { + // in this case, + // ---- + // key: + // next + nullToken := p.createNullToken(colonToken) + ctx.insertToken(ctx.idx, nullToken) + nullNode := ast.Null(nullToken) + if comment != nil { + nullNode.SetComment(comment) + } + return nullNode, nil + } + + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse mapping 'value' node") + } + if comment != nil { + value.SetComment(comment) + } + return value, nil +} + +func (p *parser) validateMapValue(ctx *context, key, value ast.Node) error { + keyColumn := key.GetToken().Position.Column + valueColumn := value.GetToken().Position.Column + if keyColumn != valueColumn { + return nil + } + if value.Type() != ast.StringType { + return nil + } + ntk := ctx.nextToken() + if ntk == nil || (ntk.Type != token.MappingValueType && ntk.Type != token.SequenceEntryType) { + return errors.ErrSyntax("could not found expected ':' token", value.GetToken()) + } + return nil +} + +func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) { + key, err := p.parseMapKey(ctx) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse map key") + } + keyText := key.GetToken().Value + key.SetPath(ctx.withChild(keyText).path) + if err := p.validateMapKey(key.GetToken()); err != nil { + return nil, errors.Wrapf(err, "validate mapping key error") + } + ctx.progress(1) // progress to mapping value token + tk := ctx.currentToken() // get mapping value token + if tk == nil { + return nil, errors.ErrSyntax("unexpected map", key.GetToken()) + } + ctx.progress(1) // progress to value token + if err := p.setSameLineCommentIfExists(ctx.withChild(keyText), key); err != nil { + return nil, errors.Wrapf(err, "failed to set same line comment to node") + } + if key.GetComment() != nil { + // if current token is comment, GetComment() is not nil. + // then progress to value token + ctx.progressIgnoreComment(1) + } + + value, err := p.parseMapValue(ctx.withChild(keyText), key, tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse map value") + } + if err := p.validateMapValue(ctx, key, value); err != nil { + return nil, errors.Wrapf(err, "failed to validate map value") + } + + mvnode := ast.MappingValue(tk, key, value) + mvnode.SetPath(ctx.withChild(keyText).path) + node := ast.Mapping(tk, false, mvnode) + node.SetPath(ctx.withChild(keyText).path) + + ntk := ctx.nextNotCommentToken() + antk := ctx.afterNextNotCommentToken() + for antk != nil && antk.Type == token.MappingValueType && + ntk.Position.Column == key.GetToken().Position.Column { + ctx.progressIgnoreComment(1) + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse mapping node") + } + switch value.Type() { + case ast.MappingType: + c := value.(*ast.MappingNode) + comment := c.GetComment() + for idx, v := range c.Values { + if idx == 0 && comment != nil { + if err := v.SetComment(comment); err != nil { + return nil, errors.Wrapf(err, "failed to set comment token to node") + } + } + node.Values = append(node.Values, v) + } + case ast.MappingValueType: + node.Values = append(node.Values, value.(*ast.MappingValueNode)) + default: + return nil, xerrors.Errorf("failed to parse mapping value node node is %s", value.Type()) + } + ntk = ctx.nextNotCommentToken() + antk = ctx.afterNextNotCommentToken() + } + if len(node.Values) == 1 { + mapKeyCol := mvnode.Key.GetToken().Position.Column + commentTk := ctx.nextToken() + if commentTk != nil && commentTk.Type == token.CommentType && mapKeyCol <= commentTk.Position.Column { + // If the comment is in the same or deeper column as the last element column in map value, + // treat it as a footer comment for the last element. + comment := p.parseFootComment(ctx, mapKeyCol) + mvnode.FootComment = comment + } + return mvnode, nil + } + mapCol := node.GetToken().Position.Column + commentTk := ctx.nextToken() + if commentTk != nil && commentTk.Type == token.CommentType && mapCol <= commentTk.Position.Column { + // If the comment is in the same or deeper column as the last element column in map value, + // treat it as a footer comment for the last element. + comment := p.parseFootComment(ctx, mapCol) + node.FootComment = comment + } + return node, nil +} + +func (p *parser) parseSequenceEntry(ctx *context) (*ast.SequenceNode, error) { + tk := ctx.currentToken() + sequenceNode := ast.Sequence(tk, false) + sequenceNode.SetPath(ctx.path) + curColumn := tk.Position.Column + for tk.Type == token.SequenceEntryType { + ctx.progress(1) // skip sequence token + tk = ctx.currentToken() + if tk == nil { + return nil, errors.ErrSyntax("empty sequence entry", ctx.previousToken()) + } + var comment *ast.CommentGroupNode + if tk.Type == token.CommentType { + comment = p.parseCommentOnly(ctx) + tk = ctx.currentToken() + if tk.Type == token.SequenceEntryType { + ctx.progress(1) // skip sequence token + } + } + value, err := p.parseToken(ctx.withIndex(uint(len(sequenceNode.Values))), ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse sequence") + } + if comment != nil { + comment.SetPath(ctx.withIndex(uint(len(sequenceNode.Values))).path) + sequenceNode.ValueHeadComments = append(sequenceNode.ValueHeadComments, comment) + } else { + sequenceNode.ValueHeadComments = append(sequenceNode.ValueHeadComments, nil) + } + sequenceNode.Values = append(sequenceNode.Values, value) + tk = ctx.nextNotCommentToken() + if tk == nil { + break + } + if tk.Type != token.SequenceEntryType { + break + } + if tk.Position.Column != curColumn { + break + } + ctx.progressIgnoreComment(1) + } + commentTk := ctx.nextToken() + if commentTk != nil && commentTk.Type == token.CommentType && curColumn <= commentTk.Position.Column { + // If the comment is in the same or deeper column as the last element column in sequence value, + // treat it as a footer comment for the last element. + comment := p.parseFootComment(ctx, curColumn) + sequenceNode.FootComment = comment + } + return sequenceNode, nil +} + +func (p *parser) parseAnchor(ctx *context) (*ast.AnchorNode, error) { + tk := ctx.currentToken() + anchor := ast.Anchor(tk) + anchor.SetPath(ctx.path) + ntk := ctx.nextToken() + if ntk == nil { + return nil, errors.ErrSyntax("unexpected anchor. anchor name is undefined", tk) + } + ctx.progress(1) // skip anchor token + name, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parser anchor name node") + } + anchor.Name = name + ntk = ctx.nextToken() + if ntk == nil { + return nil, errors.ErrSyntax("unexpected anchor. anchor value is undefined", ctx.currentToken()) + } + ctx.progress(1) + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parser anchor name node") + } + anchor.Value = value + return anchor, nil +} + +func (p *parser) parseAlias(ctx *context) (*ast.AliasNode, error) { + tk := ctx.currentToken() + alias := ast.Alias(tk) + alias.SetPath(ctx.path) + ntk := ctx.nextToken() + if ntk == nil { + return nil, errors.ErrSyntax("unexpected alias. alias name is undefined", tk) + } + ctx.progress(1) // skip alias token + name, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parser alias name node") + } + alias.Value = name + return alias, nil +} + +func (p *parser) parseMapKey(ctx *context) (ast.MapKeyNode, error) { + tk := ctx.currentToken() + if value := p.parseScalarValue(tk); value != nil { + return value, nil + } + switch tk.Type { + case token.MergeKeyType: + return ast.MergeKey(tk), nil + case token.MappingKeyType: + return p.parseMappingKey(ctx) + } + return nil, errors.ErrSyntax("unexpected mapping key", tk) +} + +func (p *parser) parseStringValue(tk *token.Token) *ast.StringNode { + switch tk.Type { + case token.StringType, + token.SingleQuoteType, + token.DoubleQuoteType: + return ast.String(tk) + } + return nil +} + +func (p *parser) parseScalarValueWithComment(ctx *context, tk *token.Token) (ast.ScalarNode, error) { + node := p.parseScalarValue(tk) + if node == nil { + return nil, nil + } + node.SetPath(ctx.path) + if p.isSameLineComment(ctx.nextToken(), node) { + ctx.progress(1) + if err := p.setSameLineCommentIfExists(ctx, node); err != nil { + return nil, errors.Wrapf(err, "failed to set same line comment to node") + } + } + return node, nil +} + +func (p *parser) parseScalarValue(tk *token.Token) ast.ScalarNode { + if node := p.parseStringValue(tk); node != nil { + return node + } + switch tk.Type { + case token.NullType: + return ast.Null(tk) + case token.BoolType: + return ast.Bool(tk) + case token.IntegerType, + token.BinaryIntegerType, + token.OctetIntegerType, + token.HexIntegerType: + return ast.Integer(tk) + case token.FloatType: + return ast.Float(tk) + case token.InfinityType: + return ast.Infinity(tk) + case token.NanType: + return ast.Nan(tk) + } + return nil +} + +func (p *parser) parseDirective(ctx *context) (*ast.DirectiveNode, error) { + node := ast.Directive(ctx.currentToken()) + ctx.progress(1) // skip directive token + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse directive value") + } + node.Value = value + ctx.progress(1) + tk := ctx.currentToken() + if tk == nil { + // Since current token is nil, use the previous token to specify + // the syntax error location. + return nil, errors.ErrSyntax("unexpected directive value. document not started", ctx.previousToken()) + } + if tk.Type != token.DocumentHeaderType { + return nil, errors.ErrSyntax("unexpected directive value. document not started", ctx.currentToken()) + } + return node, nil +} + +func (p *parser) parseLiteral(ctx *context) (*ast.LiteralNode, error) { + node := ast.Literal(ctx.currentToken()) + ctx.progress(1) // skip literal/folded token + + tk := ctx.currentToken() + var comment *ast.CommentGroupNode + if tk.Type == token.CommentType { + comment = p.parseCommentOnly(ctx) + comment.SetPath(ctx.path) + if err := node.SetComment(comment); err != nil { + return nil, errors.Wrapf(err, "failed to set comment to literal") + } + tk = ctx.currentToken() + } + value, err := p.parseToken(ctx, tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse literal/folded value") + } + snode, ok := value.(*ast.StringNode) + if !ok { + return nil, errors.ErrSyntax("unexpected token. required string token", value.GetToken()) + } + node.Value = snode + return node, nil +} + +func (p *parser) isSameLineComment(tk *token.Token, node ast.Node) bool { + if tk == nil { + return false + } + if tk.Type != token.CommentType { + return false + } + return tk.Position.Line == node.GetToken().Position.Line +} + +func (p *parser) setSameLineCommentIfExists(ctx *context, node ast.Node) error { + tk := ctx.currentToken() + if !p.isSameLineComment(tk, node) { + return nil + } + comment := ast.CommentGroup([]*token.Token{tk}) + comment.SetPath(ctx.path) + if err := node.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment token to ast.Node") + } + return nil +} + +func (p *parser) parseDocument(ctx *context) (*ast.DocumentNode, error) { + startTk := ctx.currentToken() + ctx.progress(1) // skip document header token + body, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse document body") + } + node := ast.Document(startTk, body) + if ntk := ctx.nextToken(); ntk != nil && ntk.Type == token.DocumentEndType { + node.End = ntk + ctx.progress(1) + } + return node, nil +} + +func (p *parser) parseCommentOnly(ctx *context) *ast.CommentGroupNode { + commentTokens := []*token.Token{} + for { + tk := ctx.currentToken() + if tk == nil { + break + } + if tk.Type != token.CommentType { + break + } + commentTokens = append(commentTokens, tk) + ctx.progressIgnoreComment(1) // skip comment token + } + return ast.CommentGroup(commentTokens) +} + +func (p *parser) parseFootComment(ctx *context, col int) *ast.CommentGroupNode { + commentTokens := []*token.Token{} + for { + ctx.progressIgnoreComment(1) + commentTokens = append(commentTokens, ctx.currentToken()) + + nextTk := ctx.nextToken() + if nextTk == nil { + break + } + if nextTk.Type != token.CommentType { + break + } + if col > nextTk.Position.Column { + break + } + } + return ast.CommentGroup(commentTokens) +} + +func (p *parser) parseComment(ctx *context) (ast.Node, error) { + group := p.parseCommentOnly(ctx) + node, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse node after comment") + } + if node == nil { + return group, nil + } + group.SetPath(node.GetPath()) + if err := node.SetComment(group); err != nil { + return nil, errors.Wrapf(err, "failed to set comment token to node") + } + return node, nil +} + +func (p *parser) parseMappingKey(ctx *context) (*ast.MappingKeyNode, error) { + keyTk := ctx.currentToken() + node := ast.MappingKey(keyTk) + node.SetPath(ctx.path) + ctx.progress(1) // skip mapping key token + value, err := p.parseToken(ctx.withChild(keyTk.Value), ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse map key") + } + node.Value = value + return node, nil +} + +func (p *parser) parseToken(ctx *context, tk *token.Token) (ast.Node, error) { + node, err := p.createNodeFromToken(ctx, tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to create node from token") + } + if node != nil && node.GetPath() == "" { + node.SetPath(ctx.path) + } + return node, nil +} + +func (p *parser) createNodeFromToken(ctx *context, tk *token.Token) (ast.Node, error) { + if tk == nil { + return nil, nil + } + if tk.NextType() == token.MappingValueType { + node, err := p.parseMappingValue(ctx) + return node, err + } + node, err := p.parseScalarValueWithComment(ctx, tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse scalar value") + } + if node != nil { + return node, nil + } + switch tk.Type { + case token.CommentType: + return p.parseComment(ctx) + case token.MappingKeyType: + return p.parseMappingKey(ctx) + case token.DocumentHeaderType: + return p.parseDocument(ctx) + case token.MappingStartType: + return p.parseMapping(ctx) + case token.SequenceStartType: + return p.parseSequence(ctx) + case token.SequenceEntryType: + return p.parseSequenceEntry(ctx) + case token.AnchorType: + return p.parseAnchor(ctx) + case token.AliasType: + return p.parseAlias(ctx) + case token.DirectiveType: + return p.parseDirective(ctx) + case token.TagType: + return p.parseTag(ctx) + case token.LiteralType, token.FoldedType: + return p.parseLiteral(ctx) + } + return nil, nil +} + +func (p *parser) parse(tokens token.Tokens, mode Mode) (*ast.File, error) { + ctx := newContext(tokens, mode) + file := &ast.File{Docs: []*ast.DocumentNode{}} + for ctx.next() { + node, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse") + } + ctx.progressIgnoreComment(1) + if node == nil { + continue + } + if doc, ok := node.(*ast.DocumentNode); ok { + file.Docs = append(file.Docs, doc) + } else { + file.Docs = append(file.Docs, ast.Document(nil, node)) + } + } + return file, nil +} + +type Mode uint + +const ( + ParseComments Mode = 1 << iota // parse comments and add them to AST +) + +// ParseBytes parse from byte slice, and returns ast.File +func ParseBytes(bytes []byte, mode Mode) (*ast.File, error) { + tokens := lexer.Tokenize(string(bytes)) + f, err := Parse(tokens, mode) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse") + } + return f, nil +} + +// Parse parse from token instances, and returns ast.File +func Parse(tokens token.Tokens, mode Mode) (*ast.File, error) { + var p parser + f, err := p.parse(tokens, mode) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse") + } + return f, nil +} + +// Parse parse from filename, and returns ast.File +func ParseFile(filename string, mode Mode) (*ast.File, error) { + file, err := ioutil.ReadFile(filename) + if err != nil { + return nil, errors.Wrapf(err, "failed to read file: %s", filename) + } + f, err := ParseBytes(file, mode) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse") + } + f.Name = filename + return f, nil +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/parser_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/parser_test.go new file mode 100644 index 0000000..8d697e8 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/parser_test.go @@ -0,0 +1,1010 @@ +package parser_test + +import ( + "fmt" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/lexer" + "github.com/goccy/go-yaml/parser" + "github.com/goccy/go-yaml/token" +) + +func TestParser(t *testing.T) { + sources := []string{ + "null\n", + "{}\n", + "v: hi\n", + "v: \"true\"\n", + "v: \"false\"\n", + "v: true\n", + "v: false\n", + "v: 10\n", + "v: -10\n", + "v: 42\n", + "v: 4294967296\n", + "v: \"10\"\n", + "v: 0.1\n", + "v: 0.99\n", + "v: -0.1\n", + "v: .inf\n", + "v: -.inf\n", + "v: .nan\n", + "v: null\n", + "v: \"\"\n", + "v:\n- A\n- B\n", + "a: '-'\n", + "123\n", + "hello: world\n", + "a: null\n", + "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", + "a:\n b: c\n", + "a: {x: 1}\n", + "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", + "a: [1, 2]\n", + "a: {b: c, d: e}\n", + "a: 3s\n", + "a: \n", + "a: \"1:1\"\n", + "a: 1.2.3.4\n", + "a: \"2015-02-24T18:19:39Z\"\n", + "a: 'b: c'\n", + "a: 'Hello #comment'\n", + "a: abc <> ghi", + "a: <-\n B\n C\n", + "v: |-\n 0\n", + "v: |-\n 0\nx: 0", + `"a\n1\nb"`, + `{"a":"b"}`, + `!!map { + ? !!str "explicit":!!str "entry", + ? !!str "implicit" : !!str "entry", + ? !!null "" : !!null "", +}`, + "\"a\": a\n\"b\": b", + "'a': a\n'b': b", + } + for _, src := range sources { + if _, err := parser.Parse(lexer.Tokenize(src), 0); err != nil { + t.Fatalf("parse error: source [%s]: %+v", src, err) + } + } +} + +func TestParseComplicatedDocument(t *testing.T) { + tests := []struct { + source string + expect string + }{ + { + ` +american: + - Boston Red Sox + - Detroit Tigers + - New York Yankees +national: + - New York Mets + - Chicago Cubs + - Atlanta Braves +`, ` +american: + - Boston Red Sox + - Detroit Tigers + - New York Yankees +national: + - New York Mets + - Chicago Cubs + - Atlanta Braves +`, + }, + { + ` +a: + b: c + d: e + f: g +h: + i: j + k: + l: m + n: o + p: q +r: s +`, ` +a: + b: c + d: e + f: g +h: + i: j + k: + l: m + n: o + p: q +r: s +`, + }, + { + ` +- a: + - b + - c +- d +`, ` +- a: + - b + - c +- d +`, + }, + { + ` +- a +- b +- c + - d + - e +- f +`, ` +- a +- b +- c - d - e +- f +`, + }, + { + ` +a: 0 - 1 +`, + ` +a: 0 - 1 +`, + }, + {` +- a: + b: c + d: e +- f: + g: h +`, + ` +- a: + b: c + d: e +- f: null + g: h +`, + }, + { + ` +a: + b + c +d: e +`, ` +a: b c +d: e +`, + }, + { + ` +a +b +c +`, ` +a b c +`, + }, + { + ` +a: + - b + - c +`, ` +a: + - b + - c +`, + }, + { + ` +- a : + b: c +`, ` +- a: null + b: c +`, + }, + { + ` +- a: + b + c + d + hoge: fuga +`, ` +- a: b c d + hoge: fuga +`, + }, + { + ` +- a # ' " # - : % +- b # " # - : % ' +- c # # - : % ' " +- d # - : % ' " # +- e # : % ' " # - +- f # % ' : # - : +`, + ` +- a +- b +- c +- d +- e +- f +`, + }, + { + ` +# comment +a: # comment +# comment + b: c # comment + # comment +d: e # comment +# comment +`, + ` +a: + b: c +d: e +`, + }, + { + ` +a: b#notcomment +`, + ` +a: b#notcomment +`, + }, + { + ` +anchored: &anchor foo +aliased: *anchor +`, + ` +anchored: &anchor foo +aliased: *anchor +`, + }, + { + ` +--- +- &CENTER { x: 1, y: 2 } +- &LEFT { x: 0, y: 2 } +- &BIG { r: 10 } +- &SMALL { r: 1 } + +# All the following maps are equal: + +- # Explicit keys + x: 1 + y: 2 + r: 10 + label: center/big + +- # Merge one map + << : *CENTER + r: 10 + label: center/big + +- # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + +- # Override + << : [ *BIG, *LEFT, *SMALL ] + x: 1 + label: center/big +`, + ` +--- +- &CENTER {x: 1, y: 2} +- &LEFT {x: 0, y: 2} +- &BIG {r: 10} +- &SMALL {r: 1} +- x: 1 + y: 2 + r: 10 + label: center/big +- <<: *CENTER + r: 10 + label: center/big +- <<: [*CENTER, *BIG] + label: center/big +- <<: [*BIG, *LEFT, *SMALL] + x: 1 + label: center/big +`, + }, + { + ` +a: +- - b +- - c + - d +`, + ` +a: +- - b +- - c + - d +`, + }, + { + ` +a: + b: + c: d + e: + f: g + h: i +j: k +`, + ` +a: + b: + c: d + e: + f: g + h: i +j: k +`, + }, + { + ` +--- +a: 1 +b: 2 +... +--- +c: 3 +d: 4 +... +`, + ` +--- +a: 1 +b: 2 +... +--- +c: 3 +d: 4 +... +`, + }, + { + ` +a: + b: | + { + [ 1, 2 ] + } + c: d +`, + ` +a: + b: | + { + [ 1, 2 ] + } + c: d +`, + }, + { + ` +| + hoge + fuga + piyo`, + ` +| + hoge + fuga + piyo +`, + }, + { + ` +a: | + bbbbbbb + + + ccccccc +d: eeeeeeeeeeeeeeeee +`, + ` +a: | + bbbbbbb + + + ccccccc +d: eeeeeeeeeeeeeeeee +`, + }, + { + ` +a: b + c +`, + ` +a: b c +`, + }, + { + ` +a: + b: c +`, + ` +a: + b: c +`, + }, + { + ` +a: b +c: d +`, + ` +a: b +c: d +`, + }, + { + ` +- ab - cd +- ef - gh +`, + ` +- ab - cd +- ef - gh +`, + }, + { + ` +- 0 - 1 + - 2 - 3 +`, + ` +- 0 - 1 - 2 - 3 +`, + }, + { + ` +a - b - c: value +`, + ` +a - b - c: value +`, + }, + { + ` +a: +- + b: c + d: e +- + f: g + h: i +`, + ` +a: +- b: c + d: e +- f: g + h: i +`, + }, + { + ` +a: |- + value +b: c +`, + ` +a: |- + value +b: c +`, + }, + { + ` +a: |+ + value +b: c +`, + ` +a: |+ + value +b: c +`, + }, + { + ` +- key1: val + key2: + ( + foo + + + bar + ) +`, + ` +- key1: val + key2: ( foo + bar ) +`, + }, + { + ` +"a": b +'c': d +"e": "f" +g: "h" +i: 'j' +`, + ` +"a": b +'c': d +"e": "f" +g: "h" +i: 'j' +`, + }, + } + + for _, test := range tests { + t.Run(test.source, func(t *testing.T) { + tokens := lexer.Tokenize(test.source) + f, err := parser.Parse(tokens, 0) + if err != nil { + t.Fatalf("%+v", err) + } + var v Visitor + for _, doc := range f.Docs { + ast.Walk(&v, doc.Body) + } + expect := fmt.Sprintf("\n%+v", f) + if test.expect != expect { + tokens.Dump() + t.Fatalf("unexpected output: [%s] != [%s]", test.expect, expect) + } + }) + } +} + +func TestNewLineChar(t *testing.T) { + for _, f := range []string{ + "lf.yml", + "cr.yml", + "crlf.yml", + } { + ast, err := parser.ParseFile(filepath.Join("testdata", f), 0) + if err != nil { + t.Fatalf("%+v", err) + } + actual := fmt.Sprintf("%v", ast) + expect := `a: "a" +b: 1 +` + if expect != actual { + t.Fatal("unexpected result") + } + } +} + +func TestSyntaxError(t *testing.T) { + tests := []struct { + source string + expect string + }{ + { + ` +a: +- b + c: d + e: f + g: h`, + ` +[3:3] unexpected key name + 2 | a: +> 3 | - b + 4 | c: d + ^ + 5 | e: f + 6 | g: h`, + }, + { + ` +a +- b: c`, + ` +[2:1] unexpected key name +> 2 | a + 3 | - b: c + ^ +`, + }, + { + `%YAML 1.1 {}`, + ` +[1:2] unexpected directive value. document not started +> 1 | %YAML 1.1 {} + ^ +`, + }, + { + `{invalid`, + ` +[1:2] unexpected map +> 1 | {invalid + ^ +`, + }, + { + `{ "key": "value" `, + ` +[1:1] unterminated flow mapping +> 1 | { "key": "value" + ^ +`, + }, + { + ` +a: +- b: c +- `, + ` +[4:1] empty sequence entry + 2 | a: + 3 | - b: c +> 4 | - + ^ +`, + }, + } + for _, test := range tests { + t.Run(test.source, func(t *testing.T) { + _, err := parser.ParseBytes([]byte(test.source), 0) + if err == nil { + t.Fatal("cannot catch syntax error") + } + actual := "\n" + err.Error() + if test.expect != actual { + t.Fatalf("expected: [%s] but got [%s]", test.expect, actual) + } + }) + } +} + +func TestComment(t *testing.T) { + tests := []struct { + name string + yaml string + }{ + { + name: "map with comment", + yaml: ` +# commentA +a: #commentB + # commentC + b: c # commentD + # commentE + d: e # commentF + # commentG + f: g # commentH +# commentI +f: g # commentJ +# commentK +`, + }, + { + name: "sequence with comment", + yaml: ` +# commentA +- a # commentB +# commentC +- b: # commentD + # commentE + - d # commentF + - e # commentG +# commentH +`, + }, + { + name: "anchor and alias", + yaml: ` +a: &x b # commentA +c: *x # commentB +`, + }, + { + name: "multiline", + yaml: ` +# foo comment +# foo comment2 +foo: # map key comment + # bar above comment + # bar above comment2 + bar: 10 # comment for bar + # baz above comment + # baz above comment2 + baz: bbbb # comment for baz + piyo: # sequence key comment + # sequence1 above comment 1 + # sequence1 above comment 2 + - sequence1 # sequence1 + # sequence2 above comment 1 + # sequence2 above comment 2 + - sequence2 # sequence2 + # sequence3 above comment 1 + # sequence3 above comment 2 + - false # sequence3 +# foo2 comment +# foo2 comment2 +foo2: &anchor text # anchor comment +# foo3 comment +# foo3 comment2 +foo3: *anchor # alias comment +`, + }, + { + name: "literal", + yaml: ` +foo: | # comment + x: 42 +`, + }, + { + name: "folded", + yaml: ` +foo: > # comment + x: 42 +`, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + f, err := parser.ParseBytes([]byte(test.yaml), parser.ParseComments) + if err != nil { + t.Fatalf("%+v", err) + } + got := "\n" + f.String() + if test.yaml != got { + t.Fatalf("expected:%s\ngot:%s", test.yaml, got) + } + }) + } +} + +func TestCommentWithNull(t *testing.T) { + t.Run("same line", func(t *testing.T) { + content := ` +foo: + bar: # comment + baz: 1 +` + expected := ` +foo: + bar: null # comment + baz: 1` + f, err := parser.ParseBytes([]byte(content), parser.ParseComments) + if err != nil { + t.Fatal(err) + } + if len(f.Docs) != 1 { + t.Fatal("failed to parse content with same line comment") + } + if f.Docs[0].String() != strings.TrimPrefix(expected, "\n") { + t.Fatal("failed to parse comment") + } + }) + t.Run("next line", func(t *testing.T) { + content := ` +foo: + bar: + # comment + baz: 1 +` + expected := ` +foo: + bar: null # comment + baz: 1` + f, err := parser.ParseBytes([]byte(content), parser.ParseComments) + if err != nil { + t.Fatal(err) + } + if len(f.Docs) != 1 { + t.Fatal("failed to parse content with next line comment") + } + if f.Docs[0].String() != strings.TrimPrefix(expected, "\n") { + t.Fatal("failed to parse comment") + } + }) + t.Run("next line and different indent", func(t *testing.T) { + content := ` +foo: + bar: + # comment +baz: 1` + f, err := parser.ParseBytes([]byte(content), parser.ParseComments) + if err != nil { + t.Fatal(err) + } + if len(f.Docs) != 1 { + t.Fatal("failed to parse content with next line comment") + } + expected := ` +foo: + bar: null # comment +baz: 1` + if f.Docs[0].String() != strings.TrimPrefix(expected, "\n") { + t.Fatal("failed to parse comment") + } + }) +} + +func TestSequenceComment(t *testing.T) { + content := ` +foo: + - # comment + bar: 1 +baz: + - xxx +` + f, err := parser.ParseBytes([]byte(content), parser.ParseComments) + if err != nil { + t.Fatal(err) + } + if len(f.Docs) != 1 { + t.Fatal("failed to parse content with next line with sequence") + } + expected := ` +foo: + # comment + - bar: 1 +baz: + - xxx` + if f.Docs[0].String() != strings.TrimPrefix(expected, "\n") { + t.Fatal("failed to parse comment") + } + t.Run("foo[0].bar", func(t *testing.T) { + path, err := yaml.PathString("$.foo[0].bar") + if err != nil { + t.Fatal(err) + } + v, err := path.FilterFile(f) + if err != nil { + t.Fatal(err) + } + if v.String() != "1" { + t.Fatal("failed to get foo[0].bar value") + } + }) + t.Run("baz[0]", func(t *testing.T) { + path, err := yaml.PathString("$.baz[0]") + if err != nil { + t.Fatal(err) + } + v, err := path.FilterFile(f) + if err != nil { + t.Fatal(err) + } + if v.String() != "xxx" { + t.Fatal("failed to get baz[0] value") + } + }) +} + +func TestNodePath(t *testing.T) { + yml := ` +a: # commentA + b: # commentB + c: foo # commentC + d: bar # commentD + e: baz # commentE + f: # commentF + g: hoge # commentG + h: # commentH + - list1 # comment list1 + - list2 # comment list2 + - list3 # comment list3 + i: fuga # commentI +j: piyo # commentJ +k.l.m.n: moge # commentKLMN +o#p: hogera # commentOP +q#.r: hogehoge # commentQR +` + f, err := parser.ParseBytes([]byte(yml), parser.ParseComments) + if err != nil { + t.Fatalf("%+v", err) + } + var capturer pathCapturer + for _, doc := range f.Docs { + ast.Walk(&capturer, doc.Body) + } + commentPaths := []string{} + for i := 0; i < capturer.capturedNum; i++ { + if capturer.orderedTypes[i] == ast.CommentType { + commentPaths = append(commentPaths, capturer.orderedPaths[i]) + } + } + expectedPaths := []string{ + "$.a", + "$.a.b", + "$.a.b.c", + "$.a.b.d", + "$.a.b.e", + "$.a.f", + "$.a.f.g", + "$.a.h", + "$.a.h[0]", + "$.a.h[1]", + "$.a.h[2]", + "$.a.i", + "$.j", + "$.'k.l.m.n'", + "$.o#p", + "$.'q#.r'", + } + if !reflect.DeepEqual(expectedPaths, commentPaths) { + t.Fatalf("failed to get YAMLPath to the comment node:\nexpected[%s]\ngot [%s]", expectedPaths, commentPaths) + } +} + +type pathCapturer struct { + capturedNum int + orderedPaths []string + orderedTypes []ast.NodeType + orderedTokens []*token.Token +} + +func (c *pathCapturer) Visit(node ast.Node) ast.Visitor { + c.capturedNum++ + c.orderedPaths = append(c.orderedPaths, node.GetPath()) + c.orderedTypes = append(c.orderedTypes, node.Type()) + c.orderedTokens = append(c.orderedTokens, node.GetToken()) + return c +} + +type Visitor struct { +} + +func (v *Visitor) Visit(node ast.Node) ast.Visitor { + tk := node.GetToken() + tk.Prev = nil + tk.Next = nil + return v +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/cr.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/cr.yml new file mode 100644 index 0000000..37b52a6 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/cr.yml @@ -0,0 +1 @@ +a: "a" b: 1 \ No newline at end of file diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/crlf.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/crlf.yml new file mode 100644 index 0000000..85929f9 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/crlf.yml @@ -0,0 +1,3 @@ +a: "a" + +b: 1 diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/lf.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/lf.yml new file mode 100644 index 0000000..d2fe51f --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/parser/testdata/lf.yml @@ -0,0 +1,3 @@ +a: "a" + +b: 1 diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/path.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/path.go new file mode 100644 index 0000000..72554bd --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/path.go @@ -0,0 +1,814 @@ +package yaml + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/parser" + "github.com/goccy/go-yaml/printer" +) + +// PathString create Path from string +// +// YAMLPath rule +// $ : the root object/element +// . : child operator +// .. : recursive descent +// [num] : object/element of array by number +// [*] : all objects/elements for array. +// +// If you want to use reserved characters such as `.` and `*` as a key name, +// enclose them in single quotation as follows ( $.foo.'bar.baz-*'.hoge ). +// If you want to use a single quote with reserved characters, escape it with `\` ( $.foo.'bar.baz\'s value'.hoge ). +func PathString(s string) (*Path, error) { + buf := []rune(s) + length := len(buf) + cursor := 0 + builder := &PathBuilder{} + for cursor < length { + c := buf[cursor] + switch c { + case '$': + builder = builder.Root() + cursor++ + case '.': + b, buf, c, err := parsePathDot(builder, buf, cursor) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse path of dot") + } + length = len(buf) + builder = b + cursor = c + case '[': + b, buf, c, err := parsePathIndex(builder, buf, cursor) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse path of index") + } + length = len(buf) + builder = b + cursor = c + default: + return nil, errors.Wrapf(ErrInvalidPathString, "invalid path at %d", cursor) + } + } + return builder.Build(), nil +} + +func parsePathRecursive(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + length := len(buf) + cursor += 2 // skip .. characters + start := cursor + for ; cursor < length; cursor++ { + c := buf[cursor] + switch c { + case '$': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '..' character") + case '*': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '..' character") + case '.', '[': + goto end + case ']': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '..' character") + } + } +end: + if start == cursor { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "not found recursive selector") + } + return b.Recursive(string(buf[start:cursor])), buf, cursor, nil +} + +func parsePathDot(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + length := len(buf) + if cursor+1 < length && buf[cursor+1] == '.' { + b, buf, c, err := parsePathRecursive(b, buf, cursor) + if err != nil { + return nil, nil, 0, errors.Wrapf(err, "failed to parse path of recursive") + } + return b, buf, c, nil + } + cursor++ // skip . character + start := cursor + + // if started single quote, looking for end single quote char + if cursor < length && buf[cursor] == '\'' { + return parseQuotedKey(b, buf, cursor) + } + for ; cursor < length; cursor++ { + c := buf[cursor] + switch c { + case '$': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") + case '*': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") + case '.', '[': + goto end + case ']': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") + } + } +end: + if start == cursor { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "cloud not find by empty key") + } + return b.child(string(buf[start:cursor])), buf, cursor, nil +} + +func parseQuotedKey(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + cursor++ // skip single quote + start := cursor + length := len(buf) + var foundEndDelim bool + for ; cursor < length; cursor++ { + switch buf[cursor] { + case '\\': + buf = append(append([]rune{}, buf[:cursor]...), buf[cursor+1:]...) + length = len(buf) + case '\'': + foundEndDelim = true + goto end + } + } +end: + if !foundEndDelim { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find end delimiter for key") + } + if start == cursor { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find by empty key") + } + selector := buf[start:cursor] + cursor++ + if cursor < length { + switch buf[cursor] { + case '$': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") + case '*': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") + case ']': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") + } + } + return b.child(string(selector)), buf, cursor, nil +} + +func parsePathIndex(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + length := len(buf) + cursor++ // skip '[' character + if length <= cursor { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "unexpected end of YAML Path") + } + c := buf[cursor] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*': + start := cursor + cursor++ + for ; cursor < length; cursor++ { + c := buf[cursor] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + continue + } + break + } + if buf[cursor] != ']' { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", string(buf[cursor]), cursor) + } + numOrAll := string(buf[start:cursor]) + if numOrAll == "*" { + return b.IndexAll(), buf, cursor + 1, nil + } + num, err := strconv.ParseInt(numOrAll, 10, 64) + if err != nil { + return nil, nil, 0, errors.Wrapf(err, "failed to parse number") + } + return b.Index(uint(num)), buf, cursor + 1, nil + } + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", c, cursor) +} + +// Path represent YAMLPath ( like a JSONPath ). +type Path struct { + node pathNode +} + +// String path to text. +func (p *Path) String() string { + return p.node.String() +} + +// Read decode from r and set extracted value by YAMLPath to v. +func (p *Path) Read(r io.Reader, v interface{}) error { + node, err := p.ReadNode(r) + if err != nil { + return errors.Wrapf(err, "failed to read node") + } + if err := Unmarshal([]byte(node.String()), v); err != nil { + return errors.Wrapf(err, "failed to unmarshal") + } + return nil +} + +// ReadNode create AST from r and extract node by YAMLPath. +func (p *Path) ReadNode(r io.Reader) (ast.Node, error) { + if p.node == nil { + return nil, ErrInvalidPath + } + var buf bytes.Buffer + if _, err := io.Copy(&buf, r); err != nil { + return nil, errors.Wrapf(err, "failed to copy from reader") + } + f, err := parser.ParseBytes(buf.Bytes(), 0) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse yaml") + } + node, err := p.FilterFile(f) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter from ast.File") + } + return node, nil +} + +// Filter filter from target by YAMLPath and set it to v. +func (p *Path) Filter(target, v interface{}) error { + b, err := Marshal(target) + if err != nil { + return errors.Wrapf(err, "failed to marshal target value") + } + if err := p.Read(bytes.NewBuffer(b), v); err != nil { + return errors.Wrapf(err, "failed to read") + } + return nil +} + +// FilterFile filter from ast.File by YAMLPath. +func (p *Path) FilterFile(f *ast.File) (ast.Node, error) { + for _, doc := range f.Docs { + node, err := p.FilterNode(doc.Body) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node) + } + if node != nil { + return node, nil + } + } + return nil, errors.Wrapf(ErrNotFoundNode, "failed to find path ( %s )", p.node) +} + +// FilterNode filter from node by YAMLPath. +func (p *Path) FilterNode(node ast.Node) (ast.Node, error) { + n, err := p.node.filter(node) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node) + } + return n, nil +} + +// MergeFromReader merge YAML text into ast.File. +func (p *Path) MergeFromReader(dst *ast.File, src io.Reader) error { + var buf bytes.Buffer + if _, err := io.Copy(&buf, src); err != nil { + return errors.Wrapf(err, "failed to copy from reader") + } + file, err := parser.ParseBytes(buf.Bytes(), 0) + if err != nil { + return errors.Wrapf(err, "failed to parse") + } + if err := p.MergeFromFile(dst, file); err != nil { + return errors.Wrapf(err, "failed to merge file") + } + return nil +} + +// MergeFromFile merge ast.File into ast.File. +func (p *Path) MergeFromFile(dst *ast.File, src *ast.File) error { + base, err := p.FilterFile(dst) + if err != nil { + return errors.Wrapf(err, "failed to filter file") + } + for _, doc := range src.Docs { + if err := ast.Merge(base, doc); err != nil { + return errors.Wrapf(err, "failed to merge") + } + } + return nil +} + +// MergeFromNode merge ast.Node into ast.File. +func (p *Path) MergeFromNode(dst *ast.File, src ast.Node) error { + base, err := p.FilterFile(dst) + if err != nil { + return errors.Wrapf(err, "failed to filter file") + } + if err := ast.Merge(base, src); err != nil { + return errors.Wrapf(err, "failed to merge") + } + return nil +} + +// ReplaceWithReader replace ast.File with io.Reader. +func (p *Path) ReplaceWithReader(dst *ast.File, src io.Reader) error { + var buf bytes.Buffer + if _, err := io.Copy(&buf, src); err != nil { + return errors.Wrapf(err, "failed to copy from reader") + } + file, err := parser.ParseBytes(buf.Bytes(), 0) + if err != nil { + return errors.Wrapf(err, "failed to parse") + } + if err := p.ReplaceWithFile(dst, file); err != nil { + return errors.Wrapf(err, "failed to replace file") + } + return nil +} + +// ReplaceWithFile replace ast.File with ast.File. +func (p *Path) ReplaceWithFile(dst *ast.File, src *ast.File) error { + for _, doc := range src.Docs { + if err := p.ReplaceWithNode(dst, doc); err != nil { + return errors.Wrapf(err, "failed to replace file by path ( %s )", p.node) + } + } + return nil +} + +// ReplaceNode replace ast.File with ast.Node. +func (p *Path) ReplaceWithNode(dst *ast.File, node ast.Node) error { + for _, doc := range dst.Docs { + if node.Type() == ast.DocumentType { + node = node.(*ast.DocumentNode).Body + } + if err := p.node.replace(doc.Body, node); err != nil { + return errors.Wrapf(err, "failed to replace node by path ( %s )", p.node) + } + } + return nil +} + +// AnnotateSource add annotation to passed source ( see section 5.1 in README.md ). +func (p *Path) AnnotateSource(source []byte, colored bool) ([]byte, error) { + file, err := parser.ParseBytes([]byte(source), 0) + if err != nil { + return nil, err + } + node, err := p.FilterFile(file) + if err != nil { + return nil, err + } + var pp printer.Printer + return []byte(pp.PrintErrorToken(node.GetToken(), colored)), nil +} + +// PathBuilder represent builder for YAMLPath. +type PathBuilder struct { + root *rootNode + node pathNode +} + +// Root add '$' to current path. +func (b *PathBuilder) Root() *PathBuilder { + root := newRootNode() + return &PathBuilder{root: root, node: root} +} + +// IndexAll add '[*]' to current path. +func (b *PathBuilder) IndexAll() *PathBuilder { + b.node = b.node.chain(newIndexAllNode()) + return b +} + +// Recursive add '..selector' to current path. +func (b *PathBuilder) Recursive(selector string) *PathBuilder { + b.node = b.node.chain(newRecursiveNode(selector)) + return b +} + +func (b *PathBuilder) containsReservedPathCharacters(path string) bool { + if strings.Contains(path, ".") { + return true + } + if strings.Contains(path, "*") { + return true + } + return false +} + +func (b *PathBuilder) enclosedSingleQuote(name string) bool { + return strings.HasPrefix(name, "'") && strings.HasSuffix(name, "'") +} + +func (b *PathBuilder) normalizeSelectorName(name string) string { + if b.enclosedSingleQuote(name) { + // already escaped name + return name + } + if b.containsReservedPathCharacters(name) { + escapedName := strings.ReplaceAll(name, `'`, `\'`) + return "'" + escapedName + "'" + } + return name +} + +func (b *PathBuilder) child(name string) *PathBuilder { + b.node = b.node.chain(newSelectorNode(name)) + return b +} + +// Child add '.name' to current path. +func (b *PathBuilder) Child(name string) *PathBuilder { + return b.child(b.normalizeSelectorName(name)) +} + +// Index add '[idx]' to current path. +func (b *PathBuilder) Index(idx uint) *PathBuilder { + b.node = b.node.chain(newIndexNode(idx)) + return b +} + +// Build build YAMLPath. +func (b *PathBuilder) Build() *Path { + return &Path{node: b.root} +} + +type pathNode interface { + fmt.Stringer + chain(pathNode) pathNode + filter(ast.Node) (ast.Node, error) + replace(ast.Node, ast.Node) error +} + +type basePathNode struct { + child pathNode +} + +func (n *basePathNode) chain(node pathNode) pathNode { + n.child = node + return node +} + +type rootNode struct { + *basePathNode +} + +func newRootNode() *rootNode { + return &rootNode{basePathNode: &basePathNode{}} +} + +func (n *rootNode) String() string { + s := "$" + if n.child != nil { + s += n.child.String() + } + return s +} + +func (n *rootNode) filter(node ast.Node) (ast.Node, error) { + if n.child == nil { + return nil, nil + } + filtered, err := n.child.filter(node) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + return filtered, nil +} + +func (n *rootNode) replace(node ast.Node, target ast.Node) error { + if n.child == nil { + return nil + } + if err := n.child.replace(node, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + return nil +} + +type selectorNode struct { + *basePathNode + selector string +} + +func newSelectorNode(selector string) *selectorNode { + return &selectorNode{ + basePathNode: &basePathNode{}, + selector: selector, + } +} + +func (n *selectorNode) filter(node ast.Node) (ast.Node, error) { + selector := n.selector + if len(selector) > 1 && selector[0] == '\'' && selector[len(selector)-1] == '\'' { + selector = selector[1 : len(selector)-1] + } + switch node.Type() { + case ast.MappingType: + for _, value := range node.(*ast.MappingNode).Values { + key := value.Key.GetToken().Value + if len(key) > 0 { + switch key[0] { + case '"': + var err error + key, err = strconv.Unquote(key) + if err != nil { + return nil, errors.Wrapf(err, "failed to unquote") + } + case '\'': + if len(key) > 1 && key[len(key)-1] == '\'' { + key = key[1 : len(key)-1] + } + } + } + if key == selector { + if n.child == nil { + return value.Value, nil + } + filtered, err := n.child.filter(value.Value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + return filtered, nil + } + } + case ast.MappingValueType: + value := node.(*ast.MappingValueNode) + key := value.Key.GetToken().Value + if key == selector { + if n.child == nil { + return value.Value, nil + } + filtered, err := n.child.filter(value.Value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + return filtered, nil + } + default: + return nil, errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type()) + } + return nil, nil +} + +func (n *selectorNode) replaceMapValue(value *ast.MappingValueNode, target ast.Node) error { + key := value.Key.GetToken().Value + if key != n.selector { + return nil + } + if n.child == nil { + if err := value.Replace(target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } else { + if err := n.child.replace(value.Value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + return nil +} + +func (n *selectorNode) replace(node ast.Node, target ast.Node) error { + switch node.Type() { + case ast.MappingType: + for _, value := range node.(*ast.MappingNode).Values { + if err := n.replaceMapValue(value, target); err != nil { + return errors.Wrapf(err, "failed to replace map value") + } + } + case ast.MappingValueType: + value := node.(*ast.MappingValueNode) + if err := n.replaceMapValue(value, target); err != nil { + return errors.Wrapf(err, "failed to replace map value") + } + default: + return errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type()) + } + return nil +} + +func (n *selectorNode) String() string { + var builder PathBuilder + selector := builder.normalizeSelectorName(n.selector) + s := fmt.Sprintf(".%s", selector) + if n.child != nil { + s += n.child.String() + } + return s +} + +type indexNode struct { + *basePathNode + selector uint +} + +func newIndexNode(selector uint) *indexNode { + return &indexNode{ + basePathNode: &basePathNode{}, + selector: selector, + } +} + +func (n *indexNode) filter(node ast.Node) (ast.Node, error) { + if node.Type() != ast.SequenceType { + return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + } + sequence := node.(*ast.SequenceNode) + if n.selector >= uint(len(sequence.Values)) { + return nil, errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values) + } + value := sequence.Values[n.selector] + if n.child == nil { + return value, nil + } + filtered, err := n.child.filter(value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + return filtered, nil +} + +func (n *indexNode) replace(node ast.Node, target ast.Node) error { + if node.Type() != ast.SequenceType { + return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + } + sequence := node.(*ast.SequenceNode) + if n.selector >= uint(len(sequence.Values)) { + return errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values) + } + if n.child == nil { + if err := sequence.Replace(int(n.selector), target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + return nil + } + if err := n.child.replace(sequence.Values[n.selector], target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + return nil +} + +func (n *indexNode) String() string { + s := fmt.Sprintf("[%d]", n.selector) + if n.child != nil { + s += n.child.String() + } + return s +} + +type indexAllNode struct { + *basePathNode +} + +func newIndexAllNode() *indexAllNode { + return &indexAllNode{ + basePathNode: &basePathNode{}, + } +} + +func (n *indexAllNode) String() string { + s := "[*]" + if n.child != nil { + s += n.child.String() + } + return s +} + +func (n *indexAllNode) filter(node ast.Node) (ast.Node, error) { + if node.Type() != ast.SequenceType { + return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + } + sequence := node.(*ast.SequenceNode) + if n.child == nil { + return sequence, nil + } + out := *sequence + out.Values = []ast.Node{} + for _, value := range sequence.Values { + filtered, err := n.child.filter(value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + out.Values = append(out.Values, filtered) + } + return &out, nil +} + +func (n *indexAllNode) replace(node ast.Node, target ast.Node) error { + if node.Type() != ast.SequenceType { + return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + } + sequence := node.(*ast.SequenceNode) + if n.child == nil { + for idx := range sequence.Values { + if err := sequence.Replace(idx, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + return nil + } + for _, value := range sequence.Values { + if err := n.child.replace(value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + return nil +} + +type recursiveNode struct { + *basePathNode + selector string +} + +func newRecursiveNode(selector string) *recursiveNode { + return &recursiveNode{ + basePathNode: &basePathNode{}, + selector: selector, + } +} + +func (n *recursiveNode) String() string { + s := fmt.Sprintf("..%s", n.selector) + if n.child != nil { + s += n.child.String() + } + return s +} + +func (n *recursiveNode) filterNode(node ast.Node) (*ast.SequenceNode, error) { + sequence := &ast.SequenceNode{BaseNode: &ast.BaseNode{}} + switch typedNode := node.(type) { + case *ast.MappingNode: + for _, value := range typedNode.Values { + seq, err := n.filterNode(value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + sequence.Values = append(sequence.Values, seq.Values...) + } + case *ast.MappingValueNode: + key := typedNode.Key.GetToken().Value + if n.selector == key { + sequence.Values = append(sequence.Values, typedNode.Value) + } + seq, err := n.filterNode(typedNode.Value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + sequence.Values = append(sequence.Values, seq.Values...) + case *ast.SequenceNode: + for _, value := range typedNode.Values { + seq, err := n.filterNode(value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + sequence.Values = append(sequence.Values, seq.Values...) + } + } + return sequence, nil +} + +func (n *recursiveNode) filter(node ast.Node) (ast.Node, error) { + sequence, err := n.filterNode(node) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + sequence.Start = node.GetToken() + return sequence, nil +} + +func (n *recursiveNode) replaceNode(node ast.Node, target ast.Node) error { + switch typedNode := node.(type) { + case *ast.MappingNode: + for _, value := range typedNode.Values { + if err := n.replaceNode(value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + case *ast.MappingValueNode: + key := typedNode.Key.GetToken().Value + if n.selector == key { + if err := typedNode.Replace(target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + if err := n.replaceNode(typedNode.Value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + case *ast.SequenceNode: + for _, value := range typedNode.Values { + if err := n.replaceNode(value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + } + return nil +} + +func (n *recursiveNode) replace(node ast.Node, target ast.Node) error { + if err := n.replaceNode(node, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + return nil +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/path_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/path_test.go new file mode 100644 index 0000000..c0073ce --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/path_test.go @@ -0,0 +1,666 @@ +package yaml_test + +import ( + "fmt" + "log" + "reflect" + "strings" + "testing" + + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/parser" +) + +func builder() *yaml.PathBuilder { return &yaml.PathBuilder{} } + +func TestPathBuilder(t *testing.T) { + tests := []struct { + expected string + path *yaml.Path + }{ + { + expected: `$.a.b[0]`, + path: builder().Root().Child("a").Child("b").Index(0).Build(), + }, + { + expected: `$.'a.b'.'c*d'`, + path: builder().Root().Child("a.b").Child("c*d").Build(), + }, + { + expected: `$.'a.b-*'.c`, + path: builder().Root().Child("a.b-*").Child("c").Build(), + }, + { + expected: `$.'a'.b`, + path: builder().Root().Child("'a'").Child("b").Build(), + }, + { + expected: `$.'a.b'.c`, + path: builder().Root().Child("'a.b'").Child("c").Build(), + }, + } + for _, test := range tests { + t.Run(test.expected, func(t *testing.T) { + expected := test.expected + got := test.path.String() + if expected != got { + t.Fatalf("failed to build path. expected:[%q] but got:[%q]", expected, got) + } + }) + } +} + +func TestPath(t *testing.T) { + yml := ` +store: + book: + - author: john + price: 10 + - author: ken + price: 12 + bicycle: + color: red + price: 19.95 + bicycle*unicycle: + price: 20.25 +` + tests := []struct { + name string + path *yaml.Path + expected interface{} + }{ + { + name: "$.store.book[0].author", + path: builder().Root().Child("store").Child("book").Index(0).Child("author").Build(), + expected: "john", + }, + { + name: "$.store.book[1].price", + path: builder().Root().Child("store").Child("book").Index(1).Child("price").Build(), + expected: uint64(12), + }, + { + name: "$.store.book[*].author", + path: builder().Root().Child("store").Child("book").IndexAll().Child("author").Build(), + expected: []interface{}{"john", "ken"}, + }, + { + name: "$.store.book[0]", + path: builder().Root().Child("store").Child("book").Index(0).Build(), + expected: map[string]interface{}{"author": "john", "price": uint64(10)}, + }, + { + name: "$..author", + path: builder().Root().Recursive("author").Build(), + expected: []interface{}{"john", "ken"}, + }, + { + name: "$.store.bicycle.price", + path: builder().Root().Child("store").Child("bicycle").Child("price").Build(), + expected: float64(19.95), + }, + { + name: `$.store.'bicycle*unicycle'.price`, + path: builder().Root().Child("store").Child(`bicycle*unicycle`).Child("price").Build(), + expected: float64(20.25), + }, + } + t.Run("PathString", func(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + path, err := yaml.PathString(test.name) + if err != nil { + t.Fatalf("%+v", err) + } + if test.name != path.String() { + t.Fatalf("expected %s but actual %s", test.name, path.String()) + } + }) + } + }) + t.Run("string", func(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.name != test.path.String() { + t.Fatalf("expected %s but actual %s", test.name, test.path.String()) + } + }) + } + }) + t.Run("read", func(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var v interface{} + if err := test.path.Read(strings.NewReader(yml), &v); err != nil { + t.Fatalf("%+v", err) + } + if !reflect.DeepEqual(test.expected, v) { + t.Fatalf("expected %v(%T). but actual %v(%T)", test.expected, test.expected, v, v) + } + }) + } + }) + t.Run("filter", func(t *testing.T) { + var target interface{} + if err := yaml.Unmarshal([]byte(yml), &target); err != nil { + t.Fatalf("failed to unmarshal: %+v", err) + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var v interface{} + if err := test.path.Filter(target, &v); err != nil { + t.Fatalf("%+v", err) + } + if !reflect.DeepEqual(test.expected, v) { + t.Fatalf("expected %v(%T). but actual %v(%T)", test.expected, test.expected, v, v) + } + }) + } + }) +} + +func TestPath_ReservedKeyword(t *testing.T) { + tests := []struct { + name string + path string + src string + expected interface{} + failure bool + }{ + { + name: "quoted path", + path: `$.'a.b.c'.foo`, + src: ` +a.b.c: + foo: bar +`, + expected: "bar", + }, + { + name: "contains quote key", + path: `$.a'b`, + src: `a'b: 10`, + expected: uint64(10), + }, + { + name: "escaped quote", + path: `$.'alice\'s age'`, + src: `alice's age: 10`, + expected: uint64(10), + }, + { + name: "directly use white space", + path: `$.a b`, + src: `a b: 10`, + expected: uint64(10), + }, + { + name: "empty quoted key", + path: `$.''`, + src: `a: 10`, + failure: true, + }, + { + name: "unterminated quote", + path: `$.'abcd`, + src: `abcd: 10`, + failure: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + path, err := yaml.PathString(test.path) + if test.failure { + if err == nil { + t.Fatal("expected error") + } + return + } else { + if err != nil { + t.Fatalf("%+v", err) + } + } + file, err := parser.ParseBytes([]byte(test.src), 0) + if err != nil { + t.Fatal(err) + } + var v interface{} + if err := path.Read(file, &v); err != nil { + t.Fatalf("%+v", err) + } + if v != test.expected { + t.Fatalf("failed to get value. expected:[%v] but got:[%v]", test.expected, v) + } + }) + } +} + +func TestPath_Invalid(t *testing.T) { + tests := []struct { + path string + src string + }{ + { + path: "$.wrong", + src: "foo: bar", + }, + } + for _, test := range tests { + path, err := yaml.PathString(test.path) + if err != nil { + t.Fatal(err) + } + t.Run("path.Read", func(t *testing.T) { + file, err := parser.ParseBytes([]byte(test.src), 0) + if err != nil { + t.Fatal(err) + } + var v interface{} + err = path.Read(file, &v) + if err == nil { + t.Fatal("expected error") + } + if !yaml.IsNotFoundNodeError(err) { + t.Fatalf("unexpected error %s", err) + } + }) + t.Run("path.ReadNode", func(t *testing.T) { + file, err := parser.ParseBytes([]byte(test.src), 0) + if err != nil { + t.Fatal(err) + } + _, err = path.ReadNode(file) + if err == nil { + t.Fatal("expected error") + } + if !yaml.IsNotFoundNodeError(err) { + t.Fatalf("unexpected error %s", err) + } + }) + } +} + +func TestPath_Merge(t *testing.T) { + tests := []struct { + path string + dst string + src string + expected string + }{ + { + path: "$.c", + dst: ` +a: 1 +b: 2 +c: + d: 3 + e: 4 +`, + src: ` +f: 5 +g: 6 +`, + expected: ` +a: 1 +b: 2 +c: + d: 3 + e: 4 + f: 5 + g: 6 +`, + }, + { + path: "$.a.b", + dst: ` +a: + b: + - 1 + - 2 +`, + src: ` +- 3 +- map: + - 4 + - 5 +`, + expected: ` +a: + b: + - 1 + - 2 + - 3 + - map: + - 4 + - 5 +`, + }, + } + for _, test := range tests { + t.Run(test.path, func(t *testing.T) { + path, err := yaml.PathString(test.path) + if err != nil { + t.Fatalf("%+v", err) + } + t.Run("FromReader", func(t *testing.T) { + file, err := parser.ParseBytes([]byte(test.dst), 0) + if err != nil { + t.Fatalf("%+v", err) + } + if err := path.MergeFromReader(file, strings.NewReader(test.src)); err != nil { + t.Fatalf("%+v", err) + } + actual := "\n" + file.String() + if test.expected != actual { + t.Fatalf("expected: %q. but got %q", test.expected, actual) + } + }) + t.Run("FromFile", func(t *testing.T) { + file, err := parser.ParseBytes([]byte(test.dst), 0) + if err != nil { + t.Fatalf("%+v", err) + } + src, err := parser.ParseBytes([]byte(test.src), 0) + if err != nil { + t.Fatalf("%+v", err) + } + if err := path.MergeFromFile(file, src); err != nil { + t.Fatalf("%+v", err) + } + actual := "\n" + file.String() + if test.expected != actual { + t.Fatalf("expected: %q. but got %q", test.expected, actual) + } + }) + t.Run("FromNode", func(t *testing.T) { + file, err := parser.ParseBytes([]byte(test.dst), 0) + if err != nil { + t.Fatalf("%+v", err) + } + src, err := parser.ParseBytes([]byte(test.src), 0) + if err != nil { + t.Fatalf("%+v", err) + } + if len(src.Docs) == 0 { + t.Fatalf("failed to parse") + } + if err := path.MergeFromNode(file, src.Docs[0]); err != nil { + t.Fatalf("%+v", err) + } + actual := "\n" + file.String() + if test.expected != actual { + t.Fatalf("expected: %q. but got %q", test.expected, actual) + } + }) + }) + } +} + +func TestPath_Replace(t *testing.T) { + tests := []struct { + path string + dst string + src string + expected string + }{ + { + path: "$.a", + dst: ` +a: 1 +b: 2 +`, + src: `3`, + expected: ` +a: 3 +b: 2 +`, + }, + { + path: "$.b", + dst: ` +b: 1 +c: 2 +`, + src: ` +d: e +f: + g: h + i: j +`, + expected: ` +b: + d: e + f: + g: h + i: j +c: 2 +`, + }, + { + path: "$.a.b[0]", + dst: ` +a: + b: + - hello +c: 2 +`, + src: `world`, + expected: ` +a: + b: + - world +c: 2 +`, + }, + + { + path: "$.books[*].author", + dst: ` +books: + - name: book_a + author: none + - name: book_b + author: none +pictures: + - name: picture_a + author: none + - name: picture_b + author: none +building: + author: none +`, + src: `ken`, + expected: ` +books: + - name: book_a + author: ken + - name: book_b + author: ken +pictures: + - name: picture_a + author: none + - name: picture_b + author: none +building: + author: none +`, + }, + { + path: "$..author", + dst: ` +books: + - name: book_a + author: none + - name: book_b + author: none +pictures: + - name: picture_a + author: none + - name: picture_b + author: none +building: + author: none +`, + src: `ken`, + expected: ` +books: + - name: book_a + author: ken + - name: book_b + author: ken +pictures: + - name: picture_a + author: ken + - name: picture_b + author: ken +building: + author: ken +`, + }, + } + for _, test := range tests { + t.Run(test.path, func(t *testing.T) { + path, err := yaml.PathString(test.path) + if err != nil { + t.Fatalf("%+v", err) + } + t.Run("WithReader", func(t *testing.T) { + file, err := parser.ParseBytes([]byte(test.dst), 0) + if err != nil { + t.Fatalf("%+v", err) + } + if err := path.ReplaceWithReader(file, strings.NewReader(test.src)); err != nil { + t.Fatalf("%+v", err) + } + actual := "\n" + file.String() + if test.expected != actual { + t.Fatalf("expected: %q. but got %q", test.expected, actual) + } + }) + t.Run("WithFile", func(t *testing.T) { + file, err := parser.ParseBytes([]byte(test.dst), 0) + if err != nil { + t.Fatalf("%+v", err) + } + src, err := parser.ParseBytes([]byte(test.src), 0) + if err != nil { + t.Fatalf("%+v", err) + } + if err := path.ReplaceWithFile(file, src); err != nil { + t.Fatalf("%+v", err) + } + actual := "\n" + file.String() + if test.expected != actual { + t.Fatalf("expected: %q. but got %q", test.expected, actual) + } + }) + t.Run("WithNode", func(t *testing.T) { + file, err := parser.ParseBytes([]byte(test.dst), 0) + if err != nil { + t.Fatalf("%+v", err) + } + src, err := parser.ParseBytes([]byte(test.src), 0) + if err != nil { + t.Fatalf("%+v", err) + } + if len(src.Docs) == 0 { + t.Fatalf("failed to parse") + } + if err := path.ReplaceWithNode(file, src.Docs[0]); err != nil { + t.Fatalf("%+v", err) + } + actual := "\n" + file.String() + if test.expected != actual { + t.Fatalf("expected: %q. but got %q", test.expected, actual) + } + }) + }) + } +} + +func ExamplePath_AnnotateSource() { + yml := ` +a: 1 +b: "hello" +` + var v struct { + A int + B string + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + panic(err) + } + if v.A != 2 { + // output error with YAML source + path, err := yaml.PathString("$.a") + if err != nil { + log.Fatal(err) + } + source, err := path.AnnotateSource([]byte(yml), false) + if err != nil { + log.Fatal(err) + } + fmt.Printf("a value expected 2 but actual %d:\n%s\n", v.A, string(source)) + } + // OUTPUT: + // a value expected 2 but actual 1: + // > 2 | a: 1 + // ^ + // 3 | b: "hello" +} + +func ExamplePath_AnnotateSourceWithComment() { + yml := ` +# This is my document +doc: + # This comment should be line 3 + map: + # And below should be line 5 + - value1 + - value2 + other: value3 + ` + path, err := yaml.PathString("$.doc.map[0]") + if err != nil { + log.Fatal(err) + } + msg, err := path.AnnotateSource([]byte(yml), false) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(msg)) + // OUTPUT: + // 4 | # This comment should be line 3 + // 5 | map: + // 6 | # And below should be line 5 + // > 7 | - value1 + // ^ + // 8 | - value2 + // 9 | other: value3 + // 10 | +} + +func ExamplePath_PathString() { + yml := ` +store: + book: + - author: john + price: 10 + - author: ken + price: 12 + bicycle: + color: red + price: 19.95 +` + path, err := yaml.PathString("$.store.book[*].author") + if err != nil { + log.Fatal(err) + } + var authors []string + if err := path.Read(strings.NewReader(yml), &authors); err != nil { + log.Fatal(err) + } + fmt.Println(authors) + // OUTPUT: + // [john ken] +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/printer/printer.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/printer/printer.go new file mode 100644 index 0000000..d5e25dc --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/printer/printer.go @@ -0,0 +1,352 @@ +package printer + +import ( + "fmt" + "math" + "strings" + + "github.com/fatih/color" + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/token" +) + +// Property additional property set for each the token +type Property struct { + Prefix string + Suffix string +} + +// PrintFunc returns property instance +type PrintFunc func() *Property + +// Printer create text from token collection or ast +type Printer struct { + LineNumber bool + LineNumberFormat func(num int) string + MapKey PrintFunc + Anchor PrintFunc + Alias PrintFunc + Bool PrintFunc + String PrintFunc + Number PrintFunc +} + +func defaultLineNumberFormat(num int) string { + return fmt.Sprintf("%2d | ", num) +} + +func (p *Printer) property(tk *token.Token) *Property { + prop := &Property{} + switch tk.PreviousType() { + case token.AnchorType: + if p.Anchor != nil { + return p.Anchor() + } + return prop + case token.AliasType: + if p.Alias != nil { + return p.Alias() + } + return prop + } + switch tk.NextType() { + case token.MappingValueType: + if p.MapKey != nil { + return p.MapKey() + } + return prop + } + switch tk.Type { + case token.BoolType: + if p.Bool != nil { + return p.Bool() + } + return prop + case token.AnchorType: + if p.Anchor != nil { + return p.Anchor() + } + return prop + case token.AliasType: + if p.Anchor != nil { + return p.Alias() + } + return prop + case token.StringType, token.SingleQuoteType, token.DoubleQuoteType: + if p.String != nil { + return p.String() + } + return prop + case token.IntegerType, token.FloatType: + if p.Number != nil { + return p.Number() + } + return prop + default: + } + return prop +} + +// PrintTokens create text from token collection +func (p *Printer) PrintTokens(tokens token.Tokens) string { + if len(tokens) == 0 { + return "" + } + if p.LineNumber { + if p.LineNumberFormat == nil { + p.LineNumberFormat = defaultLineNumberFormat + } + } + texts := []string{} + lineNumber := tokens[0].Position.Line + for _, tk := range tokens { + lines := strings.Split(tk.Origin, "\n") + prop := p.property(tk) + header := "" + if p.LineNumber { + header = p.LineNumberFormat(lineNumber) + } + if len(lines) == 1 { + line := prop.Prefix + lines[0] + prop.Suffix + if len(texts) == 0 { + texts = append(texts, header+line) + lineNumber++ + } else { + text := texts[len(texts)-1] + texts[len(texts)-1] = text + line + } + } else { + for idx, src := range lines { + if p.LineNumber { + header = p.LineNumberFormat(lineNumber) + } + line := prop.Prefix + src + prop.Suffix + if idx == 0 { + if len(texts) == 0 { + texts = append(texts, header+line) + lineNumber++ + } else { + text := texts[len(texts)-1] + texts[len(texts)-1] = text + line + } + } else { + texts = append(texts, fmt.Sprintf("%s%s", header, line)) + lineNumber++ + } + } + } + } + return strings.Join(texts, "\n") +} + +// PrintNode create text from ast.Node +func (p *Printer) PrintNode(node ast.Node) []byte { + return []byte(fmt.Sprintf("%+v\n", node)) +} + +const escape = "\x1b" + +func format(attr color.Attribute) string { + return fmt.Sprintf("%s[%dm", escape, attr) +} + +func (p *Printer) setDefaultColorSet() { + p.Bool = func() *Property { + return &Property{ + Prefix: format(color.FgHiMagenta), + Suffix: format(color.Reset), + } + } + p.Number = func() *Property { + return &Property{ + Prefix: format(color.FgHiMagenta), + Suffix: format(color.Reset), + } + } + p.MapKey = func() *Property { + return &Property{ + Prefix: format(color.FgHiCyan), + Suffix: format(color.Reset), + } + } + p.Anchor = func() *Property { + return &Property{ + Prefix: format(color.FgHiYellow), + Suffix: format(color.Reset), + } + } + p.Alias = func() *Property { + return &Property{ + Prefix: format(color.FgHiYellow), + Suffix: format(color.Reset), + } + } + p.String = func() *Property { + return &Property{ + Prefix: format(color.FgHiGreen), + Suffix: format(color.Reset), + } + } +} + +func (p *Printer) PrintErrorMessage(msg string, isColored bool) string { + if isColored { + return fmt.Sprintf("%s%s%s", + format(color.FgHiRed), + msg, + format(color.Reset), + ) + } + return msg +} + +func (p *Printer) removeLeftSideNewLineChar(src string) string { + return strings.TrimLeft(strings.TrimLeft(strings.TrimLeft(src, "\r"), "\n"), "\r\n") +} + +func (p *Printer) removeRightSideNewLineChar(src string) string { + return strings.TrimRight(strings.TrimRight(strings.TrimRight(src, "\r"), "\n"), "\r\n") +} + +func (p *Printer) removeRightSideWhiteSpaceChar(src string) string { + return p.removeRightSideNewLineChar(strings.TrimRight(src, " ")) +} + +func (p *Printer) newLineCount(s string) int { + src := []rune(s) + size := len(src) + cnt := 0 + for i := 0; i < size; i++ { + c := src[i] + switch c { + case '\r': + if i+1 < size && src[i+1] == '\n' { + i++ + } + cnt++ + case '\n': + cnt++ + } + } + return cnt +} + +func (p *Printer) isNewLineLastChar(s string) bool { + for i := len(s) - 1; i > 0; i-- { + c := s[i] + switch c { + case ' ': + continue + case '\n', '\r': + return true + } + break + } + return false +} + +func (p *Printer) printBeforeTokens(tk *token.Token, minLine, extLine int) token.Tokens { + for { + if tk.Prev == nil { + break + } + if tk.Prev.Position.Line < minLine { + break + } + tk = tk.Prev + } + minTk := tk.Clone() + if minTk.Prev != nil { + // add white spaces to minTk by prev token + prev := minTk.Prev + whiteSpaceLen := len(prev.Origin) - len(strings.TrimRight(prev.Origin, " ")) + minTk.Origin = strings.Repeat(" ", whiteSpaceLen) + minTk.Origin + } + minTk.Origin = p.removeLeftSideNewLineChar(minTk.Origin) + tokens := token.Tokens{minTk} + tk = minTk.Next + for tk != nil && tk.Position.Line <= extLine { + clonedTk := tk.Clone() + tokens.Add(clonedTk) + tk = clonedTk.Next + } + lastTk := tokens[len(tokens)-1] + trimmedOrigin := p.removeRightSideWhiteSpaceChar(lastTk.Origin) + suffix := lastTk.Origin[len(trimmedOrigin):] + lastTk.Origin = trimmedOrigin + + if lastTk.Next != nil && len(suffix) > 1 { + next := lastTk.Next.Clone() + // add suffix to header of next token + if suffix[0] == '\n' || suffix[0] == '\r' { + suffix = suffix[1:] + } + next.Origin = suffix + next.Origin + lastTk.Next = next + } + return tokens +} + +func (p *Printer) printAfterTokens(tk *token.Token, maxLine int) token.Tokens { + tokens := token.Tokens{} + if tk == nil { + return tokens + } + if tk.Position.Line > maxLine { + return tokens + } + minTk := tk.Clone() + minTk.Origin = p.removeLeftSideNewLineChar(minTk.Origin) + tokens.Add(minTk) + tk = minTk.Next + for tk != nil && tk.Position.Line <= maxLine { + clonedTk := tk.Clone() + tokens.Add(clonedTk) + tk = clonedTk.Next + } + return tokens +} + +func (p *Printer) setupErrorTokenFormat(annotateLine int, isColored bool) { + prefix := func(annotateLine, num int) string { + if annotateLine == num { + return fmt.Sprintf("> %2d | ", num) + } + return fmt.Sprintf(" %2d | ", num) + } + p.LineNumber = true + p.LineNumberFormat = func(num int) string { + if isColored { + fn := color.New(color.Bold, color.FgHiWhite).SprintFunc() + return fn(prefix(annotateLine, num)) + } + return prefix(annotateLine, num) + } + if isColored { + p.setDefaultColorSet() + } +} + +func (p *Printer) PrintErrorToken(tk *token.Token, isColored bool) string { + errToken := tk + curLine := tk.Position.Line + curExtLine := curLine + p.newLineCount(p.removeLeftSideNewLineChar(tk.Origin)) + if p.isNewLineLastChar(tk.Origin) { + // if last character ( exclude white space ) is new line character, ignore it. + curExtLine-- + } + + minLine := int(math.Max(float64(curLine-3), 1)) + maxLine := curExtLine + 3 + p.setupErrorTokenFormat(curLine, isColored) + + beforeTokens := p.printBeforeTokens(tk, minLine, curExtLine) + lastTk := beforeTokens[len(beforeTokens)-1] + afterTokens := p.printAfterTokens(lastTk.Next, maxLine) + + beforeSource := p.PrintTokens(beforeTokens) + prefixSpaceNum := len(fmt.Sprintf(" %2d | ", curLine)) + annotateLine := strings.Repeat(" ", prefixSpaceNum+errToken.Position.Column-1) + "^" + afterSource := p.PrintTokens(afterTokens) + return fmt.Sprintf("%s\n%s\n%s", beforeSource, annotateLine, afterSource) +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/printer/printer_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/printer/printer_test.go new file mode 100644 index 0000000..2afa74f --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/printer/printer_test.go @@ -0,0 +1,223 @@ +package printer_test + +import ( + "fmt" + "testing" + + "github.com/goccy/go-yaml/lexer" + "github.com/goccy/go-yaml/printer" +) + +func Test_Printer(t *testing.T) { + yml := `--- +text: aaaa +text2: aaaa + bbbb + cccc + dddd + eeee +text3: ffff + gggg + hhhh + iiii + jjjj +bool: true +number: 10 +anchor: &x 1 +alias: *x +` + t.Run("print starting from tokens[3]", func(t *testing.T) { + tokens := lexer.Tokenize(yml) + var p printer.Printer + actual := "\n" + p.PrintErrorToken(tokens[3], false) + expect := ` + 1 | --- +> 2 | text: aaaa + ^ + 3 | text2: aaaa + 4 | bbbb + 5 | cccc + 6 | dddd + 7 | eeee + 8 | ` + if actual != expect { + t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expect, actual) + } + }) + t.Run("print starting from tokens[4]", func(t *testing.T) { + tokens := lexer.Tokenize(yml) + var p printer.Printer + actual := "\n" + p.PrintErrorToken(tokens[4], false) + expect := ` + 1 | --- + 2 | text: aaaa +> 3 | text2: aaaa + 4 | bbbb + 5 | cccc + 6 | dddd + 7 | eeee + ^ +` + if actual != expect { + t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expect, actual) + } + }) + t.Run("print starting from tokens[6]", func(t *testing.T) { + tokens := lexer.Tokenize(yml) + var p printer.Printer + actual := "\n" + p.PrintErrorToken(tokens[6], false) + expect := ` + 1 | --- + 2 | text: aaaa +> 3 | text2: aaaa + 4 | bbbb + 5 | cccc + 6 | dddd + 7 | eeee + ^ + 8 | text3: ffff + 9 | gggg + 10 | hhhh + 11 | iiii + 12 | jjjj + 13 | ` + if actual != expect { + t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expect, actual) + } + }) + t.Run("print error token with document header", func(t *testing.T) { + tokens := lexer.Tokenize(`--- +a: + b: + c: + d: e + f: g + h: i + +--- +`) + expect := ` + 3 | b: + 4 | c: + 5 | d: e +> 6 | f: g + ^ + 7 | h: i + 8 | + 9 | ---` + var p printer.Printer + actual := "\n" + p.PrintErrorToken(tokens[12], false) + if actual != expect { + t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expect, actual) + } + }) + t.Run("output with color", func(t *testing.T) { + t.Run("token6", func(t *testing.T) { + tokens := lexer.Tokenize(yml) + var p printer.Printer + t.Logf("\n%s", p.PrintErrorToken(tokens[6], true)) + }) + t.Run("token9", func(t *testing.T) { + tokens := lexer.Tokenize(yml) + var p printer.Printer + t.Logf("\n%s", p.PrintErrorToken(tokens[9], true)) + }) + t.Run("token12", func(t *testing.T) { + tokens := lexer.Tokenize(yml) + var p printer.Printer + t.Logf("\n%s", p.PrintErrorToken(tokens[12], true)) + }) + }) + t.Run("print error message", func(t *testing.T) { + var p printer.Printer + src := "message" + msg := p.PrintErrorMessage(src, false) + if msg != src { + t.Fatal("unexpected result") + } + p.PrintErrorMessage(src, true) + }) +} + +func TestPrinter_Anchor(t *testing.T) { + expected := ` +anchor: &x 1 +alias: *x` + tokens := lexer.Tokenize(expected) + var p printer.Printer + got := p.PrintTokens(tokens) + if expected != got { + t.Fatalf("unexpected output: expect:[%s]\n actual:[%s]", expected, got) + } +} + +func Test_Printer_Multiline(t *testing.T) { + yml := ` +text1: 'aaaa + bbbb + cccc' +text2: "ffff + gggg + hhhh" +text3: hello +` + tc := []struct { + token int + want string + }{ + { + token: 2, + want: ` +> 2 | text1: 'aaaa + 3 | bbbb + 4 | cccc' + ^ + 5 | text2: "ffff + 6 | gggg + 7 | hhhh"`, + }, + {token: 3, + want: ` + 2 | text1: 'aaaa + 3 | bbbb + 4 | cccc' +> 5 | text2: "ffff + 6 | gggg + 7 | hhhh" + ^ + 8 | text3: hello`, + }, + {token: 5, + want: ` + 2 | text1: 'aaaa + 3 | bbbb + 4 | cccc' +> 5 | text2: "ffff + 6 | gggg + 7 | hhhh" + ^ + 8 | text3: hello`, + }, + {token: 6, + want: ` + 5 | text2: "ffff + 6 | gggg + 7 | hhhh" +> 8 | text3: hello + ^ +`, + }, + } + for _, tt := range tc { + name := fmt.Sprintf("print starting from tokens[%d]", tt.token) + t.Run(name, func(t *testing.T) { + tokens := lexer.Tokenize(yml) + var p printer.Printer + got := "\n" + p.PrintErrorToken(tokens[tt.token], false) + want := tt.want + if got != want { + t.Fatalf("PrintErrorToken() got: %s\n want:%s\n", want, got) + } + }) + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/scanner/context.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/scanner/context.go new file mode 100644 index 0000000..3aaec56 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/scanner/context.go @@ -0,0 +1,236 @@ +package scanner + +import ( + "sync" + + "github.com/goccy/go-yaml/token" +) + +const whitespace = ' ' + +// Context context at scanning +type Context struct { + idx int + size int + notSpaceCharPos int + notSpaceOrgCharPos int + src []rune + buf []rune + obuf []rune + tokens token.Tokens + isRawFolded bool + isLiteral bool + isFolded bool + isSingleLine bool + literalOpt string +} + +var ( + ctxPool = sync.Pool{ + New: func() interface{} { + return createContext() + }, + } +) + +func createContext() *Context { + return &Context{ + idx: 0, + tokens: token.Tokens{}, + isSingleLine: true, + } +} + +func newContext(src []rune) *Context { + ctx := ctxPool.Get().(*Context) + ctx.reset(src) + return ctx +} + +func (c *Context) release() { + ctxPool.Put(c) +} + +func (c *Context) reset(src []rune) { + c.idx = 0 + c.size = len(src) + c.src = src + c.tokens = c.tokens[:0] + c.resetBuffer() + c.isRawFolded = false + c.isSingleLine = true + c.isLiteral = false + c.isFolded = false + c.literalOpt = "" +} + +func (c *Context) resetBuffer() { + c.buf = c.buf[:0] + c.obuf = c.obuf[:0] + c.notSpaceCharPos = 0 + c.notSpaceOrgCharPos = 0 +} + +func (c *Context) isSaveIndentMode() bool { + return c.isLiteral || c.isFolded || c.isRawFolded +} + +func (c *Context) breakLiteral() { + c.isLiteral = false + c.isRawFolded = false + c.isFolded = false + c.literalOpt = "" +} + +func (c *Context) addToken(tk *token.Token) { + if tk == nil { + return + } + c.tokens = append(c.tokens, tk) +} + +func (c *Context) addBuf(r rune) { + if len(c.buf) == 0 && r == ' ' { + return + } + c.buf = append(c.buf, r) + if r != ' ' && r != '\t' { + c.notSpaceCharPos = len(c.buf) + } +} + +func (c *Context) addOriginBuf(r rune) { + c.obuf = append(c.obuf, r) + if r != ' ' && r != '\t' { + c.notSpaceOrgCharPos = len(c.obuf) + } +} + +func (c *Context) removeRightSpaceFromBuf() int { + trimmedBuf := c.obuf[:c.notSpaceOrgCharPos] + buflen := len(trimmedBuf) + diff := len(c.obuf) - buflen + if diff > 0 { + c.obuf = c.obuf[:buflen] + c.buf = c.bufferedSrc() + } + return diff +} + +func (c *Context) isDocument() bool { + return c.isLiteral || c.isFolded || c.isRawFolded +} + +func (c *Context) isEOS() bool { + return len(c.src)-1 <= c.idx +} + +func (c *Context) isNextEOS() bool { + return len(c.src)-1 <= c.idx+1 +} + +func (c *Context) next() bool { + return c.idx < c.size +} + +func (c *Context) source(s, e int) string { + return string(c.src[s:e]) +} + +func (c *Context) previousChar() rune { + if c.idx > 0 { + return c.src[c.idx-1] + } + return rune(0) +} + +func (c *Context) currentChar() rune { + if c.size > c.idx { + return c.src[c.idx] + } + return rune(0) +} + +func (c *Context) currentCharWithSkipWhitespace() rune { + idx := c.idx + for c.size > idx { + ch := c.src[idx] + if ch != whitespace { + return ch + } + idx++ + } + return rune(0) +} + +func (c *Context) nextChar() rune { + if c.size > c.idx+1 { + return c.src[c.idx+1] + } + return rune(0) +} + +func (c *Context) repeatNum(r rune) int { + cnt := 0 + for i := c.idx; i < c.size; i++ { + if c.src[i] == r { + cnt++ + } else { + break + } + } + return cnt +} + +func (c *Context) progress(num int) { + c.idx += num +} + +func (c *Context) nextPos() int { + return c.idx + 1 +} + +func (c *Context) existsBuffer() bool { + return len(c.bufferedSrc()) != 0 +} + +func (c *Context) bufferedSrc() []rune { + src := c.buf[:c.notSpaceCharPos] + if c.isDocument() && c.literalOpt == "-" { + // remove end '\n' character and trailing empty lines + // https://yaml.org/spec/1.2.2/#8112-block-chomping-indicator + for { + if len(src) > 0 && src[len(src)-1] == '\n' { + src = src[:len(src)-1] + continue + } + break + } + } + return src +} + +func (c *Context) bufferedToken(pos *token.Position) *token.Token { + if c.idx == 0 { + return nil + } + source := c.bufferedSrc() + if len(source) == 0 { + return nil + } + var tk *token.Token + if c.isDocument() { + tk = token.String(string(source), string(c.obuf), pos) + } else { + tk = token.New(string(source), string(c.obuf), pos) + } + c.resetBuffer() + return tk +} + +func (c *Context) lastToken() *token.Token { + if len(c.tokens) != 0 { + return c.tokens[len(c.tokens)-1] + } + return nil +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/scanner/scanner.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/scanner/scanner.go new file mode 100644 index 0000000..b0eac48 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/scanner/scanner.go @@ -0,0 +1,908 @@ +package scanner + +import ( + "io" + "strings" + + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +// IndentState state for indent +type IndentState int + +const ( + // IndentStateEqual equals previous indent + IndentStateEqual IndentState = iota + // IndentStateUp more indent than previous + IndentStateUp + // IndentStateDown less indent than previous + IndentStateDown + // IndentStateKeep uses not indent token + IndentStateKeep +) + +// Scanner holds the scanner's internal state while processing a given text. +// It can be allocated as part of another data structure but must be initialized via Init before use. +type Scanner struct { + source []rune + sourcePos int + sourceSize int + line int + column int + offset int + prevIndentLevel int + prevIndentNum int + prevIndentColumn int + docStartColumn int + indentLevel int + indentNum int + isFirstCharAtLine bool + isAnchor bool + startedFlowSequenceNum int + startedFlowMapNum int + indentState IndentState + savedPos *token.Position +} + +func (s *Scanner) pos() *token.Position { + return &token.Position{ + Line: s.line, + Column: s.column, + Offset: s.offset, + IndentNum: s.indentNum, + IndentLevel: s.indentLevel, + } +} + +func (s *Scanner) bufferedToken(ctx *Context) *token.Token { + if s.savedPos != nil { + tk := ctx.bufferedToken(s.savedPos) + s.savedPos = nil + return tk + } + line := s.line + column := s.column - len(ctx.buf) + level := s.indentLevel + if ctx.isSaveIndentMode() { + line -= s.newLineCount(ctx.buf) + column = strings.Index(string(ctx.obuf), string(ctx.buf)) + 1 + // Since we are in a literal, folded or raw folded + // we can use the indent level from the last token. + last := ctx.lastToken() + if last != nil { // The last token should never be nil here. + level = last.Position.IndentLevel + 1 + } + } + return ctx.bufferedToken(&token.Position{ + Line: line, + Column: column, + Offset: s.offset - len(ctx.buf), + IndentNum: s.indentNum, + IndentLevel: level, + }) +} + +func (s *Scanner) progressColumn(ctx *Context, num int) { + s.column += num + s.offset += num + ctx.progress(num) +} + +func (s *Scanner) progressLine(ctx *Context) { + s.column = 1 + s.line++ + s.offset++ + s.indentNum = 0 + s.isFirstCharAtLine = true + s.isAnchor = false + ctx.progress(1) +} + +func (s *Scanner) isNeededKeepPreviousIndentNum(ctx *Context, c rune) bool { + if !s.isChangedToIndentStateUp() { + return false + } + if ctx.isDocument() { + return true + } + if c == '-' && ctx.existsBuffer() { + return true + } + return false +} + +func (s *Scanner) isNewLineChar(c rune) bool { + if c == '\n' { + return true + } + if c == '\r' { + return true + } + return false +} + +func (s *Scanner) newLineCount(src []rune) int { + size := len(src) + cnt := 0 + for i := 0; i < size; i++ { + c := src[i] + switch c { + case '\r': + if i+1 < size && src[i+1] == '\n' { + i++ + } + cnt++ + case '\n': + cnt++ + } + } + return cnt +} + +func (s *Scanner) updateIndentState(ctx *Context) { + indentNumBasedIndentState := s.indentState + if s.prevIndentNum < s.indentNum { + s.indentLevel = s.prevIndentLevel + 1 + indentNumBasedIndentState = IndentStateUp + } else if s.prevIndentNum == s.indentNum { + s.indentLevel = s.prevIndentLevel + indentNumBasedIndentState = IndentStateEqual + } else { + indentNumBasedIndentState = IndentStateDown + if s.prevIndentLevel > 0 { + s.indentLevel = s.prevIndentLevel - 1 + } + } + + if s.prevIndentColumn > 0 { + if s.prevIndentColumn < s.column { + s.indentState = IndentStateUp + } else if s.prevIndentColumn != s.column || indentNumBasedIndentState != IndentStateEqual { + // The following case ( current position is 'd' ), some variables becomes like here + // - prevIndentColumn: 1 of 'a' + // - indentNumBasedIndentState: IndentStateDown because d's indentNum(1) is less than c's indentNum(3). + // Therefore, s.prevIndentColumn(1) == s.column(1) is true, but we want to treat this as IndentStateDown. + // So, we look also current indentState value by the above prevIndentNum based logic, and determins finally indentState. + // --- + // a: + // b + // c + // d: e + // ^ + s.indentState = IndentStateDown + } else { + s.indentState = IndentStateEqual + } + } else { + s.indentState = indentNumBasedIndentState + } +} + +func (s *Scanner) updateIndent(ctx *Context, c rune) { + if s.isFirstCharAtLine && s.isNewLineChar(c) && ctx.isDocument() { + return + } + if s.isFirstCharAtLine && c == ' ' { + s.indentNum++ + return + } + if !s.isFirstCharAtLine { + s.indentState = IndentStateKeep + return + } + s.updateIndentState(ctx) + s.isFirstCharAtLine = false + if s.isNeededKeepPreviousIndentNum(ctx, c) { + return + } + if s.indentState != IndentStateUp { + s.prevIndentColumn = 0 + } + s.prevIndentNum = s.indentNum + s.prevIndentLevel = s.indentLevel +} + +func (s *Scanner) isChangedToIndentStateDown() bool { + return s.indentState == IndentStateDown +} + +func (s *Scanner) isChangedToIndentStateUp() bool { + return s.indentState == IndentStateUp +} + +func (s *Scanner) isChangedToIndentStateEqual() bool { + return s.indentState == IndentStateEqual +} + +func (s *Scanner) addBufferedTokenIfExists(ctx *Context) { + ctx.addToken(s.bufferedToken(ctx)) +} + +func (s *Scanner) breakLiteral(ctx *Context) { + s.docStartColumn = 0 + ctx.breakLiteral() +} + +func (s *Scanner) scanSingleQuote(ctx *Context) (tk *token.Token, pos int) { + ctx.addOriginBuf('\'') + srcpos := s.pos() + startIndex := ctx.idx + 1 + src := ctx.src + size := len(src) + value := []rune{} + isFirstLineChar := false + isNewLine := false + for idx := startIndex; idx < size; idx++ { + if !isNewLine { + s.progressColumn(ctx, 1) + } else { + isNewLine = false + } + c := src[idx] + pos = idx + 1 + ctx.addOriginBuf(c) + if s.isNewLineChar(c) { + value = append(value, ' ') + isFirstLineChar = true + isNewLine = true + s.progressLine(ctx) + continue + } else if c == ' ' && isFirstLineChar { + continue + } else if c != '\'' { + value = append(value, c) + isFirstLineChar = false + continue + } + if idx+1 < len(ctx.src) && ctx.src[idx+1] == '\'' { + // '' handle as ' character + value = append(value, c) + ctx.addOriginBuf(c) + idx++ + continue + } + s.progressColumn(ctx, 1) + tk = token.SingleQuote(string(value), string(ctx.obuf), srcpos) + pos = idx - startIndex + 1 + return + } + return +} + +func hexToInt(b rune) int { + if b >= 'A' && b <= 'F' { + return int(b) - 'A' + 10 + } + if b >= 'a' && b <= 'f' { + return int(b) - 'a' + 10 + } + return int(b) - '0' +} + +func hexRunesToInt(b []rune) int { + sum := 0 + for i := 0; i < len(b); i++ { + sum += hexToInt(b[i]) << (uint(len(b)-i-1) * 4) + } + return sum +} + +func (s *Scanner) scanDoubleQuote(ctx *Context) (tk *token.Token, pos int) { + ctx.addOriginBuf('"') + srcpos := s.pos() + startIndex := ctx.idx + 1 + src := ctx.src + size := len(src) + value := []rune{} + isFirstLineChar := false + isNewLine := false + for idx := startIndex; idx < size; idx++ { + if !isNewLine { + s.progressColumn(ctx, 1) + } else { + isNewLine = false + } + c := src[idx] + pos = idx + 1 + ctx.addOriginBuf(c) + if s.isNewLineChar(c) { + value = append(value, ' ') + isFirstLineChar = true + isNewLine = true + s.progressLine(ctx) + continue + } else if c == ' ' && isFirstLineChar { + continue + } else if c == '\\' { + isFirstLineChar = false + if idx+1 < size { + nextChar := src[idx+1] + switch nextChar { + case 'b': + ctx.addOriginBuf(nextChar) + value = append(value, '\b') + idx++ + continue + case 'e': + ctx.addOriginBuf(nextChar) + value = append(value, '\x1B') + idx++ + continue + case 'f': + ctx.addOriginBuf(nextChar) + value = append(value, '\f') + idx++ + continue + case 'n': + ctx.addOriginBuf(nextChar) + value = append(value, '\n') + idx++ + continue + case 'r': + ctx.addOriginBuf(nextChar) + value = append(value, '\r') + idx++ + continue + case 'v': + ctx.addOriginBuf(nextChar) + value = append(value, '\v') + idx++ + continue + case 'L': // LS (#x2028) + ctx.addOriginBuf(nextChar) + value = append(value, []rune{'\xE2', '\x80', '\xA8'}...) + idx++ + continue + case 'N': // NEL (#x85) + ctx.addOriginBuf(nextChar) + value = append(value, []rune{'\xC2', '\x85'}...) + idx++ + continue + case 'P': // PS (#x2029) + ctx.addOriginBuf(nextChar) + value = append(value, []rune{'\xE2', '\x80', '\xA9'}...) + idx++ + continue + case '_': // #xA0 + ctx.addOriginBuf(nextChar) + value = append(value, []rune{'\xC2', '\xA0'}...) + idx++ + continue + case '"': + ctx.addOriginBuf(nextChar) + value = append(value, nextChar) + idx++ + continue + case 'x': + if idx+3 >= size { + // TODO: need to return error + //err = xerrors.New("invalid escape character \\x") + return + } + codeNum := hexRunesToInt(src[idx+2 : idx+4]) + value = append(value, rune(codeNum)) + idx += 3 + continue + case 'u': + if idx+5 >= size { + // TODO: need to return error + //err = xerrors.New("invalid escape character \\u") + return + } + codeNum := hexRunesToInt(src[idx+2 : idx+6]) + value = append(value, rune(codeNum)) + idx += 5 + continue + case 'U': + if idx+9 >= size { + // TODO: need to return error + //err = xerrors.New("invalid escape character \\U") + return + } + codeNum := hexRunesToInt(src[idx+2 : idx+10]) + value = append(value, rune(codeNum)) + idx += 9 + continue + case '\\': + ctx.addOriginBuf(nextChar) + idx++ + } + } + value = append(value, c) + continue + } else if c != '"' { + value = append(value, c) + isFirstLineChar = false + continue + } + s.progressColumn(ctx, 1) + tk = token.DoubleQuote(string(value), string(ctx.obuf), srcpos) + pos = idx - startIndex + 1 + return + } + return +} + +func (s *Scanner) scanQuote(ctx *Context, ch rune) (tk *token.Token, pos int) { + if ch == '\'' { + return s.scanSingleQuote(ctx) + } + return s.scanDoubleQuote(ctx) +} + +func (s *Scanner) isMergeKey(ctx *Context) bool { + if ctx.repeatNum('<') != 2 { + return false + } + src := ctx.src + size := len(src) + for idx := ctx.idx + 2; idx < size; idx++ { + c := src[idx] + if c == ' ' { + continue + } + if c != ':' { + return false + } + if idx+1 < size { + nc := src[idx+1] + if nc == ' ' || s.isNewLineChar(nc) { + return true + } + } + } + return false +} + +func (s *Scanner) scanTag(ctx *Context) (tk *token.Token, pos int) { + ctx.addOriginBuf('!') + ctx.progress(1) // skip '!' character + for idx, c := range ctx.src[ctx.idx:] { + pos = idx + 1 + ctx.addOriginBuf(c) + switch c { + case ' ', '\n', '\r': + value := ctx.source(ctx.idx-1, ctx.idx+idx) + tk = token.Tag(value, string(ctx.obuf), s.pos()) + pos = len([]rune(value)) + return + } + } + return +} + +func (s *Scanner) scanComment(ctx *Context) (tk *token.Token, pos int) { + ctx.addOriginBuf('#') + ctx.progress(1) // skip '#' character + for idx, c := range ctx.src[ctx.idx:] { + pos = idx + 1 + ctx.addOriginBuf(c) + switch c { + case '\n', '\r': + if ctx.previousChar() == '\\' { + continue + } + value := ctx.source(ctx.idx, ctx.idx+idx) + tk = token.Comment(value, string(ctx.obuf), s.pos()) + pos = len([]rune(value)) + 1 + return + } + } + // document ends with comment. + value := string(ctx.src[ctx.idx:]) + tk = token.Comment(value, string(ctx.obuf), s.pos()) + pos = len([]rune(value)) + 1 + return +} + +func trimCommentFromLiteralOpt(text string) (string, error) { + idx := strings.Index(text, "#") + if idx < 0 { + return text, nil + } + if idx == 0 { + return "", xerrors.New("invalid literal header") + } + return text[:idx-1], nil +} + +func (s *Scanner) scanLiteral(ctx *Context, c rune) { + ctx.addOriginBuf(c) + if ctx.isEOS() { + if ctx.isLiteral { + ctx.addBuf(c) + } + value := ctx.bufferedSrc() + ctx.addToken(token.String(string(value), string(ctx.obuf), s.pos())) + ctx.resetBuffer() + s.progressColumn(ctx, 1) + } else if s.isNewLineChar(c) { + if ctx.isLiteral { + ctx.addBuf(c) + } else { + ctx.addBuf(' ') + } + s.progressLine(ctx) + } else if s.isFirstCharAtLine && c == ' ' { + if 0 < s.docStartColumn && s.docStartColumn <= s.column { + ctx.addBuf(c) + } + s.progressColumn(ctx, 1) + } else { + if s.docStartColumn == 0 { + s.docStartColumn = s.column + } + ctx.addBuf(c) + s.progressColumn(ctx, 1) + } +} + +func (s *Scanner) scanLiteralHeader(ctx *Context) (pos int, err error) { + header := ctx.currentChar() + ctx.addOriginBuf(header) + ctx.progress(1) // skip '|' or '>' character + for idx, c := range ctx.src[ctx.idx:] { + pos = idx + ctx.addOriginBuf(c) + switch c { + case '\n', '\r': + value := ctx.source(ctx.idx, ctx.idx+idx) + opt := strings.TrimRight(value, " ") + orgOptLen := len(opt) + opt, err = trimCommentFromLiteralOpt(opt) + if err != nil { + return + } + switch opt { + case "", "+", "-", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": + hasComment := len(opt) < orgOptLen + if header == '|' { + if hasComment { + commentLen := orgOptLen - len(opt) + headerPos := strings.Index(string(ctx.obuf), "|") + litBuf := ctx.obuf[:len(ctx.obuf)-commentLen-headerPos] + commentBuf := ctx.obuf[len(litBuf):] + ctx.addToken(token.Literal("|"+opt, string(litBuf), s.pos())) + s.column += len(litBuf) + s.offset += len(litBuf) + commentHeader := strings.Index(value, "#") + ctx.addToken(token.Comment(string(value[commentHeader+1:]), string(commentBuf), s.pos())) + } else { + ctx.addToken(token.Literal("|"+opt, string(ctx.obuf), s.pos())) + } + ctx.isLiteral = true + } else if header == '>' { + if hasComment { + commentLen := orgOptLen - len(opt) + headerPos := strings.Index(string(ctx.obuf), ">") + foldedBuf := ctx.obuf[:len(ctx.obuf)-commentLen-headerPos] + commentBuf := ctx.obuf[len(foldedBuf):] + ctx.addToken(token.Folded(">"+opt, string(foldedBuf), s.pos())) + s.column += len(foldedBuf) + s.offset += len(foldedBuf) + commentHeader := strings.Index(value, "#") + ctx.addToken(token.Comment(string(value[commentHeader+1:]), string(commentBuf), s.pos())) + } else { + ctx.addToken(token.Folded(">"+opt, string(ctx.obuf), s.pos())) + } + ctx.isFolded = true + } + s.indentState = IndentStateKeep + ctx.resetBuffer() + ctx.literalOpt = opt + return + } + break + } + } + err = xerrors.New("invalid literal header") + return +} + +func (s *Scanner) scanNewLine(ctx *Context, c rune) { + if len(ctx.buf) > 0 && s.savedPos == nil { + s.savedPos = s.pos() + s.savedPos.Column -= len(ctx.bufferedSrc()) + } + + // if the following case, origin buffer has unnecessary two spaces. + // So, `removeRightSpaceFromOriginBuf` remove them, also fix column number too. + // --- + // a:[space][space] + // b: c + removedNum := ctx.removeRightSpaceFromBuf() + if removedNum > 0 { + s.column -= removedNum + s.offset -= removedNum + if s.savedPos != nil { + s.savedPos.Column -= removedNum + } + } + + if ctx.isEOS() { + s.addBufferedTokenIfExists(ctx) + } else if s.isAnchor { + s.addBufferedTokenIfExists(ctx) + } + ctx.addBuf(' ') + ctx.addOriginBuf(c) + ctx.isSingleLine = false + s.progressLine(ctx) +} + +func (s *Scanner) scan(ctx *Context) (pos int) { + for ctx.next() { + pos = ctx.nextPos() + c := ctx.currentChar() + s.updateIndent(ctx, c) + if ctx.isDocument() { + if s.isChangedToIndentStateEqual() || + s.isChangedToIndentStateDown() { + s.addBufferedTokenIfExists(ctx) + s.breakLiteral(ctx) + } else { + s.scanLiteral(ctx, c) + continue + } + } else if s.isChangedToIndentStateDown() { + s.addBufferedTokenIfExists(ctx) + } else if s.isChangedToIndentStateEqual() { + // if first character is new line character, buffer expect to raw folded literal + if len(ctx.obuf) > 0 && s.newLineCount(ctx.obuf) <= 1 { + // doesn't raw folded literal + s.addBufferedTokenIfExists(ctx) + } + } + switch c { + case '{': + if !ctx.existsBuffer() { + ctx.addOriginBuf(c) + ctx.addToken(token.MappingStart(string(ctx.obuf), s.pos())) + s.startedFlowMapNum++ + s.progressColumn(ctx, 1) + return + } + case '}': + if !ctx.existsBuffer() || s.startedFlowMapNum > 0 { + ctx.addToken(s.bufferedToken(ctx)) + ctx.addOriginBuf(c) + ctx.addToken(token.MappingEnd(string(ctx.obuf), s.pos())) + s.startedFlowMapNum-- + s.progressColumn(ctx, 1) + return + } + case '.': + if s.indentNum == 0 && s.column == 1 && ctx.repeatNum('.') == 3 { + ctx.addToken(token.DocumentEnd(string(ctx.obuf)+"...", s.pos())) + s.progressColumn(ctx, 3) + pos += 2 + return + } + case '<': + if s.isMergeKey(ctx) { + s.prevIndentColumn = s.column + ctx.addToken(token.MergeKey(string(ctx.obuf)+"<<", s.pos())) + s.progressColumn(ctx, 1) + pos++ + return + } + case '-': + if s.indentNum == 0 && s.column == 1 && ctx.repeatNum('-') == 3 { + s.addBufferedTokenIfExists(ctx) + ctx.addToken(token.DocumentHeader(string(ctx.obuf)+"---", s.pos())) + s.progressColumn(ctx, 3) + pos += 2 + return + } + if ctx.existsBuffer() && s.isChangedToIndentStateUp() { + // raw folded + ctx.isRawFolded = true + ctx.addBuf(c) + ctx.addOriginBuf(c) + s.progressColumn(ctx, 1) + continue + } + if ctx.existsBuffer() { + // '-' is literal + ctx.addBuf(c) + ctx.addOriginBuf(c) + s.progressColumn(ctx, 1) + continue + } + nc := ctx.nextChar() + if nc == ' ' || s.isNewLineChar(nc) { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + tk := token.SequenceEntry(string(ctx.obuf), s.pos()) + s.prevIndentColumn = tk.Position.Column + ctx.addToken(tk) + s.progressColumn(ctx, 1) + return + } + case '[': + if !ctx.existsBuffer() { + ctx.addOriginBuf(c) + ctx.addToken(token.SequenceStart(string(ctx.obuf), s.pos())) + s.startedFlowSequenceNum++ + s.progressColumn(ctx, 1) + return + } + case ']': + if !ctx.existsBuffer() || s.startedFlowSequenceNum > 0 { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + ctx.addToken(token.SequenceEnd(string(ctx.obuf), s.pos())) + s.startedFlowSequenceNum-- + s.progressColumn(ctx, 1) + return + } + case ',': + if s.startedFlowSequenceNum > 0 || s.startedFlowMapNum > 0 { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + ctx.addToken(token.CollectEntry(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + return + } + case ':': + nc := ctx.nextChar() + if s.startedFlowMapNum > 0 || nc == ' ' || s.isNewLineChar(nc) || ctx.isNextEOS() { + // mapping value + tk := s.bufferedToken(ctx) + if tk != nil { + s.prevIndentColumn = tk.Position.Column + ctx.addToken(tk) + } else if tk := ctx.lastToken(); tk != nil { + // If the map key is quote, the buffer does not exist because it has already been cut into tokens. + // Therefore, we need to check the last token. + if tk.Indicator == token.QuotedScalarIndicator { + s.prevIndentColumn = tk.Position.Column + } + } + ctx.addToken(token.MappingValue(s.pos())) + s.progressColumn(ctx, 1) + return + } + case '|', '>': + if !ctx.existsBuffer() { + progress, err := s.scanLiteralHeader(ctx) + if err != nil { + // TODO: returns syntax error object + return + } + s.progressColumn(ctx, progress) + s.progressLine(ctx) + continue + } + case '!': + if !ctx.existsBuffer() { + token, progress := s.scanTag(ctx) + ctx.addToken(token) + s.progressColumn(ctx, progress) + if c := ctx.previousChar(); s.isNewLineChar(c) { + s.progressLine(ctx) + } + pos += progress + return + } + case '%': + if !ctx.existsBuffer() && s.indentNum == 0 { + ctx.addToken(token.Directive(string(ctx.obuf)+"%", s.pos())) + s.progressColumn(ctx, 1) + return + } + case '?': + nc := ctx.nextChar() + if !ctx.existsBuffer() && nc == ' ' { + ctx.addToken(token.MappingKey(s.pos())) + s.progressColumn(ctx, 1) + return + } + case '&': + if !ctx.existsBuffer() { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + ctx.addToken(token.Anchor(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + s.isAnchor = true + return + } + case '*': + if !ctx.existsBuffer() { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + ctx.addToken(token.Alias(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + return + } + case '#': + if !ctx.existsBuffer() || ctx.previousChar() == ' ' { + s.addBufferedTokenIfExists(ctx) + token, progress := s.scanComment(ctx) + ctx.addToken(token) + s.progressColumn(ctx, progress) + s.progressLine(ctx) + pos += progress + return + } + case '\'', '"': + if !ctx.existsBuffer() { + token, progress := s.scanQuote(ctx, c) + ctx.addToken(token) + pos += progress + // If the non-whitespace character immediately following the quote is ':', the quote should be treated as a map key. + // Therefore, do not return and continue processing as a normal map key. + if ctx.currentCharWithSkipWhitespace() == ':' { + continue + } + return + } + case '\r', '\n': + // There is no problem that we ignore CR which followed by LF and normalize it to LF, because of following YAML1.2 spec. + // > Line breaks inside scalar content must be normalized by the YAML processor. Each such line break must be parsed into a single line feed character. + // > Outside scalar content, YAML allows any line break to be used to terminate lines. + // > -- https://yaml.org/spec/1.2/spec.html + if c == '\r' && ctx.nextChar() == '\n' { + ctx.addOriginBuf('\r') + ctx.progress(1) + c = '\n' + } + s.scanNewLine(ctx, c) + continue + case ' ': + if ctx.isSaveIndentMode() || (!s.isAnchor && !s.isFirstCharAtLine) { + ctx.addBuf(c) + ctx.addOriginBuf(c) + s.progressColumn(ctx, 1) + continue + } + if s.isFirstCharAtLine { + s.progressColumn(ctx, 1) + ctx.addOriginBuf(c) + continue + } + s.addBufferedTokenIfExists(ctx) + pos-- // to rescan white space at next scanning for adding white space to next buffer. + s.isAnchor = false + return + } + ctx.addBuf(c) + ctx.addOriginBuf(c) + s.progressColumn(ctx, 1) + } + s.addBufferedTokenIfExists(ctx) + return +} + +// Init prepares the scanner s to tokenize the text src by setting the scanner at the beginning of src. +func (s *Scanner) Init(text string) { + src := []rune(text) + s.source = src + s.sourcePos = 0 + s.sourceSize = len(src) + s.line = 1 + s.column = 1 + s.offset = 1 + s.prevIndentLevel = 0 + s.prevIndentNum = 0 + s.prevIndentColumn = 0 + s.indentLevel = 0 + s.indentNum = 0 + s.isFirstCharAtLine = true +} + +// Scan scans the next token and returns the token collection. The source end is indicated by io.EOF. +func (s *Scanner) Scan() (token.Tokens, error) { + if s.sourcePos >= s.sourceSize { + return nil, io.EOF + } + ctx := newContext(s.source[s.sourcePos:]) + defer ctx.release() + progress := s.scan(ctx) + s.sourcePos += progress + var tokens token.Tokens + tokens = append(tokens, ctx.tokens...) + return tokens, nil +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/stdlib_quote.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/stdlib_quote.go new file mode 100644 index 0000000..be50ae6 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/stdlib_quote.go @@ -0,0 +1,103 @@ +// Copied and trimmed down from https://github.com/golang/go/blob/e3769299cd3484e018e0e2a6e1b95c2b18ce4f41/src/strconv/quote.go +// We want to use the standard library's private "quoteWith" function rather than write our own so that we get robust unicode support. +// Every private function called by quoteWith was copied. +// There are 2 modifications to simplify the code: +// 1. The unicode.IsPrint function was substituted for the custom implementation of IsPrint +// 2. All code paths reachable only when ASCIIonly or grphicOnly are set to true were removed. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package yaml + +import ( + "unicode" + "unicode/utf8" +) + +const ( + lowerhex = "0123456789abcdef" +) + +func quoteWith(s string, quote byte) string { + return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote)) +} + +func appendQuotedWith(buf []byte, s string, quote byte) []byte { + // Often called with big strings, so preallocate. If there's quoting, + // this is conservative but still helps a lot. + if cap(buf)-len(buf) < len(s) { + nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1) + copy(nBuf, buf) + buf = nBuf + } + buf = append(buf, quote) + for width := 0; len(s) > 0; s = s[width:] { + r := rune(s[0]) + width = 1 + if r >= utf8.RuneSelf { + r, width = utf8.DecodeRuneInString(s) + } + if width == 1 && r == utf8.RuneError { + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[s[0]>>4]) + buf = append(buf, lowerhex[s[0]&0xF]) + continue + } + buf = appendEscapedRune(buf, r, quote) + } + buf = append(buf, quote) + return buf +} + +func appendEscapedRune(buf []byte, r rune, quote byte) []byte { + var runeTmp [utf8.UTFMax]byte + if r == rune(quote) || r == '\\' { // always backslashed + buf = append(buf, '\\') + buf = append(buf, byte(r)) + return buf + } + if unicode.IsPrint(r) { + n := utf8.EncodeRune(runeTmp[:], r) + buf = append(buf, runeTmp[:n]...) + return buf + } + switch r { + case '\a': + buf = append(buf, `\a`...) + case '\b': + buf = append(buf, `\b`...) + case '\f': + buf = append(buf, `\f`...) + case '\n': + buf = append(buf, `\n`...) + case '\r': + buf = append(buf, `\r`...) + case '\t': + buf = append(buf, `\t`...) + case '\v': + buf = append(buf, `\v`...) + default: + switch { + case r < ' ': + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[byte(r)>>4]) + buf = append(buf, lowerhex[byte(r)&0xF]) + case r > utf8.MaxRune: + r = 0xFFFD + fallthrough + case r < 0x10000: + buf = append(buf, `\u`...) + for s := 12; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + default: + buf = append(buf, `\U`...) + for s := 28; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + } + } + return buf +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/struct.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/struct.go new file mode 100644 index 0000000..a3da8dd --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/struct.go @@ -0,0 +1,130 @@ +package yaml + +import ( + "reflect" + "strings" + + "golang.org/x/xerrors" +) + +const ( + // StructTagName tag keyword for Marshal/Unmarshal + StructTagName = "yaml" +) + +// StructField information for each the field in structure +type StructField struct { + FieldName string + RenderName string + AnchorName string + AliasName string + IsAutoAnchor bool + IsAutoAlias bool + IsOmitEmpty bool + IsFlow bool + IsInline bool +} + +func getTag(field reflect.StructField) string { + // If struct tag `yaml` exist, use that. If no `yaml` + // exists, but `json` does, use that and try the best to + // adhere to its rules + tag := field.Tag.Get(StructTagName) + if tag == "" { + tag = field.Tag.Get(`json`) + } + return tag +} + +func structField(field reflect.StructField) *StructField { + tag := getTag(field) + fieldName := strings.ToLower(field.Name) + options := strings.Split(tag, ",") + if len(options) > 0 { + if options[0] != "" { + fieldName = options[0] + } + } + structField := &StructField{ + FieldName: field.Name, + RenderName: fieldName, + } + if len(options) > 1 { + for _, opt := range options[1:] { + switch { + case opt == "omitempty": + structField.IsOmitEmpty = true + case opt == "flow": + structField.IsFlow = true + case opt == "inline": + structField.IsInline = true + case strings.HasPrefix(opt, "anchor"): + anchor := strings.Split(opt, "=") + if len(anchor) > 1 { + structField.AnchorName = anchor[1] + } else { + structField.IsAutoAnchor = true + } + case strings.HasPrefix(opt, "alias"): + alias := strings.Split(opt, "=") + if len(alias) > 1 { + structField.AliasName = alias[1] + } else { + structField.IsAutoAlias = true + } + default: + } + } + } + return structField +} + +func isIgnoredStructField(field reflect.StructField) bool { + if field.PkgPath != "" && !field.Anonymous { + // private field + return true + } + tag := getTag(field) + if tag == "-" { + return true + } + return false +} + +type StructFieldMap map[string]*StructField + +func (m StructFieldMap) isIncludedRenderName(name string) bool { + for _, v := range m { + if !v.IsInline && v.RenderName == name { + return true + } + } + return false +} + +func (m StructFieldMap) hasMergeProperty() bool { + for _, v := range m { + if v.IsOmitEmpty && v.IsInline && v.IsAutoAlias { + return true + } + } + return false +} + +func structFieldMap(structType reflect.Type) (StructFieldMap, error) { + structFieldMap := StructFieldMap{} + renderNameMap := map[string]struct{}{} + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if isIgnoredStructField(field) { + continue + } + structField := structField(field) + if _, exists := renderNameMap[structField.RenderName]; exists { + return nil, xerrors.Errorf("duplicated struct field name %s", structField.RenderName) + } + structFieldMap[structField.FieldName] = structField + renderNameMap[structField.RenderName] = struct{}{} + } + return structFieldMap, nil +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/testdata/anchor.yml b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/testdata/anchor.yml new file mode 100644 index 0000000..c016c7f --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/testdata/anchor.yml @@ -0,0 +1,3 @@ +a: &a + b: 1 + c: hello diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/token/token.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/token/token.go new file mode 100644 index 0000000..c86caab --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/token/token.go @@ -0,0 +1,1070 @@ +package token + +import ( + "fmt" + "strings" +) + +// Character type for character +type Character byte + +const ( + // SequenceEntryCharacter character for sequence entry + SequenceEntryCharacter Character = '-' + // MappingKeyCharacter character for mapping key + MappingKeyCharacter Character = '?' + // MappingValueCharacter character for mapping value + MappingValueCharacter Character = ':' + // CollectEntryCharacter character for collect entry + CollectEntryCharacter Character = ',' + // SequenceStartCharacter character for sequence start + SequenceStartCharacter Character = '[' + // SequenceEndCharacter character for sequence end + SequenceEndCharacter Character = ']' + // MappingStartCharacter character for mapping start + MappingStartCharacter Character = '{' + // MappingEndCharacter character for mapping end + MappingEndCharacter Character = '}' + // CommentCharacter character for comment + CommentCharacter Character = '#' + // AnchorCharacter character for anchor + AnchorCharacter Character = '&' + // AliasCharacter character for alias + AliasCharacter Character = '*' + // TagCharacter character for tag + TagCharacter Character = '!' + // LiteralCharacter character for literal + LiteralCharacter Character = '|' + // FoldedCharacter character for folded + FoldedCharacter Character = '>' + // SingleQuoteCharacter character for single quote + SingleQuoteCharacter Character = '\'' + // DoubleQuoteCharacter character for double quote + DoubleQuoteCharacter Character = '"' + // DirectiveCharacter character for directive + DirectiveCharacter Character = '%' + // SpaceCharacter character for space + SpaceCharacter Character = ' ' + // LineBreakCharacter character for line break + LineBreakCharacter Character = '\n' +) + +// Type type identifier for token +type Type int + +const ( + // UnknownType reserve for invalid type + UnknownType Type = iota + // DocumentHeaderType type for DocumentHeader token + DocumentHeaderType + // DocumentEndType type for DocumentEnd token + DocumentEndType + // SequenceEntryType type for SequenceEntry token + SequenceEntryType + // MappingKeyType type for MappingKey token + MappingKeyType + // MappingValueType type for MappingValue token + MappingValueType + // MergeKeyType type for MergeKey token + MergeKeyType + // CollectEntryType type for CollectEntry token + CollectEntryType + // SequenceStartType type for SequenceStart token + SequenceStartType + // SequenceEndType type for SequenceEnd token + SequenceEndType + // MappingStartType type for MappingStart token + MappingStartType + // MappingEndType type for MappingEnd token + MappingEndType + // CommentType type for Comment token + CommentType + // AnchorType type for Anchor token + AnchorType + // AliasType type for Alias token + AliasType + // TagType type for Tag token + TagType + // LiteralType type for Literal token + LiteralType + // FoldedType type for Folded token + FoldedType + // SingleQuoteType type for SingleQuote token + SingleQuoteType + // DoubleQuoteType type for DoubleQuote token + DoubleQuoteType + // DirectiveType type for Directive token + DirectiveType + // SpaceType type for Space token + SpaceType + // NullType type for Null token + NullType + // InfinityType type for Infinity token + InfinityType + // NanType type for Nan token + NanType + // IntegerType type for Integer token + IntegerType + // BinaryIntegerType type for BinaryInteger token + BinaryIntegerType + // OctetIntegerType type for OctetInteger token + OctetIntegerType + // HexIntegerType type for HexInteger token + HexIntegerType + // FloatType type for Float token + FloatType + // StringType type for String token + StringType + // BoolType type for Bool token + BoolType +) + +// String type identifier to text +func (t Type) String() string { + switch t { + case UnknownType: + return "Unknown" + case DocumentHeaderType: + return "DocumentHeader" + case DocumentEndType: + return "DocumentEnd" + case SequenceEntryType: + return "SequenceEntry" + case MappingKeyType: + return "MappingKey" + case MappingValueType: + return "MappingValue" + case MergeKeyType: + return "MergeKey" + case CollectEntryType: + return "CollectEntry" + case SequenceStartType: + return "SequenceStart" + case SequenceEndType: + return "SequenceEnd" + case MappingStartType: + return "MappingStart" + case MappingEndType: + return "MappingEnd" + case CommentType: + return "Comment" + case AnchorType: + return "Anchor" + case AliasType: + return "Alias" + case TagType: + return "Tag" + case LiteralType: + return "Literal" + case FoldedType: + return "Folded" + case SingleQuoteType: + return "SingleQuote" + case DoubleQuoteType: + return "DoubleQuote" + case DirectiveType: + return "Directive" + case SpaceType: + return "Space" + case StringType: + return "String" + case BoolType: + return "Bool" + case IntegerType: + return "Integer" + case BinaryIntegerType: + return "BinaryInteger" + case OctetIntegerType: + return "OctetInteger" + case HexIntegerType: + return "HexInteger" + case FloatType: + return "Float" + case NullType: + return "Null" + case InfinityType: + return "Infinity" + case NanType: + return "Nan" + } + return "" +} + +// CharacterType type for character category +type CharacterType int + +const ( + // CharacterTypeIndicator type of indicator character + CharacterTypeIndicator CharacterType = iota + // CharacterTypeWhiteSpace type of white space character + CharacterTypeWhiteSpace + // CharacterTypeMiscellaneous type of miscellaneous character + CharacterTypeMiscellaneous + // CharacterTypeEscaped type of escaped character + CharacterTypeEscaped +) + +// String character type identifier to text +func (c CharacterType) String() string { + switch c { + case CharacterTypeIndicator: + return "Indicator" + case CharacterTypeWhiteSpace: + return "WhiteSpcae" + case CharacterTypeMiscellaneous: + return "Miscellaneous" + case CharacterTypeEscaped: + return "Escaped" + } + return "" +} + +// Indicator type for indicator +type Indicator int + +const ( + // NotIndicator not indicator + NotIndicator Indicator = iota + // BlockStructureIndicator indicator for block structure ( '-', '?', ':' ) + BlockStructureIndicator + // FlowCollectionIndicator indicator for flow collection ( '[', ']', '{', '}', ',' ) + FlowCollectionIndicator + // CommentIndicator indicator for comment ( '#' ) + CommentIndicator + // NodePropertyIndicator indicator for node property ( '!', '&', '*' ) + NodePropertyIndicator + // BlockScalarIndicator indicator for block scalar ( '|', '>' ) + BlockScalarIndicator + // QuotedScalarIndicator indicator for quoted scalar ( ''', '"' ) + QuotedScalarIndicator + // DirectiveIndicator indicator for directive ( '%' ) + DirectiveIndicator + // InvalidUseOfReservedIndicator indicator for invalid use of reserved keyword ( '@', '`' ) + InvalidUseOfReservedIndicator +) + +// String indicator to text +func (i Indicator) String() string { + switch i { + case NotIndicator: + return "NotIndicator" + case BlockStructureIndicator: + return "BlockStructure" + case FlowCollectionIndicator: + return "FlowCollection" + case CommentIndicator: + return "Comment" + case NodePropertyIndicator: + return "NodeProperty" + case BlockScalarIndicator: + return "BlockScalar" + case QuotedScalarIndicator: + return "QuotedScalar" + case DirectiveIndicator: + return "Directive" + case InvalidUseOfReservedIndicator: + return "InvalidUseOfReserved" + } + return "" +} + +var ( + reservedNullKeywords = []string{ + "null", + "Null", + "NULL", + "~", + } + reservedBoolKeywords = []string{ + "true", + "True", + "TRUE", + "false", + "False", + "FALSE", + } + // For compatibility with other YAML 1.1 parsers + // Note that we use these solely for encoding the bool value with quotes. + // go-yaml should not treat these as reserved keywords at parsing time. + // as go-yaml is supposed to be compliant only with YAML 1.2. + reservedLegacyBoolKeywords = []string{ + "y", + "Y", + "yes", + "Yes", + "YES", + "n", + "N", + "no", + "No", + "NO", + "on", + "On", + "ON", + "off", + "Off", + "OFF", + } + reservedInfKeywords = []string{ + ".inf", + ".Inf", + ".INF", + "-.inf", + "-.Inf", + "-.INF", + } + reservedNanKeywords = []string{ + ".nan", + ".NaN", + ".NAN", + } + reservedKeywordMap = map[string]func(string, string, *Position) *Token{} + // reservedEncKeywordMap contains is the keyword map used at encoding time. + // This is supposed to be a superset of reservedKeywordMap, + // and used to quote legacy keywords present in YAML 1.1 or lesser for compatibility reasons, + // even though this library is supposed to be YAML 1.2-compliant. + reservedEncKeywordMap = map[string]func(string, string, *Position) *Token{} +) + +func reservedKeywordToken(typ Type, value, org string, pos *Position) *Token { + return &Token{ + Type: typ, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +func init() { + for _, keyword := range reservedNullKeywords { + reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { + return reservedKeywordToken(NullType, value, org, pos) + } + } + for _, keyword := range reservedBoolKeywords { + f := func(value, org string, pos *Position) *Token { + return reservedKeywordToken(BoolType, value, org, pos) + } + reservedKeywordMap[keyword] = f + reservedEncKeywordMap[keyword] = f + } + for _, keyword := range reservedLegacyBoolKeywords { + reservedEncKeywordMap[keyword] = func(value, org string, pos *Position) *Token { + return reservedKeywordToken(BoolType, value, org, pos) + } + } + for _, keyword := range reservedInfKeywords { + reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { + return reservedKeywordToken(InfinityType, value, org, pos) + } + } + for _, keyword := range reservedNanKeywords { + reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { + return reservedKeywordToken(NanType, value, org, pos) + } + } +} + +// ReservedTagKeyword type of reserved tag keyword +type ReservedTagKeyword string + +const ( + // IntegerTag `!!int` tag + IntegerTag ReservedTagKeyword = "!!int" + // FloatTag `!!float` tag + FloatTag ReservedTagKeyword = "!!float" + // NullTag `!!null` tag + NullTag ReservedTagKeyword = "!!null" + // SequenceTag `!!seq` tag + SequenceTag ReservedTagKeyword = "!!seq" + // MappingTag `!!map` tag + MappingTag ReservedTagKeyword = "!!map" + // StringTag `!!str` tag + StringTag ReservedTagKeyword = "!!str" + // BinaryTag `!!binary` tag + BinaryTag ReservedTagKeyword = "!!binary" + // OrderedMapTag `!!omap` tag + OrderedMapTag ReservedTagKeyword = "!!omap" + // SetTag `!!set` tag + SetTag ReservedTagKeyword = "!!set" + // TimestampTag `!!timestamp` tag + TimestampTag ReservedTagKeyword = "!!timestamp" +) + +var ( + // ReservedTagKeywordMap map for reserved tag keywords + ReservedTagKeywordMap = map[ReservedTagKeyword]func(string, string, *Position) *Token{ + IntegerTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + FloatTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + NullTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + SequenceTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + MappingTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + StringTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + BinaryTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + OrderedMapTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + SetTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + TimestampTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + } +) + +type numType int + +const ( + numTypeNone numType = iota + numTypeBinary + numTypeOctet + numTypeHex + numTypeFloat +) + +type numStat struct { + isNum bool + typ numType +} + +func getNumberStat(str string) *numStat { + stat := &numStat{} + if str == "" { + return stat + } + if str == "-" || str == "." || str == "+" || str == "_" { + return stat + } + if str[0] == '_' { + return stat + } + dotFound := false + isNegative := false + isExponent := false + if str[0] == '-' { + isNegative = true + } + for idx, c := range str { + switch c { + case 'x': + if (isNegative && idx == 2) || (!isNegative && idx == 1) { + continue + } + case 'o': + if (isNegative && idx == 2) || (!isNegative && idx == 1) { + continue + } + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + continue + case 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F': + if (len(str) > 2 && str[0] == '0' && str[1] == 'x') || + (len(str) > 3 && isNegative && str[1] == '0' && str[2] == 'x') { + // hex number + continue + } + if c == 'b' && ((isNegative && idx == 2) || (!isNegative && idx == 1)) { + // binary number + continue + } + if (c == 'e' || c == 'E') && dotFound { + // exponent + isExponent = true + continue + } + case '.': + if dotFound { + // multiple dot + return stat + } + dotFound = true + continue + case '-': + if idx == 0 || isExponent { + continue + } + case '+': + if idx == 0 || isExponent { + continue + } + case '_': + continue + } + return stat + } + stat.isNum = true + switch { + case dotFound: + stat.typ = numTypeFloat + case strings.HasPrefix(str, "0b") || strings.HasPrefix(str, "-0b"): + stat.typ = numTypeBinary + case strings.HasPrefix(str, "0x") || strings.HasPrefix(str, "-0x"): + stat.typ = numTypeHex + case strings.HasPrefix(str, "0o") || strings.HasPrefix(str, "-0o"): + stat.typ = numTypeOctet + case (len(str) > 1 && str[0] == '0') || (len(str) > 1 && str[0] == '-' && str[1] == '0'): + stat.typ = numTypeOctet + } + return stat +} + +func looksLikeTimeValue(value string) bool { + for i, c := range value { + switch c { + case ':', '1', '2', '3', '4', '5', '6', '7', '8', '9': + continue + case '0': + if i == 0 { + return false + } + continue + } + return false + } + return true +} + +// IsNeedQuoted whether need quote for passed string or not +func IsNeedQuoted(value string) bool { + if value == "" { + return true + } + if _, exists := reservedEncKeywordMap[value]; exists { + return true + } + if stat := getNumberStat(value); stat.isNum { + return true + } + first := value[0] + switch first { + case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"', '@', ' ': + return true + } + last := value[len(value)-1] + switch last { + case ':', ' ': + return true + } + if looksLikeTimeValue(value) { + return true + } + for i, c := range value { + switch c { + case '#', '\\': + return true + case ':': + if i+1 < len(value) && value[i+1] == ' ' { + return true + } + } + } + return false +} + +// LiteralBlockHeader detect literal block scalar header +func LiteralBlockHeader(value string) string { + lbc := DetectLineBreakCharacter(value) + + switch { + case !strings.Contains(value, lbc): + return "" + case strings.HasSuffix(value, fmt.Sprintf("%s%s", lbc, lbc)): + return "|+" + case strings.HasSuffix(value, lbc): + return "|" + default: + return "|-" + } +} + +// New create reserved keyword token or number token and other string token +func New(value string, org string, pos *Position) *Token { + fn := reservedKeywordMap[value] + if fn != nil { + return fn(value, org, pos) + } + if stat := getNumberStat(value); stat.isNum { + tk := &Token{ + Type: IntegerType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: value, + Origin: org, + Position: pos, + } + switch stat.typ { + case numTypeFloat: + tk.Type = FloatType + case numTypeBinary: + tk.Type = BinaryIntegerType + case numTypeOctet: + tk.Type = OctetIntegerType + case numTypeHex: + tk.Type = HexIntegerType + } + return tk + } + return String(value, org, pos) +} + +// Position type for position in YAML document +type Position struct { + Line int + Column int + Offset int + IndentNum int + IndentLevel int +} + +// String position to text +func (p *Position) String() string { + return fmt.Sprintf("[level:%d,line:%d,column:%d,offset:%d]", p.IndentLevel, p.Line, p.Column, p.Offset) +} + +// Token type for token +type Token struct { + Type Type + CharacterType CharacterType + Indicator Indicator + Value string + Origin string + Position *Position + Next *Token + Prev *Token +} + +// PreviousType previous token type +func (t *Token) PreviousType() Type { + if t.Prev != nil { + return t.Prev.Type + } + return UnknownType +} + +// NextType next token type +func (t *Token) NextType() Type { + if t.Next != nil { + return t.Next.Type + } + return UnknownType +} + +// AddColumn append column number to current position of column +func (t *Token) AddColumn(col int) { + if t == nil { + return + } + t.Position.Column += col +} + +// Clone copy token ( preserve Prev/Next reference ) +func (t *Token) Clone() *Token { + if t == nil { + return nil + } + copied := *t + if t.Position != nil { + pos := *(t.Position) + copied.Position = &pos + } + return &copied +} + +// Tokens type of token collection +type Tokens []*Token + +func (t *Tokens) add(tk *Token) { + tokens := *t + if len(tokens) == 0 { + tokens = append(tokens, tk) + } else { + last := tokens[len(tokens)-1] + last.Next = tk + tk.Prev = last + tokens = append(tokens, tk) + } + *t = tokens +} + +// Add append new some tokens +func (t *Tokens) Add(tks ...*Token) { + for _, tk := range tks { + t.add(tk) + } +} + +// Dump dump all token structures for debugging +func (t Tokens) Dump() { + for _, tk := range t { + fmt.Printf("- %+v\n", tk) + } +} + +// String create token for String +func String(value string, org string, pos *Position) *Token { + return &Token{ + Type: StringType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// SequenceEntry create token for SequenceEntry +func SequenceEntry(org string, pos *Position) *Token { + return &Token{ + Type: SequenceEntryType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockStructureIndicator, + Value: string(SequenceEntryCharacter), + Origin: org, + Position: pos, + } +} + +// MappingKey create token for MappingKey +func MappingKey(pos *Position) *Token { + return &Token{ + Type: MappingKeyType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockStructureIndicator, + Value: string(MappingKeyCharacter), + Origin: string(MappingKeyCharacter), + Position: pos, + } +} + +// MappingValue create token for MappingValue +func MappingValue(pos *Position) *Token { + return &Token{ + Type: MappingValueType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockStructureIndicator, + Value: string(MappingValueCharacter), + Origin: string(MappingValueCharacter), + Position: pos, + } +} + +// CollectEntry create token for CollectEntry +func CollectEntry(org string, pos *Position) *Token { + return &Token{ + Type: CollectEntryType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(CollectEntryCharacter), + Origin: org, + Position: pos, + } +} + +// SequenceStart create token for SequenceStart +func SequenceStart(org string, pos *Position) *Token { + return &Token{ + Type: SequenceStartType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(SequenceStartCharacter), + Origin: org, + Position: pos, + } +} + +// SequenceEnd create token for SequenceEnd +func SequenceEnd(org string, pos *Position) *Token { + return &Token{ + Type: SequenceEndType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(SequenceEndCharacter), + Origin: org, + Position: pos, + } +} + +// MappingStart create token for MappingStart +func MappingStart(org string, pos *Position) *Token { + return &Token{ + Type: MappingStartType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(MappingStartCharacter), + Origin: org, + Position: pos, + } +} + +// MappingEnd create token for MappingEnd +func MappingEnd(org string, pos *Position) *Token { + return &Token{ + Type: MappingEndType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(MappingEndCharacter), + Origin: org, + Position: pos, + } +} + +// Comment create token for Comment +func Comment(value string, org string, pos *Position) *Token { + return &Token{ + Type: CommentType, + CharacterType: CharacterTypeIndicator, + Indicator: CommentIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// Anchor create token for Anchor +func Anchor(org string, pos *Position) *Token { + return &Token{ + Type: AnchorType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: string(AnchorCharacter), + Origin: org, + Position: pos, + } +} + +// Alias create token for Alias +func Alias(org string, pos *Position) *Token { + return &Token{ + Type: AliasType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: string(AliasCharacter), + Origin: org, + Position: pos, + } +} + +// Tag create token for Tag +func Tag(value string, org string, pos *Position) *Token { + fn := ReservedTagKeywordMap[ReservedTagKeyword(value)] + if fn != nil { + return fn(value, org, pos) + } + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// Literal create token for Literal +func Literal(value string, org string, pos *Position) *Token { + return &Token{ + Type: LiteralType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockScalarIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// Folded create token for Folded +func Folded(value string, org string, pos *Position) *Token { + return &Token{ + Type: FoldedType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockScalarIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// SingleQuote create token for SingleQuote +func SingleQuote(value string, org string, pos *Position) *Token { + return &Token{ + Type: SingleQuoteType, + CharacterType: CharacterTypeIndicator, + Indicator: QuotedScalarIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// DoubleQuote create token for DoubleQuote +func DoubleQuote(value string, org string, pos *Position) *Token { + return &Token{ + Type: DoubleQuoteType, + CharacterType: CharacterTypeIndicator, + Indicator: QuotedScalarIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// Directive create token for Directive +func Directive(org string, pos *Position) *Token { + return &Token{ + Type: DirectiveType, + CharacterType: CharacterTypeIndicator, + Indicator: DirectiveIndicator, + Value: string(DirectiveCharacter), + Origin: org, + Position: pos, + } +} + +// Space create token for Space +func Space(pos *Position) *Token { + return &Token{ + Type: SpaceType, + CharacterType: CharacterTypeWhiteSpace, + Indicator: NotIndicator, + Value: string(SpaceCharacter), + Origin: string(SpaceCharacter), + Position: pos, + } +} + +// MergeKey create token for MergeKey +func MergeKey(org string, pos *Position) *Token { + return &Token{ + Type: MergeKeyType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: "<<", + Origin: org, + Position: pos, + } +} + +// DocumentHeader create token for DocumentHeader +func DocumentHeader(org string, pos *Position) *Token { + return &Token{ + Type: DocumentHeaderType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: "---", + Origin: org, + Position: pos, + } +} + +// DocumentEnd create token for DocumentEnd +func DocumentEnd(org string, pos *Position) *Token { + return &Token{ + Type: DocumentEndType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: "...", + Origin: org, + Position: pos, + } +} + +// DetectLineBreakCharacter detect line break character in only one inside scalar content scope. +func DetectLineBreakCharacter(src string) string { + nc := strings.Count(src, "\n") + rc := strings.Count(src, "\r") + rnc := strings.Count(src, "\r\n") + switch { + case nc == rnc && rc == rnc: + return "\r\n" + case rc > nc: + return "\r" + default: + return "\n" + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/token/token_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/token/token_test.go new file mode 100644 index 0000000..dc1b4df --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/token/token_test.go @@ -0,0 +1,138 @@ +package token_test + +import ( + "testing" + + "github.com/goccy/go-yaml/token" +) + +func TestToken(t *testing.T) { + pos := &token.Position{} + tokens := token.Tokens{ + token.SequenceEntry("-", pos), + token.MappingKey(pos), + token.MappingValue(pos), + token.CollectEntry(",", pos), + token.SequenceStart("[", pos), + token.SequenceEnd("]", pos), + token.MappingStart("{", pos), + token.MappingEnd("}", pos), + token.Comment("#", "#", pos), + token.Anchor("&", pos), + token.Alias("*", pos), + token.Literal("|", "|", pos), + token.Folded(">", ">", pos), + token.SingleQuote("'", "'", pos), + token.DoubleQuote(`"`, `"`, pos), + token.Directive("%", pos), + token.Space(pos), + token.MergeKey("<<", pos), + token.DocumentHeader("---", pos), + token.DocumentEnd("...", pos), + token.New("1", "1", pos), + token.New("3.14", "3.14", pos), + token.New("-0b101010", "-0b101010", pos), + token.New("0xA", "0xA", pos), + token.New("685.230_15e+03", "685.230_15e+03", pos), + token.New("02472256", "02472256", pos), + token.New("0o2472256", "0o2472256", pos), + token.New("", "", pos), + token.New("_1", "_1", pos), + token.New("1.1.1.1", "1.1.1.1", pos), + token.New("+", "+", pos), + token.New("-", "-", pos), + token.New("_", "_", pos), + token.New("~", "~", pos), + token.New("true", "true", pos), + token.New("false", "false", pos), + token.New(".nan", ".nan", pos), + token.New(".inf", ".inf", pos), + token.New("-.inf", "-.inf", pos), + token.New("null", "null", pos), + token.Tag("!!null", "!!null", pos), + token.Tag("!!map", "!!map", pos), + token.Tag("!!str", "!!str", pos), + token.Tag("!!seq", "!!seq", pos), + token.Tag("!!binary", "!!binary", pos), + token.Tag("!!omap", "!!omap", pos), + token.Tag("!!set", "!!set", pos), + token.Tag("!!int", "!!int", pos), + token.Tag("!!float", "!!float", pos), + token.Tag("!hoge", "!hoge", pos), + } + tokens.Dump() + tokens.Add(token.New("hoge", "hoge", pos)) + if tokens[len(tokens)-1].PreviousType() != token.TagType { + t.Fatal("invalid previous token type") + } + if tokens[0].PreviousType() != token.UnknownType { + t.Fatal("invalid previous token type") + } + if tokens[len(tokens)-2].NextType() != token.StringType { + t.Fatal("invalid next token type") + } + if tokens[len(tokens)-1].NextType() != token.UnknownType { + t.Fatal("invalid next token type") + } +} + +func TestIsNeedQuoted(t *testing.T) { + needQuotedTests := []string{ + "", + "true", + "1.234", + "1:1", + "hoge # comment", + "\\0", + "#a b", + "*a b", + "&a b", + "{a b", + "}a b", + "[a b", + "]a b", + ",a b", + "!a b", + "|a b", + ">a b", + ">a b", + "%a b", + `'a b`, + `"a b`, + "a:", + "a: b", + "y", + "Y", + "yes", + "Yes", + "YES", + "n", + "N", + "no", + "No", + "NO", + "on", + "On", + "ON", + "off", + "Off", + "OFF", + "@test", + " a", + " a ", + "a ", + } + for i, test := range needQuotedTests { + if !token.IsNeedQuoted(test) { + t.Fatalf("%d: failed to quoted judge for %s", i, test) + } + } + notNeedQuotedTests := []string{ + "Hello World", + } + for i, test := range notNeedQuotedTests { + if token.IsNeedQuoted(test) { + t.Fatalf("%d: failed to quoted judge for %s", i, test) + } + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/validate.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/validate.go new file mode 100644 index 0000000..20a2d6d --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/validate.go @@ -0,0 +1,13 @@ +package yaml + +// StructValidator need to implement Struct method only +// ( see https://pkg.go.dev/github.com/go-playground/validator/v10#Validate.Struct ) +type StructValidator interface { + Struct(interface{}) error +} + +// FieldError need to implement StructField method only +// ( see https://pkg.go.dev/github.com/go-playground/validator/v10#FieldError ) +type FieldError interface { + StructField() string +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/validate_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/validate_test.go new file mode 100644 index 0000000..265deb8 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/validate_test.go @@ -0,0 +1,198 @@ +package yaml_test + +import ( + "strings" + "testing" + + "github.com/go-playground/validator/v10" + "github.com/goccy/go-yaml" +) + +func TestStructValidator(t *testing.T) { + type Inner struct { + Required string `validate:"required"` + Lt10 int `validate:"lt=10"` + } + + cases := []struct { + TestName string + YAMLContent string + ExpectedErr string + Instance interface{} + }{ + { + TestName: "Test Simple Validation", + YAMLContent: `--- +- name: john + age: 20 +- name: tom + age: -1 +- name: ken + age: 10`, + ExpectedErr: `[5:8] Key: 'Age' Error:Field validation for 'Age' failed on the 'gte' tag + 2 | - name: john + 3 | age: 20 + 4 | - name: tom +> 5 | age: -1 + ^ + 6 | - name: ken + 7 | age: 10`, + Instance: &[]struct { + Name string `yaml:"name" validate:"required"` + Age int `yaml:"age" validate:"gte=0,lt=120"` + }{}, + }, + { + TestName: "Test Missing Required Field", + YAMLContent: `--- +- name: john + age: 20 +- age: 10`, + ExpectedErr: `[4:1] Key: 'Name' Error:Field validation for 'Name' failed on the 'required' tag + 1 | --- + 2 | - name: john + 3 | age: 20 +> 4 | - age: 10 + ^ +`, + Instance: &[]struct { + Name string `yaml:"name" validate:"required"` + Age int `yaml:"age" validate:"gte=0,lt=120"` + }{}, + }, + { + TestName: "Test Nested Validation Missing Internal Required", + YAMLContent: `--- +name: john +age: 10 +addr: + number: seven`, + ExpectedErr: `[4:5] Key: 'State' Error:Field validation for 'State' failed on the 'required' tag + 1 | --- + 2 | name: john + 3 | age: 10 +> 4 | addr: + ^ + 5 | number: seven`, + Instance: &struct { + Name string `yaml:"name" validate:"required"` + Age int `yaml:"age" validate:"gte=0,lt=120"` + Addr struct { + Number string `yaml:"number" validate:"required"` + State string `yaml:"state" validate:"required"` + } `yaml:"addr"` + }{}, + }, + { + TestName: "Test nested Validation with unknown field", + YAMLContent: `--- +name: john +age: 20 +addr: + number: seven + state: washington + error: error +`, + ExpectedErr: `[7:3] unknown field "error" + 4 | addr: + 5 | number: seven + 6 | state: washington +> 7 | error: error + ^ +`, + Instance: &struct { + Name string `yaml:"name" validate:"required"` + Age int `yaml:"age" validate:"gte=0,lt=120"` + Addr *struct { + Number string `yaml:"number" validate:"required"` + State string `yaml:"state" validate:"required"` + } `yaml:"addr" validate:"required"` + }{}, + }, + { + TestName: "Test Validation with wrong field type", + YAMLContent: `--- +name: myDocument +roles: + name: myRole + permissions: + - hello + - how + - are + - you + `, + ExpectedErr: `[4:7] mapping was used where sequence is expected + 1 | --- + 2 | name: myDocument + 3 | roles: +> 4 | name: myRole + ^ + 5 | permissions: + 6 | - hello + 7 | - how + 8 | `, + Instance: &struct { + Name string `yaml:"name"` + Roles []struct { + Name string `yaml:"name"` + Permissions []string `yaml:"permissions"` + } `yaml:"roles"` + }{}, + }, + { + TestName: "Test inline validation missing required", + YAMLContent: `--- +name: john +age: 20 +`, + ExpectedErr: `Key: 'Inner.Required' Error:Field validation for 'Required' failed on the 'required' tag`, + Instance: &struct { + Name string `yaml:"name" validate:"required"` + Age int `yaml:"age" validate:"gte=0,lt=120"` + Inner `yaml:",inline"` + }{}, + }, + { + TestName: "Test inline validation field error", + YAMLContent: `--- +name: john +age: 20 +required: present +lt10: 20 +`, + ExpectedErr: `[5:7] Key: 'Inner.Lt10' Error:Field validation for 'Lt10' failed on the 'lt' tag + 2 | name: john + 3 | age: 20 + 4 | required: present +> 5 | lt10: 20 + ^ +`, + Instance: &struct { + Name string `yaml:"name" validate:"required"` + Age int `yaml:"age" validate:"gte=0,lt=120"` + Inner `yaml:",inline"` + }{}, + }, + } + + for _, tc := range cases { + tc := tc // NOTE: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + t.Run(tc.TestName, func(t *testing.T) { + validate := validator.New() + dec := yaml.NewDecoder( + strings.NewReader(tc.YAMLContent), + yaml.Validator(validate), + yaml.Strict(), + ) + err := dec.Decode(tc.Instance) + switch { + case tc.ExpectedErr != "" && err == nil: + t.Fatal("expected error") + case tc.ExpectedErr == "" && err != nil: + t.Fatalf("unexpected error: %v", err) + case tc.ExpectedErr != "" && tc.ExpectedErr != err.Error(): + t.Fatalf("expected `%s` but actual `%s`", tc.ExpectedErr, err.Error()) + } + }) + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/yaml.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/yaml.go new file mode 100644 index 0000000..25b1056 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/yaml.go @@ -0,0 +1,288 @@ +package yaml + +import ( + "bytes" + "context" + "io" + "reflect" + "sync" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "golang.org/x/xerrors" +) + +// BytesMarshaler interface may be implemented by types to customize their +// behavior when being marshaled into a YAML document. The returned value +// is marshaled in place of the original value implementing Marshaler. +// +// If an error is returned by MarshalYAML, the marshaling procedure stops +// and returns with the provided error. +type BytesMarshaler interface { + MarshalYAML() ([]byte, error) +} + +// BytesMarshalerContext interface use BytesMarshaler with context.Context. +type BytesMarshalerContext interface { + MarshalYAML(context.Context) ([]byte, error) +} + +// InterfaceMarshaler interface has MarshalYAML compatible with github.com/go-yaml/yaml package. +type InterfaceMarshaler interface { + MarshalYAML() (interface{}, error) +} + +// InterfaceMarshalerContext interface use InterfaceMarshaler with context.Context. +type InterfaceMarshalerContext interface { + MarshalYAML(context.Context) (interface{}, error) +} + +// BytesUnmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a YAML document. +type BytesUnmarshaler interface { + UnmarshalYAML([]byte) error +} + +// BytesUnmarshalerContext interface use BytesUnmarshaler with context.Context. +type BytesUnmarshalerContext interface { + UnmarshalYAML(context.Context, []byte) error +} + +// InterfaceUnmarshaler interface has UnmarshalYAML compatible with github.com/go-yaml/yaml package. +type InterfaceUnmarshaler interface { + UnmarshalYAML(func(interface{}) error) error +} + +// InterfaceUnmarshalerContext interface use InterfaceUnmarshaler with context.Context. +type InterfaceUnmarshalerContext interface { + UnmarshalYAML(context.Context, func(interface{}) error) error +} + +// MapItem is an item in a MapSlice. +type MapItem struct { + Key, Value interface{} +} + +// MapSlice encodes and decodes as a YAML map. +// The order of keys is preserved when encoding and decoding. +type MapSlice []MapItem + +// ToMap convert to map[interface{}]interface{}. +func (s MapSlice) ToMap() map[interface{}]interface{} { + v := map[interface{}]interface{}{} + for _, item := range s { + v[item.Key] = item.Value + } + return v +} + +// Marshal serializes the value provided into a YAML document. The structure +// of the generated document will reflect the structure of the value itself. +// Maps and pointers (to struct, string, int, etc) are accepted as the in value. +// +// Struct fields are only marshalled if they are exported (have an upper case +// first letter), and are marshalled using the field name lowercased as the +// default key. Custom keys may be defined via the "yaml" name in the field +// tag: the content preceding the first comma is used as the key, and the +// following comma-separated options are used to tweak the marshalling process. +// Conflicting names result in a runtime error. +// +// The field tag format accepted is: +// +// `(...) yaml:"[][,[,]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Zero valued structs will be omitted if all their public +// fields are zero, unless they implement an IsZero +// method (see the IsZeroer interface type), in which +// case the field will be included if that method returns true. +// +// flow Marshal using a flow style (useful for structs, +// sequences and maps). +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. +// +// anchor Marshal with anchor. If want to define anchor name explicitly, use anchor=name style. +// Otherwise, if used 'anchor' name only, used the field name lowercased as the anchor name +// +// alias Marshal with alias. If want to define alias name explicitly, use alias=name style. +// Otherwise, If omitted alias name and the field type is pointer type, +// assigned anchor name automatically from same pointer address. +// +// In addition, if the key is "-", the field is ignored. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}) // Returns "a: 1\nb: 0\n" +func Marshal(v interface{}) ([]byte, error) { + return MarshalWithOptions(v) +} + +// MarshalWithOptions serializes the value provided into a YAML document with EncodeOptions. +func MarshalWithOptions(v interface{}, opts ...EncodeOption) ([]byte, error) { + return MarshalContext(context.Background(), v, opts...) +} + +// MarshalContext serializes the value provided into a YAML document with context.Context and EncodeOptions. +func MarshalContext(ctx context.Context, v interface{}, opts ...EncodeOption) ([]byte, error) { + var buf bytes.Buffer + if err := NewEncoder(&buf, opts...).EncodeContext(ctx, v); err != nil { + return nil, errors.Wrapf(err, "failed to marshal") + } + return buf.Bytes(), nil +} + +// ValueToNode convert from value to ast.Node. +func ValueToNode(v interface{}, opts ...EncodeOption) (ast.Node, error) { + var buf bytes.Buffer + node, err := NewEncoder(&buf, opts...).EncodeToNode(v) + if err != nil { + return nil, errors.Wrapf(err, "failed to convert value to node") + } + return node, nil +} + +// Unmarshal decodes the first document found within the in byte slice +// and assigns decoded values into the out value. +// +// Struct fields are only unmarshalled if they are exported (have an +// upper case first letter), and are unmarshalled using the field name +// lowercased as the default key. Custom keys may be defined via the +// "yaml" name in the field tag: the content preceding the first comma +// is used as the key, and the following comma-separated options are +// used to tweak the marshalling process (see Marshal). +// Conflicting names result in a runtime error. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// +// See the documentation of Marshal for the format of tags and a list of +// supported tag options. +func Unmarshal(data []byte, v interface{}) error { + return UnmarshalWithOptions(data, v) +} + +// UnmarshalWithOptions decodes with DecodeOptions the first document found within the in byte slice +// and assigns decoded values into the out value. +func UnmarshalWithOptions(data []byte, v interface{}, opts ...DecodeOption) error { + return UnmarshalContext(context.Background(), data, v, opts...) +} + +// UnmarshalContext decodes with context.Context and DecodeOptions. +func UnmarshalContext(ctx context.Context, data []byte, v interface{}, opts ...DecodeOption) error { + dec := NewDecoder(bytes.NewBuffer(data), opts...) + if err := dec.DecodeContext(ctx, v); err != nil { + if err == io.EOF { + return nil + } + return errors.Wrapf(err, "failed to unmarshal") + } + return nil +} + +// NodeToValue converts node to the value pointed to by v. +func NodeToValue(node ast.Node, v interface{}, opts ...DecodeOption) error { + var buf bytes.Buffer + if err := NewDecoder(&buf, opts...).DecodeFromNode(node, v); err != nil { + return errors.Wrapf(err, "failed to convert node to value") + } + return nil +} + +// FormatError is a utility function that takes advantage of the metadata +// stored in the errors returned by this package's parser. +// +// If the second argument `colored` is true, the error message is colorized. +// If the third argument `inclSource` is true, the error message will +// contain snippets of the YAML source that was used. +func FormatError(e error, colored, inclSource bool) string { + var pp errors.PrettyPrinter + if xerrors.As(e, &pp) { + var buf bytes.Buffer + pp.PrettyPrint(&errors.Sink{&buf}, colored, inclSource) + return buf.String() + } + + return e.Error() +} + +// YAMLToJSON convert YAML bytes to JSON. +func YAMLToJSON(bytes []byte) ([]byte, error) { + var v interface{} + if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal") + } + out, err := MarshalWithOptions(v, JSON()) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal with json option") + } + return out, nil +} + +// JSONToYAML convert JSON bytes to YAML. +func JSONToYAML(bytes []byte) ([]byte, error) { + var v interface{} + if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal from json bytes") + } + out, err := Marshal(v) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal") + } + return out, nil +} + +var ( + globalCustomMarshalerMu sync.Mutex + globalCustomUnmarshalerMu sync.Mutex + globalCustomMarshalerMap = map[reflect.Type]func(interface{}) ([]byte, error){} + globalCustomUnmarshalerMap = map[reflect.Type]func(interface{}, []byte) error{} +) + +// RegisterCustomMarshaler overrides any encoding process for the type specified in generics. +// If you want to switch the behavior for each encoder, use `CustomMarshaler` defined as EncodeOption. +// +// NOTE: If type T implements MarshalYAML for pointer receiver, the type specified in RegisterCustomMarshaler must be *T. +// If RegisterCustomMarshaler and CustomMarshaler of EncodeOption are specified for the same type, +// the CustomMarshaler specified in EncodeOption takes precedence. +func RegisterCustomMarshaler[T any](marshaler func(T) ([]byte, error)) { + globalCustomMarshalerMu.Lock() + defer globalCustomMarshalerMu.Unlock() + + var typ T + globalCustomMarshalerMap[reflect.TypeOf(typ)] = func(v interface{}) ([]byte, error) { + return marshaler(v.(T)) + } +} + +// RegisterCustomUnmarshaler overrides any decoding process for the type specified in generics. +// If you want to switch the behavior for each decoder, use `CustomUnmarshaler` defined as DecodeOption. +// +// NOTE: If RegisterCustomUnmarshaler and CustomUnmarshaler of DecodeOption are specified for the same type, +// the CustomUnmarshaler specified in DecodeOption takes precedence. +func RegisterCustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) { + globalCustomUnmarshalerMu.Lock() + defer globalCustomUnmarshalerMu.Unlock() + + var typ *T + globalCustomUnmarshalerMap[reflect.TypeOf(typ)] = func(v interface{}, b []byte) error { + return unmarshaler(v.(*T), b) + } +} diff --git a/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/yaml_test.go b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/yaml_test.go new file mode 100644 index 0000000..4446d31 --- /dev/null +++ b/dependencies/pkg/mod/github.com/goccy/go-yaml@v1.11.3/yaml_test.go @@ -0,0 +1,1284 @@ +package yaml_test + +import ( + "bytes" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "golang.org/x/xerrors" + + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/ast" +) + +func TestMarshal(t *testing.T) { + var v struct { + A int + B string + } + v.A = 1 + v.B = "hello" + bytes, err := yaml.Marshal(v) + if err != nil { + t.Fatalf("%+v", err) + } + if string(bytes) != "a: 1\nb: hello\n" { + t.Fatal("failed to marshal") + } +} + +func TestUnmarshal(t *testing.T) { + yml := ` +%YAML 1.2 +--- +a: 1 +b: c +` + var v struct { + A int + B string + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatalf("%+v", err) + } +} + +type marshalTest struct{} + +func (t *marshalTest) MarshalYAML() ([]byte, error) { + return yaml.Marshal(yaml.MapSlice{ + { + "a", 1, + }, + { + "b", "hello", + }, + { + "c", true, + }, + { + "d", map[string]string{"x": "y"}, + }, + }) +} + +type marshalTest2 struct{} + +func (t *marshalTest2) MarshalYAML() (interface{}, error) { + return yaml.MapSlice{ + { + "a", 2, + }, + { + "b", "world", + }, + { + "c", true, + }, + }, nil +} + +func TestMarshalYAML(t *testing.T) { + var v struct { + A *marshalTest + B *marshalTest2 + } + v.A = &marshalTest{} + v.B = &marshalTest2{} + bytes, err := yaml.Marshal(v) + if err != nil { + t.Fatalf("failed to Marshal: %+v", err) + } + expect := ` +a: + a: 1 + b: hello + c: true + d: + x: "y" +b: + a: 2 + b: world + c: true +` + actual := "\n" + string(bytes) + if expect != actual { + t.Fatalf("failed to MarshalYAML expect:[%s], actual:[%s]", expect, actual) + } +} + +type unmarshalTest struct { + a int + b string + c bool +} + +func (t *unmarshalTest) UnmarshalYAML(b []byte) error { + if t.a != 0 { + return xerrors.New("unexpected field value to a") + } + if t.b != "" { + return xerrors.New("unexpected field value to b") + } + if t.c { + return xerrors.New("unexpected field value to c") + } + var v struct { + A int + B string + C bool + } + if err := yaml.Unmarshal(b, &v); err != nil { + return err + } + t.a = v.A + t.b = v.B + t.c = v.C + return nil +} + +type unmarshalTest2 struct { + a int + b string + c bool +} + +func (t *unmarshalTest2) UnmarshalYAML(unmarshal func(interface{}) error) error { + var v struct { + A int + B string + C bool + } + if t.a != 0 { + return xerrors.New("unexpected field value to a") + } + if t.b != "" { + return xerrors.New("unexpected field value to b") + } + if t.c { + return xerrors.New("unexpected field value to c") + } + if err := unmarshal(&v); err != nil { + return err + } + t.a = v.A + t.b = v.B + t.c = v.C + return nil +} + +func TestUnmarshalYAML(t *testing.T) { + yml := ` +a: + a: 1 + b: hello + c: true +b: + a: 2 + b: world + c: true +` + var v struct { + A *unmarshalTest + B *unmarshalTest2 + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatalf("failed to Unmarshal: %+v", err) + } + if v.A == nil { + t.Fatal("failed to UnmarshalYAML") + } + if v.A.a != 1 { + t.Fatal("failed to UnmarshalYAML") + } + if v.A.b != "hello" { + t.Fatal("failed to UnmarshalYAML") + } + if !v.A.c { + t.Fatal("failed to UnmarshalYAML") + } + if v.B == nil { + t.Fatal("failed to UnmarshalYAML") + } + if v.B.a != 2 { + t.Fatal("failed to UnmarshalYAML") + } + if v.B.b != "world" { + t.Fatal("failed to UnmarshalYAML") + } + if !v.B.c { + t.Fatal("failed to UnmarshalYAML") + } +} + +type ObjectMap map[string]*Object +type ObjectDecl struct { + Name string `yaml:"-"` + *Object `yaml:",inline,anchor"` +} + +func (m ObjectMap) MarshalYAML() (interface{}, error) { + newMap := map[string]*ObjectDecl{} + for k, v := range m { + newMap[k] = &ObjectDecl{Name: k, Object: v} + } + return newMap, nil +} + +type rootObject struct { + Single ObjectMap `yaml:"single"` + Collection map[string][]*Object `yaml:"collection"` +} + +type Object struct { + *Object `yaml:",omitempty,inline,alias"` + MapValue map[string]interface{} `yaml:",omitempty,inline"` +} + +func TestInlineAnchorAndAlias(t *testing.T) { + yml := `--- +single: + default: &default + id: 1 + name: john + user_1: &user_1 + id: 1 + name: ken + user_2: &user_2 + <<: *default + id: 2 +collection: + defaults: + - *default + - <<: *default + - <<: *default + id: 2 + users: + - <<: *user_1 + - <<: *user_2 + - <<: *user_1 + id: 3 + - <<: *user_1 + id: 4 + - <<: *user_1 + id: 5 +` + var v rootObject + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatal(err) + } + opt := yaml.MarshalAnchor(func(anchor *ast.AnchorNode, value interface{}) error { + if o, ok := value.(*ObjectDecl); ok { + return anchor.SetName(o.Name) + } + return nil + }) + var buf bytes.Buffer + if err := yaml.NewEncoder(&buf, opt).Encode(v); err != nil { + t.Fatalf("%+v", err) + } + actual := "---\n" + buf.String() + if yml != actual { + t.Fatalf("failed to marshal: expected:[%s] actual:[%s]", yml, actual) + } +} + +func TestMapSlice_Map(t *testing.T) { + yml := ` +a: b +c: d +` + var v yaml.MapSlice + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatal(err) + } + m := v.ToMap() + if len(m) != 2 { + t.Fatal("failed to convert MapSlice to map") + } + if m["a"] != "b" { + t.Fatal("failed to convert MapSlice to map") + } + if m["c"] != "d" { + t.Fatal("failed to convert MapSlice to map") + } +} + +func TestMarshalWithModifiedAnchorAlias(t *testing.T) { + yml := ` +a: &a 1 +b: *a +` + var v struct { + A *int `yaml:"a,anchor"` + B *int `yaml:"b,alias"` + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + t.Fatal(err) + } + node, err := yaml.ValueToNode(v) + if err != nil { + t.Fatal(err) + } + anchors := ast.Filter(ast.AnchorType, node) + if len(anchors) != 1 { + t.Fatal("failed to filter node") + } + anchor := anchors[0].(*ast.AnchorNode) + if err := anchor.SetName("b"); err != nil { + t.Fatal(err) + } + aliases := ast.Filter(ast.AliasType, node) + if len(anchors) != 1 { + t.Fatal("failed to filter node") + } + alias := aliases[0].(*ast.AliasNode) + if err := alias.SetName("b"); err != nil { + t.Fatal(err) + } + + expected := ` +a: &b 1 +b: *b` + + actual := "\n" + node.String() + if expected != actual { + t.Fatalf("failed to marshal: expected:[%q] but got [%q]", expected, actual) + } +} + +func Test_YAMLToJSON(t *testing.T) { + yml := ` +foo: + bar: + - a + - b + - c +a: 1 +` + actual, err := yaml.YAMLToJSON([]byte(yml)) + if err != nil { + t.Fatal(err) + } + expected := `{"foo": {"bar": ["a", "b", "c"]}, "a": 1}` + if expected+"\n" != string(actual) { + t.Fatalf("failed to convert yaml to json: expected [%q] but got [%q]", expected, actual) + } +} + +func Test_JSONToYAML(t *testing.T) { + json := `{"foo": {"bar": ["a", "b", "c"]}, "a": 1}` + expected := ` +foo: + bar: + - a + - b + - c +a: 1 +` + actual, err := yaml.JSONToYAML([]byte(json)) + if err != nil { + t.Fatal(err) + } + if expected != "\n"+string(actual) { + t.Fatalf("failed to convert json to yaml: expected [%q] but got [%q]", expected, actual) + } +} + +func Test_WithCommentOption(t *testing.T) { + t.Run("line comment", func(t *testing.T) { + v := struct { + Foo string `yaml:"foo"` + Bar map[string]interface{} `yaml:"bar"` + Baz struct { + X int `yaml:"x"` + } `yaml:"baz"` + }{ + Foo: "aaa", + Bar: map[string]interface{}{"bbb": "ccc"}, + Baz: struct { + X int `yaml:"x"` + }{X: 10}, + } + b, err := yaml.MarshalWithOptions(v, yaml.WithComment( + yaml.CommentMap{ + "$.foo": []*yaml.Comment{yaml.LineComment("foo comment")}, + "$.bar": []*yaml.Comment{yaml.LineComment("bar comment")}, + "$.bar.bbb": []*yaml.Comment{yaml.LineComment("bbb comment")}, + "$.baz.x": []*yaml.Comment{yaml.LineComment("x comment")}, + }, + )) + if err != nil { + t.Fatal(err) + } + expected := ` +foo: aaa #foo comment +bar: #bar comment + bbb: ccc #bbb comment +baz: + x: 10 #x comment +` + actual := "\n" + string(b) + if expected != actual { + t.Fatalf("expected:%s but got %s", expected, actual) + } + }) + t.Run("line comment2", func(t *testing.T) { + v := struct { + Foo map[string]interface{} `yaml:"foo"` + }{ + Foo: map[string]interface{}{ + "bar": map[string]interface{}{ + "baz": true, + }, + }, + } + b, err := yaml.MarshalWithOptions(v, yaml.WithComment( + yaml.CommentMap{ + "$.foo.bar": []*yaml.Comment{yaml.HeadComment(" bar head comment"), yaml.LineComment(" bar line comment")}, + "$.foo.bar.baz": []*yaml.Comment{yaml.LineComment(" baz line comment")}, + }, + )) + if err != nil { + t.Fatal(err) + } + expected := ` +foo: + # bar head comment + bar: # bar line comment + baz: true # baz line comment +` + actual := "\n" + string(b) + if expected != actual { + t.Fatalf("expected:%s but got %s", expected, actual) + } + }) + t.Run("single head comment", func(t *testing.T) { + v := struct { + Foo string `yaml:"foo"` + Bar map[string]interface{} `yaml:"bar"` + Baz struct { + X int `yaml:"x"` + } `yaml:"baz"` + }{ + Foo: "aaa", + Bar: map[string]interface{}{"bbb": "ccc"}, + Baz: struct { + X int `yaml:"x"` + }{X: 10}, + } + + b, err := yaml.MarshalWithOptions(v, yaml.WithComment( + yaml.CommentMap{ + "$.foo": []*yaml.Comment{yaml.HeadComment("foo comment")}, + "$.bar": []*yaml.Comment{yaml.HeadComment("bar comment")}, + "$.bar.bbb": []*yaml.Comment{yaml.HeadComment("bbb comment")}, + "$.baz.x": []*yaml.Comment{yaml.HeadComment("x comment")}, + }, + )) + if err != nil { + t.Fatal(err) + } + expected := ` +#foo comment +foo: aaa +#bar comment +bar: + #bbb comment + bbb: ccc +baz: + #x comment + x: 10 +` + actual := "\n" + string(b) + if expected != actual { + t.Fatalf("expected:%s but got %s", expected, actual) + } + }) + + t.Run("multiple head comment", func(t *testing.T) { + v := struct { + Foo string `yaml:"foo"` + Bar map[string]interface{} `yaml:"bar"` + Baz struct { + X int `yaml:"x"` + } `yaml:"baz"` + }{ + Foo: "aaa", + Bar: map[string]interface{}{"bbb": "ccc"}, + Baz: struct { + X int `yaml:"x"` + }{X: 10}, + } + + b, err := yaml.MarshalWithOptions(v, yaml.WithComment( + yaml.CommentMap{ + "$.foo": []*yaml.Comment{ + yaml.HeadComment( + "foo comment", + "foo comment2", + ), + }, + "$.bar": []*yaml.Comment{ + yaml.HeadComment( + "bar comment", + "bar comment2", + ), + }, + "$.bar.bbb": []*yaml.Comment{ + yaml.HeadComment( + "bbb comment", + "bbb comment2", + ), + }, + "$.baz.x": []*yaml.Comment{ + yaml.HeadComment( + "x comment", + "x comment2", + ), + }, + }, + )) + if err != nil { + t.Fatal(err) + } + expected := ` +#foo comment +#foo comment2 +foo: aaa +#bar comment +#bar comment2 +bar: + #bbb comment + #bbb comment2 + bbb: ccc +baz: + #x comment + #x comment2 + x: 10 +` + actual := "\n" + string(b) + if expected != actual { + t.Fatalf("expected:%s but got %s", expected, actual) + } + }) + t.Run("foot comment", func(t *testing.T) { + v := struct { + Bar map[string]interface{} `yaml:"bar"` + Baz []int `yaml:"baz"` + }{ + Bar: map[string]interface{}{"bbb": "ccc"}, + Baz: []int{1, 2}, + } + + b, err := yaml.MarshalWithOptions(v, yaml.IndentSequence(true), yaml.WithComment( + yaml.CommentMap{ + "$.bar.bbb": []*yaml.Comment{yaml.FootComment("ccc: ddd")}, + "$.baz[1]": []*yaml.Comment{yaml.FootComment("- 3")}, + "$.baz": []*yaml.Comment{yaml.FootComment(" foot comment", "foot comment2")}, + }, + )) + if err != nil { + t.Fatal(err) + } + expected := ` +bar: + bbb: ccc + #ccc: ddd +baz: + - 1 + - 2 + #- 3 +# foot comment +#foot comment2 +` + actual := "\n" + string(b) + if expected != actual { + t.Fatalf("expected:%s but got %s", expected, actual) + } + }) + + t.Run("combination", func(t *testing.T) { + v := struct { + Foo map[string]interface{} `yaml:"foo"` + O map[string]interface{} `yaml:"o"` + T map[string]bool `yaml:"t"` + Bar map[string]interface{} `yaml:"bar"` + Baz []int `yaml:"baz"` + Hoge map[string]interface{} `yaml:"hoge"` + }{ + Foo: map[string]interface{}{ + "a": map[string]interface{}{ + "b": map[string]interface{}{ + "c": "d", + }, + }, + }, + O: map[string]interface{}{ + "p": map[string]interface{}{ + "q": map[string]interface{}{ + "r": "s", + }, + }, + }, + T: map[string]bool{ + "u": true, + }, + Bar: map[string]interface{}{"bbb": "ccc"}, + Baz: []int{1, 2}, + Hoge: map[string]interface{}{ + "moga": true, + }, + } + + b, err := yaml.MarshalWithOptions(v, yaml.IndentSequence(true), yaml.WithComment( + yaml.CommentMap{ + "$.foo": []*yaml.Comment{ + yaml.HeadComment(" foo head comment", " foo head comment2"), + yaml.LineComment(" foo line comment"), + }, + "$.foo.a": []*yaml.Comment{ + yaml.HeadComment(" a head comment"), + yaml.LineComment(" a line comment"), + }, + "$.foo.a.b": []*yaml.Comment{ + yaml.HeadComment(" b head comment"), + yaml.LineComment(" b line comment"), + }, + "$.foo.a.b.c": []*yaml.Comment{ + yaml.LineComment(" c line comment"), + }, + "$.o": []*yaml.Comment{ + yaml.LineComment(" o line comment"), + }, + "$.o.p": []*yaml.Comment{ + yaml.HeadComment(" p head comment", " p head comment2"), + yaml.LineComment(" p line comment"), + }, + "$.o.p.q": []*yaml.Comment{ + yaml.HeadComment(" q head comment", " q head comment2"), + yaml.LineComment(" q line comment"), + }, + "$.o.p.q.r": []*yaml.Comment{ + yaml.LineComment(" r line comment"), + }, + "$.t.u": []*yaml.Comment{ + yaml.LineComment(" u line comment"), + }, + "$.bar": []*yaml.Comment{ + yaml.HeadComment(" bar head comment"), + yaml.LineComment(" bar line comment"), + }, + "$.bar.bbb": []*yaml.Comment{ + yaml.HeadComment(" bbb head comment"), + yaml.LineComment(" bbb line comment"), + yaml.FootComment(" bbb foot comment"), + }, + "$.baz[0]": []*yaml.Comment{ + yaml.HeadComment(" sequence head comment"), + yaml.LineComment(" sequence line comment"), + }, + "$.baz[1]": []*yaml.Comment{ + yaml.HeadComment(" sequence head comment2"), + yaml.LineComment(" sequence line comment2"), + yaml.FootComment(" sequence foot comment"), + }, + "$.baz": []*yaml.Comment{ + yaml.HeadComment(" baz head comment", " baz head comment2"), + yaml.LineComment(" baz line comment"), + yaml.FootComment(" baz foot comment"), + }, + "$.hoge.moga": []*yaml.Comment{ + yaml.LineComment(" moga line comment"), + yaml.FootComment(" moga foot comment"), + }, + }, + )) + if err != nil { + t.Fatal(err) + } + expected := ` +# foo head comment +# foo head comment2 +foo: # foo line comment + # a head comment + a: # a line comment + # b head comment + b: # b line comment + c: d # c line comment +o: # o line comment + # p head comment + # p head comment2 + p: # p line comment + # q head comment + # q head comment2 + q: # q line comment + r: s # r line comment +t: + u: true # u line comment +# bar head comment +bar: # bar line comment + # bbb head comment + bbb: ccc # bbb line comment + # bbb foot comment +# baz head comment +# baz head comment2 +baz: # baz line comment + # sequence head comment + - 1 # sequence line comment + # sequence head comment2 + - 2 # sequence line comment2 + # sequence foot comment +# baz foot comment +hoge: + moga: true # moga line comment + # moga foot comment +` + actual := "\n" + string(b) + if expected != actual { + t.Fatalf("expected:%s but got %s", expected, actual) + } + }) + +} + +func Test_CommentToMapOption(t *testing.T) { + t.Run("line comment", func(t *testing.T) { + yml := ` +foo: aaa #foo comment +bar: #bar comment + bbb: ccc #bbb comment +baz: + x: 10 #x comment +` + var ( + v interface{} + cm = yaml.CommentMap{} + ) + if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { + t.Fatal(err) + } + expected := []struct { + path string + comments []*yaml.Comment + }{ + { + path: "$.foo", + comments: []*yaml.Comment{yaml.LineComment("foo comment")}, + }, + { + path: "$.bar", + comments: []*yaml.Comment{yaml.LineComment("bar comment")}, + }, + { + path: "$.bar.bbb", + comments: []*yaml.Comment{yaml.LineComment("bbb comment")}, + }, + { + path: "$.baz.x", + comments: []*yaml.Comment{yaml.LineComment("x comment")}, + }, + } + for _, exp := range expected { + comments := cm[exp.path] + if comments == nil { + t.Fatalf("failed to get path %s", exp.path) + } + if diff := cmp.Diff(exp.comments, comments); diff != "" { + t.Errorf("(-got, +want)\n%s", diff) + } + } + }) + t.Run("line comment2", func(t *testing.T) { + yml := ` +foo: + bar: baz # comment` + var ( + v interface{} + cm = yaml.CommentMap{} + ) + if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { + t.Fatal(err) + } + expected := []struct { + path string + comments []*yaml.Comment + }{ + { + path: "$.foo.bar", + comments: []*yaml.Comment{yaml.LineComment(" comment")}, + }, + } + for _, exp := range expected { + comments := cm[exp.path] + if comments == nil { + t.Fatalf("failed to get path %s", exp.path) + } + if diff := cmp.Diff(exp.comments, comments); diff != "" { + t.Errorf("(-got, +want)\n%s", diff) + } + } + }) + + t.Run("single head comment", func(t *testing.T) { + yml := ` +#foo comment +foo: aaa +#bar comment +bar: + #bbb comment + bbb: ccc +baz: + #x comment + x: 10 +` + var ( + v interface{} + cm = yaml.CommentMap{} + ) + if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { + t.Fatal(err) + } + expected := []struct { + path string + comments []*yaml.Comment + }{ + { + path: "$.foo", + comments: []*yaml.Comment{yaml.HeadComment("foo comment")}, + }, + { + path: "$.bar", + comments: []*yaml.Comment{yaml.HeadComment("bar comment")}, + }, + { + path: "$.bar.bbb", + comments: []*yaml.Comment{yaml.HeadComment("bbb comment")}, + }, + { + path: "$.baz.x", + comments: []*yaml.Comment{yaml.HeadComment("x comment")}, + }, + } + for _, exp := range expected { + comments := cm[exp.path] + if comments == nil { + t.Fatalf("failed to get path %s", exp.path) + } + if diff := cmp.Diff(exp.comments, comments); diff != "" { + t.Errorf("(-got, +want)\n%s", diff) + } + } + }) + t.Run("multiple head comments", func(t *testing.T) { + yml := ` +#foo comment +#foo comment2 +foo: aaa +#bar comment +#bar comment2 +bar: + #bbb comment + #bbb comment2 + bbb: ccc +baz: + #x comment + #x comment2 + x: 10 +` + var ( + v interface{} + cm = yaml.CommentMap{} + ) + if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { + t.Fatal(err) + } + expected := []struct { + path string + comments []*yaml.Comment + }{ + { + path: "$.foo", + comments: []*yaml.Comment{ + yaml.HeadComment( + "foo comment", + "foo comment2", + ), + }, + }, + { + path: "$.bar", + comments: []*yaml.Comment{ + yaml.HeadComment( + "bar comment", + "bar comment2", + ), + }, + }, + { + path: "$.bar.bbb", + comments: []*yaml.Comment{ + yaml.HeadComment( + "bbb comment", + "bbb comment2", + ), + }, + }, + { + path: "$.baz.x", + comments: []*yaml.Comment{ + yaml.HeadComment( + "x comment", + "x comment2", + ), + }, + }, + } + for _, exp := range expected { + comments := cm[exp.path] + if comments == nil { + t.Fatalf("failed to get path %s", exp.path) + } + if diff := cmp.Diff(exp.comments, comments); diff != "" { + t.Errorf("(-got, +want)\n%s", diff) + } + } + }) + t.Run("foot comment", func(t *testing.T) { + yml := ` +bar: + bbb: ccc + #ccc: ddd +baz: + - 1 + - 2 + #- 3 + # foot comment +#foot comment2 +` + var ( + v interface{} + cm = yaml.CommentMap{} + ) + if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { + t.Fatal(err) + } + expected := []struct { + path string + comments []*yaml.Comment + }{ + { + path: "$.bar.bbb", + comments: []*yaml.Comment{yaml.FootComment("ccc: ddd")}, + }, + { + path: "$.baz[1]", + comments: []*yaml.Comment{yaml.FootComment("- 3")}, + }, + { + path: "$.baz", + comments: []*yaml.Comment{yaml.FootComment(" foot comment", "foot comment2")}, + }, + } + for _, exp := range expected { + comments := cm[exp.path] + if comments == nil { + t.Fatalf("failed to get path %s", exp.path) + } + if diff := cmp.Diff(exp.comments, comments); diff != "" { + t.Errorf("(-got, +want)\n%s", diff) + } + } + }) + t.Run("combination", func(t *testing.T) { + yml := ` +# foo head comment +# foo head comment2 +foo: # foo line comment + # a head comment + a: # a line comment + # b head comment + b: # b line comment + c: d # c line comment +o: # o line comment + # p head comment + # p head comment2 + p: # p line comment + # q head comment + # q head comment2 + q: # q line comment + r: s # r line comment +t: + u: true # u line comment +# bar head comment +bar: # bar line comment + # bbb head comment + bbb: ccc # bbb line comment + # bbb foot comment +# baz head comment +# baz head comment2 +baz: # baz line comment + # sequence head comment + - 1 # sequence line comment + # sequence head comment2 + - 2 # sequence line comment2 + # sequence foot comment +hoge: + moga: true # moga line comment + # moga foot comment +# hoge foot comment +` + var ( + v interface{} + cm = yaml.CommentMap{} + ) + if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil { + t.Fatal(err) + } + expected := []struct { + path string + comments []*yaml.Comment + }{ + { + path: "$.foo", + comments: []*yaml.Comment{ + yaml.HeadComment(" foo head comment", " foo head comment2"), + yaml.LineComment(" foo line comment"), + }, + }, + { + path: "$.foo.a", + comments: []*yaml.Comment{ + yaml.HeadComment(" a head comment"), + yaml.LineComment(" a line comment"), + }, + }, + { + path: "$.foo.a.b", + comments: []*yaml.Comment{ + yaml.HeadComment(" b head comment"), + yaml.LineComment(" b line comment"), + }, + }, + { + path: "$.foo.a.b.c", + comments: []*yaml.Comment{ + yaml.LineComment(" c line comment"), + }, + }, + { + path: "$.o", + comments: []*yaml.Comment{ + yaml.LineComment(" o line comment"), + }, + }, + { + path: "$.o.p", + comments: []*yaml.Comment{ + yaml.HeadComment(" p head comment", " p head comment2"), + yaml.LineComment(" p line comment"), + }, + }, + { + path: "$.o.p.q", + comments: []*yaml.Comment{ + yaml.HeadComment(" q head comment", " q head comment2"), + yaml.LineComment(" q line comment"), + }, + }, + { + path: "$.o.p.q.r", + comments: []*yaml.Comment{ + yaml.LineComment(" r line comment"), + }, + }, + { + path: "$.t.u", + comments: []*yaml.Comment{ + yaml.LineComment(" u line comment"), + }, + }, + { + path: "$.bar", + comments: []*yaml.Comment{ + yaml.HeadComment(" bar head comment"), + yaml.LineComment(" bar line comment"), + }, + }, + { + path: "$.bar.bbb", + comments: []*yaml.Comment{ + yaml.HeadComment(" bbb head comment"), + yaml.LineComment(" bbb line comment"), + yaml.FootComment(" bbb foot comment"), + }, + }, + { + path: "$.baz[0]", + comments: []*yaml.Comment{ + yaml.HeadComment(" sequence head comment"), + yaml.LineComment(" sequence line comment"), + }, + }, + { + path: "$.baz[1]", + comments: []*yaml.Comment{ + yaml.HeadComment(" sequence head comment2"), + yaml.LineComment(" sequence line comment2"), + yaml.FootComment(" sequence foot comment"), + }, + }, + { + path: "$.baz", + comments: []*yaml.Comment{ + yaml.HeadComment(" baz head comment", " baz head comment2"), + yaml.LineComment(" baz line comment"), + }, + }, + { + path: "$.hoge", + comments: []*yaml.Comment{ + yaml.FootComment(" hoge foot comment"), + }, + }, + { + path: "$.hoge.moga", + comments: []*yaml.Comment{ + yaml.LineComment(" moga line comment"), + yaml.FootComment(" moga foot comment"), + }, + }, + } + for _, exp := range expected { + comments := cm[exp.path] + if comments == nil { + t.Fatalf("failed to get path %s", exp.path) + } + if diff := cmp.Diff(exp.comments, comments); diff != "" { + t.Errorf("%s: (-got, +want)\n%s", exp.path, diff) + } + } + }) +} + +func TestCommentMapRoundTrip(t *testing.T) { + // test that an unmarshal and marshal round trip retains comments. + // if expect is empty, the test will use the input as the expected result. + tests := []struct { + name string + source string + expect string + encodeOptions []yaml.EncodeOption + }{ + { + name: "simple map", + source: ` +# head +a: 1 # line +# foot +`, + }, + { + name: "nesting", + source: ` +- 1 # one +- foo: + a: b + # c comment + c: d # d comment + "e#f": g # g comment + h.i: j # j comment + "k.#l": m # m comment +`, + }, + { + name: "single quotes", + source: `'a#b': c # c comment`, + encodeOptions: []yaml.EncodeOption{yaml.UseSingleQuote(true)}, + }, + { + name: "single quotes added in encode", + source: `a#b: c # c comment`, + encodeOptions: []yaml.EncodeOption{yaml.UseSingleQuote(true)}, + expect: `'a#b': c # c comment`, + }, + { + name: "double quotes quotes transformed to single quotes", + source: `"a#b": c # c comment`, + encodeOptions: []yaml.EncodeOption{yaml.UseSingleQuote(true)}, + expect: `'a#b': c # c comment`, + }, + { + name: "single quotes quotes transformed to double quotes", + source: `'a#b': c # c comment`, + expect: `"a#b": c # c comment`, + }, + { + name: "single quotes removed", + source: `'a': b # b comment`, + expect: `a: b # b comment`, + }, + { + name: "double quotes removed", + source: `"a": b # b comment`, + expect: `a: b # b comment`, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var val any + cm := yaml.CommentMap{} + source := strings.TrimSpace(test.source) + if err := yaml.UnmarshalWithOptions([]byte(source), &val, yaml.CommentToMap(cm)); err != nil { + t.Fatalf("%+v", err) + } + marshaled, err := yaml.MarshalWithOptions(val, append(test.encodeOptions, yaml.WithComment(cm))...) + if err != nil { + t.Fatalf("%+v", err) + } + got := strings.TrimSpace(string(marshaled)) + expect := strings.TrimSpace(test.expect) + if expect == "" { + expect = source + } + if got != expect { + t.Fatalf("expected:\n%s\ngot:\n%s\n", expect, got) + } + }) + + } +} + +func TestRegisterCustomMarshaler(t *testing.T) { + type T struct { + Foo []byte `yaml:"foo"` + } + yaml.RegisterCustomMarshaler[T](func(_ T) ([]byte, error) { + return []byte(`"override"`), nil + }) + b, err := yaml.Marshal(&T{Foo: []byte("bar")}) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(b, []byte("\"override\"\n")) { + t.Fatalf("failed to register custom marshaler. got: %q", b) + } +} + +func TestRegisterCustomUnmarshaler(t *testing.T) { + type T struct { + Foo []byte `yaml:"foo"` + } + yaml.RegisterCustomUnmarshaler[T](func(v *T, _ []byte) error { + v.Foo = []byte("override") + return nil + }) + var v T + if err := yaml.Unmarshal([]byte(`"foo: "bar"`), &v); err != nil { + t.Fatal(err) + } + if !bytes.Equal(v.Foo, []byte("override")) { + t.Fatalf("failed to decode. got %q", v.Foo) + } +} -- cgit v1.2.3