github.com/olliephillips/hugo@v0.42.2/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  }