github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/resources/page/taxonomy.go (about) 1 // Copyright 2023 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package page 15 16 import ( 17 "fmt" 18 "sort" 19 20 "github.com/gohugoio/hugo/compare" 21 "github.com/gohugoio/hugo/langs" 22 ) 23 24 // The TaxonomyList is a list of all taxonomies and their values 25 // e.g. List['tags'] => TagTaxonomy (from above) 26 type TaxonomyList map[string]Taxonomy 27 28 func (tl TaxonomyList) String() string { 29 return fmt.Sprintf("TaxonomyList(%d)", len(tl)) 30 } 31 32 // A Taxonomy is a map of keywords to a list of pages. 33 // For example 34 // 35 // TagTaxonomy['technology'] = WeightedPages 36 // TagTaxonomy['go'] = WeightedPages 37 type Taxonomy map[string]WeightedPages 38 39 // OrderedTaxonomy is another representation of an Taxonomy using an array rather than a map. 40 // Important because you can't order a map. 41 type OrderedTaxonomy []OrderedTaxonomyEntry 42 43 // getOneOPage returns one page in the taxonomy, 44 // nil if there is none. 45 func (t OrderedTaxonomy) getOneOPage() Page { 46 if len(t) == 0 { 47 return nil 48 } 49 return t[0].Pages()[0] 50 } 51 52 // OrderedTaxonomyEntry is similar to an element of a Taxonomy, but with the key embedded (as name) 53 // e.g: {Name: Technology, WeightedPages: TaxonomyPages} 54 type OrderedTaxonomyEntry struct { 55 Name string 56 WeightedPages 57 } 58 59 // Get the weighted pages for the given key. 60 func (i Taxonomy) Get(key string) WeightedPages { 61 return i[key] 62 } 63 64 // Count the weighted pages for the given key. 65 func (i Taxonomy) Count(key string) int { return len(i[key]) } 66 67 // TaxonomyArray returns an ordered taxonomy with a non defined order. 68 func (i Taxonomy) TaxonomyArray() OrderedTaxonomy { 69 ies := make([]OrderedTaxonomyEntry, len(i)) 70 count := 0 71 for k, v := range i { 72 ies[count] = OrderedTaxonomyEntry{Name: k, WeightedPages: v} 73 count++ 74 } 75 return ies 76 } 77 78 // Alphabetical returns an ordered taxonomy sorted by key name. 79 func (i Taxonomy) Alphabetical() OrderedTaxonomy { 80 ia := i.TaxonomyArray() 81 p := ia.getOneOPage() 82 if p == nil { 83 return ia 84 } 85 currentSite := p.Site().Current() 86 coll := langs.GetCollator(currentSite.Language()) 87 coll.Lock() 88 defer coll.Unlock() 89 name := func(i1, i2 *OrderedTaxonomyEntry) bool { 90 return coll.CompareStrings(i1.Name, i2.Name) < 0 91 } 92 oiBy(name).Sort(ia) 93 return ia 94 } 95 96 // ByCount returns an ordered taxonomy sorted by # of pages per key. 97 // If taxonomies have the same # of pages, sort them alphabetical 98 func (i Taxonomy) ByCount() OrderedTaxonomy { 99 count := func(i1, i2 *OrderedTaxonomyEntry) bool { 100 li1 := len(i1.WeightedPages) 101 li2 := len(i2.WeightedPages) 102 103 if li1 == li2 { 104 return compare.LessStrings(i1.Name, i2.Name) 105 } 106 return li1 > li2 107 } 108 109 ia := i.TaxonomyArray() 110 oiBy(count).Sort(ia) 111 return ia 112 } 113 114 // Pages returns the Pages for this taxonomy. 115 func (ie OrderedTaxonomyEntry) Pages() Pages { 116 return ie.WeightedPages.Pages() 117 } 118 119 // Count returns the count the pages in this taxonomy. 120 func (ie OrderedTaxonomyEntry) Count() int { 121 return len(ie.WeightedPages) 122 } 123 124 // Term returns the name given to this taxonomy. 125 func (ie OrderedTaxonomyEntry) Term() string { 126 return ie.Name 127 } 128 129 // Reverse reverses the order of the entries in this taxonomy. 130 func (t OrderedTaxonomy) Reverse() OrderedTaxonomy { 131 for i, j := 0, len(t)-1; i < j; i, j = i+1, j-1 { 132 t[i], t[j] = t[j], t[i] 133 } 134 135 return t 136 } 137 138 // A type to implement the sort interface for TaxonomyEntries. 139 type orderedTaxonomySorter struct { 140 taxonomy OrderedTaxonomy 141 by oiBy 142 } 143 144 // Closure used in the Sort.Less method. 145 type oiBy func(i1, i2 *OrderedTaxonomyEntry) bool 146 147 func (by oiBy) Sort(taxonomy OrderedTaxonomy) { 148 ps := &orderedTaxonomySorter{ 149 taxonomy: taxonomy, 150 by: by, // The Sort method's receiver is the function (closure) that defines the sort order. 151 } 152 sort.Stable(ps) 153 } 154 155 // Len is part of sort.Interface. 156 func (s *orderedTaxonomySorter) Len() int { 157 return len(s.taxonomy) 158 } 159 160 // Swap is part of sort.Interface. 161 func (s *orderedTaxonomySorter) Swap(i, j int) { 162 s.taxonomy[i], s.taxonomy[j] = s.taxonomy[j], s.taxonomy[i] 163 } 164 165 // Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. 166 func (s *orderedTaxonomySorter) Less(i, j int) bool { 167 return s.by(&s.taxonomy[i], &s.taxonomy[j]) 168 }