generated from sraphs/go-starter
/
flag.go
119 lines (92 loc) · 2.27 KB
/
flag.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package flag
import (
"bytes"
"fmt"
"reflect"
"sort"
"strings"
"github.com/mitchellh/mapstructure"
"github.com/tidwall/gjson"
"google.golang.org/protobuf/proto"
"github.com/sraphs/flat"
"github.com/sraphs/encoding/json"
)
// Name is the name registered for the flag codec.
const Name = "flag"
// Codec is a Codec implementation with flag.
type Codec struct{}
func (Codec) Marshal(v interface{}) ([]byte, error) {
b, err := json.Codec{}.Marshal(v)
if err != nil {
return nil, err
}
m := gjson.ParseBytes(b).Value().(map[string]interface{})
fo := flat.Option{
Separator: ".",
}
f := fo.Flatten(m)
// sort the keys
keys := make([]string, 0, len(f))
for key := range f {
keys = append(keys, key)
}
sort.Strings(keys)
var buf bytes.Buffer
for i, k := range keys {
if i == len(keys)-1 {
fmt.Fprintf(&buf, "--%s=%v", k, f[k])
} else {
fmt.Fprintf(&buf, "--%s=%v ", k, f[k])
}
}
return buf.Bytes(), nil
}
func (Codec) Unmarshal(data []byte, v interface{}) error {
s := string(data)
args := strings.Split(s, " ")
m, err := Parse(args, v)
if err != nil {
return err
}
if pm, ok := v.(proto.Message); ok {
return DecodeValues(pm, m)
} else if pm, ok := reflect.Indirect(reflect.ValueOf(v)).Interface().(proto.Message); ok {
return DecodeValues(pm, m)
}
fo := flat.Option{
Separator: ".",
}
mi := mapStringToInterface(m)
uf := fo.Unflatten(mi)
decoder, err := mapstructure.NewDecoder(defaultDecoderConfig(v))
if err != nil {
return err
}
err = decoder.Decode(uf)
return err
}
func (Codec) Name() string {
return Name
}
// mapStringToInterface converts a map[string]string to a map[string]interface{}
func mapStringToInterface(m map[string]string) map[string]interface{} {
out := make(map[string]interface{})
for k, v := range m {
out[k] = v
}
return out
}
// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot
// of time.Duration values & string slices
func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig {
c := &mapstructure.DecoderConfig{
Metadata: nil,
Result: output,
WeaklyTypedInput: true,
DecodeHook: mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
),
}
return c
}