1package altsrc 2 3import ( 4 "fmt" 5 "reflect" 6 "strings" 7 "time" 8 9 "github.com/urfave/cli" 10) 11 12// MapInputSource implements InputSourceContext to return 13// data from the map that is loaded. 14type MapInputSource struct { 15 valueMap map[interface{}]interface{} 16} 17 18// nestedVal checks if the name has '.' delimiters. 19// If so, it tries to traverse the tree by the '.' delimited sections to find 20// a nested value for the key. 21func nestedVal(name string, tree map[interface{}]interface{}) (interface{}, bool) { 22 if sections := strings.Split(name, "."); len(sections) > 1 { 23 node := tree 24 for _, section := range sections[:len(sections)-1] { 25 child, ok := node[section] 26 if !ok { 27 return nil, false 28 } 29 ctype, ok := child.(map[interface{}]interface{}) 30 if !ok { 31 return nil, false 32 } 33 node = ctype 34 } 35 if val, ok := node[sections[len(sections)-1]]; ok { 36 return val, true 37 } 38 } 39 return nil, false 40} 41 42// Int returns an int from the map if it exists otherwise returns 0 43func (fsm *MapInputSource) Int(name string) (int, error) { 44 otherGenericValue, exists := fsm.valueMap[name] 45 if exists { 46 otherValue, isType := otherGenericValue.(int) 47 if !isType { 48 return 0, incorrectTypeForFlagError(name, "int", otherGenericValue) 49 } 50 return otherValue, nil 51 } 52 nestedGenericValue, exists := nestedVal(name, fsm.valueMap) 53 if exists { 54 otherValue, isType := nestedGenericValue.(int) 55 if !isType { 56 return 0, incorrectTypeForFlagError(name, "int", nestedGenericValue) 57 } 58 return otherValue, nil 59 } 60 61 return 0, nil 62} 63 64// Duration returns a duration from the map if it exists otherwise returns 0 65func (fsm *MapInputSource) Duration(name string) (time.Duration, error) { 66 otherGenericValue, exists := fsm.valueMap[name] 67 if exists { 68 otherValue, isType := otherGenericValue.(time.Duration) 69 if !isType { 70 return 0, incorrectTypeForFlagError(name, "duration", otherGenericValue) 71 } 72 return otherValue, nil 73 } 74 nestedGenericValue, exists := nestedVal(name, fsm.valueMap) 75 if exists { 76 otherValue, isType := nestedGenericValue.(time.Duration) 77 if !isType { 78 return 0, incorrectTypeForFlagError(name, "duration", nestedGenericValue) 79 } 80 return otherValue, nil 81 } 82 83 return 0, nil 84} 85 86// Float64 returns an float64 from the map if it exists otherwise returns 0 87func (fsm *MapInputSource) Float64(name string) (float64, error) { 88 otherGenericValue, exists := fsm.valueMap[name] 89 if exists { 90 otherValue, isType := otherGenericValue.(float64) 91 if !isType { 92 return 0, incorrectTypeForFlagError(name, "float64", otherGenericValue) 93 } 94 return otherValue, nil 95 } 96 nestedGenericValue, exists := nestedVal(name, fsm.valueMap) 97 if exists { 98 otherValue, isType := nestedGenericValue.(float64) 99 if !isType { 100 return 0, incorrectTypeForFlagError(name, "float64", nestedGenericValue) 101 } 102 return otherValue, nil 103 } 104 105 return 0, nil 106} 107 108// String returns a string from the map if it exists otherwise returns an empty string 109func (fsm *MapInputSource) String(name string) (string, error) { 110 otherGenericValue, exists := fsm.valueMap[name] 111 if exists { 112 otherValue, isType := otherGenericValue.(string) 113 if !isType { 114 return "", incorrectTypeForFlagError(name, "string", otherGenericValue) 115 } 116 return otherValue, nil 117 } 118 nestedGenericValue, exists := nestedVal(name, fsm.valueMap) 119 if exists { 120 otherValue, isType := nestedGenericValue.(string) 121 if !isType { 122 return "", incorrectTypeForFlagError(name, "string", nestedGenericValue) 123 } 124 return otherValue, nil 125 } 126 127 return "", nil 128} 129 130// StringSlice returns an []string from the map if it exists otherwise returns nil 131func (fsm *MapInputSource) StringSlice(name string) ([]string, error) { 132 otherGenericValue, exists := fsm.valueMap[name] 133 if !exists { 134 otherGenericValue, exists = nestedVal(name, fsm.valueMap) 135 if !exists { 136 return nil, nil 137 } 138 } 139 140 otherValue, isType := otherGenericValue.([]interface{}) 141 if !isType { 142 return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue) 143 } 144 145 var stringSlice = make([]string, 0, len(otherValue)) 146 for i, v := range otherValue { 147 stringValue, isType := v.(string) 148 149 if !isType { 150 return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "string", v) 151 } 152 153 stringSlice = append(stringSlice, stringValue) 154 } 155 156 return stringSlice, nil 157} 158 159// IntSlice returns an []int from the map if it exists otherwise returns nil 160func (fsm *MapInputSource) IntSlice(name string) ([]int, error) { 161 otherGenericValue, exists := fsm.valueMap[name] 162 if !exists { 163 otherGenericValue, exists = nestedVal(name, fsm.valueMap) 164 if !exists { 165 return nil, nil 166 } 167 } 168 169 otherValue, isType := otherGenericValue.([]interface{}) 170 if !isType { 171 return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue) 172 } 173 174 var intSlice = make([]int, 0, len(otherValue)) 175 for i, v := range otherValue { 176 intValue, isType := v.(int) 177 178 if !isType { 179 return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "int", v) 180 } 181 182 intSlice = append(intSlice, intValue) 183 } 184 185 return intSlice, nil 186} 187 188// Generic returns an cli.Generic from the map if it exists otherwise returns nil 189func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) { 190 otherGenericValue, exists := fsm.valueMap[name] 191 if exists { 192 otherValue, isType := otherGenericValue.(cli.Generic) 193 if !isType { 194 return nil, incorrectTypeForFlagError(name, "cli.Generic", otherGenericValue) 195 } 196 return otherValue, nil 197 } 198 nestedGenericValue, exists := nestedVal(name, fsm.valueMap) 199 if exists { 200 otherValue, isType := nestedGenericValue.(cli.Generic) 201 if !isType { 202 return nil, incorrectTypeForFlagError(name, "cli.Generic", nestedGenericValue) 203 } 204 return otherValue, nil 205 } 206 207 return nil, nil 208} 209 210// Bool returns an bool from the map otherwise returns false 211func (fsm *MapInputSource) Bool(name string) (bool, error) { 212 otherGenericValue, exists := fsm.valueMap[name] 213 if exists { 214 otherValue, isType := otherGenericValue.(bool) 215 if !isType { 216 return false, incorrectTypeForFlagError(name, "bool", otherGenericValue) 217 } 218 return otherValue, nil 219 } 220 nestedGenericValue, exists := nestedVal(name, fsm.valueMap) 221 if exists { 222 otherValue, isType := nestedGenericValue.(bool) 223 if !isType { 224 return false, incorrectTypeForFlagError(name, "bool", nestedGenericValue) 225 } 226 return otherValue, nil 227 } 228 229 return false, nil 230} 231 232// BoolT returns an bool from the map otherwise returns true 233func (fsm *MapInputSource) BoolT(name string) (bool, error) { 234 otherGenericValue, exists := fsm.valueMap[name] 235 if exists { 236 otherValue, isType := otherGenericValue.(bool) 237 if !isType { 238 return true, incorrectTypeForFlagError(name, "bool", otherGenericValue) 239 } 240 return otherValue, nil 241 } 242 nestedGenericValue, exists := nestedVal(name, fsm.valueMap) 243 if exists { 244 otherValue, isType := nestedGenericValue.(bool) 245 if !isType { 246 return true, incorrectTypeForFlagError(name, "bool", nestedGenericValue) 247 } 248 return otherValue, nil 249 } 250 251 return true, nil 252} 253 254func incorrectTypeForFlagError(name, expectedTypeName string, value interface{}) error { 255 valueType := reflect.TypeOf(value) 256 valueTypeName := "" 257 if valueType != nil { 258 valueTypeName = valueType.Name() 259 } 260 261 return fmt.Errorf("Mismatched type for flag '%s'. Expected '%s' but actual is '%s'", name, expectedTypeName, valueTypeName) 262} 263