Skip to content

Commit

Permalink
Adding support for struct tags.
Browse files Browse the repository at this point in the history
  • Loading branch information
xiam committed Apr 7, 2013
1 parent 94a1cfe commit 274dece
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 42 deletions.
27 changes: 20 additions & 7 deletions mysql/mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package mysql

import (
"fmt"
"github.com/kr/pretty"
"math/rand"
"menteslibres.net/gosexy/db"
"menteslibres.net/gosexy/dig"
"menteslibres.net/gosexy/to"
"github.com/kr/pretty"
"math/rand"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -184,7 +184,14 @@ func TestAppend(t *testing.T) {
people.Truncate()

for _, name := range names {
people.Append(struct{ Name string }{name})
people.Append(struct {
ignoreMe string
LastName string `ignorenil:"true"`
// Must ignore OtherName and use "name" as column.
OtherName string `field:"name",ignorenil:"true"`
// Should not get inserted.
nothing string
}{"nuff said", "", name, "nothing"})
}

total, _ = people.Count()
Expand Down Expand Up @@ -242,7 +249,11 @@ func TestFind(t *testing.T) {
}

// Fetch into struct slice.
dst2 := []struct{ Name string }{}
dst2 := []struct {
foo string
PersonName string `field:"name"`
none string
}{}

res, err = people.Query(db.Cond{"name": "José"})

Expand All @@ -260,7 +271,7 @@ func TestFind(t *testing.T) {
t.Fatalf("Could not find a recently appended item.")
}

if dst2[0].Name != "José" {
if dst2[0].PersonName != "José" {
t.Fatalf("Could not find a recently appended item.")
}

Expand Down Expand Up @@ -305,15 +316,17 @@ func TestFind(t *testing.T) {
t.Fatalf(err.Error())
}

dst5 := struct{ Name string }{}
dst5 := struct {
PersonName string `field:"name"`
}{}
found := false

for {
err = res.Next(&dst5)
if err != nil {
break
}
if dst5.Name == "José" {
if dst5.PersonName == "José" {
found = true
}
}
Expand Down
Binary file modified sqlite/_dumps/gotest.sqlite3.db
Binary file not shown.
84 changes: 68 additions & 16 deletions util/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,58 @@ func (self *C) RelationCollection(name string, terms db.On) (db.Collection, erro
return col, nil
}

func columnCompare(s string) string {
return strings.ToLower(columnCompareExclude.ReplaceAllString(s, ""))
}

/*
Returns the most appropriate struct field index for a given column name.
If no column matches returns -1.
*/
func MatchStructField(s reflect.Type, columnName string) int {
n := s.NumField()

columnNameLower := columnCompare(columnName)

for i := 0; i < n; i++ {

field := s.Field(i)

// Field is exported.
if field.PkgPath == "" {

tag := field.Tag

// Tag: field:"columnName"
fieldName := tag.Get("field")

if fieldName != "" {
if fieldName == columnName {
return i
}
}

// Matching column to name.
fieldNameLower := columnCompare(field.Name)

if fieldNameLower == columnNameLower {
return i
}

}

}

// No match.
return -1
}

/*
Returns true if a table column looks like a struct field.
*/
func CompareColumnToField(s, c string) bool {
s = columnCompareExclude.ReplaceAllString(s, "")
c = columnCompareExclude.ReplaceAllString(c, "")
return strings.ToLower(s) == strings.ToLower(c)
return columnCompare(s) == columnCompare(c)
}

/*
Expand Down Expand Up @@ -105,12 +150,14 @@ func Fetch(dst interface{}, item db.Item) error {
switch el.Kind() {
case reflect.Struct:
for column, _ := range item {
f := func(s string) bool {
return CompareColumnToField(s, column)
}
v := dstv.Elem().FieldByNameFunc(f)
if v.IsValid() {
v.Set(reflect.ValueOf(item[column]))
fi := MatchStructField(dstv.Type(), column)
if fi < 0 {
continue
} else {
v := dstv.Elem().Field(fi)
if v.IsValid() {
v.Set(reflect.ValueOf(item[column]))
}
}
}
case reflect.Map:
Expand Down Expand Up @@ -144,10 +191,12 @@ func fetchItemRelations(itemv reflect.Value, relations []db.Relation, convertFn
var val reflect.Value
switch itemk {
case reflect.Struct:
f := func(s string) bool {
return CompareColumnToField(s, extkey)
fi := MatchStructField(itemv.Type(), extkey)
if fi < 0 {
continue
} else {
val = itemv.Field(fi)
}
val = itemv.FieldByNameFunc(f)
case reflect.Map:
val = itemv.MapIndex(reflect.ValueOf(extkey))
}
Expand All @@ -167,12 +216,15 @@ func fetchItemRelations(itemv reflect.Value, relations []db.Relation, convertFn

switch itemk {
case reflect.Struct:
var val reflect.Value

f := func(s string) bool {
return CompareColumnToField(s, relation.Name)
}
fi := MatchStructField(itemv.Type(), relation.Name)

val := itemv.FieldByNameFunc(f)
if fi < 0 {
continue
} else {
val = itemv.Field(fi)
}

if val.IsValid() {
var res db.Result
Expand Down
79 changes: 60 additions & 19 deletions util/sqlutil/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,11 @@ func (self *T) fetchResult(itemt reflect.Type, rows *sql.Rows, columns []string)

// Range over row values.
for i, value := range values {

if value != nil {
// Real column name
column := columns[i]
// Value as string.
svalue := string(*value)

var cv reflect.Value
Expand All @@ -121,23 +124,29 @@ func (self *T) fetchResult(itemt reflect.Type, rows *sql.Rows, columns []string)
}
// Destionation is a struct.
case reflect.Struct:
// Get appropriate column.
f := func(s string) bool {
return util.CompareColumnToField(s, column)
}
// Destination field.
destf := item.Elem().FieldByNameFunc(f)
if destf.IsValid() {
if cv.Type().Kind() != destf.Type().Kind() {
if destf.Type().Kind() != reflect.Interface {
// Converting value.
cv, _ = util.ConvertValue(svalue, destf.Type().Kind())

fi := util.MatchStructField(itemt, column)

if fi < 0 {
continue
} else {

// Destination field.
destf := item.Elem().Field(fi)

if destf.IsValid() {
if cv.Type().Kind() != destf.Type().Kind() {
if destf.Type().Kind() != reflect.Interface {
// Converting value.
cv, _ = util.ConvertValue(svalue, destf.Type().Kind())
}
}
// Copying value.
if cv.IsValid() {
destf.Set(cv)
}
}
// Copying value.
if cv.IsValid() {
destf.Set(cv)
}

}
}
}
Expand Down Expand Up @@ -246,24 +255,56 @@ func (self *T) FieldValues(item interface{}, convertFn func(interface{}) string)
itemt := itemv.Type()

switch itemt.Kind() {

case reflect.Struct:
nfields := itemv.NumField()
values = make([]string, nfields)
fields = make([]string, nfields)

values = make([]string, 0, nfields)
fields = make([]string, 0, nfields)

for i := 0; i < nfields; i++ {
fields[i] = self.ColumnLike(itemt.Field(i).Name)
values[i] = convertFn(itemv.Field(i).Interface())

field := itemt.Field(i)

if field.PkgPath == "" {

value := itemv.Field(i).Interface()

// Struct tags
tag := field.Tag

// omitempty:bool
ignoreNil := tag.Get("ignorenil")

if ignoreNil == "true" {
if value == nil || value == "" {
continue
}
}

// field:string
fieldName := tag.Get("field")

if fieldName == "" {
fieldName = self.ColumnLike(field.Name)
}

fields = append(fields, fieldName)
values = append(values, convertFn(value))
}
}
case reflect.Map:
nfields := itemv.Len()
values = make([]string, nfields)
fields = make([]string, nfields)
mkeys := itemv.MapKeys()

for i, keyv := range mkeys {
valv := itemv.MapIndex(keyv)
fields[i] = self.ColumnLike(to.String(keyv.Interface()))
values[i] = convertFn(valv.Interface())
}

default:
return nil, nil, fmt.Errorf("Expecting Struct or Map, received %v.", itemt.Kind())
}
Expand Down

0 comments on commit 274dece

Please sign in to comment.