output: Improve layout path construction
authorCameron Moore <moorereason@gmail.com>
Thu, 15 Oct 2020 18:54:47 +0000 (13:54 -0500)
committerGitHub <noreply@github.com>
Thu, 15 Oct 2020 18:54:47 +0000 (20:54 +0200)
output/layout.go
output/layout_test.go

index 4dd37b7b186b6dc09e4f6f0251fe637ae511cd2c..55e7fa30570cafd60a68dd6c37c928a6d690250d 100644 (file)
@@ -14,7 +14,6 @@
 package output
 
 import (
-       "fmt"
        "strings"
        "sync"
 
@@ -65,7 +64,6 @@ func NewLayoutHandler() *LayoutHandler {
 // For returns a layout for the given LayoutDescriptor and options.
 // Layouts are rendered and cached internally.
 func (l *LayoutHandler) For(d LayoutDescriptor, f Format) ([]string, error) {
-
        // We will get lots of requests for the same layouts, so avoid recalculations.
        key := layoutCacheKey{d, f.Name}
        l.mu.RLock()
@@ -131,7 +129,6 @@ func (l *layoutBuilder) addKind() {
 const renderingHookRoot = "/_markup"
 
 func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
-
        b := &layoutBuilder{d: d, f: f}
 
        if !d.RenderingHook && d.Layout != "" {
@@ -208,11 +205,9 @@ func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
        }
 
        return layouts
-
 }
 
 func (l *layoutBuilder) resolveVariations() []string {
-
        var layouts []string
 
        var variations []string
@@ -220,7 +215,7 @@ func (l *layoutBuilder) resolveVariations() []string {
 
        if l.d.Lang != "" {
                // We prefer the most specific type before language.
-               variations = append(variations, []string{fmt.Sprintf("%s.%s", l.d.Lang, name), name, l.d.Lang}...)
+               variations = append(variations, []string{l.d.Lang + "." + name, name, l.d.Lang}...)
        } else {
                variations = append(variations, name)
        }
@@ -233,55 +228,63 @@ func (l *layoutBuilder) resolveVariations() []string {
                                if variation == "" && layoutVar == "" {
                                        continue
                                }
-                               template := layoutTemplate(typeVar, layoutVar)
-                               layouts = append(layouts, replaceKeyValues(template,
-                                       "TYPE", typeVar,
-                                       "LAYOUT", layoutVar,
-                                       "VARIATIONS", variation,
-                                       "EXTENSION", l.f.MediaType.Suffix(),
-                               ))
+
+                               s := constructLayoutPath(typeVar, layoutVar, variation, l.f.MediaType.Suffix())
+                               if s != "" {
+                                       layouts = append(layouts, s)
+                               }
                        }
                }
-
        }
 
-       return filterDotLess(layouts)
+       return layouts
 }
 
-func layoutTemplate(typeVar, layoutVar string) string {
+// constructLayoutPath constructs a layout path given a type, layout,
+// variations, and extension.  The path constructed follows the pattern of
+// type/layout.variations.extension.  If any value is empty, it will be left out
+// of the path construction.
+//
+// Path construction requires at least 2 of 3 out of layout, variations, and extension.
+// If more than one of those is empty, an empty string is returned.
+func constructLayoutPath(typ, layout, variations, extension string) string {
+       // we already know that layout and variations are not both empty because of
+       // checks in resolveVariants().
+       if extension == "" && (layout == "" || variations == "") {
+               return ""
+       }
+
+       // Commence valid path construction...
 
-       var l string
+       var (
+               p       strings.Builder
+               needDot bool
+       )
 
-       if typeVar != "" {
-               l = "TYPE/"
+       if typ != "" {
+               p.WriteString(typ)
+               p.WriteString("/")
        }
 
-       if layoutVar != "" {
-               l += "LAYOUT.VARIATIONS.EXTENSION"
-       } else {
-               l += "VARIATIONS.EXTENSION"
+       if layout != "" {
+               p.WriteString(layout)
+               needDot = true
        }
 
-       return l
-}
-
-func filterDotLess(layouts []string) []string {
-       var filteredLayouts []string
-
-       for _, l := range layouts {
-               l = strings.Replace(l, "..", ".", -1)
-               l = strings.Trim(l, ".")
-               // If media type has no suffix, we have "index" type of layouts in this list, which
-               // doesn't make much sense.
-               if strings.Contains(l, ".") {
-                       filteredLayouts = append(filteredLayouts, l)
+       if variations != "" {
+               if needDot {
+                       p.WriteString(".")
                }
+               p.WriteString(variations)
+               needDot = true
        }
 
-       return filteredLayouts
-}
+       if extension != "" {
+               if needDot {
+                       p.WriteString(".")
+               }
+               p.WriteString(extension)
+       }
 
-func replaceKeyValues(s string, oldNew ...string) string {
-       replacer := strings.NewReplacer(oldNew...)
-       return replacer.Replace(s)
+       return p.String()
 }
index 5651a09b940a217c76609e64c13edf101eac9e88..38b9d5faf51b3abe3e6457d14fa236b055fcb02d 100644 (file)
@@ -663,13 +663,25 @@ func TestLayout(t *testing.T) {
 }
 
 func BenchmarkLayout(b *testing.B) {
-       c := qt.New(b)
        descriptor := LayoutDescriptor{Kind: "taxonomy", Section: "categories"}
        l := NewLayoutHandler()
 
        for i := 0; i < b.N; i++ {
-               layouts, err := l.For(descriptor, HTMLFormat)
-               c.Assert(err, qt.IsNil)
-               c.Assert(layouts, qt.Not(qt.HasLen), 0)
+               _, err := l.For(descriptor, HTMLFormat)
+               if err != nil {
+                       panic(err)
+               }
+       }
+}
+
+func BenchmarkLayoutUncached(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               descriptor := LayoutDescriptor{Kind: "taxonomy", Section: "categories"}
+               l := NewLayoutHandler()
+
+               _, err := l.For(descriptor, HTMLFormat)
+               if err != nil {
+                       panic(err)
+               }
        }
 }