You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
781 lines
19 KiB
781 lines
19 KiB
package goconf |
|
|
|
import ( |
|
"bufio" |
|
"errors" |
|
"fmt" |
|
"io" |
|
"os" |
|
"reflect" |
|
"strconv" |
|
"strings" |
|
"time" |
|
) |
|
|
|
const ( |
|
// formatter |
|
CRLF = '\n' |
|
Comment = "#" |
|
Spliter = " " |
|
SectionS = "[" |
|
SectionE = "]" |
|
// memory unit |
|
Byte = 1 |
|
KB = 1024 * Byte |
|
MB = 1024 * KB |
|
GB = 1024 * MB |
|
) |
|
|
|
// Section is the key-value data object. |
|
type Section struct { |
|
data map[string]string // key:value |
|
dataOrder []string |
|
dataComments map[string][]string // key:comments |
|
Name string |
|
comments []string |
|
Comment string |
|
} |
|
|
|
// Config is the key-value configuration object. |
|
type Config struct { |
|
data map[string]*Section |
|
dataOrder []string |
|
file string |
|
Comment string |
|
Spliter string |
|
} |
|
|
|
// New return a new default Config object (Comment = '#', spliter = ' '). |
|
func New() *Config { |
|
return &Config{Comment: Comment, Spliter: Spliter, data: map[string]*Section{}} |
|
} |
|
|
|
// ParseReader parse config file by a io.Reader. |
|
func (c *Config) ParseReader(reader io.Reader) error { |
|
var ( |
|
err error |
|
line int |
|
idx int |
|
row string |
|
key string |
|
value string |
|
comments []string |
|
section *Section |
|
rd = bufio.NewReader(reader) |
|
) |
|
for { |
|
line++ |
|
row, err = rd.ReadString(CRLF) |
|
if err == io.EOF && len(row) == 0 { |
|
// file end |
|
break |
|
} else if err != nil && err != io.EOF { |
|
return err |
|
} |
|
row = strings.TrimSpace(row) |
|
// ignore blank line |
|
// ignore Comment line |
|
if len(row) == 0 || strings.HasPrefix(row, c.Comment) { |
|
comments = append(comments, row) |
|
continue |
|
} |
|
// get secion |
|
if strings.HasPrefix(row, SectionS) { |
|
if !strings.HasSuffix(row, SectionE) { |
|
return errors.New(fmt.Sprintf("no end section: %s at :%d", SectionE, line)) |
|
} |
|
sectionStr := row[1 : len(row)-1] |
|
// store the section |
|
s, ok := c.data[sectionStr] |
|
if !ok { |
|
s = &Section{data: map[string]string{}, dataComments: map[string][]string{}, comments: comments, Comment: c.Comment, Name: sectionStr} |
|
c.data[sectionStr] = s |
|
c.dataOrder = append(c.dataOrder, sectionStr) |
|
} else { |
|
return errors.New(fmt.Sprintf("section: %s already exists at %d", sectionStr, line)) |
|
} |
|
section = s |
|
comments = []string{} |
|
continue |
|
} |
|
// get the spliter index |
|
idx = strings.Index(row, c.Spliter) |
|
if idx > 0 { |
|
// get the key and value |
|
key = strings.TrimSpace(row[:idx]) |
|
if len(row) > idx { |
|
value = strings.TrimSpace(row[idx+1:]) |
|
} |
|
} else { |
|
return errors.New(fmt.Sprintf("no spliter in key: %s at %d", row, line)) |
|
} |
|
// check section exists |
|
if section == nil { |
|
return errors.New(fmt.Sprintf("no section for key: %s at %d", key, line)) |
|
} |
|
// check key already exists |
|
if _, ok := section.data[key]; ok { |
|
return errors.New(fmt.Sprintf("section: %s already has key: %s at %d", section.Name, key, line)) |
|
} |
|
// save key-value |
|
section.data[key] = value |
|
// save comments for key |
|
section.dataComments[key] = comments |
|
section.dataOrder = append(section.dataOrder, key) |
|
// clean comments |
|
comments = []string{} |
|
} |
|
return nil |
|
} |
|
|
|
// Parse parse the specified config file. |
|
func (c *Config) Parse(file string) error { |
|
// open config file |
|
if f, err := os.Open(file); err != nil { |
|
return err |
|
} else { |
|
defer f.Close() |
|
c.file = file |
|
return c.ParseReader(f) |
|
} |
|
} |
|
|
|
// Get get a config section by key. |
|
func (c *Config) Get(section string) *Section { |
|
s, _ := c.data[section] |
|
return s |
|
} |
|
|
|
// Add add a new config section, if exist the section key then return the existing one. |
|
func (c *Config) Add(section string, comments ...string) *Section { |
|
s, ok := c.data[section] |
|
if !ok { |
|
var dataComments []string |
|
for _, comment := range comments { |
|
for _, line := range strings.Split(comment, string(CRLF)) { |
|
dataComments = append(dataComments, fmt.Sprintf("%s%s", c.Comment, line)) |
|
} |
|
} |
|
s = &Section{data: map[string]string{}, Name: section, comments: dataComments, Comment: c.Comment, dataComments: map[string][]string{}} |
|
c.data[section] = s |
|
c.dataOrder = append(c.dataOrder, section) |
|
} |
|
return s |
|
} |
|
|
|
// Remove remove the specified section. |
|
func (c *Config) Remove(section string) { |
|
if _, ok := c.data[section]; ok { |
|
for i, k := range c.dataOrder { |
|
if k == section { |
|
c.dataOrder = append(c.dataOrder[:i], c.dataOrder[i+1:]...) |
|
break |
|
} |
|
} |
|
delete(c.data, section) |
|
} |
|
} |
|
|
|
// Sections return all the config sections. |
|
func (c *Config) Sections() []string { |
|
// safe-copy |
|
sections := []string{} |
|
for _, k := range c.dataOrder { |
|
sections = append(sections, k) |
|
} |
|
return sections |
|
} |
|
|
|
// Save save current configuration to specified file, if file is "" then rewrite the original file. |
|
func (c *Config) Save(file string) error { |
|
if file == "" { |
|
file = c.file |
|
} else { |
|
c.file = file |
|
} |
|
// save core file |
|
return c.saveFile(file) |
|
} |
|
|
|
// saveFile save config info in specified file. |
|
func (c *Config) saveFile(file string) error { |
|
f, err := os.OpenFile(file, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) |
|
if err != nil { |
|
return err |
|
} |
|
defer f.Close() |
|
// sections |
|
for _, section := range c.dataOrder { |
|
data, _ := c.data[section] |
|
// comments |
|
for _, comment := range data.comments { |
|
if _, err := f.WriteString(fmt.Sprintf("%s%c", comment, CRLF)); err != nil { |
|
return err |
|
} |
|
} |
|
// section |
|
if _, err := f.WriteString(fmt.Sprintf("[%s]%c", section, CRLF)); err != nil { |
|
return err |
|
} |
|
// key-values |
|
for _, k := range data.dataOrder { |
|
v, _ := data.data[k] |
|
// comments |
|
for _, comment := range data.dataComments[k] { |
|
if _, err := f.WriteString(fmt.Sprintf("%s%c", comment, CRLF)); err != nil { |
|
return err |
|
} |
|
} |
|
// key-value |
|
if _, err := f.WriteString(fmt.Sprintf("%s%s%s%c", k, c.Spliter, v, CRLF)); err != nil { |
|
return err |
|
} |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
// Reload reload the config file and return a new Config. |
|
func (c *Config) Reload() (*Config, error) { |
|
nc := &Config{Comment: c.Comment, Spliter: c.Spliter, file: c.file, data: map[string]*Section{}} |
|
if err := nc.Parse(c.file); err != nil { |
|
return nil, err |
|
} |
|
return nc, nil |
|
} |
|
|
|
// Add add a new key-value configuration for the section. |
|
func (s *Section) Add(k, v string, comments ...string) { |
|
if _, ok := s.data[k]; !ok { |
|
s.dataOrder = append(s.dataOrder, k) |
|
for _, comment := range comments { |
|
for _, line := range strings.Split(comment, string(CRLF)) { |
|
s.dataComments[k] = append(s.dataComments[k], fmt.Sprintf("%s%s", s.Comment, line)) |
|
} |
|
} |
|
} |
|
s.data[k] = v |
|
} |
|
|
|
// Remove remove the specified key configuration for the section. |
|
func (s *Section) Remove(k string) { |
|
delete(s.data, k) |
|
for i, key := range s.dataOrder { |
|
if key == k { |
|
s.dataOrder = append(s.dataOrder[:i], s.dataOrder[i+1:]...) |
|
break |
|
} |
|
} |
|
} |
|
|
|
// An NoKeyError describes a goconf key that was not found in the section. |
|
type NoKeyError struct { |
|
Key string |
|
Section string |
|
} |
|
|
|
func (e *NoKeyError) Error() string { |
|
return fmt.Sprintf("key: \"%s\" not found in [%s]", e.Key, e.Section) |
|
} |
|
|
|
// String get config string value. |
|
func (s *Section) String(key string) (string, error) { |
|
if v, ok := s.data[key]; ok { |
|
return v, nil |
|
} else { |
|
return "", &NoKeyError{Key: key, Section: s.Name} |
|
} |
|
} |
|
|
|
// Strings get config []string value. |
|
func (s *Section) Strings(key, delim string) ([]string, error) { |
|
if v, ok := s.data[key]; ok { |
|
return strings.Split(v, delim), nil |
|
} else { |
|
return nil, &NoKeyError{Key: key, Section: s.Name} |
|
} |
|
} |
|
|
|
// Int get config int value. |
|
func (s *Section) Int(key string) (int64, error) { |
|
if v, ok := s.data[key]; ok { |
|
return strconv.ParseInt(v, 10, 64) |
|
} else { |
|
return 0, &NoKeyError{Key: key, Section: s.Name} |
|
} |
|
} |
|
|
|
// Uint get config uint value. |
|
func (s *Section) Uint(key string) (uint64, error) { |
|
if v, ok := s.data[key]; ok { |
|
return strconv.ParseUint(v, 10, 64) |
|
} else { |
|
return 0, &NoKeyError{Key: key, Section: s.Name} |
|
} |
|
} |
|
|
|
// Float get config float value. |
|
func (s *Section) Float(key string) (float64, error) { |
|
if v, ok := s.data[key]; ok { |
|
return strconv.ParseFloat(v, 64) |
|
} else { |
|
return 0, &NoKeyError{Key: key, Section: s.Name} |
|
} |
|
} |
|
|
|
// Bool get config boolean value. |
|
// |
|
// "yes", "1", "y", "true", "enable" means true. |
|
// |
|
// "no", "0", "n", "false", "disable" means false. |
|
// |
|
// if the specified value unknown then return false. |
|
func (s *Section) Bool(key string) (bool, error) { |
|
if v, ok := s.data[key]; ok { |
|
v = strings.ToLower(v) |
|
return parseBool(v), nil |
|
} else { |
|
return false, &NoKeyError{Key: key, Section: s.Name} |
|
} |
|
} |
|
|
|
func parseBool(v string) bool { |
|
if v == "true" || v == "yes" || v == "1" || v == "y" || v == "enable" { |
|
return true |
|
} else if v == "false" || v == "no" || v == "0" || v == "n" || v == "disable" { |
|
return false |
|
} else { |
|
return false |
|
} |
|
} |
|
|
|
// Byte get config byte number value. |
|
// |
|
// 1kb = 1k = 1024. |
|
// |
|
// 1mb = 1m = 1024 * 1024. |
|
// |
|
// 1gb = 1g = 1024 * 1024 * 1024. |
|
func (s *Section) MemSize(key string) (int, error) { |
|
if v, ok := s.data[key]; ok { |
|
return parseMemory(v) |
|
} else { |
|
return 0, &NoKeyError{Key: key, Section: s.Name} |
|
} |
|
} |
|
|
|
func parseMemory(v string) (int, error) { |
|
unit := Byte |
|
subIdx := len(v) |
|
if strings.HasSuffix(v, "k") { |
|
unit = KB |
|
subIdx = subIdx - 1 |
|
} else if strings.HasSuffix(v, "kb") { |
|
unit = KB |
|
subIdx = subIdx - 2 |
|
} else if strings.HasSuffix(v, "m") { |
|
unit = MB |
|
subIdx = subIdx - 1 |
|
} else if strings.HasSuffix(v, "mb") { |
|
unit = MB |
|
subIdx = subIdx - 2 |
|
} else if strings.HasSuffix(v, "g") { |
|
unit = GB |
|
subIdx = subIdx - 1 |
|
} else if strings.HasSuffix(v, "gb") { |
|
unit = GB |
|
subIdx = subIdx - 2 |
|
} |
|
b, err := strconv.ParseInt(v[:subIdx], 10, 64) |
|
if err != nil { |
|
return 0, err |
|
} |
|
return int(b) * unit, nil |
|
} |
|
|
|
// Duration get config second value. |
|
// |
|
// 1s = 1sec = 1. |
|
// |
|
// 1m = 1min = 60. |
|
// |
|
// 1h = 1hour = 60 * 60. |
|
func (s *Section) Duration(key string) (time.Duration, error) { |
|
if v, ok := s.data[key]; ok { |
|
if t, err := parseTime(v); err != nil { |
|
return 0, err |
|
} else { |
|
return time.Duration(t), nil |
|
} |
|
} else { |
|
return 0, &NoKeyError{Key: key, Section: s.Name} |
|
} |
|
} |
|
|
|
func parseTime(v string) (int64, error) { |
|
unit := int64(time.Nanosecond) |
|
subIdx := len(v) |
|
if strings.HasSuffix(v, "ms") { |
|
unit = int64(time.Millisecond) |
|
subIdx = subIdx - 2 |
|
} else if strings.HasSuffix(v, "s") { |
|
unit = int64(time.Second) |
|
subIdx = subIdx - 1 |
|
} else if strings.HasSuffix(v, "sec") { |
|
unit = int64(time.Second) |
|
subIdx = subIdx - 3 |
|
} else if strings.HasSuffix(v, "m") { |
|
unit = int64(time.Minute) |
|
subIdx = subIdx - 1 |
|
} else if strings.HasSuffix(v, "min") { |
|
unit = int64(time.Minute) |
|
subIdx = subIdx - 3 |
|
} else if strings.HasSuffix(v, "h") { |
|
unit = int64(time.Hour) |
|
subIdx = subIdx - 1 |
|
} else if strings.HasSuffix(v, "hour") { |
|
unit = int64(time.Hour) |
|
subIdx = subIdx - 4 |
|
} |
|
b, err := strconv.ParseInt(v[:subIdx], 10, 64) |
|
if err != nil { |
|
return 0, err |
|
} |
|
return b * unit, nil |
|
} |
|
|
|
// Keys return all the section keys. |
|
func (s *Section) Keys() []string { |
|
keys := []string{} |
|
for k, _ := range s.data { |
|
keys = append(keys, k) |
|
} |
|
return keys |
|
} |
|
|
|
// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. |
|
// (The argument to Unmarshal must be a non-nil pointer.) |
|
type InvalidUnmarshalError struct { |
|
Type reflect.Type |
|
} |
|
|
|
func (e *InvalidUnmarshalError) Error() string { |
|
if e.Type == nil { |
|
return "goconf: Unmarshal(nil)" |
|
} |
|
if e.Type.Kind() != reflect.Ptr { |
|
return "goconf: Unmarshal(non-pointer " + e.Type.String() + ")" |
|
} |
|
return "goconf: Unmarshal(nil " + e.Type.String() + ")" |
|
} |
|
|
|
// Unmarshal parses the goconf struct and stores the result in the value |
|
// pointed to by v. |
|
// |
|
// Struct values encode as goconf objects. Each exported struct field |
|
// becomes a member of the object unless |
|
// - the field's tag is "-", or |
|
// - the field is empty and its tag specifies the "omitempty" option. |
|
// The empty values are false, 0, any |
|
// nil pointer or interface value, and any array, slice, map, or string of |
|
// length zero. The object's section and key string is the struct field name |
|
// but can be specified in the struct field's tag value. The "goconf" key in |
|
// the struct field's tag value is the key name, followed by an optional comma |
|
// and options. Examples: |
|
// |
|
// // Field is ignored by this package. |
|
// Field int `goconf:"-"` |
|
// |
|
// // Field appears in goconf section "base" as key "myName". |
|
// Field int `goconf:"base:myName"` |
|
// |
|
// // Field appears in goconf section "base" as key "myName", the value split |
|
// // by delimiter ",". |
|
// Field []string `goconf:"base:myName:,"` |
|
// |
|
// // Field appears in goconf section "base" as key "myName", the value split |
|
// // by delimiter "," and key-value is splited by "=". |
|
// Field map[int]string `goconf:"base:myName:,"` |
|
// |
|
// // Field appears in goconf section "base" as key "myName", the value |
|
// // conver to time.Duration. When has extra tag "time", then goconf can |
|
// // parse such "1h", "1s" config values. |
|
// // |
|
// // Note the extra tag "time" only effect the int64 (time.Duration is int64) |
|
// Field time.Duration `goconf:"base:myName:time"` |
|
// |
|
// // Field appears in goconf section "base" as key "myName", when has extra |
|
// // tag, then goconf can parse like "1gb", "1mb" config values. |
|
// // |
|
// // Note the extra tag "memory" only effect the int (memory size is int). |
|
// Field int `goconf:"base:myName:memory"` |
|
// |
|
func (c *Config) Unmarshal(v interface{}) error { |
|
vv := reflect.ValueOf(v) |
|
if vv.Kind() != reflect.Ptr || vv.IsNil() { |
|
return &InvalidUnmarshalError{reflect.TypeOf(v)} |
|
} |
|
rv := vv.Elem() |
|
rt := rv.Type() |
|
n := rv.NumField() |
|
// enum every struct field |
|
for i := 0; i < n; i++ { |
|
vf := rv.Field(i) |
|
tf := rt.Field(i) |
|
tag := tf.Tag.Get("goconf") |
|
// if tag empty or "-" ignore |
|
if tag == "-" || tag == "" || tag == "omitempty" { |
|
continue |
|
} |
|
tagArr := strings.SplitN(tag, ":", 3) |
|
if len(tagArr) < 2 { |
|
return errors.New(fmt.Sprintf("error tag: %s, must be section:field:delim(optional)", tag)) |
|
} |
|
section := tagArr[0] |
|
key := tagArr[1] |
|
s := c.Get(section) |
|
if s == nil { |
|
// no config section |
|
continue |
|
} |
|
value, ok := s.data[key] |
|
if !ok { |
|
// no confit key |
|
continue |
|
} |
|
switch vf.Kind() { |
|
case reflect.String: |
|
vf.SetString(value) |
|
case reflect.Bool: |
|
vf.SetBool(parseBool(value)) |
|
case reflect.Float32: |
|
if tmp, err := strconv.ParseFloat(value, 32); err != nil { |
|
return err |
|
} else { |
|
vf.SetFloat(tmp) |
|
} |
|
case reflect.Float64: |
|
if tmp, err := strconv.ParseFloat(value, 64); err != nil { |
|
return err |
|
} else { |
|
vf.SetFloat(tmp) |
|
} |
|
case reflect.Int: |
|
if len(tagArr) == 3 { |
|
format := tagArr[2] |
|
// parse memory size |
|
if format == "memory" { |
|
if tmp, err := parseMemory(value); err != nil { |
|
return err |
|
} else { |
|
vf.SetInt(int64(tmp)) |
|
} |
|
} else { |
|
return errors.New(fmt.Sprintf("unknown tag: %s in struct field: %s (support tags: \"memory\")", format, tf.Name)) |
|
} |
|
} else { |
|
if tmp, err := strconv.ParseInt(value, 10, 32); err != nil { |
|
return err |
|
} else { |
|
vf.SetInt(tmp) |
|
} |
|
} |
|
case reflect.Int8: |
|
if tmp, err := strconv.ParseInt(value, 10, 8); err != nil { |
|
return err |
|
} else { |
|
vf.SetInt(tmp) |
|
} |
|
case reflect.Int16: |
|
if tmp, err := strconv.ParseInt(value, 10, 16); err != nil { |
|
return err |
|
} else { |
|
vf.SetInt(tmp) |
|
} |
|
case reflect.Int32: |
|
if tmp, err := strconv.ParseInt(value, 10, 32); err != nil { |
|
return err |
|
} else { |
|
vf.SetInt(tmp) |
|
} |
|
case reflect.Int64: |
|
if len(tagArr) == 3 { |
|
format := tagArr[2] |
|
// parse time |
|
if format == "time" { |
|
if tmp, err := parseTime(value); err != nil { |
|
return err |
|
} else { |
|
vf.SetInt(tmp) |
|
} |
|
} else { |
|
return errors.New(fmt.Sprintf("unknown tag: %s in struct field: %s (support tags: \"time\")", format, tf.Name)) |
|
} |
|
} else { |
|
if tmp, err := strconv.ParseInt(value, 10, 64); err != nil { |
|
return err |
|
} else { |
|
vf.SetInt(tmp) |
|
} |
|
} |
|
case reflect.Uint: |
|
if tmp, err := strconv.ParseUint(value, 10, 32); err != nil { |
|
return err |
|
} else { |
|
vf.SetUint(tmp) |
|
} |
|
case reflect.Uint8: |
|
if tmp, err := strconv.ParseUint(value, 10, 8); err != nil { |
|
return err |
|
} else { |
|
vf.SetUint(tmp) |
|
} |
|
case reflect.Uint16: |
|
if tmp, err := strconv.ParseUint(value, 10, 16); err != nil { |
|
return err |
|
} else { |
|
vf.SetUint(tmp) |
|
} |
|
case reflect.Uint32: |
|
if tmp, err := strconv.ParseUint(value, 10, 32); err != nil { |
|
return err |
|
} else { |
|
vf.SetUint(tmp) |
|
} |
|
case reflect.Uint64: |
|
if tmp, err := strconv.ParseUint(value, 10, 64); err != nil { |
|
return err |
|
} else { |
|
vf.SetUint(tmp) |
|
} |
|
case reflect.Slice: |
|
delim := "," |
|
if len(tagArr) > 2 { |
|
delim = tagArr[2] |
|
} |
|
strs := strings.Split(value, delim) |
|
sli := reflect.MakeSlice(tf.Type, 0, len(strs)) |
|
for _, str := range strs { |
|
vv, err := getValue(tf.Type.Elem().String(), str) |
|
if err != nil { |
|
return err |
|
} |
|
sli = reflect.Append(sli, vv) |
|
} |
|
vf.Set(sli) |
|
case reflect.Map: |
|
delim := "," |
|
if len(tagArr) > 2 { |
|
delim = tagArr[2] |
|
} |
|
strs := strings.Split(value, delim) |
|
m := reflect.MakeMap(tf.Type) |
|
for _, str := range strs { |
|
mapStrs := strings.SplitN(str, "=", 2) |
|
if len(mapStrs) < 2 { |
|
return errors.New(fmt.Sprintf("error map: %s, must be split by \"=\"", str)) |
|
} |
|
vk, err := getValue(tf.Type.Key().String(), mapStrs[0]) |
|
if err != nil { |
|
return err |
|
} |
|
vv, err := getValue(tf.Type.Elem().String(), mapStrs[1]) |
|
if err != nil { |
|
return err |
|
} |
|
m.SetMapIndex(vk, vv) |
|
} |
|
vf.Set(m) |
|
default: |
|
return errors.New(fmt.Sprintf("cannot unmarshall unsuported kind: %s into struct field: %s", vf.Kind().String(), tf.Name)) |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
// getValue parse String to the type "t" reflect.Value. |
|
func getValue(t, v string) (reflect.Value, error) { |
|
var vv reflect.Value |
|
switch t { |
|
case "bool": |
|
d := parseBool(v) |
|
vv = reflect.ValueOf(d) |
|
case "int": |
|
d, err := strconv.ParseInt(v, 10, 32) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(int(d)) |
|
case "int8": |
|
d, err := strconv.ParseInt(v, 10, 8) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(int8(d)) |
|
case "int16": |
|
d, err := strconv.ParseInt(v, 10, 16) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(int16(d)) |
|
case "int32": |
|
d, err := strconv.ParseInt(v, 10, 32) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(int32(d)) |
|
case "int64": |
|
d, err := strconv.ParseInt(v, 10, 64) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(int64(d)) |
|
case "uint": |
|
d, err := strconv.ParseUint(v, 10, 32) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(uint(d)) |
|
case "uint8": |
|
d, err := strconv.ParseUint(v, 10, 8) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(uint8(d)) |
|
case "uint16": |
|
d, err := strconv.ParseUint(v, 10, 16) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(uint16(d)) |
|
case "uint32": |
|
d, err := strconv.ParseUint(v, 10, 32) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(uint32(d)) |
|
case "uint64": |
|
d, err := strconv.ParseUint(v, 10, 64) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(uint64(d)) |
|
case "float32": |
|
d, err := strconv.ParseFloat(v, 32) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(float32(d)) |
|
case "float64": |
|
d, err := strconv.ParseFloat(v, 64) |
|
if err != nil { |
|
return vv, err |
|
} |
|
vv = reflect.ValueOf(float64(d)) |
|
case "string": |
|
vv = reflect.ValueOf(v) |
|
default: |
|
return vv, errors.New(fmt.Sprintf("unkown type: %s", t)) |
|
} |
|
return vv, nil |
|
}
|
|
|