github.com/shohhei1126/hugo@v0.42.2-0.20180623210752-3d5928889ad7/hugolib/scratch.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 "reflect" 18 "sort" 19 "sync" 20 21 "github.com/gohugoio/hugo/tpl/math" 22 ) 23 24 // Scratch is a writable context used for stateful operations in Page/Node rendering. 25 type Scratch struct { 26 values map[string]interface{} 27 mu sync.RWMutex 28 } 29 30 // Add will, for single values, add (using the + operator) the addend to the existing addend (if found). 31 // Supports numeric values and strings. 32 // 33 // If the first add for a key is an array or slice, then the next value(s) will be appended. 34 func (c *Scratch) Add(key string, newAddend interface{}) (string, error) { 35 36 var newVal interface{} 37 c.mu.RLock() 38 existingAddend, found := c.values[key] 39 c.mu.RUnlock() 40 if found { 41 var err error 42 43 addendV := reflect.ValueOf(existingAddend) 44 45 if addendV.Kind() == reflect.Slice || addendV.Kind() == reflect.Array { 46 nav := reflect.ValueOf(newAddend) 47 if nav.Kind() == reflect.Slice || nav.Kind() == reflect.Array { 48 newVal = reflect.AppendSlice(addendV, nav).Interface() 49 } else { 50 newVal = reflect.Append(addendV, nav).Interface() 51 } 52 } else { 53 newVal, err = math.DoArithmetic(existingAddend, newAddend, '+') 54 if err != nil { 55 return "", err 56 } 57 } 58 } else { 59 newVal = newAddend 60 } 61 c.mu.Lock() 62 c.values[key] = newVal 63 c.mu.Unlock() 64 return "", nil // have to return something to make it work with the Go templates 65 } 66 67 // Set stores a value with the given key in the Node context. 68 // This value can later be retrieved with Get. 69 func (c *Scratch) Set(key string, value interface{}) string { 70 c.mu.Lock() 71 c.values[key] = value 72 c.mu.Unlock() 73 return "" 74 } 75 76 // Reset deletes the given key 77 func (c *Scratch) Delete(key string) string { 78 c.mu.Lock() 79 delete(c.values, key) 80 c.mu.Unlock() 81 return "" 82 } 83 84 // Get returns a value previously set by Add or Set 85 func (c *Scratch) Get(key string) interface{} { 86 c.mu.RLock() 87 val := c.values[key] 88 c.mu.RUnlock() 89 90 return val 91 } 92 93 // SetInMap stores a value to a map with the given key in the Node context. 94 // This map can later be retrieved with GetSortedMapValues. 95 func (c *Scratch) SetInMap(key string, mapKey string, value interface{}) string { 96 c.mu.Lock() 97 _, found := c.values[key] 98 if !found { 99 c.values[key] = make(map[string]interface{}) 100 } 101 102 c.values[key].(map[string]interface{})[mapKey] = value 103 c.mu.Unlock() 104 return "" 105 } 106 107 // GetSortedMapValues returns a sorted map previously filled with SetInMap 108 func (c *Scratch) GetSortedMapValues(key string) interface{} { 109 c.mu.RLock() 110 111 if c.values[key] == nil { 112 c.mu.RUnlock() 113 return nil 114 } 115 116 unsortedMap := c.values[key].(map[string]interface{}) 117 c.mu.RUnlock() 118 var keys []string 119 for mapKey := range unsortedMap { 120 keys = append(keys, mapKey) 121 } 122 123 sort.Strings(keys) 124 125 sortedArray := make([]interface{}, len(unsortedMap)) 126 for i, mapKey := range keys { 127 sortedArray[i] = unsortedMap[mapKey] 128 } 129 130 return sortedArray 131 } 132 133 func newScratch() *Scratch { 134 return &Scratch{values: make(map[string]interface{})} 135 }