new utils
This commit is contained in:
parent
117c17b029
commit
43bbbb5acb
152
dbtool/dbtool.go
152
dbtool/dbtool.go
@ -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
|
|
||||||
}
|
|
166
utils.go
Normal file
166
utils.go
Normal 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
58
utils_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user