From 2fb7827a641d1154624fc6dd2ca1d53baf8fbbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 13 Nov 2024 12:35:22 +0100 Subject: [PATCH] Replace to gopkg.in/yaml with github.com/goccy/go-yaml (note) Fixes #8822 Fixes #13043 --- commands/gen.go | 2 +- commands/server.go | 1 - common/herrors/file_error.go | 19 ++- common/herrors/line_number_extractors.go | 16 ++- common/herrors/line_number_extractors_test.go | 31 +++++ go.mod | 3 +- go.sum | 2 + hugolib/frontmatter_test.go | 4 +- hugolib/hugo_sites_build_errors_test.go | 34 ++++- hugolib/page__content.go | 25 ++-- hugolib/pages_capture.go | 2 +- langs/i18n/translationProvider.go | 2 +- parser/frontmatter.go | 4 +- parser/metadecoders/decoder.go | 81 +---------- parser/metadecoders/decoder_test.go | 131 +----------------- parser/metadecoders/encoder.go | 25 ++++ .../server__error_recovery_edit_content.txt | 2 +- 17 files changed, 140 insertions(+), 244 deletions(-) create mode 100644 common/herrors/line_number_extractors_test.go create mode 100644 parser/metadecoders/encoder.go diff --git a/commands/gen.go b/commands/gen.go index 83b4d637c66..fad392578d6 100644 --- a/commands/gen.go +++ b/commands/gen.go @@ -27,6 +27,7 @@ import ( "github.com/alecthomas/chroma/v2/formatters/html" "github.com/alecthomas/chroma/v2/styles" "github.com/bep/simplecobra" + "github.com/goccy/go-yaml" "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/docshelper" "github.com/gohugoio/hugo/helpers" @@ -35,7 +36,6 @@ import ( "github.com/gohugoio/hugo/parser" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" - "gopkg.in/yaml.v2" ) func newGenCommand() *genCommand { diff --git a/commands/server.go b/commands/server.go index c2fee68b239..219d97acd28 100644 --- a/commands/server.go +++ b/commands/server.go @@ -1160,7 +1160,6 @@ func chmodFilter(dst, src os.FileInfo) bool { } func cleanErrorLog(content string) string { - content = strings.ReplaceAll(content, "\n", " ") content = logReplacer.Replace(content) content = logDuplicateTemplateExecuteRe.ReplaceAllString(content, "") content = logDuplicateTemplateParseRe.ReplaceAllString(content, "") diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go index 32a6f0081f8..f83a6eefb3d 100644 --- a/common/herrors/file_error.go +++ b/common/herrors/file_error.go @@ -112,11 +112,11 @@ func (fe *fileError) UpdateContent(r io.Reader, linematcher LineMatcherFn) FileE fe.errorContext = ectx - if ectx.Position.LineNumber > 0 { + if ectx.Position.LineNumber > 0 && ectx.Position.LineNumber > fe.position.LineNumber { fe.position.LineNumber = ectx.Position.LineNumber } - if ectx.Position.ColumnNumber > 0 { + if ectx.Position.ColumnNumber > 0 && ectx.Position.ColumnNumber > fe.position.ColumnNumber { fe.position.ColumnNumber = ectx.Position.ColumnNumber } @@ -181,6 +181,7 @@ func NewFileErrorFromName(err error, name string) FileError { // Filetype is used to determine the Chroma lexer to use. fileType, pos := extractFileTypePos(err) pos.Filename = name + if fileType == "" { _, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(name)) } @@ -238,7 +239,9 @@ func NewFileErrorFromFile(err error, filename string, fs afero.Fs, linematcher L return NewFileErrorFromName(err, realFilename) } defer f.Close() - return NewFileErrorFromName(err, realFilename).UpdateContent(f, linematcher) + fe := NewFileErrorFromName(err, realFilename) + fe = fe.UpdateContent(f, linematcher) + return fe } func openFile(filename string, fs afero.Fs) (afero.File, string, error) { @@ -306,13 +309,9 @@ func extractFileTypePos(err error) (string, text.Position) { } // Look in the error message for the line number. - for _, handle := range lineNumberExtractors { - lno, col := handle(err) - if lno > 0 { - pos.ColumnNumber = col - pos.LineNumber = lno - break - } + if lno, col := commonLineNumberExtractor(err); lno > 0 { + pos.ColumnNumber = col + pos.LineNumber = lno } if fileType == "" && pos.Filename != "" { diff --git a/common/herrors/line_number_extractors.go b/common/herrors/line_number_extractors.go index f70a2691fc3..121506bb07a 100644 --- a/common/herrors/line_number_extractors.go +++ b/common/herrors/line_number_extractors.go @@ -19,17 +19,27 @@ import ( ) var lineNumberExtractors = []lineNumberExtractor{ + // YAML parse errors. + newLineNumberErrHandlerFromRegexp(`\[(\d+):(\d+)\]`), + // Template/shortcode parse errors newLineNumberErrHandlerFromRegexp(`:(\d+):(\d*):`), newLineNumberErrHandlerFromRegexp(`:(\d+):`), - // YAML parse errors - newLineNumberErrHandlerFromRegexp(`line (\d+):`), - // i18n bundle errors newLineNumberErrHandlerFromRegexp(`\((\d+),\s(\d*)`), } +func commonLineNumberExtractor(e error) (int, int) { + for _, handler := range lineNumberExtractors { + lno, col := handler(e) + if lno > 0 { + return lno, col + } + } + return 0, 0 +} + type lineNumberExtractor func(e error) (int, int) func newLineNumberErrHandlerFromRegexp(expression string) lineNumberExtractor { diff --git a/common/herrors/line_number_extractors_test.go b/common/herrors/line_number_extractors_test.go new file mode 100644 index 00000000000..7209ac9aa07 --- /dev/null +++ b/common/herrors/line_number_extractors_test.go @@ -0,0 +1,31 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package herrors + +import ( + "errors" + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestCommonLineNumberExtractor(t *testing.T) { + t.Parallel() + + c := qt.New(t) + + lno, col := commonLineNumberExtractor(errors.New("[4:9] value is not allowed in this context")) + c.Assert(lno, qt.Equals, 4) + c.Assert(col, qt.Equals, 9) +} diff --git a/go.mod b/go.mod index b12a889a9c6..d33c3355fd7 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/gobuffalo/flect v1.0.3 github.com/gobwas/glob v0.2.3 + github.com/goccy/go-yaml v1.14.0 github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e github.com/gohugoio/hashstructure v0.1.0 github.com/gohugoio/httpcache v0.7.0 @@ -83,7 +84,6 @@ require ( golang.org/x/text v0.19.0 golang.org/x/tools v0.26.0 google.golang.org/api v0.191.0 - gopkg.in/yaml.v2 v2.4.0 ) require ( @@ -164,6 +164,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 // indirect google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.0 // indirect software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect diff --git a/go.sum b/go.sum index fcd88e55ace..91461cd3f0c 100644 --- a/go.sum +++ b/go.sum @@ -227,6 +227,8 @@ github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4 github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-yaml v1.14.0 h1:G/NDXJvf1CX0FshjxKn2AOL0MnrxsSJNpY9FpvMRblw= +github.com/goccy/go-yaml v1.14.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY= github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ= github.com/gohugoio/hashstructure v0.1.0 h1:kBSTMLMyTXbrJVAxaKI+wv30MMJJxn9Q8kfQtJaZ400= diff --git a/hugolib/frontmatter_test.go b/hugolib/frontmatter_test.go index 3a2080b0ec1..c4cbfa72ea7 100644 --- a/hugolib/frontmatter_test.go +++ b/hugolib/frontmatter_test.go @@ -40,7 +40,7 @@ Strings: {{ printf "%T" .Params.strings }} {{ range .Params.strings }}Strings: { b.Build() - b.AssertFileContent("public/post/one/index.html", "Ints: []interface {} Int: 1 (int)|Int: 2 (int)|Int: 3 (int)|") - b.AssertFileContent("public/post/one/index.html", "Mixed: []interface {} Mixed: 1 (string)|Mixed: 2 (int)|Mixed: 3 (int)|") + b.AssertFileContent("public/post/one/index.html", "Ints: []interface {} Int: 1 (uint64)|Int: 2 (uint64)|Int: 3 (uint64)|") + b.AssertFileContent("public/post/one/index.html", "Mixed: []interface {} Mixed: 1 (string)|Mixed: 2 (uint64)|Mixed: 3 (uint64)|") b.AssertFileContent("public/post/one/index.html", "Strings: []string Strings: 1 (string)|Strings: 2 (string)|Strings: 3 (string)|") } diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go index 71afe676772..27ab990760c 100644 --- a/hugolib/hugo_sites_build_errors_test.go +++ b/hugolib/hugo_sites_build_errors_test.go @@ -476,7 +476,7 @@ line 5 errors := herrors.UnwrapFileErrorsWithErrorContext(err) b.Assert(errors, qt.HasLen, 3) - b.Assert(errors[0].Error(), qt.Contains, filepath.FromSlash(`"/content/_index.md:1:1": "/layouts/_default/_markup/render-heading.html:2:5": execute of template failed`)) + b.Assert(errors[0].Error(), qt.Contains, filepath.FromSlash(`"/content/_index.md:2:5": "/layouts/_default/_markup/render-heading.html:2:5": execute of template failed`)) } func TestErrorRenderHookCodeblock(t *testing.T) { @@ -645,3 +645,35 @@ Home. b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`/layouts/index.html:2:3`)) b.Assert(err.Error(), qt.Contains, `can't evaluate field ThisDoesNotExist`) } + +func TestErrorFrontmatterYAMLSyntax(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +-- content/_index.md -- + + + + + +--- +line1: 'value1' +x +line2: 'value2' +line3: 'value3' +--- +` + + b, err := TestE(t, files) + + b.Assert(err, qt.Not(qt.IsNil)) + b.Assert(err.Error(), qt.Contains, "> 3 |") + fe := herrors.UnwrapFileError(err) + b.Assert(fe, qt.Not(qt.IsNil)) + pos := fe.Position() + b.Assert(pos.Filename, qt.Contains, filepath.FromSlash("content/_index.md")) + b.Assert(fe.ErrorContext(), qt.Not(qt.IsNil)) + b.Assert(pos.LineNumber, qt.Equals, 9) + b.Assert(pos.ColumnNumber, qt.Equals, 1) +} diff --git a/hugolib/page__content.go b/hugolib/page__content.go index 4ec91f7b5cd..b6bfe76e88f 100644 --- a/hugolib/page__content.go +++ b/hugolib/page__content.go @@ -283,23 +283,20 @@ func (c *contentParseInfo) parseFrontMatter(it pageparser.Item, iter *pageparser var err error c.frontMatter, err = metadecoders.Default.UnmarshalToMap(it.Val(source), f) if err != nil { - if fe, ok := err.(herrors.FileError); ok { - pos := fe.Position() + fe := herrors.UnwrapFileError(err) + if fe == nil { + fe = herrors.NewFileError(err) + } + pos := fe.Position() - // Offset the starting position of front matter. - offset := iter.LineNumber(source) - 1 - if f == metadecoders.YAML { - offset -= 1 - } - pos.LineNumber += offset + // Offset the starting position of front matter. + offset := iter.LineNumber(source) - 1 - fe.UpdatePosition(pos) - fe.SetFilename("") // It will be set later. + pos.LineNumber += offset - return fe - } else { - return err - } + fe.UpdatePosition(pos) + fe.SetFilename("") // It will be set later. + return fe } return nil diff --git a/hugolib/pages_capture.go b/hugolib/pages_capture.go index 96c2c0f96e2..0b6234d3b07 100644 --- a/hugolib/pages_capture.go +++ b/hugolib/pages_capture.go @@ -123,7 +123,7 @@ func (c *pagesCollector) Collect() (collectErr error) { Handle: func(ctx context.Context, fi hugofs.FileMetaInfo) error { numPages, numResources, err := c.m.AddFi(fi, c.buildConfig) if err != nil { - return hugofs.AddFileInfoToError(err, fi, c.fs) + return hugofs.AddFileInfoToError(err, fi, c.h.SourceFs) } numFilesProcessedTotal.Add(1) numPagesProcessedTotal.Add(numPages) diff --git a/langs/i18n/translationProvider.go b/langs/i18n/translationProvider.go index 9ede538d233..78a134708d5 100644 --- a/langs/i18n/translationProvider.go +++ b/langs/i18n/translationProvider.go @@ -20,9 +20,9 @@ import ( "github.com/gohugoio/hugo/common/paths" + yaml "github.com/goccy/go-yaml" "github.com/gohugoio/hugo/common/herrors" "golang.org/x/text/language" - yaml "gopkg.in/yaml.v2" "github.com/gohugoio/go-i18n/v2/i18n" "github.com/gohugoio/hugo/helpers" diff --git a/parser/frontmatter.go b/parser/frontmatter.go index 18e55f9ad4f..398aecc3005 100644 --- a/parser/frontmatter.go +++ b/parser/frontmatter.go @@ -22,8 +22,6 @@ import ( toml "github.com/pelletier/go-toml/v2" - yaml "gopkg.in/yaml.v2" - xml "github.com/clbanning/mxj/v2" ) @@ -39,7 +37,7 @@ func InterfaceToConfig(in any, format metadecoders.Format, w io.Writer) error { switch format { case metadecoders.YAML: - b, err := yaml.Marshal(in) + b, err := metadecoders.MarshalYAML(in) if err != nil { return err } diff --git a/parser/metadecoders/decoder.go b/parser/metadecoders/decoder.go index 5dac23f0328..145cc72fc8e 100644 --- a/parser/metadecoders/decoder.go +++ b/parser/metadecoders/decoder.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2024 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,10 +28,10 @@ import ( "github.com/niklasfasching/go-org/org" xml "github.com/clbanning/mxj/v2" + yaml "github.com/goccy/go-yaml" toml "github.com/pelletier/go-toml/v2" "github.com/spf13/afero" "github.com/spf13/cast" - yaml "gopkg.in/yaml.v2" ) // Decoder provides some configuration options for the decoders. @@ -164,35 +164,7 @@ func (d Decoder) UnmarshalTo(data []byte, f Format, v any) error { case TOML: err = toml.Unmarshal(data, v) case YAML: - err = yaml.Unmarshal(data, v) - if err != nil { - return toFileError(f, data, fmt.Errorf("failed to unmarshal YAML: %w", err)) - } - - // To support boolean keys, the YAML package unmarshals maps to - // map[interface{}]interface{}. Here we recurse through the result - // and change all maps to map[string]interface{} like we would've - // gotten from `json`. - var ptr any - switch vv := v.(type) { - case *map[string]any: - ptr = *vv - case *any: - ptr = *vv - default: - // Not a map. - } - - if ptr != nil { - if mm, changed := stringifyMapKeys(ptr); changed { - switch vv := v.(type) { - case *map[string]any: - *vv = mm.(map[string]any) - case *any: - *vv = mm - } - } - } + return yaml.Unmarshal(data, v) case CSV: return d.unmarshalCSV(data, v) @@ -269,50 +241,3 @@ func (d Decoder) unmarshalORG(data []byte, v any) error { func toFileError(f Format, data []byte, err error) error { return herrors.NewFileErrorFromName(err, fmt.Sprintf("_stream.%s", f)).UpdateContent(bytes.NewReader(data), nil) } - -// stringifyMapKeys recurses into in and changes all instances of -// map[interface{}]interface{} to map[string]interface{}. This is useful to -// work around the impedance mismatch between JSON and YAML unmarshaling that's -// described here: https://github.com/go-yaml/yaml/issues/139 -// -// Inspired by https://github.com/stripe/stripe-mock, MIT licensed -func stringifyMapKeys(in any) (any, bool) { - switch in := in.(type) { - case []any: - for i, v := range in { - if vv, replaced := stringifyMapKeys(v); replaced { - in[i] = vv - } - } - case map[string]any: - for k, v := range in { - if vv, changed := stringifyMapKeys(v); changed { - in[k] = vv - } - } - case map[any]any: - res := make(map[string]any) - var ( - ok bool - err error - ) - for k, v := range in { - var ks string - - if ks, ok = k.(string); !ok { - ks, err = cast.ToStringE(k) - if err != nil { - ks = fmt.Sprintf("%v", k) - } - } - if vv, replaced := stringifyMapKeys(v); replaced { - res[ks] = vv - } else { - res[ks] = v - } - } - return res, true - } - - return nil, false -} diff --git a/parser/metadecoders/decoder_test.go b/parser/metadecoders/decoder_test.go index 49f7868cc18..f43bb209bc5 100644 --- a/parser/metadecoders/decoder_test.go +++ b/parser/metadecoders/decoder_test.go @@ -14,7 +14,6 @@ package metadecoders import ( - "reflect" "testing" qt "github.com/frankban/quicktest" @@ -91,8 +90,8 @@ func TestUnmarshalToMap(t *testing.T) { {`a = "b"`, TOML, expect}, {`a: "b"`, YAML, expect}, // Make sure we get all string keys, even for YAML - {"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": 2, "d": []any{3, 4}}}}, - {"a:\n true: 1\n false: 2", YAML, map[string]any{"a": map[string]any{"true": 1, "false": 2}}}, + {"a: Easy!\nb:\n c: 2\n d: [3, 4]", YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": uint64(2), "d": []any{uint64(3), uint64(4)}}}}, + {"a:\n true: 1\n false: 2", YAML, map[string]any{"a": map[string]any{"true": uint64(1), "false": uint64(2)}}}, {`{ "a": "b" }`, JSON, expect}, {`b`, XML, expect}, {`#+a: b`, ORG, expect}, @@ -137,7 +136,7 @@ func TestUnmarshalToInterface(t *testing.T) { {[]byte(`a: "b"`), YAML, expect}, {[]byte(`b`), XML, expect}, {[]byte(`a,b,c`), CSV, [][]string{{"a", "b", "c"}}}, - {[]byte("a: Easy!\nb:\n c: 2\n d: [3, 4]"), YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": 2, "d": []any{3, 4}}}}, + {[]byte("a: Easy!\nb:\n c: 2\n d: [3, 4]"), YAML, map[string]any{"a": "Easy!", "b": map[string]any{"c": uint64(2), "d": []any{uint64(3), uint64(4)}}}}, // errors {[]byte(`a = "`), TOML, false}, } { @@ -170,7 +169,7 @@ func TestUnmarshalStringTo(t *testing.T) { {"32", int64(1234), int64(32)}, {"32", int(1234), int(32)}, {"3.14159", float64(1), float64(3.14159)}, - {"[3,7,9]", []any{}, []any{3, 7, 9}}, + {"[3,7,9]", []any{}, []any{uint64(3), uint64(7), uint64(9)}}, {"[3.1,7.2,9.3]", []any{}, []any{3.1, 7.2, 9.3}}, } { msg := qt.Commentf("%d: %T", i, test.to) @@ -185,128 +184,6 @@ func TestUnmarshalStringTo(t *testing.T) { } } -func TestStringifyYAMLMapKeys(t *testing.T) { - cases := []struct { - input any - want any - replaced bool - }{ - { - map[any]any{"a": 1, "b": 2}, - map[string]any{"a": 1, "b": 2}, - true, - }, - { - map[any]any{"a": []any{1, map[any]any{"b": 2}}}, - map[string]any{"a": []any{1, map[string]any{"b": 2}}}, - true, - }, - { - map[any]any{true: 1, "b": false}, - map[string]any{"true": 1, "b": false}, - true, - }, - { - map[any]any{1: "a", 2: "b"}, - map[string]any{"1": "a", "2": "b"}, - true, - }, - { - map[any]any{"a": map[any]any{"b": 1}}, - map[string]any{"a": map[string]any{"b": 1}}, - true, - }, - { - map[string]any{"a": map[string]any{"b": 1}}, - map[string]any{"a": map[string]any{"b": 1}}, - false, - }, - { - []any{map[any]any{1: "a", 2: "b"}}, - []any{map[string]any{"1": "a", "2": "b"}}, - false, - }, - } - - for i, c := range cases { - res, replaced := stringifyMapKeys(c.input) - - if c.replaced != replaced { - t.Fatalf("[%d] Replaced mismatch: %t", i, replaced) - } - if !c.replaced { - res = c.input - } - if !reflect.DeepEqual(res, c.want) { - t.Errorf("[%d] given %q\nwant: %q\n got: %q", i, c.input, c.want, res) - } - } -} - -func BenchmarkStringifyMapKeysStringsOnlyInterfaceMaps(b *testing.B) { - maps := make([]map[any]any, b.N) - for i := 0; i < b.N; i++ { - maps[i] = map[any]any{ - "a": map[any]any{ - "b": 32, - "c": 43, - "d": map[any]any{ - "b": 32, - "c": 43, - }, - }, - "b": []any{"a", "b"}, - "c": "d", - } - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - stringifyMapKeys(maps[i]) - } -} - -func BenchmarkStringifyMapKeysStringsOnlyStringMaps(b *testing.B) { - m := map[string]any{ - "a": map[string]any{ - "b": 32, - "c": 43, - "d": map[string]any{ - "b": 32, - "c": 43, - }, - }, - "b": []any{"a", "b"}, - "c": "d", - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - stringifyMapKeys(m) - } -} - -func BenchmarkStringifyMapKeysIntegers(b *testing.B) { - maps := make([]map[any]any, b.N) - for i := 0; i < b.N; i++ { - maps[i] = map[any]any{ - 1: map[any]any{ - 4: 32, - 5: 43, - 6: map[any]any{ - 7: 32, - 8: 43, - }, - }, - 2: []any{"a", "b"}, - 3: "d", - } - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - stringifyMapKeys(maps[i]) - } -} - func BenchmarkDecodeYAMLToMap(b *testing.B) { d := Default diff --git a/parser/metadecoders/encoder.go b/parser/metadecoders/encoder.go new file mode 100644 index 00000000000..a18da443a1f --- /dev/null +++ b/parser/metadecoders/encoder.go @@ -0,0 +1,25 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metadecoders + +import yaml "github.com/goccy/go-yaml" + +var yamlEncodeOptions = []yaml.EncodeOption{ + yaml.UseSingleQuote(true), +} + +// MarshalYAML marshals the given value to YAML. +var MarshalYAML = func(v any) ([]byte, error) { + return yaml.MarshalWithOptions(v, yamlEncodeOptions...) +} diff --git a/testscripts/commands/server__error_recovery_edit_content.txt b/testscripts/commands/server__error_recovery_edit_content.txt index f5ea7e94baf..e640cd8f383 100644 --- a/testscripts/commands/server__error_recovery_edit_content.txt +++ b/testscripts/commands/server__error_recovery_edit_content.txt @@ -8,7 +8,7 @@ waitServer httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1' replace $WORK/content/p1/index.md 'title:' 'titlecolon' -httpget ${HUGOTEST_BASEURL_0}p1/ 'failed' +httpget ${HUGOTEST_BASEURL_0}p1/ 'Error' replace $WORK/content/p1/index.md 'titlecolon' 'title:' httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'