new utils

This commit is contained in:
Jay 2018-12-13 16:06:36 +08:00
parent 117c17b029
commit 43bbbb5acb
4 changed files with 225 additions and 152 deletions

View File

@ -1,152 +0,0 @@
package dbtool
import (
"database/sql"
"errors"
"reflect"
"strconv"
"strings"
"time"
)
// ScanToStruct - scan query rows to struct
func ScanToStruct(rows *sql.Rows, ss interface{}) (ok bool, err error) {
data, err := ResultToMap(rows)
if err != nil {
return false, err
}
if len(data) == 0 {
return false, nil
}
err = MapToStruct(data[0], ss)
if err != nil {
return false, err
}
return true, nil
}
// ScanToStructAll -
func ScanToStructAll(rows *sql.Rows, ss interface{}) error {
sliceVal := reflect.Indirect(reflect.ValueOf(ss))
if sliceVal.Kind() != reflect.Slice {
return errors.New("need a pointer to a slice ")
}
vt := sliceVal.Type().Elem()
if vt.Kind() != reflect.Struct {
return errors.New("need struct slice")
}
data, err := ResultToMap(rows)
if err != nil {
return err
}
for i := range data {
tmp := reflect.New(vt)
iface := tmp.Interface()
err = MapToStruct(data[i], iface)
if err != nil {
return err
}
sliceVal.Set(reflect.Append(sliceVal, reflect.ValueOf(iface).Elem()))
}
return nil
}
// ResultToMap -
func ResultToMap(r *sql.Rows) ([]map[string]interface{}, error) {
result := make([]map[string]interface{}, 0)
cols, err := r.Columns()
if err != nil {
return nil, err
}
vals := make([]sql.RawBytes, len(cols))
scans := make([]interface{}, len(vals))
for i := range vals {
scans[i] = &vals[i]
}
for r.Next() {
tmp := make(map[string]interface{})
err := r.Scan(scans...)
if err != nil {
return nil, err
}
for i, v := range vals {
tmp[cols[i]] = v
vals[i] = nil
}
result = append(result, tmp)
}
return result, nil
}
// MapToStruct -
func MapToStruct(data map[string]interface{}, out interface{}) error {
ss := reflect.ValueOf(out).Elem()
for i := 0; i < ss.NumField(); i++ {
tag := ss.Type().Field(i).Tag.Get("sql")
name := ss.Type().Field(i).Name
fname := strings.ToLower(name)
if len(tag) > 0 {
fname = tag
}
if fname == "-" {
continue
}
d, ok := data[fname]
if !ok {
continue
}
switch ss.Field(i).Interface().(type) {
case string:
ss.Field(i).SetString(string(d.(sql.RawBytes)))
case int, int8, int16, int32, int64:
str := string(d.(sql.RawBytes))
tmpi, err := strconv.ParseInt(str, 10, 64)
if err != nil {
return err
}
ss.Field(i).SetInt(tmpi)
case uint, uint8, uint16, uint32, uint64:
str := string(d.(sql.RawBytes))
tmpi, err := strconv.ParseUint(str, 10, 64)
if err != nil {
return err
}
ss.Field(i).SetUint(tmpi)
case bool:
str := string(d.(sql.RawBytes))
tmpb, err := strconv.ParseBool(str)
if err != nil {
return err
}
ss.Field(i).SetBool(tmpb)
case float32, float64:
str := string(d.(sql.RawBytes))
tmpf, err := strconv.ParseFloat(str, 64)
if err != nil {
return err
}
ss.Field(i).SetFloat(tmpf)
case time.Time:
str := string(d.(sql.RawBytes))
t, err := time.Parse(time.RFC3339, str)
if err != nil {
return err
}
ss.Field(i).Set(reflect.ValueOf(t))
default:
str := string(d.(sql.RawBytes))
ss.Field(i).Set(reflect.ValueOf(str))
}
}
return nil
}

1
go.mod Normal file
View File

@ -0,0 +1 @@
module git.trj.tw/golang/utils

166
utils.go Normal file
View File

