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 }