gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/stretchr/objx/accessors.go (about)

     1  package objx
     2  
     3  import (
     4  	"regexp"
     5  	"strconv"
     6  	"strings"
     7  )
     8  
     9  const (
    10  	// PathSeparator is the character used to separate the elements
    11  	// of the keypath.
    12  	//
    13  	// For example, `location.address.city`
    14  	PathSeparator string = "."
    15  
    16  	// arrayAccesRegexString is the regex used to extract the array number
    17  	// from the access path
    18  	arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
    19  )
    20  
    21  // arrayAccesRegex is the compiled arrayAccesRegexString
    22  var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
    23  
    24  // Get gets the value using the specified selector and
    25  // returns it inside a new Obj object.
    26  //
    27  // If it cannot find the value, Get will return a nil
    28  // value inside an instance of Obj.
    29  //
    30  // Get can only operate directly on map[string]interface{} and []interface.
    31  //
    32  // Example
    33  //
    34  // To access the title of the third chapter of the second book, do:
    35  //
    36  //    o.Get("books[1].chapters[2].title")
    37  func (m Map) Get(selector string) *Value {
    38  	rawObj := access(m, selector, nil, false)
    39  	return &Value{data: rawObj}
    40  }
    41  
    42  // Set sets the value using the specified selector and
    43  // returns the object on which Set was called.
    44  //
    45  // Set can only operate directly on map[string]interface{} and []interface
    46  //
    47  // Example
    48  //
    49  // To set the title of the third chapter of the second book, do:
    50  //
    51  //    o.Set("books[1].chapters[2].title","Time to Go")
    52  func (m Map) Set(selector string, value interface{}) Map {
    53  	access(m, selector, value, true)
    54  	return m
    55  }
    56  
    57  // getIndex returns the index, which is hold in s by two braches.
    58  // It also returns s withour the index part, e.g. name[1] will return (1, name).
    59  // If no index is found, -1 is returned
    60  func getIndex(s string) (int, string) {
    61  	arrayMatches := arrayAccesRegex.FindStringSubmatch(s)
    62  	if len(arrayMatches) > 0 {
    63  		// Get the key into the map
    64  		selector := arrayMatches[1]
    65  		// Get the index into the array at the key
    66  		// We know this cannt fail because arrayMatches[2] is an int for sure
    67  		index, _ := strconv.Atoi(arrayMatches[2])
    68  		return index, selector
    69  	}
    70  	return -1, s
    71  }
    72  
    73  // access accesses the object using the selector and performs the
    74  // appropriate action.
    75  func access(current interface{}, selector string, value interface{}, isSet bool) interface{} {
    76  	selSegs := strings.SplitN(selector, PathSeparator, 2)
    77  	thisSel := selSegs[0]
    78  	index := -1
    79  
    80  	if strings.Contains(thisSel, "[") {
    81  		index, thisSel = getIndex(thisSel)
    82  	}
    83  
    84  	if curMap, ok := current.(Map); ok {
    85  		current = map[string]interface{}(curMap)
    86  	}
    87  	// get the object in question
    88  	switch current.(type) {
    89  	case map[string]interface{}:
    90  		curMSI := current.(map[string]interface{})
    91  		if len(selSegs) <= 1 && isSet {
    92  			curMSI[thisSel] = value
    93  			return nil
    94  		}
    95  
    96  		_, ok := curMSI[thisSel].(map[string]interface{})
    97  		if (curMSI[thisSel] == nil || !ok) && index == -1 && isSet {
    98  			curMSI[thisSel] = map[string]interface{}{}
    99  		}
   100  
   101  		current = curMSI[thisSel]
   102  	default:
   103  		current = nil
   104  	}
   105  	// do we need to access the item of an array?
   106  	if index > -1 {
   107  		if array, ok := current.([]interface{}); ok {
   108  			if index < len(array) {
   109  				current = array[index]
   110  			} else {
   111  				current = nil
   112  			}
   113  		}
   114  	}
   115  	if len(selSegs) > 1 {
   116  		current = access(current, selSegs[1], value, isSet)
   117  	}
   118  	return current
   119  }