]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
transform: Add support for "format" option in transform.Unmarshal
authorn1xx1 <me@simonemiraglia.it>
Tue, 5 Aug 2025 20:19:51 +0000 (22:19 +0200)
committerGitHub <noreply@github.com>
Tue, 5 Aug 2025 20:19:51 +0000 (22:19 +0200)
Fixes #13887

parser/metadecoders/decoder.go
tpl/transform/unmarshal.go
tpl/transform/unmarshal_test.go

index 419fbf4d2118beab5fb1b27d8a3d8c7f68cd516d..83b4b04cd2cfc2f6db5e1874d0d05540a6c91eb0 100644 (file)
@@ -36,6 +36,10 @@ import (
 
 // Decoder provides some configuration options for the decoders.
 type Decoder struct {
+       // Format specifies a specific format to decode from. If empty or
+       // unspecified, it's inferred from the contents or the filename.
+       Format string
+
        // Delimiter is the field delimiter. Used in the CSV decoder. Default is
        // ','.
        Delimiter rune
@@ -57,6 +61,7 @@ type Decoder struct {
 // OptionsKey is used in cache keys.
 func (d Decoder) OptionsKey() string {
        var sb strings.Builder
+       sb.WriteString(d.Format)
        sb.WriteRune(d.Delimiter)
        sb.WriteRune(d.Comment)
        sb.WriteString(strconv.FormatBool(d.LazyQuotes))
index a3a9c040a471ba9fa7c14990ece97a724c42a77e..d0edda4531ef21b2006c7264dfa1a10037abaa1f 100644 (file)
@@ -72,9 +72,17 @@ func (ns *Namespace) Unmarshal(args ...any) (any, error) {
                }
 
                v, err := ns.cacheUnmarshal.GetOrCreate(key, func(string) (*resources.StaleValue[any], error) {
-                       f := metadecoders.FormatFromStrings(r.MediaType().Suffixes()...)
-                       if f == "" {
-                               return nil, fmt.Errorf("MIME %q not supported", r.MediaType())
+                       var f metadecoders.Format
+                       if decoder.Format != "" {
+                               f = metadecoders.FormatFromString(decoder.Format)
+                               if f == "" {
+                                       return nil, fmt.Errorf("format %q not supported", decoder.Format)
+                               }
+                       } else {
+                               f = metadecoders.FormatFromStrings(r.MediaType().Suffixes()...)
+                               if f == "" {
+                                       return nil, fmt.Errorf("MIME %q not supported", r.MediaType())
+                               }
                        }
 
                        reader, err := r.ReadSeekCloser()
@@ -119,10 +127,22 @@ func (ns *Namespace) Unmarshal(args ...any) (any, error) {
 
        key := hashing.MD5FromStringHexEncoded(dataStr)
 
+       if decoder != metadecoders.Default {
+               key += decoder.OptionsKey()
+       }
+
        v, err := ns.cacheUnmarshal.GetOrCreate(key, func(string) (*resources.StaleValue[any], error) {
-               f := decoder.FormatFromContentString(dataStr)
-               if f == "" {
-                       return nil, errors.New("unknown format")
+               var f metadecoders.Format
+               if decoder.Format != "" {
+                       f = metadecoders.FormatFromString(decoder.Format)
+                       if f == "" {
+                               return nil, fmt.Errorf("format %q not supported", decoder.Format)
+                       }
+               } else {
+                       f = decoder.FormatFromContentString(dataStr)
+                       if f == "" {
+                               return nil, errors.New("unknown format")
+                       }
                }
 
                v, err := decoder.Unmarshal([]byte(dataStr), f)
index 9b34e1daa8ee7b3c90c081a5581fef5f775f5039..934b29ee1235cd1b094de9dde94c2126f6964fa8 100644 (file)
@@ -128,6 +128,9 @@ func TestUnmarshal(t *testing.T) {
                {testContentResource{key: "r1", content: `a;b;c`, mime: media.Builtin.CSVType}, map[string]any{"delimiter": ";"}, func(r [][]string) {
                        b.Assert([][]string{{"a", "b", "c"}}, qt.DeepEquals, r)
                }},
+               {testContentResource{key: "r1", content: `{p: [a, b]}`, mime: media.Builtin.JSONType}, map[string]any{"format": "yaml"}, func(m map[string]any) {
+                       b.Assert(m, qt.DeepEquals, map[string]any{"p": []any{"a", "b"}})
+               }},
                {"a,b,c", nil, func(r [][]string) {
                        b.Assert([][]string{{"a", "b", "c"}}, qt.DeepEquals, r)
                }},
@@ -141,6 +144,9 @@ a;b;c`, mime: media.Builtin.CSVType}, map[string]any{"DElimiter": ";", "Comment"
                }},
                {``, nil, nil},
                {`   `, nil, nil},
+               {`{p: [a, b]}`, map[string]any{"format": "yaml"}, func(m map[string]any) {
+                       b.Assert(m, qt.DeepEquals, map[string]any{"p": []any{"a", "b"}})
+               }},
                // errors
                {"thisisnotavaliddataformat", nil, false},
                {testContentResource{key: "r1", content: `invalid&toml"`, mime: media.Builtin.TOMLType}, nil, false},
@@ -148,6 +154,8 @@ a;b;c`, mime: media.Builtin.CSVType}, map[string]any{"DElimiter": ";", "Comment"
                {"thisisnotavaliddataformat", nil, false},
                {`{ notjson }`, nil, false},
                {tstNoStringer{}, nil, false},
+               {"<root><a>notjson</a></root>", map[string]any{"format": "json"}, false},
+               {"anything", map[string]any{"format": "invalidformat"}, false},
        } {
 
                ns.Reset()