shithub: hugo

Download patch

ref: 5eadc4c0a8e5c51e72670591c4b7877e79c15e3c
parent: 18cb21ff2e4a60e7094908e4d6113a9d5a086316
author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
date: Thu Mar 12 13:09:49 EDT 2020

metrics: Fix --templateMetricsHints

Also improve non-string comparisons.

Fixes #7048

--- a/common/types/convert.go
+++ b/common/types/convert.go
@@ -13,8 +13,12 @@
 
 package types
 
-import "github.com/spf13/cast"
+import (
+	"html/template"
 
+	"github.com/spf13/cast"
+)
+
 // ToStringSlicePreserveString converts v to a string slice.
 // If v is a string, it will be wrapped in a string slice.
 func ToStringSlicePreserveString(v interface{}) []string {
@@ -25,4 +29,40 @@
 		return []string{sds}
 	}
 	return cast.ToStringSlice(v)
+}
+
+// TypeToString converts v to a string if it's a valid string type.
+// Note that this will not try to convert numeric values etc.,
+// use ToString for that.
+func TypeToString(v interface{}) (string, bool) {
+	switch s := v.(type) {
+	case string:
+		return s, true
+	case template.HTML:
+		return string(s), true
+	case template.CSS:
+		return string(s), true
+	case template.HTMLAttr:
+		return string(s), true
+	case template.JS:
+		return string(s), true
+	case template.JSStr:
+		return string(s), true
+	case template.URL:
+		return string(s), true
+	case template.Srcset:
+		return string(s), true
+	}
+
+	return "", false
+}
+
+// ToString converts v to a string.
+func ToString(v interface{}) string {
+	if s, ok := TypeToString(v); ok {
+		return s
+	}
+
+	return cast.ToString(v)
+
 }
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -15,6 +15,12 @@
 package metrics
 
 import (
+	"reflect"
+
+	"github.com/gohugoio/hugo/helpers"
+
+	"github.com/gohugoio/hugo/common/types"
+
 	"fmt"
 	"io"
 	"math"
@@ -25,8 +31,6 @@
 	"time"
 
 	"github.com/gohugoio/hugo/compare"
-
-	"github.com/gohugoio/hugo/common/hreflect"
 )
 
 // The Provider interface defines an interface for measuring metrics.
@@ -51,15 +55,17 @@
 	simSum   int
 }
 
+var counter = 0
+
 func (d *diff) add(v interface{}) *diff {
-	if !hreflect.IsTruthful(v) {
+	if types.IsNil(d.baseline) {
 		d.baseline = v
 		d.count = 1
 		d.simSum = 100 // If we get only one it is very cache friendly.
 		return d
 	}
-
-	d.simSum += howSimilar(v, d.baseline)
+	adder := howSimilar(v, d.baseline)
+	d.simSum += adder
 	d.count++
 
 	return d
@@ -113,6 +119,7 @@
 	}
 
 	d.add(value)
+
 	s.diffmu.Unlock()
 }
 
@@ -135,6 +142,7 @@
 		var max time.Duration
 
 		diff, found := s.diffs[k]
+
 		cacheFactor := 0
 		if found {
 			cacheFactor = int(math.Floor(float64(diff.simSum) / float64(diff.count)))
@@ -196,12 +204,20 @@
 // howSimilar is a naive diff implementation that returns
 // a number between 0-100 indicating how similar a and b are.
 func howSimilar(a, b interface{}) int {
-	// TODO(bep) object equality fast path, but remember that
-	// we can get anytning in here.
+	t1, t2 := reflect.TypeOf(a), reflect.TypeOf(b)
+	if t1 != t2 {
+		return 0
+	}
 
-	as, ok1 := a.(string)
-	bs, ok2 := b.(string)
+	if t1.Comparable() && t2.Comparable() {
+		if a == b {
+			return 100
+		}
+	}
 
+	as, ok1 := types.TypeToString(a)
+	bs, ok2 := types.TypeToString(b)
+
 	if ok1 && ok2 {
 		return howSimilarStrings(as, bs)
 	}
@@ -222,7 +238,12 @@
 		return 90
 	}
 
+	h1, h2 := helpers.HashString(a), helpers.HashString(b)
+	if h1 == h2 {
+		return 100
+	}
 	return 0
+
 }
 
 // howSimilar is a naive diff implementation that returns
@@ -229,6 +250,9 @@
 // a number between 0-100 indicating how similar a and b are.
 // 100 is when all words in a also exists in b.
 func howSimilarStrings(a, b string) int {
+	if a == b {
+		return 100
+	}
 
 	// Give some weight to the word positions.
 	const partitionSize = 4
--- a/metrics/metrics_test.go
+++ b/metrics/metrics_test.go
@@ -14,6 +14,7 @@
 package metrics
 
 import (
+	"html/template"
 	"strings"
 	"testing"
 
@@ -39,13 +40,23 @@
 	c.Assert(howSimilar("The Hugo", "The Hugo Rules"), qt.Equals, 66)
 	c.Assert(howSimilar("Totally different", "Not Same"), qt.Equals, 0)
 	c.Assert(howSimilar(sentence, sentenceReversed), qt.Equals, 14)
+	c.Assert(howSimilar(template.HTML("Hugo Rules"), template.HTML("Hugo Rules")), qt.Equals, 100)
+	c.Assert(howSimilar(map[string]interface{}{"a": 32, "b": 33}, map[string]interface{}{"a": 32, "b": 33}), qt.Equals, 100)
+	c.Assert(howSimilar(map[string]interface{}{"a": 32, "b": 33}, map[string]interface{}{"a": 32, "b": 34}), qt.Equals, 0)
 
 }
 
+type testStruct struct {
+	Name string
+}
+
 func TestSimilarPercentageNonString(t *testing.T) {
 	c := qt.New(t)
 	c.Assert(howSimilar(page.NopPage, page.NopPage), qt.Equals, 100)
 	c.Assert(howSimilar(page.Pages{}, page.Pages{}), qt.Equals, 90)
+	c.Assert(howSimilar(testStruct{Name: "A"}, testStruct{Name: "B"}), qt.Equals, 0)
+	c.Assert(howSimilar(testStruct{Name: "A"}, testStruct{Name: "A"}), qt.Equals, 100)
+
 }
 
 func BenchmarkHowSimilar(b *testing.B) {