media: Make Type comparable

So we can use it and output.Format as map key etc.

This commit also fixes the media.Type implementation so it does not need to mutate itself to handle different suffixes for the same MIME type, e.g. jpg vs. jpeg.

This means that there are no Suffix or FullSuffix on media.Type anymore.

Fixes #8317
Fixes #8324
This commit is contained in:
Bjørn Erik Pedersen
2021-03-11 09:18:01 +01:00
parent 1b1dcf586e
commit ba1d0051b4
19 changed files with 254 additions and 195 deletions

View File

@@ -76,7 +76,7 @@ func createLayoutExamples() interface{} {
Example: example.name,
Kind: example.d.Kind,
OutputFormat: example.f.Name,
Suffix: example.f.MediaType.Suffix(),
Suffix: example.f.MediaType.FirstSuffix.Suffix,
Layouts: makeLayoutsPresentable(layouts),
})
}

View File

@@ -229,7 +229,7 @@ func (l *layoutBuilder) resolveVariations() []string {
continue
}
s := constructLayoutPath(typeVar, layoutVar, variation, l.f.MediaType.Suffix())
s := constructLayoutPath(typeVar, layoutVar, variation, l.f.MediaType.FirstSuffix.Suffix)
if s != "" {
layouts = append(layouts, s)
}

View File