@ -0,0 +1,166 @@
package utils
import (
"math"
"os"
"path"
"reflect"
"regexp"
"runtime"
"strings"
)
// PageObject -
type PageObject struct {
Page int `json:"page" cc:"page"`
Total int `json:"total" cc:"total"`
Offset int `json:"offset" cc:"offset"`
Limit int `json:"limit" cc:"limit"`
}
// CalcPage -
func CalcPage(count, page, max int) (po PageObject) {
if count < 0 {
count = 0
}
if page < 1 {
page = 1
}
if max < 1 {
max = 1
}
total := int(math.Ceil(float64(count) / float64(max)))
if total < 1 {
total = 1
}
if page > total {
page = total
}
offset := (page - 1) * max
if offset > count {
offset = count
}
limit := max
po = PageObject{}
po.Limit = limit
po.Page = page
po.Offset = offset
po.Total = total
return
}
// ToMap struct to map[string]interface{}
func ToMap(ss interface{}) map[string]interface{} {
t := reflect.ValueOf(ss)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
smap := make(map[string]interface{})
mtag := regexp.MustCompile(`cc:\"(.+)\"`)
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
tag := string(t.Type().Field(i).Tag)
str := mtag.FindStringSubmatch(tag)
name := t.Type().Field(i).Name
leveling := false
if len(str) > 1 {
strArr := strings.Split(str[1], ",")
name = strArr[0]
if len(strArr) > 1 && strArr[1] == "<<" {
leveling = true
}
}
if f.Kind() == reflect.Slice {
if name == "-" {
continue
}
if f.Len() > 0 && f.Index(0).Kind() != reflect.Struct {
smap[name] = f.Interface()
continue
}
tmp := make([]map[string]interface{}, f.Len())
for i := 0; i < f.Len(); i++ {
tmp[i] = ToMap(f.Index(i).Interface())
}
smap[name] = tmp
continue
}
if f.Kind() == reflect.Struct {
if name == "-" && leveling == false {
continue
}
if leveling == true {
tmp := ToMap(f.Interface())
for k, v := range tmp {
smap[k] = v
}
} else if name != "-" && leveling == false {
smap[name] = f.Interface()
}
continue
}
if name != "-" {
smap[name] = f.Interface()
}
}
return smap
}
// ParsePath - parse file path to absPath
func ParsePath(dst string) string {
wd, err := os.Getwd()
if err != nil {
wd = ""
}
if []rune(dst)[0] == '~' {
home := UserHomeDir()
if len(home) > 0 {
dst = strings.Replace(dst, "~", home, -1)
}
}
if path.IsAbs(dst) {
dst = path.Clean(dst)
return dst
}
str := path.Join(wd, dst)
str = path.Clean(str)
return str
}
// UserHomeDir - get user home directory
func UserHomeDir() string {
env := "HOME"
if runtime.GOOS == "windows" {
env = "USERPROFILE"
} else if runtime.GOOS == "plan9" {
env = "home"
}
return os.Getenv(env)
}
// CheckExists - check file exists
func CheckExists(filePath string, allowDir bool) bool {
filePath = ParsePath(filePath)
stat, err := os.Stat(filePath)
if err != nil && !os.IsExist(err) {
return false
}
if !allowDir && stat.IsDir() {
return false
}
return true
}

58
utils_test.go Normal file
View File

@ -0,0 +1,58 @@
package utils
import (
"testing"
"time"
)
// Commands - struct
type Commands struct {
Cmd string `db:"cmd" cc:"cmd"`
Message string `db:"message" cc:"message"`
Group string `db:"group" cc:"group"`
Ctime time.Time `db:"ctime" cc:"ctime"`
Mtime time.Time `db:"mtime" cc:"ctime"`
}
// CommandsWithGroup -
type CommandsWithGroup struct {
Commands `cc:"-,<<"`
GroupName string `db:"group_name" cc:"group_name"`
}
func TestToMap(t *testing.T) {
cmd := Commands{}
cmd.Cmd = "test"
cmd.Message = "test message"
cmd.Group = ""
cmd.Ctime = time.Now()
cmd.Mtime = time.Now()
cmdWGroup := CommandsWithGroup{}
cmdWGroup.Commands = cmd
cmdWGroup.GroupName = "asd"
ToMap(cmdWGroup)
}
func TestCalcPage(t *testing.T) {
page := CalcPage(10, 1, 10)
if page.Page != 1 {
t.Error("Page Calc fail")
}
if page.Total != 1 {
t.Error("Page Calc fail")
}
if page.Limit != 10 {
t.Error("limit calc fail")
}
if page.Offset != 0 {
t.Error("offset calc fail")
}
}
func BenchmarkCalcPage(b *testing.B) {
for i := 0; i < b.N; i++ {
CalcPage(10000, 30, 10)
}
}