add config module
This commit is contained in:
parent
f9bb9e3d84
commit
cde2e20315
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
Package config is a simple tool to handle key-value config, like Redis's configuration:
|
||||
|
||||
# commet
|
||||
key = value
|
||||
|
||||
*/
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNil = errors.New("nil value")
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Values map[string]string
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
c := &Config{}
|
||||
c.Values = make(map[string]string)
|
||||
return c
|
||||
}
|
||||
|
||||
/*
|
||||
bool: true, false, 0, 1, or ""
|
||||
*/
|
||||
func (c *Config) GetBool(key string) (bool, error) {
|
||||
v, err := c.GetString(key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
v = strings.ToLower(v)
|
||||
switch v {
|
||||
case "true", "1":
|
||||
return true, nil
|
||||
case "false", "0", "":
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("invalid bool format %s", v)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
int may be pure number or below format:
|
||||
|
||||
# 1k => 1000 bytes
|
||||
# 1kb => 1024 bytes
|
||||
# 1m => 1000000 bytes
|
||||
# 1mb => 1024*1024 bytes
|
||||
# 1g => 1000000000 bytes
|
||||
# 1gb => 1024*1024*1024 bytes
|
||||
|
||||
*/
|
||||
func (c *Config) GetInt64(key string) (int64, error) {
|
||||
v, err := c.GetString(key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(v) == 0 {
|
||||
return 0, ErrNil
|
||||
}
|
||||
|
||||
var scale int64 = 1
|
||||
v = strings.ToLower(v)
|
||||
|
||||
var b bool = false
|
||||
if v[len(v)-1] == 'b' {
|
||||
v = v[0 : len(v)-1]
|
||||
b = true
|
||||
}
|
||||
|
||||
if len(v) == 0 {
|
||||
return 0, fmt.Errorf("invalid number format %s", v)
|
||||
}
|
||||
|
||||
switch v[len(v)-1] {
|
||||
case 'k':
|
||||
v = v[0 : len(v)-1]
|
||||
if b {
|
||||
scale = 1024
|
||||
} else {
|
||||
scale = 1000
|
||||
}
|
||||
break
|
||||
case 'm':
|
||||
v = v[0 : len(v)-1]
|
||||
if b {
|
||||
scale = 1024 * 1024
|
||||
} else {
|
||||
scale = 1000 * 1000
|
||||
}
|
||||
case 'g':
|
||||
v = v[0 : len(v)-1]
|
||||
if b {
|
||||
scale = 1024 * 1024 * 1024
|
||||
} else {
|
||||
scale = 1000 * 1000 * 1000
|
||||
}
|
||||
}
|
||||
|
||||
var n int64
|
||||
n, err = strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return n * scale, nil
|
||||
}
|
||||
|
||||
func (c *Config) GetUint64(key string) (uint64, error) {
|
||||
v, err := c.GetInt64(key)
|
||||
if v < 0 {
|
||||
return 0, fmt.Errorf("negative number %d", v)
|
||||
}
|
||||
return uint64(v), err
|
||||
}
|
||||
|
||||
func (c *Config) GetInt(key string) (int, error) {
|
||||
v, err := c.GetInt64(key)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (c *Config) GetString(key string) (string, error) {
|
||||
v, ok := c.Values[key]
|
||||
if !ok {
|
||||
return "", ErrNil
|
||||
} else {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) SetString(key string, value string) {
|
||||
c.Values[key] = value
|
||||
}
|
||||
|
||||
func (c *Config) SetInt64(key string, n int64) {
|
||||
c.Values[key] = strconv.FormatInt(n, 10)
|
||||
}
|
||||
|
||||
func (c *Config) SetUint64(key string, n uint64) {
|
||||
c.Values[key] = strconv.FormatUint(n, 10)
|
||||
}
|
||||
|
||||
func (c *Config) SetInt(key string, n int) {
|
||||
c.SetInt64(key, int64(n))
|
||||
}
|
||||
|
||||
func (c *Config) SetBool(key string, v bool) {
|
||||
if v {
|
||||
c.Values[key] = "true"
|
||||
} else {
|
||||
c.Values[key] = "false"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testConfig(cfg *Config, t *testing.T) {
|
||||
if v, err := cfg.GetBool("a"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if v != true {
|
||||
t.Fatal(v)
|
||||
}
|
||||
|
||||
checkInt := func(t *testing.T, cfg *Config, key string, check int) {
|
||||
if v, err := cfg.GetInt(key); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if v != check {
|
||||
t.Fatal(fmt.Sprintf("%s %d != %d", key, v, check))
|
||||
}
|
||||
}
|
||||
|
||||
checkInt(t, cfg, "b", 100)
|
||||
checkInt(t, cfg, "kb", 1024)
|
||||
checkInt(t, cfg, "k", 1000)
|
||||
checkInt(t, cfg, "mb", 1024*1024)
|
||||
checkInt(t, cfg, "m", 1000*1000)
|
||||
checkInt(t, cfg, "gb", 1024*1024*1024)
|
||||
checkInt(t, cfg, "g", 1000*1000*1000)
|
||||
}
|
||||
|
||||
func TestGetConfig(t *testing.T) {
|
||||
cfg := NewConfig()
|
||||
cfg.Values["a"] = "true"
|
||||
cfg.Values["b"] = "100"
|
||||
cfg.Values["kb"] = "1kb"
|
||||
cfg.Values["k"] = "1k"
|
||||
cfg.Values["mb"] = "1mb"
|
||||
cfg.Values["m"] = "1m"
|
||||
cfg.Values["gb"] = "1gb"
|
||||
cfg.Values["g"] = "1g"
|
||||
|
||||
testConfig(cfg, t)
|
||||
}
|
||||
|
||||
func TestReadWriteConfig(t *testing.T) {
|
||||
var b = []byte(`
|
||||
# comment
|
||||
a = true
|
||||
b = 100
|
||||
kb = 1kb
|
||||
k = 1k
|
||||
mb = 1mb
|
||||
m = 1m
|
||||
gb = 1gb
|
||||
g = 1g
|
||||
`)
|
||||
|
||||
cfg, err := ReadConfig(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testConfig(cfg, t)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
if err := cfg.Write(&buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg.Values = make(map[string]string)
|
||||
if err := cfg.Read(&buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testConfig(cfg, t)
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ReadConfigFile(name string) (*Config, error) {
|
||||
data, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ReadConfig(data)
|
||||
|
||||
}
|
||||
|
||||
func ReadConfig(data []byte) (*Config, error) {
|
||||
cfg := NewConfig()
|
||||
|
||||
if err := cfg.Read(bytes.NewBuffer(data)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (c *Config) Read(r io.Reader) error {
|
||||
rb := bufio.NewReaderSize(r, 4096)
|
||||
|
||||
for {
|
||||
l, err := rb.ReadString('\n')
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
} else if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
l = strings.TrimSpace(l)
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
//comment
|
||||
if l[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
ps := strings.Split(l, "=")
|
||||
if len(ps) > 2 {
|
||||
return fmt.Errorf("invalid line format %s", l)
|
||||
} else if len(ps) == 1 {
|
||||
c.SetString(ps[0], "")
|
||||
} else {
|
||||
c.SetString(strings.TrimSpace(ps[0]), strings.TrimSpace(ps[1]))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func (c *Config) Write(w io.Writer) error {
|
||||
var buf bytes.Buffer
|
||||
|
||||
for k, v := range c.Values {
|
||||
buf.WriteString(fmt.Sprintf("%s = %s\n", k, v))
|
||||
}
|
||||
|
||||
_, err := w.Write(buf.Bytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Config) WriteFile(filePath string) error {
|
||||
filePathBak := fmt.Sprintf("%s.bak.tmp", filePath)
|
||||
|
||||
fd, err := os.OpenFile(filePathBak, os.O_CREATE|os.O_WRONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.Write(fd)
|
||||
fd.Close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Rename(filePathBak, filePath)
|
||||
}
|
Loading…
Reference in New Issue