Files
cgrates/utils/reflect.go
2017-09-10 15:02:46 +00:00

124 lines
3.6 KiB
Go

/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"fmt"
"reflect"
"strconv"
)
func CastFieldIfToString(fld interface{}) (string, bool) {
var strVal string
var converted bool
switch fld.(type) {
case string:
strVal = fld.(string)
converted = true
case int:
strVal = strconv.Itoa(fld.(int))
converted = true
case int64:
strVal = strconv.FormatInt(fld.(int64), 10)
converted = true
case bool:
strVal = strconv.FormatBool(fld.(bool))
case float64:
strVal = strconv.FormatFloat(fld.(float64), 'f', -1, 64)
converted = true
case []uint8:
var byteVal []byte
if byteVal, converted = fld.([]byte); converted {
strVal = string(byteVal)
}
default: // Maybe we are lucky and the value converts to string
strVal, converted = fld.(string)
}
return strVal, converted
}
// ReflectFieldAsString parses intf and attepting to return the field as string or error otherwise
// Supports "ExtraFields" where additional fields are dynamically inserted in map with field name: extraFieldsLabel
func ReflectFieldAsString(intf interface{}, fldName, extraFieldsLabel string) (string, error) {
v := reflect.ValueOf(intf)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
var field reflect.Value
switch v.Kind() {
case reflect.Struct:
field = v.FieldByName(fldName)
case reflect.Map:
field = v.MapIndex(reflect.ValueOf(fldName))
if !field.IsValid() { // Not looking in extra fields anymore
return "", ErrNotFound
}
default:
return "", fmt.Errorf("Unsupported field kind: %v", v.Kind())
}
if !field.IsValid() {
if extraFieldsLabel == "" {
return "", ErrNotFound
}
mpVal := v.FieldByName(extraFieldsLabel)
if !mpVal.IsValid() || mpVal.Kind() != reflect.Map {
return "", ErrNotFound
}
field = mpVal.MapIndex(reflect.ValueOf(fldName))
if !field.IsValid() {
return "", ErrNotFound
}
}
switch field.Kind() {
case reflect.String:
return field.String(), nil
case reflect.Int, reflect.Int64:
return strconv.FormatInt(field.Int(), 10), nil
case reflect.Float64:
return strconv.FormatFloat(field.Float(), 'f', -1, 64), nil
case reflect.Interface:
strVal, converted := CastFieldIfToString(field.Interface())
if !converted {
return "", fmt.Errorf("Cannot convert to string field type: %s", field.Kind().String())
} else {
return strVal, nil
}
default:
return "", fmt.Errorf("Cannot convert to string field type: %s", field.Kind().String())
}
}
// AsMapStringIface converts an item (mostly struct) as map[string]interface{}
func AsMapStringIface(item interface{}) (map[string]interface{}, error) {
out := make(map[string]interface{})
v := reflect.ValueOf(item)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct { // Only structs for now
return nil, fmt.Errorf("AsMapStringIface only accepts structs; got %T", v)
}
typ := v.Type()
for i := 0; i < v.NumField(); i++ {
out[typ.Field(i).Name] = v.Field(i).Interface()
}
return out, nil
}