Newer
Older
// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package util
import (
"reflect"
"regexp"
"strings"
"time"
"menteslibres.net/gosexy/to"
var reColumnCompareExclude = regexp.MustCompile(`[^a-zA-Z0-9]`)
var (
durationType = reflect.TypeOf(time.Duration(0))
timeType = reflect.TypeOf(time.Time{})
)
type tagOptions map[string]bool
func parseTagOptions(s string) tagOptions {
opts := make(tagOptions)
chunks := strings.Split(s, `,`)
for _, chunk := range chunks {
opts[strings.TrimSpace(chunk)] = true
}
return opts
}
// Based on http://golang.org/src/pkg/encoding/json/tags.go
func ParseTag(tag string) (string, tagOptions) {
if i := strings.Index(tag, `,`); i != -1 {
return tag[:i], parseTagOptions(tag[i+1:])
}
return tag, parseTagOptions(``)
}
// Returns the most appropriate struct field index for a given column name.
//
// If no column matches returns nil.
José Carlos Nieto
committed
func GetStructFieldIndex(t reflect.Type, columnName string) []int {
n := t.NumField()
José Carlos Nieto
committed
field := t.Field(i)
if field.PkgPath != "" {
// Field is unexported.
continue
}
// Attempt to use db:"column_name"
fieldName, fieldOptions := ParseTag(field.Tag.Get("db"))
// Deprecated "field" tag.
if deprecatedField := field.Tag.Get("field"); deprecatedField != "" {
fieldName = deprecatedField
}
// Deprecated "inline" tag.
if deprecatedInline := field.Tag.Get("inline"); deprecatedInline != "" {
fieldOptions["inline"] = true
}
// Matching fieldName
if fieldName == "-" {
continue
}
// Attempt to match field name.
if fieldName == columnName {
return []int{i}
}
if fieldName == "" {
if NormalizeColumn(field.Name) == NormalizeColumn(columnName) {
José Carlos Nieto
committed
return []int{i}
}
}
José Carlos Nieto
committed
// Inline option.
if fieldOptions["inline"] == true {
index := GetStructFieldIndex(field.Type, columnName)
if index != nil {
res := append([]int{i}, index...)
return res
}
}
}
// No match.
José Carlos Nieto
committed
return nil
func StringToType(src string, dstt reflect.Type) (reflect.Value, error) {
var srcv reflect.Value
switch dstt {
case durationType:
srcv = reflect.ValueOf(to.Duration(src))
case timeType:
// Destination is time.Time
srcv = reflect.ValueOf(to.Time(src))
default:
return StringToKind(src, dstt.Kind())
}
return srcv, nil
}
func StringToKind(src string, dstk reflect.Kind) (reflect.Value, error) {
var srcv reflect.Value
// Destination type.
switch dstk {
case reflect.Interface:
// Destination is interface, nuff said.
srcv = reflect.ValueOf(src)
default:
cv, err := to.Convert(src, dstk)
if err != nil {
return srcv, nil
}
srcv = reflect.ValueOf(cv)
}
return srcv, nil
}
func NormalizeColumn(s string) string {
return strings.ToLower(reColumnCompareExclude.ReplaceAllString(s, ""))
}