1package parseutil
2
3import (
4	"encoding/json"
5	"errors"
6	"fmt"
7	"strconv"
8	"strings"
9	"time"
10
11	"github.com/hashicorp/errwrap"
12	sockaddr "github.com/hashicorp/go-sockaddr"
13	"github.com/hashicorp/vault/sdk/helper/strutil"
14	"github.com/mitchellh/mapstructure"
15)
16
17func ParseDurationSecond(in interface{}) (time.Duration, error) {
18	var dur time.Duration
19	jsonIn, ok := in.(json.Number)
20	if ok {
21		in = jsonIn.String()
22	}
23	switch inp := in.(type) {
24	case nil:
25		// return default of zero
26	case string:
27		if inp == "" {
28			return dur, nil
29		}
30		var err error
31		// Look for a suffix otherwise its a plain second value
32		if strings.HasSuffix(inp, "s") || strings.HasSuffix(inp, "m") || strings.HasSuffix(inp, "h") || strings.HasSuffix(inp, "ms") {
33			dur, err = time.ParseDuration(inp)
34			if err != nil {
35				return dur, err
36			}
37		} else {
38			// Plain integer
39			secs, err := strconv.ParseInt(inp, 10, 64)
40			if err != nil {
41				return dur, err
42			}
43			dur = time.Duration(secs) * time.Second
44		}
45	case int:
46		dur = time.Duration(inp) * time.Second
47	case int32:
48		dur = time.Duration(inp) * time.Second
49	case int64:
50		dur = time.Duration(inp) * time.Second
51	case uint:
52		dur = time.Duration(inp) * time.Second
53	case uint32:
54		dur = time.Duration(inp) * time.Second
55	case uint64:
56		dur = time.Duration(inp) * time.Second
57	case float32:
58		dur = time.Duration(inp) * time.Second
59	case float64:
60		dur = time.Duration(inp) * time.Second
61	case time.Duration:
62		dur = inp
63	default:
64		return 0, errors.New("could not parse duration from input")
65	}
66
67	return dur, nil
68}
69
70func ParseInt(in interface{}) (int64, error) {
71	var ret int64
72	jsonIn, ok := in.(json.Number)
73	if ok {
74		in = jsonIn.String()
75	}
76	switch in.(type) {
77	case string:
78		inp := in.(string)
79		if inp == "" {
80			return 0, nil
81		}
82		var err error
83		left, err := strconv.ParseInt(inp, 10, 64)
84		if err != nil {
85			return ret, err
86		}
87		ret = left
88	case int:
89		ret = int64(in.(int))
90	case int32:
91		ret = int64(in.(int32))
92	case int64:
93		ret = in.(int64)
94	case uint:
95		ret = int64(in.(uint))
96	case uint32:
97		ret = int64(in.(uint32))
98	case uint64:
99		ret = int64(in.(uint64))
100	default:
101		return 0, errors.New("could not parse value from input")
102	}
103
104	return ret, nil
105}
106
107func ParseBool(in interface{}) (bool, error) {
108	var result bool
109	if err := mapstructure.WeakDecode(in, &result); err != nil {
110		return false, err
111	}
112	return result, nil
113}
114
115func ParseCommaStringSlice(in interface{}) ([]string, error) {
116	rawString, ok := in.(string)
117	if ok && rawString == "" {
118		return []string{}, nil
119	}
120	var result []string
121	config := &mapstructure.DecoderConfig{
122		Result:           &result,
123		WeaklyTypedInput: true,
124		DecodeHook:       mapstructure.StringToSliceHookFunc(","),
125	}
126	decoder, err := mapstructure.NewDecoder(config)
127	if err != nil {
128		return nil, err
129	}
130	if err := decoder.Decode(in); err != nil {
131		return nil, err
132	}
133	return strutil.TrimStrings(result), nil
134}
135
136func ParseAddrs(addrs interface{}) ([]*sockaddr.SockAddrMarshaler, error) {
137	out := make([]*sockaddr.SockAddrMarshaler, 0)
138	stringAddrs := make([]string, 0)
139
140	switch addrs.(type) {
141	case string:
142		stringAddrs = strutil.ParseArbitraryStringSlice(addrs.(string), ",")
143		if len(stringAddrs) == 0 {
144			return nil, fmt.Errorf("unable to parse addresses from %v", addrs)
145		}
146
147	case []string:
148		stringAddrs = addrs.([]string)
149
150	case []interface{}:
151		for _, v := range addrs.([]interface{}) {
152			stringAddr, ok := v.(string)
153			if !ok {
154				return nil, fmt.Errorf("error parsing %v as string", v)
155			}
156			stringAddrs = append(stringAddrs, stringAddr)
157		}
158
159	default:
160		return nil, fmt.Errorf("unknown address input type %T", addrs)
161	}
162
163	for _, addr := range stringAddrs {
164		sa, err := sockaddr.NewSockAddr(addr)
165		if err != nil {
166			return nil, errwrap.Wrapf(fmt.Sprintf("error parsing address %q: {{err}}", addr), err)
167		}
168		out = append(out, &sockaddr.SockAddrMarshaler{
169			SockAddr: sa,
170		})
171	}
172
173	return out, nil
174}
175