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