Improve handling of <nil> Params
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 30 Jul 2021 08:56:45 +0000 (10:56 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 30 Jul 2021 19:07:52 +0000 (21:07 +0200)
Fixes #8825

common/maps/maps.go
common/maps/maps_test.go
hugolib/menu_test.go
hugolib/site.go
navigation/menu.go
navigation/pagemenus.go

index 79fcc23d0002ceead8ca480d32d20ab08feb570c..b5e9ba2f54f9e2157c16cf28d859e527fd2b9a7b 100644 (file)
@@ -17,6 +17,8 @@ import (
        "fmt"
        "strings"
 
+       "github.com/gohugoio/hugo/common/types"
+
        "github.com/gobwas/glob"
        "github.com/spf13/cast"
 )
@@ -39,8 +41,12 @@ func ToStringMapE(in interface{}) (map[string]interface{}, error) {
 }
 
 // ToParamsAndPrepare converts in to Params and prepares it for use.
+// If in is nil, an empty map is returned.
 // See PrepareParams.
 func ToParamsAndPrepare(in interface{}) (Params, bool) {
+       if types.IsNil(in) {
+               return Params{}, true
+       }
        m, err := ToStringMapE(in)
        if err != nil {
                return nil, false
index ba3c2508757e7b260ff73b7f71aa7c79abe7460d..f0c32b9febc408d5a649521b5fb0c35094a7e41c 100644 (file)
@@ -114,6 +114,16 @@ func TestToSliceStringMap(t *testing.T) {
        }
 }
 
+func TestToParamsAndPrepare(t *testing.T) {
+       c := qt.New(t)
+       _, ok := ToParamsAndPrepare(map[string]interface{}{"A": "av"})
+       c.Assert(ok, qt.IsTrue)
+
+       params, ok := ToParamsAndPrepare(nil)
+       c.Assert(ok, qt.IsTrue)
+       c.Assert(params, qt.DeepEquals, Params{})
+}
+
 func TestRenameKeys(t *testing.T) {
        c := qt.New(t)
 
index a647c5bfacceb740c5e5161db24128991116f5d4..796ac3bebaaf46a7b6db4dc1cc4d3144629e0c05 100644 (file)
@@ -350,6 +350,25 @@ menu:
        b.AssertFileContent("public/index.html", "A|Children:C|B|")
 }
 
+// Issue #8825
+func TestMenuParamsEmptyYaml(t *testing.T) {
+       b := newTestSitesBuilder(t).WithConfigFile("yaml", `
+
+`)
+
+       b.WithTemplates("index.html", `{{ site.Menus }}`)
+
+       b.WithContent("p1.md", `---
+menus:
+  main: 
+    identity: journal
+    weight: 2
+    params:
+---    
+`)
+       b.Build(BuildCfg{})
+}
+
 func TestMenuParams(t *testing.T) {
        b := newTestSitesBuilder(t).WithConfigFile("toml", `
 [[menus.main]]
index e687710bf5a375e1dfb71a76dc99bdb89d17f73a..18c9bfc8093c4c47b9ff27efb707bc02a0072ad5 100644 (file)
@@ -1400,17 +1400,25 @@ func (s *Site) getMenusFromConfig() navigation.Menus {
                                s.Log.Errorf("unable to process menus in site config\n")
                                s.Log.Errorln(err)
                        } else {
+                               handleErr := func(err error) {
+                                       if err == nil {
+                                               return
+                                       }
+                                       s.Log.Errorf("unable to process menus in site config\n")
+                                       s.Log.Errorln(err)
+
+                               }
+
                                for _, entry := range m {
                                        s.Log.Debugf("found menu: %q, in site config\n", name)
 
                                        menuEntry := navigation.MenuEntry{Menu: name}
                                        ime, err := maps.ToStringMapE(entry)
-                                       if err != nil {
-                                               s.Log.Errorf("unable to process menus in site config\n")
-                                               s.Log.Errorln(err)
-                                       }
+                                       handleErr(err)
+
+                                       err = menuEntry.MarshallMap(ime)
+                                       handleErr(err)
 
-                                       menuEntry.MarshallMap(ime)
                                        // TODO(bep) clean up all of this
                                        menuEntry.ConfiguredURL = s.Info.createNodeMenuEntryURL(menuEntry.ConfiguredURL)
 
index dd62f825ebda51cc73c94c390d1cd775afb609a1..b4f5e7af54b156627bd06a51b1b266a00d249d72 100644 (file)
 package navigation
 
 import (
+       "fmt"
        "html/template"
        "sort"
        "strings"
 
+       "github.com/pkg/errors"
+
        "github.com/gohugoio/hugo/common/maps"
        "github.com/gohugoio/hugo/common/types"
        "github.com/gohugoio/hugo/compare"
@@ -65,6 +68,7 @@ func (m *MenuEntry) URL() string {
 type Page interface {
        LinkTitle() string
        RelPermalink() string
+       Path() string
        Section() string
        Weight() int
        IsPage() bool
@@ -127,7 +131,8 @@ func (m *MenuEntry) isSamePage(p Page) bool {
        return false
 }
 
-func (m *MenuEntry) MarshallMap(ime map[string]interface{}) {
+func (m *MenuEntry) MarshallMap(ime map[string]interface{}) error {
+       var err error
        for k, v := range ime {
                loki := strings.ToLower(k)
                switch loki {
@@ -150,10 +155,19 @@ func (m *MenuEntry) MarshallMap(ime map[string]interface{}) {
                case "parent":
                        m.Parent = cast.ToString(v)
                case "params":
-                       m.Params = maps.MustToParamsAndPrepare(v)
-
+                       var ok bool
+                       m.Params, ok = maps.ToParamsAndPrepare(v)
+                       if !ok {
+                               err = fmt.Errorf("cannot convert %T to Params", v)
+                       }
                }
        }
+
+       if err != nil {
+               return errors.Wrapf(err, "failed to marshal menu entry %q", m.KeyName())
+       }
+
+       return nil
 }
 
 func (m Menu) Add(me *MenuEntry) Menu {
index f783e30cf3487ffdae895bd96aef19f83b6df349..46ed5221e6a3487508089187a106532c75344c32 100644 (file)
@@ -75,10 +75,14 @@ func PageMenusFromPage(p Page) (PageMenus, error) {
                return pm, nil
        }
 
+       var wrapErr = func(err error) error {
+               return errors.Wrapf(err, "unable to process menus for page %q", p.Path())
+       }
+
        // Could be a structured menu entry
        menus, err := maps.ToStringMapE(ms)
        if err != nil {
-               return pm, errors.Wrapf(err, "unable to process menus for %q", p.LinkTitle())
+               return pm, wrapErr(err)
        }
 
        for name, menu := range menus {
@@ -86,10 +90,12 @@ func PageMenusFromPage(p Page) (PageMenus, error) {
                if menu != nil {
                        ime, err := maps.ToStringMapE(menu)
                        if err != nil {
-                               return pm, errors.Wrapf(err, "unable to process menus for %q", p.LinkTitle())
+                               return pm, wrapErr(err)
                        }
 
-                       menuEntry.MarshallMap(ime)
+                       if err = menuEntry.MarshallMap(ime); err != nil {
+                               return pm, wrapErr(err)
+                       }
                }
                pm[name] = &menuEntry
        }