@@ -28,12 +28,8 @@ import (
func TestLayout(t *testing.T) {
c := qt.New(t)
noExtNoDelimMediaType := media.TextType
noExtNoDelimMediaType.Suffixes = nil
noExtNoDelimMediaType.Delimiter = ""
noExtMediaType := media.TextType
noExtMediaType.Suffixes = nil
noExtNoDelimMediaType := media.WithDelimiterAndSuffixes(media.TextType, "", "")
noExtMediaType := media.WithDelimiterAndSuffixes(media.TextType, ".", "")
var (
ampType = Format{

View File

@@ -20,6 +20,8 @@ import (
"sort"
"strings"
"github.com/pkg/errors"
"github.com/mitchellh/mapstructure"
"github.com/gohugoio/hugo/media"
@@ -207,14 +209,16 @@ func (formats Formats) Less(i, j int) bool {
// The lookup is case insensitive.
func (formats Formats) GetBySuffix(suffix string) (f Format, found bool) {
for _, ff := range formats {
if strings.EqualFold(suffix, ff.MediaType.Suffix()) {
if found {
// ambiguous
found = false
return
for _, suffix2 := range ff.MediaType.Suffixes() {
if strings.EqualFold(suffix, suffix2) {
if found {
// ambiguous
found = false
return
}
f = ff
found = true
}
f = ff
found = true
}
}
return
@@ -310,6 +314,7 @@ func DecodeFormats(mediaTypes media.Types, maps ...map[string]interface{}) (Form
}
f = append(f, newOutFormat)
}
}
}
@@ -319,7 +324,7 @@ func DecodeFormats(mediaTypes media.Types, maps ...map[string]interface{}) (Form
return f, nil
}
func decode(mediaTypes media.Types, input, output interface{}) error {
func decode(mediaTypes media.Types, input interface{}, output *Format) error {
config := &mapstructure.DecoderConfig{
Metadata: nil,
Result: output,
@@ -337,12 +342,19 @@ func decode(mediaTypes media.Types, input, output interface{}) error {
// If mediaType is a string, look it up and replace it
// in the map.
vv := dataVal.MapIndex(key)
if mediaTypeStr, ok := vv.Interface().(string); ok {
mediaType, found := mediaTypes.GetByType(mediaTypeStr)
vvi := vv.Interface()
switch vviv := vvi.(type) {
case media.Type:
// OK
case string:
mediaType, found := mediaTypes.GetByType(vviv)
if !found {
return c, fmt.Errorf("media type %q not found", mediaTypeStr)
return c, fmt.Errorf("media type %q not found", vviv)
}
dataVal.SetMapIndex(key, reflect.ValueOf(mediaType))
default:
return nil, errors.Errorf("invalid output format configuration; wrong type for media type, expected string (e.g. text/html), got %T", vvi)
}
}
}
@@ -357,12 +369,13 @@ func decode(mediaTypes media.Types, input, output interface{}) error {
}
return decoder.Decode(input)
}
// BaseFilename returns the base filename of f including an extension (ie.
// "index.xml").
func (f Format) BaseFilename() string {
return f.BaseName + f.MediaType.FullSuffix()
return f.BaseName + f.MediaType.FirstSuffix.FullSuffix
}
// MarshalJSON returns the JSON encoding of f.

View File

@@ -19,36 +19,26 @@ import (
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/media"
"github.com/google/go-cmp/cmp"
)
var eq = qt.CmpEquals(
cmp.Comparer(func(m1, m2 media.Type) bool {
return m1.Type() == m2.Type()
}),
cmp.Comparer(func(o1, o2 Format) bool {
return o1.Name == o2.Name
}),
)
func TestDefaultTypes(t *testing.T) {
c := qt.New(t)
c.Assert(CalendarFormat.Name, qt.Equals, "Calendar")
c.Assert(CalendarFormat.MediaType, eq, media.CalendarType)
c.Assert(CalendarFormat.MediaType, qt.Equals, media.CalendarType)
c.Assert(CalendarFormat.Protocol, qt.Equals, "webcal://")
c.Assert(CalendarFormat.Path, qt.HasLen, 0)
c.Assert(CalendarFormat.IsPlainText, qt.Equals, true)
c.Assert(CalendarFormat.IsHTML, qt.Equals, false)
c.Assert(CSSFormat.Name, qt.Equals, "CSS")
c.Assert(CSSFormat.MediaType, eq, media.CSSType)
c.Assert(CSSFormat.MediaType, qt.Equals, media.CSSType)
c.Assert(CSSFormat.Path, qt.HasLen, 0)
c.Assert(CSSFormat.Protocol, qt.HasLen, 0) // Will inherit the BaseURL protocol.
c.Assert(CSSFormat.IsPlainText, qt.Equals, true)
c.Assert(CSSFormat.IsHTML, qt.Equals, false)
c.Assert(CSVFormat.Name, qt.Equals, "CSV")
c.Assert(CSVFormat.MediaType, eq, media.CSVType)
c.Assert(CSVFormat.MediaType, qt.Equals, media.CSVType)
c.Assert(CSVFormat.Path, qt.HasLen, 0)
c.Assert(CSVFormat.Protocol, qt.HasLen, 0)
c.Assert(CSVFormat.IsPlainText, qt.Equals, true)
@@ -56,7 +46,7 @@ func TestDefaultTypes(t *testing.T) {
c.Assert(CSVFormat.Permalinkable, qt.Equals, false)
c.Assert(HTMLFormat.Name, qt.Equals, "HTML")
c.Assert(HTMLFormat.MediaType, eq, media.HTMLType)
c.Assert(HTMLFormat.MediaType, qt.Equals, media.HTMLType)
c.Assert(HTMLFormat.Path, qt.HasLen, 0)
c.Assert(HTMLFormat.Protocol, qt.HasLen, 0)
c.Assert(HTMLFormat.IsPlainText, qt.Equals, false)
@@ -64,7 +54,7 @@ func TestDefaultTypes(t *testing.T) {
c.Assert(AMPFormat.Permalinkable, qt.Equals, true)
c.Assert(AMPFormat.Name, qt.Equals, "AMP")
c.Assert(AMPFormat.MediaType, eq, media.HTMLType)
c.Assert(AMPFormat.MediaType, qt.Equals, media.HTMLType)
c.Assert(AMPFormat.Path, qt.Equals, "amp")
c.Assert(AMPFormat.Protocol, qt.HasLen, 0)
c.Assert(AMPFormat.IsPlainText, qt.Equals, false)
@@ -72,7 +62,7 @@ func TestDefaultTypes(t *testing.T) {
c.Assert(AMPFormat.Permalinkable, qt.Equals, true)
c.Assert(RSSFormat.Name, qt.Equals, "RSS")
c.Assert(RSSFormat.MediaType, eq, media.RSSType)
c.Assert(RSSFormat.MediaType, qt.Equals, media.RSSType)
c.Assert(RSSFormat.Path, qt.HasLen, 0)
c.Assert(RSSFormat.IsPlainText, qt.Equals, false)
c.Assert(RSSFormat.NoUgly, qt.Equals, true)
@@ -83,7 +73,7 @@ func TestGetFormatByName(t *testing.T) {
c := qt.New(t)
formats := Formats{AMPFormat, CalendarFormat}
tp, _ := formats.GetByName("AMp")
c.Assert(tp, eq, AMPFormat)
c.Assert(tp, qt.Equals, AMPFormat)
_, found := formats.GetByName("HTML")
c.Assert(found, qt.Equals, false)
_, found = formats.GetByName("FOO")
@@ -95,9 +85,9 @@ func TestGetFormatByExt(t *testing.T) {
formats1 := Formats{AMPFormat, CalendarFormat}
formats2 := Formats{AMPFormat, HTMLFormat, CalendarFormat}
tp, _ := formats1.GetBySuffix("html")
c.Assert(tp, eq, AMPFormat)
c.Assert(tp, qt.Equals, AMPFormat)
tp, _ = formats1.GetBySuffix("ics")
c.Assert(tp, eq, CalendarFormat)
c.Assert(tp, qt.Equals, CalendarFormat)
_, found := formats1.GetBySuffix("not")
c.Assert(found, qt.Equals, false)
@@ -129,18 +119,18 @@ func TestGetFormatByFilename(t *testing.T) {
formats := Formats{AMPFormat, HTMLFormat, noExtDelimFormat, noExt, CalendarFormat}
f, found := formats.FromFilename("my.amp.html")
c.Assert(found, qt.Equals, true)
c.Assert(f, eq, AMPFormat)
c.Assert(f, qt.Equals, AMPFormat)
_, found = formats.FromFilename("my.ics")
c.Assert(found, qt.Equals, true)
f, found = formats.FromFilename("my.html")
c.Assert(found, qt.Equals, true)
c.Assert(f, eq, HTMLFormat)
c.Assert(f, qt.Equals, HTMLFormat)
f, found = formats.FromFilename("my.nem")
c.Assert(found, qt.Equals, true)
c.Assert(f, eq, noExtDelimFormat)
c.Assert(f, qt.Equals, noExtDelimFormat)
f, found = formats.FromFilename("my.nex")
c.Assert(found, qt.Equals, true)
c.Assert(f, eq, noExt)
c.Assert(f, qt.Equals, noExt)
_, found = formats.FromFilename("my.css")
c.Assert(found, qt.Equals, false)
}
@@ -172,7 +162,7 @@ func TestDecodeFormats(t *testing.T) {
c.Assert(len(f), qt.Equals, len(DefaultFormats), msg)
json, _ := f.GetByName("JSON")
c.Assert(json.BaseName, qt.Equals, "myindex")
c.Assert(json.MediaType, eq, media.JSONType)
c.Assert(json.MediaType, qt.Equals, media.JSONType)
c.Assert(json.IsPlainText, qt.Equals, false)
},
},
@@ -192,7 +182,7 @@ func TestDecodeFormats(t *testing.T) {
xml, found := f.GetByName("MYXMLFORMAT")
c.Assert(found, qt.Equals, true)
c.Assert(xml.BaseName, qt.Equals, "myxml")
c.Assert(xml.MediaType, eq, media.XMLType)
c.Assert(xml.MediaType, qt.Equals, media.XMLType)
// Verify that we haven't changed the DefaultFormats slice.
json, _ := f.GetByName("JSON")
@@ -234,7 +224,7 @@ func TestDecodeFormats(t *testing.T) {
xml, found := f.GetByName("MYOTHERXMLFORMAT")
c.Assert(found, qt.Equals, true)
c.Assert(xml.BaseName, qt.Equals, "myredefined")
c.Assert(xml.MediaType, eq, media.XMLType)
c.Assert(xml.MediaType, qt.Equals, media.XMLType)
},
},
}