diff options
Diffstat (limited to 'resources/page/pages_sort.go')
-rw-r--r-- | resources/page/pages_sort.go | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/resources/page/pages_sort.go b/resources/page/pages_sort.go new file mode 100644 index 000000000..eb3a28247 --- /dev/null +++ b/resources/page/pages_sort.go @@ -0,0 +1,348 @@ +// 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 page + +import ( + "sort" + + "github.com/gohugoio/hugo/resources/resource" + + "github.com/spf13/cast" +) + +var spc = newPageCache() + +/* + * Implementation of a custom sorter for Pages + */ + +// A pageSorter implements the sort interface for Pages +type pageSorter struct { + pages Pages + by pageBy +} + +// pageBy is a closure used in the Sort.Less method. +type pageBy func(p1, p2 Page) bool + +// Sort stable sorts the pages given the receiver's sort order. +func (by pageBy) Sort(pages Pages) { + ps := &pageSorter{ + pages: pages, + by: by, // The Sort method's receiver is the function (closure) that defines the sort order. + } + sort.Stable(ps) +} + +// DefaultPageSort is the default sort func for pages in Hugo: +// Order by Weight, Date, LinkTitle and then full file path. +var DefaultPageSort = func(p1, p2 Page) bool { + if p1.Weight() == p2.Weight() { + if p1.Date().Unix() == p2.Date().Unix() { + if p1.LinkTitle() == p2.LinkTitle() { + if p1.File() == nil || p2.File() == nil { + return p1.File() == nil + } + return p1.File().Filename() < p2.File().Filename() + } + return (p1.LinkTitle() < p2.LinkTitle()) + } + return p1.Date().Unix() > p2.Date().Unix() + } + + if p2.Weight() == 0 { + return true + } + + if p1.Weight() == 0 { + return false + } + + return p1.Weight() < p2.Weight() +} + +var languagePageSort = func(p1, p2 Page) bool { + + if p1.Language().Weight == p2.Language().Weight { + if p1.Date().Unix() == p2.Date().Unix() { + if p1.LinkTitle() == p2.LinkTitle() { + if p1.File() != nil && p2.File() != nil { + return p1.File().Filename() < p2.File().Filename() + } + } + return (p1.LinkTitle() < p2.LinkTitle()) + } + return p1.Date().Unix() > p2.Date().Unix() + } + + if p2.Language().Weight == 0 { + return true + } + + if p1.Language().Weight == 0 { + return false + } + + return p1.Language().Weight < p2.Language().Weight +} + +func (ps *pageSorter) Len() int { return len(ps.pages) } +func (ps *pageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], ps.pages[i] } + +// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. +func (ps *pageSorter) Less(i, j int) bool { return ps.by(ps.pages[i], ps.pages[j]) } + +// Limit limits the number of pages returned to n. +func (p Pages) Limit(n int) Pages { + if len(p) > n { + return p[0:n] + } + return p +} + +// ByWeight sorts the Pages by weight and returns a copy. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByWeight() Pages { + const key = "pageSort.ByWeight" + pages, _ := spc.get(key, pageBy(DefaultPageSort).Sort, p) + return pages +} + +// SortByDefault sorts pages by the default sort. +func SortByDefault(pages Pages) { + pageBy(DefaultPageSort).Sort(pages) +} + +// ByTitle sorts the Pages by title and returns a copy. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByTitle() Pages { + + const key = "pageSort.ByTitle" + + title := func(p1, p2 Page) bool { + return p1.Title() < p2.Title() + } + + pages, _ := spc.get(key, pageBy(title).Sort, p) + return pages +} + +// ByLinkTitle sorts the Pages by link title and returns a copy. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByLinkTitle() Pages { + + const key = "pageSort.ByLinkTitle" + + linkTitle := func(p1, p2 Page) bool { + return p1.LinkTitle() < p2.LinkTitle() + } + + pages, _ := spc.get(key, pageBy(linkTitle).Sort, p) + + return pages +} + +// ByDate sorts the Pages by date and returns a copy. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByDate() Pages { + + const key = "pageSort.ByDate" + + date := func(p1, p2 Page) bool { + return p1.Date().Unix() < p2.Date().Unix() + } + + pages, _ := spc.get(key, pageBy(date).Sort, p) + + return pages +} + +// ByPublishDate sorts the Pages by publish date and returns a copy. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByPublishDate() Pages { + + const key = "pageSort.ByPublishDate" + + pubDate := func(p1, p2 Page) bool { + return p1.PublishDate().Unix() < p2.PublishDate().Unix() + } + + pages, _ := spc.get(key, pageBy(pubDate).Sort, p) + + return pages +} + +// ByExpiryDate sorts the Pages by publish date and returns a copy. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByExpiryDate() Pages { + + const key = "pageSort.ByExpiryDate" + + expDate := func(p1, p2 Page) bool { + return p1.ExpiryDate().Unix() < p2.ExpiryDate().Unix() + } + + pages, _ := spc.get(key, pageBy(expDate).Sort, p) + + return pages +} + +// ByLastmod sorts the Pages by the last modification date and returns a copy. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByLastmod() Pages { + + const key = "pageSort.ByLastmod" + + date := func(p1, p2 Page) bool { + return p1.Lastmod().Unix() < p2.Lastmod().Unix() + } + + pages, _ := spc.get(key, pageBy(date).Sort, p) + + return pages +} + +// ByLength sorts the Pages by length and returns a copy. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByLength() Pages { + + const key = "pageSort.ByLength" + + length := func(p1, p2 Page) bool { + + p1l, ok1 := p1.(resource.LengthProvider) + p2l, ok2 := p2.(resource.LengthProvider) + + if !ok1 { + return true + } + + if !ok2 { + return false + } + + return p1l.Len() < p2l.Len() + } + + pages, _ := spc.get(key, pageBy(length).Sort, p) + + return pages +} + +// ByLanguage sorts the Pages by the language's Weight. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByLanguage() Pages { + + const key = "pageSort.ByLanguage" + + pages, _ := spc.get(key, pageBy(languagePageSort).Sort, p) + + return pages +} + +// SortByLanguage sorts the pages by language. +func SortByLanguage(pages Pages) { + pageBy(languagePageSort).Sort(pages) +} + +// Reverse reverses the order in Pages and returns a copy. +// +// Adjacent invocations on the same receiver will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) Reverse() Pages { + const key = "pageSort.Reverse" + + reverseFunc := func(pages Pages) { + for i, j := 0, len(pages)-1; i < j; i, j = i+1, j-1 { + pages[i], pages[j] = pages[j], pages[i] + } + } + + pages, _ := spc.get(key, reverseFunc, p) + + return pages +} + +// ByParam sorts the pages according to the given page Params key. +// +// Adjacent invocations on the same receiver with the same paramsKey will return a cached result. +// +// This may safely be executed in parallel. +func (p Pages) ByParam(paramsKey interface{}) Pages { + paramsKeyStr := cast.ToString(paramsKey) + key := "pageSort.ByParam." + paramsKeyStr + + paramsKeyComparator := func(p1, p2 Page) bool { + v1, _ := p1.Param(paramsKeyStr) + v2, _ := p2.Param(paramsKeyStr) + + if v1 == nil { + return false + } + + if v2 == nil { + return true + } + + isNumeric := func(v interface{}) bool { + switch v.(type) { + case uint8, uint16, uint32, uint64, int, int8, int16, int32, int64, float32, float64: + return true + default: + return false + } + } + + if isNumeric(v1) && isNumeric(v2) { + return cast.ToFloat64(v1) < cast.ToFloat64(v2) + } + + s1 := cast.ToString(v1) + s2 := cast.ToString(v2) + + return s1 < s2 + } + + pages, _ := spc.get(key, pageBy(paramsKeyComparator).Sort, p) + + return pages +} |