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