summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/herrors/errors.go11
-rw-r--r--common/hugio/copy.go90
-rw-r--r--common/hugo/vars_extended.go2
-rw-r--r--common/hugo/vars_regular.go2
-rw-r--r--common/hugo/version.go16
-rw-r--r--common/hugo/version_test.go6
-rw-r--r--common/loggers/loggers.go5
-rw-r--r--common/maps/params.go96
-rw-r--r--common/maps/params_test.go45
9 files changed, 269 insertions, 4 deletions
diff --git a/common/herrors/errors.go b/common/herrors/errors.go
index be98ceb39..1a6107050 100644
--- a/common/herrors/errors.go
+++ b/common/herrors/errors.go
@@ -19,6 +19,7 @@ import (
"fmt"
"io"
"os"
+ "runtime/debug"
_errors "github.com/pkg/errors"
)
@@ -46,6 +47,16 @@ func FprintStackTrace(w io.Writer, err error) {
}
}
+// Recover is a helper function that can be used to capture panics.
+// Put this at the top of a method/function that crashes in a template:
+// defer herrors.Recover()
+func Recover() {
+ if r := recover(); r != nil {
+ fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
+ }
+
+}
+
// ErrFeatureNotAvailable denotes that a feature is unavailable.
//
// We will, at least to begin with, make some Hugo features (SCSS with libsass) optional,
diff --git a/common/hugio/copy.go b/common/hugio/copy.go
new file mode 100644
index 000000000..2b756cb44
--- /dev/null
+++ b/common/hugio/copy.go
@@ -0,0 +1,90 @@
+// Copyright 2019 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugio
+
+import (
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/pkg/errors"
+
+ "github.com/spf13/afero"
+)
+
+// CopyFile copies a file.
+func CopyFile(fs afero.Fs, from, to string) error {
+ sf, err := os.Open(from)
+ if err != nil {
+ return err
+ }
+ defer sf.Close()
+ df, err := os.Create(to)
+ if err != nil {
+ return err
+ }
+ defer df.Close()
+ _, err = io.Copy(df, sf)
+ if err == nil {
+ si, err := os.Stat(from)
+ if err != nil {
+ err = os.Chmod(to, si.Mode())
+
+ if err != nil {
+ return err
+ }
+ }
+
+ }
+ return nil
+}
+
+// CopyDir copies a directory.
+func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool) error {
+ fi, err := os.Stat(from)
+ if err != nil {
+ return err
+ }
+
+ if !fi.IsDir() {
+ return errors.Errorf("%q is not a directory", from)
+ }
+
+ err = fs.MkdirAll(to, 0777) // before umask
+ if err != nil {
+ return err
+ }
+
+ entries, _ := ioutil.ReadDir(from)
+ for _, entry := range entries {
+ fromFilename := filepath.Join(from, entry.Name())
+ toFilename := filepath.Join(to, entry.Name())
+ if entry.IsDir() {
+ if shouldCopy != nil && !shouldCopy(fromFilename) {
+ continue
+ }
+ if err := CopyDir(fs, fromFilename, toFilename, shouldCopy); err != nil {
+ return err
+ }
+ } else {
+ if err := CopyFile(fs, fromFilename, toFilename); err != nil {
+ return err
+ }
+ }
+
+ }
+
+ return nil
+}
diff --git a/common/hugo/vars_extended.go b/common/hugo/vars_extended.go
index 20683b804..bb96bade6 100644
--- a/common/hugo/vars_extended.go
+++ b/common/hugo/vars_extended.go
@@ -15,4 +15,4 @@
package hugo
-var isExtended = true
+var IsExtended = true
diff --git a/common/hugo/vars_regular.go b/common/hugo/vars_regular.go
index e1ece83fb..fae18df14 100644
--- a/common/hugo/vars_regular.go
+++ b/common/hugo/vars_regular.go
@@ -15,4 +15,4 @@
package hugo
-var isExtended = false
+var IsExtended = false
diff --git a/common/hugo/version.go b/common/hugo/version.go
index 47641f10c..848393f97 100644
--- a/common/hugo/version.go
+++ b/common/hugo/version.go
@@ -15,6 +15,7 @@ package hugo
import (
"fmt"
+ "strconv"
"runtime"
"strings"
@@ -133,7 +134,7 @@ func BuildVersionString() string {
if commitHash != "" {
version += "-" + strings.ToUpper(commitHash)
}
- if isExtended {
+ if IsExtended {
version += "/extended"
}
@@ -235,3 +236,16 @@ func compareFloatVersions(version float32, v float32) int {
}
return 1
}
+
+func GoMinorVersion() int {
+ return goMinorVersion(runtime.Version())
+}
+
+func goMinorVersion(version string) int {
+ if strings.HasPrefix(version, "devel") {
+ return 9999 // magic
+ }
+ i, _ := strconv.Atoi(strings.Split(version, ".")[1])
+ return i
+
+}
diff --git a/common/hugo/version_test.go b/common/hugo/version_test.go
index 08059189e..e2aeeabbf 100644
--- a/common/hugo/version_test.go
+++ b/common/hugo/version_test.go
@@ -77,3 +77,9 @@ func TestParseHugoVersion(t *testing.T) {
require.Equal(t, "0.25-DEV", MustParseVersion("0.25-DEV").String())
}
+
+func TestGoMinorVersion(t *testing.T) {
+ assert := require.New(t)
+ assert.Equal(12, goMinorVersion("go1.12.5"))
+ assert.True(GoMinorVersion() >= 11)
+}
diff --git a/common/loggers/loggers.go b/common/loggers/loggers.go
index e711de468..082fd1487 100644
--- a/common/loggers/loggers.go
+++ b/common/loggers/loggers.go
@@ -40,6 +40,7 @@ func init() {
type Logger struct {
*jww.Notepad
ErrorCounter *jww.Counter
+ WarnCounter *jww.Counter
// This is only set in server mode.
errors *bytes.Buffer
@@ -143,9 +144,10 @@ func getLogWriters(outHandle, logHandle io.Writer) (io.Writer, io.Writer) {
func newLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer, saveErrors bool) *Logger {
errorCounter := &jww.Counter{}
+ warnCounter := &jww.Counter{}
outHandle, logHandle = getLogWriters(outHandle, logHandle)
- listeners := []jww.LogListener{jww.LogCounter(errorCounter, jww.LevelError)}
+ listeners := []jww.LogListener{jww.LogCounter(errorCounter, jww.LevelError), jww.LogCounter(warnCounter, jww.LevelWarn)}
var errorBuff *bytes.Buffer
if saveErrors {
errorBuff = new(bytes.Buffer)
@@ -164,6 +166,7 @@ func newLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle
return &Logger{
Notepad: jww.NewNotepad(stdoutThreshold, logThreshold, outHandle, logHandle, "", log.Ldate|log.Ltime, listeners...),
ErrorCounter: errorCounter,
+ WarnCounter: warnCounter,
errors: errorBuff,
}
}
diff --git a/common/maps/params.go b/common/maps/params.go
new file mode 100644
index 000000000..0b81057b1
--- /dev/null
+++ b/common/maps/params.go
@@ -0,0 +1,96 @@
+// Copyright 2019 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package maps
+
+import (
+ "strings"
+
+ "github.com/pkg/errors"
+
+ "github.com/spf13/cast"
+)
+
+// GetNestedParam gets the first match of the keyStr in the candidates given.
+// It will first try the exact match and then try to find it as a nested map value,
+// using the given separator, e.g. "mymap.name".
+// It assumes that all the maps given have lower cased keys.
+func GetNestedParam(keyStr, separator string, candidates ...map[string]interface{}) (interface{}, error) {
+ keyStr = strings.ToLower(keyStr)
+
+ lookupFn := func(key string) interface{} {
+ for _, m := range candidates {
+ if v, ok := m[key]; ok {
+ return v
+ }
+ }
+
+ return nil
+ }
+
+ v, _, _, err := GetNestedParamFn(keyStr, separator, lookupFn)
+ return v, err
+}
+
+func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
+ result, _ := traverseDirectParams(keyStr, lookupFn)
+ if result != nil {
+ return result, keyStr, nil, nil
+ }
+
+ keySegments := strings.Split(keyStr, separator)
+ if len(keySegments) == 1 {
+ return nil, keyStr, nil, nil
+ }
+
+ return traverseNestedParams(keySegments, lookupFn)
+}
+
+func traverseDirectParams(keyStr string, lookupFn func(key string) interface{}) (interface{}, error) {
+ return lookupFn(keyStr), nil
+}
+
+func traverseNestedParams(keySegments []string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
+ firstKey, rest := keySegments[0], keySegments[1:]
+ result := lookupFn(firstKey)
+ if result == nil || len(rest) == 0 {
+ return result, firstKey, nil, nil
+ }
+
+ switch m := result.(type) {
+ case map[string]interface{}:
+ v, key, owner := traverseParams(rest, m)
+ return v, key, owner, nil
+ default:
+ return nil, "", nil, errors.Errorf("unsupported Params type: %T", result)
+ }
+}
+
+func traverseParams(keys []string, m map[string]interface{}) (interface{}, string, map[string]interface{}) {
+ // Shift first element off.
+ firstKey, rest := keys[0], keys[1:]
+ result := m[firstKey]
+
+ // No point in continuing here.
+ if result == nil {
+ return result, "", nil
+ }
+
+ if len(rest) == 0 {
+ // That was the last key.
+ return result, firstKey, m
+ }
+
+ // That was not the last key.
+ return traverseParams(rest, cast.ToStringMap(result))
+}
diff --git a/common/maps/params_test.go b/common/maps/params_test.go
new file mode 100644
index 000000000..89b149617
--- /dev/null
+++ b/common/maps/params_test.go
@@ -0,0 +1,45 @@
+// Copyright 2019 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package maps
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestGetNestedParam(t *testing.T) {
+
+ m := map[string]interface{}{
+ "first": 1,
+ "with_underscore": 2,
+ "nested": map[string]interface{}{
+ "color": "blue",
+ },
+ }
+
+ assert := require.New(t)
+
+ must := func(keyStr, separator string, candidates ...map[string]interface{}) interface{} {
+ v, err := GetNestedParam(keyStr, separator, candidates...)
+ assert.NoError(err)
+ return v
+ }
+
+ assert.Equal(1, must("first", "_", m))
+ assert.Equal(1, must("First", "_", m))
+ assert.Equal(2, must("with_underscore", "_", m))
+ assert.Equal("blue", must("nested_color", "_", m))
+
+}