Create a struct with all of Hugo's config options

Primary motivation is documentation, but it will also hopefully simplify the code.

Also,

* Lower case the default output format names; this is in line with the custom ones (map keys) and how
it's treated all the places. This avoids doing `stringds.EqualFold` everywhere.

Closes #10896
Closes #10620
This commit is contained in:
Bjørn Erik Pedersen
2023-01-04 18:24:36 +01:00
parent 6aededf6b4
commit 241b21b0fd
337 changed files with 13377 additions and 14898 deletions

View File

@@ -19,6 +19,8 @@ import (
"regexp"
"unicode"
"unicode/utf8"
"github.com/gohugoio/hugo/common/hreflect"
)
// Regexp definitions
@@ -57,3 +59,58 @@ func (c LowerCaseCamelJSONMarshaller) MarshalJSON() ([]byte, error) {
return converted, err
}
type ReplacingJSONMarshaller struct {
Value any
KeysToLower bool
OmitEmpty bool
}
func (c ReplacingJSONMarshaller) MarshalJSON() ([]byte, error) {
converted, err := json.Marshal(c.Value)
if c.KeysToLower {
converted = keyMatchRegex.ReplaceAllFunc(
converted,
func(match []byte) []byte {
return bytes.ToLower(match)
},
)
}
if c.OmitEmpty {
// It's tricky to do this with a regexp, so convert it to a map, remove zero values and convert back.
var m map[string]interface{}
err = json.Unmarshal(converted, &m)
if err != nil {
return nil, err
}
var removeZeroVAlues func(m map[string]any)
removeZeroVAlues = func(m map[string]any) {
for k, v := range m {
if !hreflect.IsTruthful(v) {
delete(m, k)
} else {
switch v.(type) {
case map[string]interface{}:
removeZeroVAlues(v.(map[string]any))
case []interface{}:
for _, vv := range v.([]interface{}) {
if m, ok := vv.(map[string]any); ok {
removeZeroVAlues(m)
}
}
}
}
}
}
removeZeroVAlues(m)
converted, err = json.Marshal(m)
}
return converted, err
}

View File

@@ -0,0 +1,33 @@
package parser
import (
"testing"
qt "github.com/frankban/quicktest"
)
func TestReplacingJSONMarshaller(t *testing.T) {
c := qt.New(t)
m := map[string]any{
"foo": "bar",
"baz": 42,
"zeroInt1": 0,
"zeroInt2": 0,
"zeroFloat": 0.0,
"zeroString": "",
"zeroBool": false,
"nil": nil,
}
marshaller := ReplacingJSONMarshaller{
Value: m,
KeysToLower: true,
OmitEmpty: true,
}
b, err := marshaller.MarshalJSON()
c.Assert(err, qt.IsNil)
c.Assert(string(b), qt.Equals, `{"baz":42,"foo":"bar"}`)
}

View File

@@ -16,8 +16,6 @@ package metadecoders
import (
"path/filepath"
"strings"
"github.com/gohugoio/hugo/media"
)
type Format string
@@ -33,6 +31,16 @@ const (
XML Format = "xml"
)
// FormatFromStrings returns the first non-empty Format from the given strings.
func FormatFromStrings(ss ...string) Format {
for _, s := range ss {
if f := FormatFromString(s); f != "" {
return f
}
}
return ""
}
// FormatFromString turns formatStr, typically a file extension without any ".",
// into a Format. It returns an empty string for unknown formats.
func FormatFromString(formatStr string) Format {
@@ -59,18 +67,6 @@ func FormatFromString(formatStr string) Format {
return ""
}
// FormatFromMediaType gets the Format given a MIME type, empty string
// if unknown.
func FormatFromMediaType(m media.Type) Format {
for _, suffix := range m.Suffixes() {
if f := FormatFromString(suffix); f != "" {
return f
}
}
return ""
}
// FormatFromContentString tries to detect the format (JSON, YAML, TOML or XML)
// in the given string.
// It return an empty string if no format could be detected.

View File

@@ -16,8 +16,6 @@ package metadecoders
import (
"testing"
"github.com/gohugoio/hugo/media"
qt "github.com/frankban/quicktest"
)
@@ -41,23 +39,6 @@ func TestFormatFromString(t *testing.T) {
}
}
func TestFormatFromMediaType(t *testing.T) {
c := qt.New(t)
for _, test := range []struct {
m media.Type
expect Format
}{
{media.JSONType, JSON},
{media.YAMLType, YAML},
{media.XMLType, XML},
{media.RSSType, XML},
{media.TOMLType, TOML},
{media.CalendarType, ""},
} {
c.Assert(FormatFromMediaType(test.m), qt.Equals, test.expect)
}
}
func TestFormatFromContentString(t *testing.T) {
t.Parallel()
c := qt.New(t)