Skip to content

Commit ecc3dd1

Browse files
authored
transform: Add support for "format" option in transform.Unmarshal
Fixes #13887
1 parent de4a7f1 commit ecc3dd1

File tree

3 files changed

+39
-6
lines changed

3 files changed

+39
-6
lines changed

parser/metadecoders/decoder.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ import (
3636

3737
// Decoder provides some configuration options for the decoders.
3838
type Decoder struct {
39+
// Format specifies a specific format to decode from. If empty or
40+
// unspecified, it's inferred from the contents or the filename.
41+
Format string
42+
3943
// Delimiter is the field delimiter. Used in the CSV decoder. Default is
4044
// ','.
4145
Delimiter rune
@@ -57,6 +61,7 @@ type Decoder struct {
5761
// OptionsKey is used in cache keys.
5862
func (d Decoder) OptionsKey() string {
5963
var sb strings.Builder
64+
sb.WriteString(d.Format)
6065
sb.WriteRune(d.Delimiter)
6166
sb.WriteRune(d.Comment)
6267
sb.WriteString(strconv.FormatBool(d.LazyQuotes))

tpl/transform/unmarshal.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,17 @@ func (ns *Namespace) Unmarshal(args ...any) (any, error) {
7272
}
7373

7474
v, err := ns.cacheUnmarshal.GetOrCreate(key, func(string) (*resources.StaleValue[any], error) {
75-
f := metadecoders.FormatFromStrings(r.MediaType().Suffixes()...)
76-
if f == "" {
77-
return nil, fmt.Errorf("MIME %q not supported", r.MediaType())
75+
var f metadecoders.Format
76+
if decoder.Format != "" {
77+
f = metadecoders.FormatFromString(decoder.Format)
78+
if f == "" {
79+
return nil, fmt.Errorf("format %q not supported", decoder.Format)
80+
}
81+
} else {
82+
f = metadecoders.FormatFromStrings(r.MediaType().Suffixes()...)
83+
if f == "" {
84+
return nil, fmt.Errorf("MIME %q not supported", r.MediaType())
85+
}
7886
}
7987

8088
reader, err := r.ReadSeekCloser()
@@ -119,10 +127,22 @@ func (ns *Namespace) Unmarshal(args ...any) (any, error) {
119127

120128
key := hashing.MD5FromStringHexEncoded(dataStr)
121129

130+
if decoder != metadecoders.Default {
131+
key += decoder.OptionsKey()
132+
}
133+
122134
v, err := ns.cacheUnmarshal.GetOrCreate(key, func(string) (*resources.StaleValue[any], error) {
123-
f := decoder.FormatFromContentString(dataStr)
124-
if f == "" {
125-
return nil, errors.New("unknown format")
135+
var f metadecoders.Format
136+
if decoder.Format != "" {
137+
f = metadecoders.FormatFromString(decoder.Format)
138+
if f == "" {
139+
return nil, fmt.Errorf("format %q not supported", decoder.Format)
140+
}
141+
} else {
142+
f = decoder.FormatFromContentString(dataStr)
143+
if f == "" {
144+
return nil, errors.New("unknown format")
145+
}
126146
}
127147

128148
v, err := decoder.Unmarshal([]byte(dataStr), f)

tpl/transform/unmarshal_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ func TestUnmarshal(t *testing.T) {
128128
{testContentResource{key: "r1", content: `a;b;c`, mime: media.Builtin.CSVType}, map[string]any{"delimiter": ";"}, func(r [][]string) {
129129
b.Assert([][]string{{"a", "b", "c"}}, qt.DeepEquals, r)
130130
}},
131+
{testContentResource{key: "r1", content: `{p: [a, b]}`, mime: media.Builtin.JSONType}, map[string]any{"format": "yaml"}, func(m map[string]any) {
132+
b.Assert(m, qt.DeepEquals, map[string]any{"p": []any{"a", "b"}})
133+
}},
131134
{"a,b,c", nil, func(r [][]string) {
132135
b.Assert([][]string{{"a", "b", "c"}}, qt.DeepEquals, r)
133136
}},
@@ -141,13 +144,18 @@ a;b;c`, mime: media.Builtin.CSVType}, map[string]any{"DElimiter": ";", "Comment"
141144
}},
142145
{``, nil, nil},
143146
{` `, nil, nil},
147+
{`{p: [a, b]}`, map[string]any{"format": "yaml"}, func(m map[string]any) {
148+
b.Assert(m, qt.DeepEquals, map[string]any{"p": []any{"a", "b"}})
149+
}},
144150
// errors
145151
{"thisisnotavaliddataformat", nil, false},
146152
{testContentResource{key: "r1", content: `invalid&toml"`, mime: media.Builtin.TOMLType}, nil, false},
147153
{testContentResource{key: "r1", content: `unsupported: MIME"`, mime: media.Builtin.CalendarType}, nil, false},
148154
{"thisisnotavaliddataformat", nil, false},
149155
{`{ notjson }`, nil, false},
150156
{tstNoStringer{}, nil, false},
157+
{"<root><a>notjson</a></root>", map[string]any{"format": "json"}, false},
158+
{"anything", map[string]any{"format": "invalidformat"}, false},
151159
} {
152160

153161
ns.Reset()

0 commit comments

Comments
 (0)