Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
anrs committed Apr 29, 2019
0 parents commit 232951f
Show file tree
Hide file tree
Showing 226 changed files with 40,557 additions and 0 deletions.
40 changes: 40 additions & 0 deletions config/batch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package config

import (
"strings"

"github.com/projecteru2/yavirt/errors"
)

// Batch .
type Batch struct {
Bins []string `toml:"bins"`
FlagFile string `toml:"flag_file"`
ForceOK bool `toml:"force_ok"`
Timeout Duration `toml:"timeout"`
Retry bool `toml:"retry"`
Interval Duration `toml:"interval"`
}

// IsRunOnce .
func (b Batch) IsRunOnce() bool {
return len(b.FlagFile) > 0
}

// GetCommands .
func (b Batch) GetCommands() (map[string][]string, error) {
var cmds = map[string][]string{}

for _, bin := range b.Bins {
switch parts := strings.Split(bin, " "); len(parts) {
case 0:
return nil, errors.Annotatef(errors.ErrInvalidValue, "invalid command: %s", bin)
case 1:
cmds[parts[0]] = nil
default:
cmds[parts[0]] = parts[1:]
}
}

return cmds, nil
}
149 changes: 149 additions & 0 deletions config/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package config

import (
"fmt"
"reflect"
"strconv"
"strings"

"github.com/projecteru2/yavirt/errors"
)

type checker struct { //nolint
conf interface{}
field string
fieldObj reflect.StructField
val interface{}
}

func newChecker(conf interface{}, field string) *checker { //nolint
return &checker{
conf: conf,
field: field,
}
}

func (c *checker) check() (err error) { //nolint
if c.conf == nil {
return errors.Errorf("nil *Config")
}

if c.fieldObj, c.val, err = c.getFieldValue(reflect.ValueOf(c.conf), c.field); err != nil {
return errors.Trace(err)
}

if err := c.checkEnum(); err != nil {
return errors.Trace(err)
}

if err := c.checkRange(); err != nil {
return errors.Trace(err)
}

return
}

func (c *checker) checkRange() error { //nolint
var rang, found = c.fieldObj.Tag.Lookup("range")
if !found || len(rang) < 1 {
return nil
}

var part = strings.Split(rang, "-")
if len(part) != 2 { //nolint
return errors.Errorf("invalid range: %s", rang)
}

var atoi = func(s []string) ([]int, error) {
var ar = make([]int, len(s))
var err error
for i := 0; i < len(s); i++ {
ar[i], err = strconv.Atoi(s[i])
if err != nil {
return nil, err
}
}
return ar, nil
}

var ar, err = atoi(part)
if err != nil {
return err
}

var min, max = ar[0], ar[1]
var leng int
var kind = c.fieldObj.Type.Kind()

switch kind {
case reflect.Int:
leng = c.val.(int)
case reflect.String:
leng = len(c.val.(string))
default:
return errors.Errorf("invalid type: %d", kind)
}

if leng < min || leng > max {
return errors.Errorf("invalid length: %d, it should be [%d, %d]", leng, min, max)
}

return nil
}

func (c *checker) checkEnum() error { //nolint
var enum, found = c.fieldObj.Tag.Lookup("enum")
if !found || len(enum) < 1 {
return nil
}

for _, part := range strings.Split(enum, ",") {
if fmt.Sprintf("%v", c.val) == strings.TrimSpace(part) {
return nil
}
}

return errors.Errorf("invalid value: %v", c.val)
}

func (c *checker) getFieldValue(valObj reflect.Value, field string) ( //nolint
reflect.StructField, interface{}, error) {

var fieldObj reflect.StructField

if valObj.Kind() != reflect.Ptr {
return fieldObj, nil, errors.Errorf("require a ptr")
}

var elem = valObj.Elem()
if !elem.IsValid() {
return fieldObj, nil, errors.Errorf("invalid value")
}

for i := 0; i < elem.NumField(); i++ {
var name, rest = c.split(field)

fieldObj = elem.Type().Field(i)
if !strings.EqualFold(fieldObj.Name, name) {
continue
}

var val = elem.Field(i).Interface()

if len(rest) < 1 {
return fieldObj, val, nil
}

return c.getFieldValue(reflect.ValueOf(val), rest)
}

return fieldObj, nil, errors.Errorf("no such field: %s", field)
}

func (c *checker) split(field string) (string, string) { //nolint
var i = strings.Index(field, ".")
if i < 0 {
return field, ""
}
return field[:i], field[i+1:]
}
79 changes: 79 additions & 0 deletions config/check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package config

import (
"testing"

"github.com/projecteru2/yavirt/test/assert"
)

type config struct {
Debug string
Env string `enum:"dev,test,uat,live"`
ProfHTTPPort int `toml:"prof_http_port" enum:"9999,8888"`
Mysql *mysql
}

type mysql struct {
User string `range:"2-8"`
Port int `range:"6000-9999" json:"port"`
Host string `json:"host"`
}

var newconf = func() *config {
return &config{
Mysql: &mysql{},
}
}

func TestCheckNotFound(t *testing.T) {
var conf = newconf()
assert.NotNil(t, newChecker(conf, "NotFound").check())
assert.NotNil(t, newChecker(conf, "Mysql.NotFound").check())
}

func TestCheckStrEnum(t *testing.T) {
var conf = newconf()

conf.Env = ""
assert.NotNil(t, newChecker(conf, "Env").check())

conf.Env = "unknown"
assert.NotNil(t, newChecker(conf, "Env").check())

conf.Env = "dev"
assert.Nil(t, newChecker(conf, "Env").check())
}

func TestCheckStrRange(t *testing.T) {
var conf = newconf()

conf.Mysql.User = "a"
assert.NotNil(t, newChecker(conf, "Mysql.User").check())

conf.Mysql.User = string(make([]byte, 256))
assert.NotNil(t, newChecker(conf, "Mysql.User").check())
}

func TestCheckIntEnum(t *testing.T) {
var conf = newconf()
conf.ProfHTTPPort = 80
assert.NotNil(t, newChecker(conf, "ProfHTTPPort").check())

conf.ProfHTTPPort = 8888
assert.Nil(t, newChecker(conf, "ProfHTTPPort").check())
}

func TestCheckIntRange(t *testing.T) {
var conf = newconf()
conf.Mysql.Port = 3306
assert.NotNil(t, newChecker(conf, "Mysql.Port").check())

conf.Mysql.Port = 6606
assert.Nil(t, newChecker(conf, "Mysql.Port").check())
}

func TestCheckNone(t *testing.T) {
var conf = newconf()
assert.Nil(t, newChecker(conf, "Debug").check())
assert.Nil(t, newChecker(conf, "Mysql.Host").check())
}
39 changes: 39 additions & 0 deletions config/codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package config

import (
"bytes"

"github.com/BurntSushi/toml"

"github.com/projecteru2/yavirt/errors"
)

// Decode .
func Decode(raw string, conf *Config) error {
if _, err := toml.Decode(raw, conf); err != nil {
return errors.Trace(err)
}
return nil
}

// Encode .
func Encode(conf *Config, noIndents ...bool) (string, error) {
var buf bytes.Buffer
var enc = toml.NewEncoder(&buf)

if len(noIndents) < 1 || !noIndents[0] {
enc.Indent = " "
}

if err := enc.Encode(conf); err != nil {
return "", errors.Trace(err)
}

return buf.String(), nil
}

// DecodeFile .
func DecodeFile(file string, conf *Config) (err error) {
_, err = toml.DecodeFile(file, conf)
return
}
Loading

0 comments on commit 232951f

Please sign in to comment.