1/*
2 *    Copyright (C) 2017 Christian Muehlhaeuser
3 *
4 *    This program is free software: you can redistribute it and/or modify
5 *    it under the terms of the GNU Affero General Public License as published
6 *    by the Free Software Foundation, either version 3 of the License, or
7 *    (at your option) any later version.
8 *
9 *    This program is distributed in the hope that it will be useful,
10 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 *    GNU Affero General Public License for more details.
13 *
14 *    You should have received a copy of the GNU Affero General Public License
15 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 *
17 *    Authors:
18 *      Christian Muehlhaeuser <muesli@gmail.com>
19 */
20
21// Package bees is Beehive's central module system.
22package bees
23
24import (
25	"errors"
26	"fmt"
27	"net/url"
28	"reflect"
29	"strconv"
30	"strings"
31	"time"
32)
33
34// Placeholders is an array of Placeholder.
35type Placeholders []Placeholder
36
37// Placeholder used by ins & outs of a bee.
38type Placeholder struct {
39	Name  string
40	Type  string
41	Value interface{}
42}
43
44// SetValue sets a value in the Placeholder slice.
45func (ph *Placeholders) SetValue(name string, _type string, value interface{}) {
46	if ph.Value(name) == nil {
47		p := Placeholder{
48			Name:  name,
49			Type:  _type,
50			Value: value,
51		}
52		*ph = append(*ph, p)
53	} else {
54		for i := 0; i < len(*ph); i++ {
55			if (*ph)[i].Name == name {
56				(*ph)[i].Type = _type
57				(*ph)[i].Value = value
58			}
59		}
60	}
61}
62
63// Value retrieves a value from a Placeholder slice.
64func (ph Placeholders) Value(name string) interface{} {
65	for _, p := range ph {
66		if p.Name == name {
67			return p.Value
68		}
69	}
70
71	return nil
72}
73
74// Bind a value from a Placeholder slice.
75func (ph Placeholders) Bind(name string, dst interface{}) error {
76	v := ph.Value(name)
77	if v == nil {
78		return errors.New("Placeholder with name " + name + " not found")
79	}
80
81	return ConvertValue(v, dst)
82}
83
84// ConvertValue tries to convert v to dst.
85func ConvertValue(v interface{}, dst interface{}) error {
86	switch d := dst.(type) {
87	case *string:
88		switch vt := v.(type) {
89		case string:
90			*d = vt
91		case []string:
92			*d = strings.Join(vt, ",")
93		case bool:
94			*d = strconv.FormatBool(vt)
95		case int64:
96			*d = strconv.FormatInt(vt, 10)
97		case float64:
98			*d = strconv.FormatFloat(vt, 'f', -1, 64)
99		case int:
100			*d = strconv.FormatInt(int64(vt), 10)
101		default:
102			panic(fmt.Sprintf("Unhandled type %+v for string conversion", reflect.TypeOf(vt)))
103		}
104
105	case *[]string:
106		switch vt := v.(type) {
107		case []interface{}:
108			*d = []string{}
109			for _, v := range vt {
110				*d = append(*d, v.(string))
111			}
112		case []string:
113			*d = vt
114		case string:
115			*d = strings.Split(vt, ",")
116		default:
117			panic(fmt.Sprintf("Unhandled type %+v for []string conversion", reflect.TypeOf(vt)))
118		}
119
120	case *bool:
121		switch vt := v.(type) {
122		case bool:
123			*d = vt
124		case string:
125			vt = strings.ToLower(vt)
126			if vt == "true" || vt == "on" || vt == "yes" || vt == "1" || vt == "t" {
127				*d = true
128			} else {
129				*d = false
130			}
131		case int64:
132			*d = vt > 0
133		case int:
134			*d = vt > 0
135		case uint64:
136			*d = vt > 0
137		case uint:
138			*d = vt > 0
139		case float64:
140			*d = vt > 0
141		default:
142			panic(fmt.Sprintf("Unhandled type %+v for bool conversion", reflect.TypeOf(vt)))
143		}
144
145	case *float64:
146		switch vt := v.(type) {
147		case int64:
148			*d = float64(vt)
149		case int32:
150			*d = float64(vt)
151		case int16:
152			*d = float64(vt)
153		case int8:
154			*d = float64(vt)
155		case int:
156			*d = float64(vt)
157		case uint64:
158			*d = float64(vt)
159		case uint32:
160			*d = float64(vt)
161		case uint16:
162			*d = float64(vt)
163		case uint8:
164			*d = float64(vt)
165		case uint:
166			*d = float64(vt)
167		case float64:
168			*d = vt
169		case float32:
170			*d = float64(vt)
171		case string:
172			x, _ := strconv.ParseFloat(vt, 64)
173			*d = float64(x)
174		default:
175			panic(fmt.Sprintf("Unhandled type %+v for float64 conversion", reflect.TypeOf(vt)))
176		}
177
178	case *int:
179		switch vt := v.(type) {
180		case int64:
181			*d = int(vt)
182		case int32:
183			*d = int(vt)
184		case int16:
185			*d = int(vt)
186		case int8:
187			*d = int(vt)
188		case int:
189			*d = vt
190		case uint64:
191			*d = int(vt)
192		case uint32:
193			*d = int(vt)
194		case uint16:
195			*d = int(vt)
196		case uint8:
197			*d = int(vt)
198		case uint:
199			*d = int(vt)
200		case float64:
201			*d = int(vt)
202		case float32:
203			*d = int(vt)
204		case string:
205			*d, _ = strconv.Atoi(vt)
206		default:
207			panic(fmt.Sprintf("Unhandled type %+v for int conversion", reflect.TypeOf(vt)))
208		}
209
210	case *time.Time:
211		switch vt := v.(type) {
212		case time.Time:
213			*d = vt
214		case int:
215			*d = time.Unix(int64(vt), 0)
216		case int64:
217			*d = time.Unix(vt, 0)
218		default:
219			panic(fmt.Sprintf("Unhandled type %+v for time.Time conversion", reflect.TypeOf(vt)))
220		}
221
222	case *url.Values:
223		switch vt := v.(type) {
224		case string:
225			*d, _ = url.ParseQuery(vt)
226		default:
227			panic(fmt.Sprintf("Unhandled type %+v for url.Values conversion", reflect.TypeOf(vt)))
228		}
229
230	default:
231		panic(fmt.Sprintf("Unhandled dst type %+v", reflect.TypeOf(dst)))
232	}
233
234	return nil
235}
236