Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'tpl/strings/strings.go')
-rw-r--r--tpl/strings/strings.go505
1 files changed, 505 insertions, 0 deletions
diff --git a/tpl/strings/strings.go b/tpl/strings/strings.go
new file mode 100644
index 000000000..a49451483
--- /dev/null
+++ b/tpl/strings/strings.go
@@ -0,0 +1,505 @@
+// Copyright 2017 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 strings provides template functions for manipulating strings.
+package strings
+
+import (
+ "errors"
+ "fmt"
+ "html/template"
+ "regexp"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/gohugoio/hugo/common/text"
+ "github.com/gohugoio/hugo/deps"
+ "github.com/gohugoio/hugo/helpers"
+ "github.com/gohugoio/hugo/tpl"
+
+ "github.com/spf13/cast"
+)
+
+// New returns a new instance of the strings-namespaced template functions.
+func New(d *deps.Deps) *Namespace {
+ titleCaseStyle := d.Cfg.GetString("titleCaseStyle")
+ titleFunc := helpers.GetTitleFunc(titleCaseStyle)
+ return &Namespace{deps: d, titleFunc: titleFunc}
+}
+
+// Namespace provides template functions for the "strings" namespace.
+// Most functions mimic the Go stdlib, but the order of the parameters may be
+// different to ease their use in the Go template system.
+type Namespace struct {
+ titleFunc func(s string) string
+ deps *deps.Deps
+}
+
+// CountRunes returns the number of runes in s, excluding whitespace.
+func (ns *Namespace) CountRunes(s any) (int, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return 0, fmt.Errorf("Failed to convert content to string: %w", err)
+ }
+
+ counter := 0
+ for _, r := range tpl.StripHTML(ss) {
+ if !helpers.IsWhitespace(r) {
+ counter++
+ }
+ }
+
+ return counter, nil
+}
+
+// RuneCount returns the number of runes in s.
+func (ns *Namespace) RuneCount(s any) (int, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return 0, fmt.Errorf("Failed to convert content to string: %w", err)
+ }
+ return utf8.RuneCountInString(ss), nil
+}
+
+// CountWords returns the approximate word count in s.
+func (ns *Namespace) CountWords(s any) (int, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return 0, fmt.Errorf("Failed to convert content to string: %w", err)
+ }
+
+ isCJKLanguage, err := regexp.MatchString(`\p{Han}|\p{Hangul}|\p{Hiragana}|\p{Katakana}`, ss)
+ if err != nil {
+ return 0, fmt.Errorf("Failed to match regex pattern against string: %w", err)
+ }
+
+ if !isCJKLanguage {
+ return len(strings.Fields(tpl.StripHTML(ss))), nil
+ }
+
+ counter := 0
+ for _, word := range strings.Fields(tpl.StripHTML(ss)) {
+ runeCount := utf8.RuneCountInString(word)
+ if len(word) == runeCount {
+ counter++
+ } else {
+ counter += runeCount
+ }
+ }
+
+ return counter, nil
+}
+
+// Count counts the number of non-overlapping instances of substr in s.
+// If substr is an empty string, Count returns 1 + the number of Unicode code points in s.
+func (ns *Namespace) Count(substr, s any) (int, error) {
+ substrs, err := cast.ToStringE(substr)
+ if err != nil {
+ return 0, fmt.Errorf("Failed to convert substr to string: %w", err)
+ }
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return 0, fmt.Errorf("Failed to convert s to string: %w", err)
+ }
+ return strings.Count(ss, substrs), nil
+}
+
+// Chomp returns a copy of s with all trailing newline characters removed.
+func (ns *Namespace) Chomp(s any) (any, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ res := text.Chomp(ss)
+ switch s.(type) {
+ case template.HTML:
+ return template.HTML(res), nil
+ default:
+ return res, nil
+ }
+}
+
+// Contains reports whether substr is in s.
+func (ns *Namespace) Contains(s, substr any) (bool, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return false, err
+ }
+
+ su, err := cast.ToStringE(substr)
+ if err != nil {
+ return false, err
+ }
+
+ return strings.Contains(ss, su), nil
+}
+
+// ContainsAny reports whether any Unicode code points in chars are within s.
+func (ns *Namespace) ContainsAny(s, chars any) (bool, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return false, err
+ }
+
+ sc, err := cast.ToStringE(chars)
+ if err != nil {
+ return false, err
+ }
+
+ return strings.ContainsAny(ss, sc), nil
+}
+
+// HasPrefix tests whether the input s begins with prefix.
+func (ns *Namespace) HasPrefix(s, prefix any) (bool, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return false, err
+ }
+
+ sx, err := cast.ToStringE(prefix)
+ if err != nil {
+ return false, err
+ }
+
+ return strings.HasPrefix(ss, sx), nil
+}
+
+// HasSuffix tests whether the input s begins with suffix.
+func (ns *Namespace) HasSuffix(s, suffix any) (bool, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return false, err
+ }
+
+ sx, err := cast.ToStringE(suffix)
+ if err != nil {
+ return false, err
+ }
+
+ return strings.HasSuffix(ss, sx), nil
+}
+
+// Replace returns a copy of the string s with all occurrences of old replaced
+// with new. The number of replacements can be limited with an optional fourth
+// parameter.
+func (ns *Namespace) Replace(s, old, new any, limit ...any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ so, err := cast.ToStringE(old)
+ if err != nil {
+ return "", err
+ }
+
+ sn, err := cast.ToStringE(new)
+ if err != nil {
+ return "", err
+ }
+
+ if len(limit) == 0 {
+ return strings.ReplaceAll(ss, so, sn), nil
+ }
+
+ lim, err := cast.ToIntE(limit[0])
+ if err != nil {
+ return "", err
+ }
+
+ return strings.Replace(ss, so, sn, lim), nil
+}
+
+// SliceString slices a string by specifying a half-open range with
+// two indices, start and end. 1 and 4 creates a slice including elements 1 through 3.
+// The end index can be omitted, it defaults to the string's length.
+func (ns *Namespace) SliceString(a any, startEnd ...any) (string, error) {
+ aStr, err := cast.ToStringE(a)
+ if err != nil {
+ return "", err
+ }
+
+ var argStart, argEnd int
+
+ argNum := len(startEnd)
+
+ if argNum > 0 {
+ if argStart, err = cast.ToIntE(startEnd[0]); err != nil {
+ return "", errors.New("start argument must be integer")
+ }
+ }
+ if argNum > 1 {
+ if argEnd, err = cast.ToIntE(startEnd[1]); err != nil {
+ return "", errors.New("end argument must be integer")
+ }
+ }
+
+ if argNum > 2 {
+ return "", errors.New("too many arguments")
+ }
+
+ asRunes := []rune(aStr)
+
+ if argNum > 0 && (argStart < 0 || argStart >= len(asRunes)) {
+ return "", errors.New("slice bounds out of range")
+ }
+
+ if argNum == 2 {
+ if argEnd < 0 || argEnd > len(asRunes) {
+ return "", errors.New("slice bounds out of range")
+ }
+ return string(asRunes[argStart:argEnd]), nil
+ } else if argNum == 1 {
+ return string(asRunes[argStart:]), nil
+ } else {
+ return string(asRunes[:]), nil
+ }
+}
+
+// Split slices an input string into all substrings separated by delimiter.
+func (ns *Namespace) Split(a any, delimiter string) ([]string, error) {
+ aStr, err := cast.ToStringE(a)
+ if err != nil {
+ return []string{}, err
+ }
+
+ return strings.Split(aStr, delimiter), nil
+}
+
+// Substr extracts parts of a string, beginning at the character at the specified
+// position, and returns the specified number of characters.
+//
+// It normally takes two parameters: start and length.
+// It can also take one parameter: start, i.e. length is omitted, in which case
+// the substring starting from start until the end of the string will be returned.
+//
+// To extract characters from the end of the string, use a negative start number.
+//
+// In addition, borrowing from the extended behavior described at http://php.net/substr,
+// if length is given and is negative, then that many characters will be omitted from
+// the end of string.
+func (ns *Namespace) Substr(a any, nums ...any) (string, error) {
+ s, err := cast.ToStringE(a)
+ if err != nil {
+ return "", err
+ }
+
+ asRunes := []rune(s)
+ rlen := len(asRunes)
+
+ var start, length int
+
+ switch len(nums) {
+ case 0:
+ return "", errors.New("too few arguments")
+ case 1:
+ if start, err = cast.ToIntE(nums[0]); err != nil {
+ return "", errors.New("start argument must be an integer")
+ }
+ length = rlen
+ case 2:
+ if start, err = cast.ToIntE(nums[0]); err != nil {
+ return "", errors.New("start argument must be an integer")
+ }
+ if length, err = cast.ToIntE(nums[1]); err != nil {
+ return "", errors.New("length argument must be an integer")
+ }
+ default:
+ return "", errors.New("too many arguments")
+ }
+
+ if rlen == 0 {
+ return "", nil
+ }
+
+ if start < 0 {
+ start += rlen
+ }
+
+ // start was originally negative beyond rlen
+ if start < 0 {
+ start = 0
+ }
+
+ if start > rlen-1 {
+ return "", nil
+ }
+
+ end := rlen
+
+ switch {
+ case length == 0:
+ return "", nil
+ case length < 0:
+ end += length
+ case length > 0:
+ end = start + length
+ }
+
+ if start >= end {
+ return "", nil
+ }
+
+ if end < 0 {
+ return "", nil
+ }
+
+ if end > rlen {
+ end = rlen
+ }
+
+ return string(asRunes[start:end]), nil
+}
+
+// Title returns a copy of the input s with all Unicode letters that begin words
+// mapped to their title case.
+func (ns *Namespace) Title(s any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ return ns.titleFunc(ss), nil
+}
+
+// FirstUpper converts s making the first character upper case.
+func (ns *Namespace) FirstUpper(s any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ return helpers.FirstUpper(ss), nil
+}
+
+// ToLower returns a copy of the input s with all Unicode letters mapped to their
+// lower case.
+func (ns *Namespace) ToLower(s any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.ToLower(ss), nil
+}
+
+// ToUpper returns a copy of the input s with all Unicode letters mapped to their
+// upper case.
+func (ns *Namespace) ToUpper(s any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.ToUpper(ss), nil
+}
+
+// Trim returns converts the strings s removing all leading and trailing characters defined
+// contained.
+func (ns *Namespace) Trim(s, cutset any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ sc, err := cast.ToStringE(cutset)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.Trim(ss, sc), nil
+}
+
+// TrimLeft returns a slice of the string s with all leading characters
+// contained in cutset removed.
+func (ns *Namespace) TrimLeft(cutset, s any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ sc, err := cast.ToStringE(cutset)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.TrimLeft(ss, sc), nil
+}
+
+// TrimPrefix returns s without the provided leading prefix string. If s doesn't
+// start with prefix, s is returned unchanged.
+func (ns *Namespace) TrimPrefix(prefix, s any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ sx, err := cast.ToStringE(prefix)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.TrimPrefix(ss, sx), nil
+}
+
+// TrimRight returns a slice of the string s with all trailing characters
+// contained in cutset removed.
+func (ns *Namespace) TrimRight(cutset, s any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ sc, err := cast.ToStringE(cutset)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.TrimRight(ss, sc), nil
+}
+
+// TrimSuffix returns s without the provided trailing suffix string. If s
+// doesn't end with suffix, s is returned unchanged.
+func (ns *Namespace) TrimSuffix(suffix, s any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ sx, err := cast.ToStringE(suffix)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.TrimSuffix(ss, sx), nil
+}
+
+// Repeat returns a new string consisting of n copies of the string s.
+func (ns *Namespace) Repeat(n, s any) (string, error) {
+ ss, err := cast.ToStringE(s)
+ if err != nil {
+ return "", err
+ }
+
+ sn, err := cast.ToIntE(n)
+ if err != nil {
+ return "", err
+ }
+
+ if sn < 0 {
+ return "", errors.New("strings: negative Repeat count")
+ }
+
+ return strings.Repeat(ss, sn), nil
+}