git.greeks.studio/lethews/hugo@v0.47.1/hugolib/taxonomy.go (about) 1 // Copyright 2015 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 hugolib 15 16 import ( 17 "fmt" 18 "sort" 19 ) 20 21 // The TaxonomyList is a list of all taxonomies and their values 22 // e.g. List['tags'] => TagTaxonomy (from above) 23 type TaxonomyList map[string]Taxonomy 24 25 func (tl TaxonomyList) String() string { 26 return fmt.Sprintf("TaxonomyList(%d)", len(tl)) 27 } 28 29 // A Taxonomy is a map of keywords to a list of pages. 30 // For example 31 // TagTaxonomy['technology'] = WeightedPages 32 // TagTaxonomy['go'] = WeightedPages2 33 type Taxonomy map[string]WeightedPages 34 35 // WeightedPages is a list of Pages with their corresponding (and relative) weight 36 // [{Weight: 30, Page: *1}, {Weight: 40, Page: *2}] 37 type WeightedPages []WeightedPage 38 39 // A WeightedPage is a Page with a weight. 40 type WeightedPage struct { 41 Weight int 42 *Page 43 } 44 45 func (w WeightedPage) String() string { 46 return fmt.Sprintf("WeightedPage(%d,%q)", w.Weight, w.Page.title) 47 } 48 49 // OrderedTaxonomy is another representation of an Taxonomy using an array rather than a map. 50 // Important because you can't order a map. 51 type OrderedTaxonomy []OrderedTaxonomyEntry 52 53 // OrderedTaxonomyEntry is similar to an element of a Taxonomy, but with the key embedded (as name) 54 // e.g: {Name: Technology, WeightedPages: Taxonomyedpages} 55 type OrderedTaxonomyEntry struct { 56 Name string 57 WeightedPages WeightedPages 58 } 59 60 // Get the weighted pages for the given key. 61 func (i Taxonomy) Get(key string) WeightedPages { 62 return i[key] 63 } 64 65 // Count the weighted pages for the given key. 66 func (i Taxonomy) Count(key string) int { return len(i[key]) } 67 68 func (i Taxonomy) add(key string, w WeightedPage) { 69 i[key] = append(i[key], w) 70 } 71 72 // TaxonomyArray returns an ordered taxonomy with a non defined order. 73 func (i Taxonomy) TaxonomyArray() OrderedTaxonomy { 74 ies := make([]OrderedTaxonomyEntry, len(i)) 75 count := 0 76 for k, v := range i { 77 ies[count] = OrderedTaxonomyEntry{Name: k, WeightedPages: v} 78 count++ 79 } 80 return ies 81 } 82 83 // Alphabetical returns an ordered taxonomy sorted by key name. 84 func (i Taxonomy) Alphabetical() OrderedTaxonomy { 85 name := func(i1, i2 *OrderedTaxonomyEntry) bool { 86 return i1.Name < i2.Name 87 } 88 89 ia := i.TaxonomyArray() 90 oiBy(name).Sort(ia) 91 return ia 92 } 93 94 // ByCount returns an ordered taxonomy sorted by # of pages per key. 95 // If taxonomies have the same # of pages, sort them alphabetical 96 func (i Taxonomy) ByCount() OrderedTaxonomy { 97 count := func(i1, i2 *OrderedTaxonomyEntry) bool { 98 li1 := len(i1.WeightedPages) 99 li2 := len(i2.WeightedPages) 100 101 if li1 == li2 { 102 return i1.Name < i2.Name 103 } 104 return li1 > li2 105 } 106 107 ia := i.TaxonomyArray() 108 oiBy(count).Sort(ia) 109 return ia 110 } 111 112 // Pages returns the Pages for this taxonomy. 113 func (ie OrderedTaxonomyEntry) Pages() Pages { 114 return ie.WeightedPages.Pages() 115 } 116 117 // Count returns the count the pages in this taxonomy. 118 func (ie OrderedTaxonomyEntry) Count() int { 119 return len(ie.WeightedPages) 120 } 121 122 // Term returns the name given to this taxonomy. 123 func (ie OrderedTaxonomyEntry) Term() string { 124 return ie.Name 125 } 126 127 // Reverse reverses the order of the entries in this taxonomy. 128 func (t OrderedTaxonomy) Reverse() OrderedTaxonomy { 129 for i, j := 0, len(t)-1; i < j; i, j = i+1, j-1 { 130 t[i], t[j] = t[j], t[i] 131 } 132 133 return t 134 } 135 136 // A type to implement the sort interface for TaxonomyEntries. 137 type orderedTaxonomySorter struct { 138 taxonomy OrderedTaxonomy 139 by oiBy 140 } 141 142 // Closure used in the Sort.Less method. 143 type oiBy func(i1, i2 *OrderedTaxonomyEntry) bool 144 145 func (by oiBy) Sort(taxonomy OrderedTaxonomy) { 146 ps := &orderedTaxonomySorter{ 147 taxonomy: taxonomy, 148 by: by, // The Sort method's receiver is the function (closure) that defines the sort order. 149 } 150 sort.Stable(ps) 151 } 152 153 // Len is part of sort.Interface. 154 func (s *orderedTaxonomySorter) Len() int { 155 return len(s.taxonomy) 156 } 157 158 // Swap is part of sort.Interface. 159 func (s *orderedTaxonomySorter) Swap(i, j int) { 160 s.taxonomy[i], s.taxonomy[j] = s.taxonomy[j], s.taxonomy[i] 161 } 162 163 // Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. 164 func (s *orderedTaxonomySorter) Less(i, j int) bool { 165 return s.by(&s.taxonomy[i], &s.taxonomy[j]) 166 } 167 168 // Pages returns the Pages in this weighted page set. 169 func (wp WeightedPages) Pages() Pages { 170 pages := make(Pages, len(wp)) 171 for i := range wp { 172 pages[i] = wp[i].Page 173 } 174 return pages 175 } 176 177 // Prev returns the previous Page relative to the given Page in 178 // this weighted page set. 179 func (wp WeightedPages) Prev(cur *Page) *Page { 180 for x, c := range wp { 181 if c.Page.UniqueID() == cur.UniqueID() { 182 if x == 0 { 183 return wp[len(wp)-1].Page 184 } 185 return wp[x-1].Page 186 } 187 } 188 return nil 189 } 190 191 // Next returns the next Page relative to the given Page in 192 // this weighted page set. 193 func (wp WeightedPages) Next(cur *Page) *Page { 194 for x, c := range wp { 195 if c.Page.UniqueID() == cur.UniqueID() { 196 if x < len(wp)-1 { 197 return wp[x+1].Page 198 } 199 return wp[0].Page 200 } 201 } 202 return nil 203 } 204 205 func (wp WeightedPages) Len() int { return len(wp) } 206 func (wp WeightedPages) Swap(i, j int) { wp[i], wp[j] = wp[j], wp[i] } 207 208 // Sort stable sorts this weighted page set. 209 func (wp WeightedPages) Sort() { sort.Stable(wp) } 210 211 // Count returns the number of pages in this weighted page set. 212 func (wp WeightedPages) Count() int { return len(wp) } 213 214 func (wp WeightedPages) Less(i, j int) bool { 215 if wp[i].Weight == wp[j].Weight { 216 if wp[i].Page.Date.Equal(wp[j].Page.Date) { 217 return wp[i].Page.title < wp[j].Page.title 218 } 219 return wp[i].Page.Date.After(wp[i].Page.Date) 220 } 221 return wp[i].Weight < wp[j].Weight 222 } 223 224 // TODO mimic PagesSorter for WeightedPages