output: Improve layout path construction

This commit is contained in:
Cameron Moore 2020-10-15 13:54:47 -05:00 committed by GitHub
parent f033d9f01d
commit acfa153863
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 46 deletions

View file

@ -14,7 +14,6 @@
package output package output
import ( import (
"fmt"
"strings" "strings"
"sync" "sync"
@ -65,7 +64,6 @@ func NewLayoutHandler() *LayoutHandler {
// For returns a layout for the given LayoutDescriptor and options. // For returns a layout for the given LayoutDescriptor and options.
// Layouts are rendered and cached internally. // Layouts are rendered and cached internally.
func (l *LayoutHandler) For(d LayoutDescriptor, f Format) ([]string, error) { func (l *LayoutHandler) For(d LayoutDescriptor, f Format) ([]string, error) {
// We will get lots of requests for the same layouts, so avoid recalculations. // We will get lots of requests for the same layouts, so avoid recalculations.
key := layoutCacheKey{d, f.Name} key := layoutCacheKey{d, f.Name}
l.mu.RLock() l.mu.RLock()
@ -131,7 +129,6 @@ func (l *layoutBuilder) addKind() {
const renderingHookRoot = "/_markup" const renderingHookRoot = "/_markup"
func resolvePageTemplate(d LayoutDescriptor, f Format) []string { func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
b := &layoutBuilder{d: d, f: f} b := &layoutBuilder{d: d, f: f}
if !d.RenderingHook && d.Layout != "" { if !d.RenderingHook && d.Layout != "" {
@ -208,11 +205,9 @@ func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
} }
return layouts return layouts
} }
func (l *layoutBuilder) resolveVariations() []string { func (l *layoutBuilder) resolveVariations() []string {
var layouts []string var layouts []string
var variations []string var variations []string
@ -220,7 +215,7 @@ func (l *layoutBuilder) resolveVariations() []string {
if l.d.Lang != "" { if l.d.Lang != "" {
// We prefer the most specific type before language. // 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 { } else {
variations = append(variations, name) variations = append(variations, name)
} }
@ -233,55 +228,63 @@ func (l *layoutBuilder) resolveVariations() []string {
if variation == "" && layoutVar == "" { if variation == "" && layoutVar == "" {
continue continue
} }
template := layoutTemplate(typeVar, layoutVar)
layouts = append(layouts, replaceKeyValues(template, s := constructLayoutPath(typeVar, layoutVar, variation, l.f.MediaType.Suffix())
"TYPE", typeVar, if s != "" {
"LAYOUT", layoutVar, layouts = append(layouts, s)
"VARIATIONS", variation, }
"EXTENSION", l.f.MediaType.Suffix(), }
))
} }
} }
return layouts
} }
return filterDotLess(layouts) // 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 ""
} }
func layoutTemplate(typeVar, layoutVar string) string { // Commence valid path construction...
var l string var (
p strings.Builder
needDot bool
)
if typeVar != "" { if typ != "" {
l = "TYPE/" p.WriteString(typ)
p.WriteString("/")
} }
if layoutVar != "" { if layout != "" {
l += "LAYOUT.VARIATIONS.EXTENSION" p.WriteString(layout)
} else { needDot = true
l += "VARIATIONS.EXTENSION"
} }
return l if variations != "" {
if needDot {
p.WriteString(".")
}
p.WriteString(variations)
needDot = true
} }
func filterDotLess(layouts []string) []string { if extension != "" {
var filteredLayouts []string if needDot {
p.WriteString(".")
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)
} }
p.WriteString(extension)
} }
return filteredLayouts return p.String()
}
func replaceKeyValues(s string, oldNew ...string) string {
replacer := strings.NewReplacer(oldNew...)
return replacer.Replace(s)
} }

View file

@ -663,13 +663,25 @@ func TestLayout(t *testing.T) {
} }
func BenchmarkLayout(b *testing.B) { func BenchmarkLayout(b *testing.B) {
c := qt.New(b)
descriptor := LayoutDescriptor{Kind: "taxonomy", Section: "categories"} descriptor := LayoutDescriptor{Kind: "taxonomy", Section: "categories"}
l := NewLayoutHandler() l := NewLayoutHandler()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
layouts, err := l.For(descriptor, HTMLFormat) _, err := l.For(descriptor, HTMLFormat)
c.Assert(err, qt.IsNil) if err != nil {
c.Assert(layouts, qt.Not(qt.HasLen), 0) 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)
}
} }
} }