forked from peterbourgon/ff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjson.go
92 lines (83 loc) · 2.2 KB
/
json.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package ff
import (
"encoding/json"
"fmt"
"io"
"strconv"
)
// JSONParser is a parser for config files in JSON format. Input should be
// an object. The object's keys are treated as flag names, and the object's
// values as flag values. If the value is an array, the flag will be set
// multiple times.
func JSONParser(r io.Reader, set func(name, value string) error) error {
var m map[string]interface{}
d := json.NewDecoder(r)
d.UseNumber() // must set UseNumber for stringifyValue to work
if err := d.Decode(&m); err != nil {
return JSONParseError{Inner: err}
}
for key, val := range m {
values, err := stringifySlice(val)
if err != nil {
return JSONParseError{Inner: err}
}
for _, value := range values {
if err := set(key, value); err != nil {
return err
}
}
}
return nil
}
func stringifySlice(val interface{}) ([]string, error) {
if vals, ok := val.([]interface{}); ok {
ss := make([]string, len(vals))
for i := range vals {
s, err := stringifyValue(vals[i])
if err != nil {
return nil, err
}
ss[i] = s
}
return ss, nil
}
s, err := stringifyValue(val)
if err != nil {
return nil, err
}
return []string{s}, nil
}
func stringifyValue(val interface{}) (string, error) {
switch v := val.(type) {
case string:
return v, nil
case json.Number:
return v.String(), nil
case bool:
return strconv.FormatBool(v), nil
default:
return "", StringConversionError{Value: val}
}
}
// JSONParseError wraps all errors originating from the JSONParser.
type JSONParseError struct {
Inner error
}
// Error implenents the error interface.
func (e JSONParseError) Error() string {
return fmt.Sprintf("error parsing JSON config: %v", e.Inner)
}
// Unwrap implements the errors.Wrapper interface, allowing errors.Is and
// errors.As to work with JSONParseErrors.
func (e JSONParseError) Unwrap() error {
return e.Inner
}
// StringConversionError is returned when a value in a config file
// can't be converted to a string, to be provided to a flag.
type StringConversionError struct {
Value interface{}
}
// Error implements the error interface.
func (e StringConversionError) Error() string {
return fmt.Sprintf("couldn't convert %q (type %T) to string", e.Value, e.Value)
}