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

     1  package objx
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"errors"
     7  	"io/ioutil"
     8  	"net/url"
     9  	"strings"
    10  )
    11  
    12  // MSIConvertable is an interface that defines methods for converting your
    13  // custom types to a map[string]interface{} representation.
    14  type MSIConvertable interface {
    15  	// MSI gets a map[string]interface{} (msi) representing the
    16  	// object.
    17  	MSI() map[string]interface{}
    18  }
    19  
    20  // Map provides extended functionality for working with
    21  // untyped data, in particular map[string]interface (msi).
    22  type Map map[string]interface{}
    23  
    24  // Value returns the internal value instance
    25  func (m Map) Value() *Value {
    26  	return &Value{data: m}
    27  }
    28  
    29  // Nil represents a nil Map.
    30  var Nil = New(nil)
    31  
    32  // New creates a new Map containing the map[string]interface{} in the data argument.
    33  // If the data argument is not a map[string]interface, New attempts to call the
    34  // MSI() method on the MSIConvertable interface to create one.
    35  func New(data interface{}) Map {
    36  	if _, ok := data.(map[string]interface{}); !ok {
    37  		if converter, ok := data.(MSIConvertable); ok {
    38  			data = converter.MSI()
    39  		} else {
    40  			return nil
    41  		}
    42  	}
    43  	return Map(data.(map[string]interface{}))
    44  }
    45  
    46  // MSI creates a map[string]interface{} and puts it inside a new Map.
    47  //
    48  // The arguments follow a key, value pattern.
    49  //
    50  //
    51  // Returns nil if any key argument is non-string or if there are an odd number of arguments.
    52  //
    53  // Example
    54  //
    55  // To easily create Maps:
    56  //
    57  //     m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
    58  //
    59  //     // creates an Map equivalent to
    60  //     m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}}
    61  func MSI(keyAndValuePairs ...interface{}) Map {
    62  	newMap := Map{}
    63  	keyAndValuePairsLen := len(keyAndValuePairs)
    64  	if keyAndValuePairsLen%2 != 0 {
    65  		return nil
    66  	}
    67  	for i := 0; i < keyAndValuePairsLen; i = i + 2 {
    68  		key := keyAndValuePairs[i]
    69  		value := keyAndValuePairs[i+1]
    70  
    71  		// make sure the key is a string
    72  		keyString, keyStringOK := key.(string)
    73  		if !keyStringOK {
    74  			return nil
    75  		}
    76  		newMap[keyString] = value
    77  	}
    78  	return newMap
    79  }
    80  
    81  // ****** Conversion Constructors
    82  
    83  // MustFromJSON creates a new Map containing the data specified in the
    84  // jsonString.
    85  //
    86  // Panics if the JSON is invalid.
    87  func MustFromJSON(jsonString string) Map {
    88  	o, err := FromJSON(jsonString)
    89  	if err != nil {
    90  		panic("objx: MustFromJSON failed with error: " + err.Error())
    91  	}
    92  	return o
    93  }
    94  
    95  // FromJSON creates a new Map containing the data specified in the
    96  // jsonString.
    97  //
    98  // Returns an error if the JSON is invalid.
    99  func FromJSON(jsonString string) (Map, error) {
   100  	var m Map
   101  	err := json.Unmarshal([]byte(jsonString), &m)
   102  	if err != nil {
   103  		return Nil, err
   104  	}
   105  	m.tryConvertFloat64()
   106  	return m, nil
   107  }
   108  
   109  func (m Map) tryConvertFloat64() {
   110  	for k, v := range m {
   111  		switch v.(type) {
   112  		case float64:
   113  			f := v.(float64)
   114  			if float64(int(f)) == f {
   115  				m[k] = int(f)
   116  			}
   117  		case map[string]interface{}:
   118  			t := New(v)
   119  			t.tryConvertFloat64()
   120  			m[k] = t
   121  		case []interface{}:
   122  			m[k] = tryConvertFloat64InSlice(v.([]interface{}))
   123  		}
   124  	}
   125  }
   126  
   127  func tryConvertFloat64InSlice(s []interface{}) []interface{} {
   128  	for k, v := range s {
   129  		switch v.(type) {
   130  		case float64:
   131  			f := v.(float64)
   132  			if float64(int(f)) == f {
   133  				s[k] = int(f)
   134  			}
   135  		case map[string]interface{}:
   136  			t := New(v)
   137  			t.tryConvertFloat64()
   138  			s[k] = t
   139  		case []interface{}:
   140  			s[k] = tryConvertFloat64InSlice(v.([]interface{}))
   141  		}
   142  	}
   143  	return s
   144  }
   145  
   146  // FromBase64 creates a new Obj containing the data specified
   147  // in the Base64 string.
   148  //
   149  // The string is an encoded JSON string returned by Base64
   150  func FromBase64(base64String string) (Map, error) {
   151  	decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String))
   152  	decoded, err := ioutil.ReadAll(decoder)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	return FromJSON(string(decoded))
   157  }
   158  
   159  // MustFromBase64 creates a new Obj containing the data specified
   160  // in the Base64 string and panics if there is an error.
   161  //
   162  // The string is an encoded JSON string returned by Base64
   163  func MustFromBase64(base64String string) Map {
   164  	result, err := FromBase64(base64String)
   165  	if err != nil {
   166  		panic("objx: MustFromBase64 failed with error: " + err.Error())
   167  	}
   168  	return result
   169  }
   170  
   171  // FromSignedBase64 creates a new Obj containing the data specified
   172  // in the Base64 string.
   173  //
   174  // The string is an encoded JSON string returned by SignedBase64
   175  func FromSignedBase64(base64String, key string) (Map, error) {
   176  	parts := strings.Split(base64String, SignatureSeparator)
   177  	if len(parts) != 2 {
   178  		return nil, errors.New("objx: Signed base64 string is malformed")
   179  	}
   180  
   181  	sig := HashWithKey(parts[0], key)
   182  	if parts[1] != sig {
   183  		return nil, errors.New("objx: Signature for base64 data does not match")
   184  	}
   185  	return FromBase64(parts[0])
   186  }
   187  
   188  // MustFromSignedBase64 creates a new Obj containing the data specified
   189  // in the Base64 string and panics if there is an error.
   190  //
   191  // The string is an encoded JSON string returned by Base64
   192  func MustFromSignedBase64(base64String, key string) Map {
   193  	result, err := FromSignedBase64(base64String, key)
   194  	if err != nil {
   195  		panic("objx: MustFromSignedBase64 failed with error: " + err.Error())
   196  	}
   197  	return result
   198  }
   199  
   200  // FromURLQuery generates a new Obj by parsing the specified
   201  // query.
   202  //
   203  // For queries with multiple values, the first value is selected.
   204  func FromURLQuery(query string) (Map, error) {
   205  	vals, err := url.ParseQuery(query)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	m := Map{}
   210  	for k, vals := range vals {
   211  		m[k] = vals[0]
   212  	}
   213  	return m, nil
   214  }
   215  
   216  // MustFromURLQuery generates a new Obj by parsing the specified
   217  // query.
   218  //
   219  // For queries with multiple values, the first value is selected.
   220  //
   221  // Panics if it encounters an error
   222  func MustFromURLQuery(query string) Map {
   223  	o, err := FromURLQuery(query)
   224  	if err != nil {
   225  		panic("objx: MustFromURLQuery failed with error: " + err.Error())
   226  	}
   227  	return o
   228  }