diff --git a/config/config.go b/config/config.go
new file mode 100644
index 00000000..0e6a00b8
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,43 @@
+package config
+
+import (
+ "fmt"
+)
+
+type ConfigContainer interface {
+ Set(key, val string) error
+ String(key string) string
+ Int(key string) (int, error)
+ Int64(key string) (int64, error)
+ Bool(key string) (bool, error)
+ Float(key string) (float64, error)
+ DIY(key string) (interface{}, error)
+}
+
+type Config interface {
+ Parse(key string) (ConfigContainer, error)
+}
+
+var adapters = make(map[string]Config)
+
+// Register makes a config adapter available by the adapter name.
+// If Register is called twice with the same name or if driver is nil,
+// it panics.
+func Register(name string, adapter Config) {
+ if adapter == nil {
+ panic("config: Register adapter is nil")
+ }
+ if _, dup := adapters[name]; dup {
+ panic("config: Register called twice for adapter " + name)
+ }
+ adapters[name] = adapter
+}
+
+// config need to be correct JSON as string: {"interval":360}
+func NewConfig(adapterName, fileaname string) (ConfigContainer, error) {
+ adapter, ok := adapters[adapterName]
+ if !ok {
+ return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName)
+ }
+ return adapter.Parse(fileaname)
+}
diff --git a/config/ini.go b/config/ini.go
new file mode 100644
index 00000000..1effaea1
--- /dev/null
+++ b/config/ini.go
@@ -0,0 +1,134 @@
+package config
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+ "unicode"
+)
+
+var (
+ bComment = []byte{'#'}
+ bEmpty = []byte{}
+ bEqual = []byte{'='}
+ bDQuote = []byte{'"'}
+)
+
+type IniConfig struct {
+}
+
+// ParseFile creates a new Config and parses the file configuration from the
+// named file.
+func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
+ file, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+
+ cfg := &IniConfigContainer{
+ file.Name(),
+ make(map[int][]string),
+ make(map[string]string),
+ make(map[string]int64),
+ sync.RWMutex{},
+ }
+ cfg.Lock()
+ defer cfg.Unlock()
+ defer file.Close()
+
+ var comment bytes.Buffer
+ buf := bufio.NewReader(file)
+
+ for nComment, off := 0, int64(1); ; {
+ line, _, err := buf.ReadLine()
+ if err == io.EOF {
+ break
+ }
+ if bytes.Equal(line, bEmpty) {
+ continue
+ }
+
+ off += int64(len(line))
+
+ if bytes.HasPrefix(line, bComment) {
+ line = bytes.TrimLeft(line, "#")
+ line = bytes.TrimLeftFunc(line, unicode.IsSpace)
+ comment.Write(line)
+ comment.WriteByte('\n')
+ continue
+ }
+ if comment.Len() != 0 {
+ cfg.comment[nComment] = []string{comment.String()}
+ comment.Reset()
+ nComment++
+ }
+
+ val := bytes.SplitN(line, bEqual, 2)
+ if bytes.HasPrefix([]byte(strings.TrimSpace(string(val[1]))), bDQuote) {
+ val[1] = bytes.Trim([]byte(strings.TrimSpace(string(val[1]))), `"`)
+ }
+
+ key := strings.TrimSpace(string(val[0]))
+ cfg.comment[nComment-1] = append(cfg.comment[nComment-1], key)
+ cfg.data[key] = strings.TrimSpace(string(val[1]))
+ cfg.offset[key] = off
+ }
+ return cfg, nil
+}
+
+// A Config represents the configuration.
+type IniConfigContainer struct {
+ filename string
+ comment map[int][]string // id: []{comment, key...}; id 1 is for main comment.
+ data map[string]string // key: value
+ offset map[string]int64 // key: offset; for editing.
+ sync.RWMutex
+}
+
+// Bool returns the boolean value for a given key.
+func (c *IniConfigContainer) Bool(key string) (bool, error) {
+ return strconv.ParseBool(c.data[key])
+}
+
+// Int returns the integer value for a given key.
+func (c *IniConfigContainer) Int(key string) (int, error) {
+ return strconv.Atoi(c.data[key])
+}
+
+func (c *IniConfigContainer) Int64(key string) (int64, error) {
+ return strconv.ParseInt(c.data[key], 10, 64)
+}
+
+// Float returns the float value for a given key.
+func (c *IniConfigContainer) Float(key string) (float64, error) {
+ return strconv.ParseFloat(c.data[key], 64)
+}
+
+// String returns the string value for a given key.
+func (c *IniConfigContainer) String(key string) string {
+ return c.data[key]
+}
+
+// WriteValue writes a new value for key.
+func (c *IniConfigContainer) Set(key, value string) error {
+ c.Lock()
+ defer c.Unlock()
+ c.data[key] = value
+ return nil
+}
+
+func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) {
+ if v, ok := c.data[key]; ok {
+ return v, nil
+ }
+ return v, errors.New("key not find")
+}
+
+func init() {
+ Register("ini", &IniConfig{})
+}
diff --git a/config/ini_test.go b/config/ini_test.go
new file mode 100644
index 00000000..444fa40f
--- /dev/null
+++ b/config/ini_test.go
@@ -0,0 +1,66 @@
+package config
+
+import (
+ "os"
+ "testing"
+)
+
+var inicontext = `
+appname = beeapi
+httpport = 8080
+mysqlport = 3600
+PI = 3.1415976
+runmode = "dev"
+autorender = false
+copyrequestbody = true
+`
+
+func TestIni(t *testing.T) {
+ f, err := os.Create("testini.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = f.WriteString(inicontext)
+ if err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ f.Close()
+ defer os.Remove("testini.conf")
+ iniconf, err := NewConfig("ini", "testini.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if iniconf.String("appname") != "beeapi" {
+ t.Fatal("appname not equal to beeapi")
+ }
+ if port, err := iniconf.Int("httpport"); err != nil || port != 8080 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if port, err := iniconf.Int64("mysqlport"); err != nil || port != 3600 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if pi, err := iniconf.Float("PI"); err != nil || pi != 3.1415976 {
+ t.Error(pi)
+ t.Fatal(err)
+ }
+ if iniconf.String("runmode") != "dev" {
+ t.Fatal("runmode not equal to dev")
+ }
+ if v, err := iniconf.Bool("autorender"); err != nil || v != false {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if v, err := iniconf.Bool("copyrequestbody"); err != nil || v != true {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if err = iniconf.Set("name", "astaxie"); err != nil {
+ t.Fatal(err)
+ }
+ if iniconf.String("name") != "astaxie" {
+ t.Fatal("get name error")
+ }
+}
diff --git a/config/json.go b/config/json.go
new file mode 100644
index 00000000..2b4021e8
--- /dev/null
+++ b/config/json.go
@@ -0,0 +1,90 @@
+package config
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "os"
+ "sync"
+)
+
+type JsonConfig struct {
+}
+
+func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ x := &JsonConfigContainer{
+ data: make(map[string]interface{}),
+ }
+ content, err := ioutil.ReadAll(file)
+ if err != nil {
+ return nil, err
+ }
+ err = json.Unmarshal(content, &x.data)
+ if err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+type JsonConfigContainer struct {
+ data map[string]interface{}
+ sync.Mutex
+}
+
+func (c *JsonConfigContainer) Bool(key string) (bool, error) {
+ if v, ok := c.data[key].(bool); ok {
+ return v, nil
+ }
+ return false, errors.New("not bool value")
+}
+
+func (c *JsonConfigContainer) Int(key string) (int, error) {
+ if v, ok := c.data[key].(float64); ok {
+ return int(v), nil
+ }
+ return 0, errors.New("not int value")
+}
+
+func (c *JsonConfigContainer) Int64(key string) (int64, error) {
+ if v, ok := c.data[key].(float64); ok {
+ return int64(v), nil
+ }
+ return 0, errors.New("not bool value")
+}
+
+func (c *JsonConfigContainer) Float(key string) (float64, error) {
+ if v, ok := c.data[key].(float64); ok {
+ return v, nil
+ }
+ return 0.0, errors.New("not float64 value")
+}
+
+func (c *JsonConfigContainer) String(key string) string {
+ if v, ok := c.data[key].(string); ok {
+ return v
+ }
+ return ""
+}
+
+func (c *JsonConfigContainer) Set(key, val string) error {
+ c.Lock()
+ defer c.Unlock()
+ c.data[key] = val
+ return nil
+}
+
+func (c *JsonConfigContainer) DIY(key string) (v interface{}, err error) {
+ if v, ok := c.data[key]; ok {
+ return v, nil
+ }
+ return nil, errors.New("not exist key")
+}
+
+func init() {
+ Register("json", &JsonConfig{})
+}
diff --git a/config/json_test.go b/config/json_test.go
new file mode 100644
index 00000000..6e207585
--- /dev/null
+++ b/config/json_test.go
@@ -0,0 +1,66 @@
+package config
+
+import (
+ "os"
+ "testing"
+)
+
+var jsoncontext = `{
+"appname": "beeapi",
+"httpport": 8080,
+"mysqlport": 3600,
+"PI": 3.1415976,
+"runmode": "dev",
+"autorender": false,
+"copyrequestbody": true
+}`
+
+func TestJson(t *testing.T) {
+ f, err := os.Create("testjson.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = f.WriteString(jsoncontext)
+ if err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ f.Close()
+ defer os.Remove("testjson.conf")
+ jsonconf, err := NewConfig("json", "testjson.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if jsonconf.String("appname") != "beeapi" {
+ t.Fatal("appname not equal to beeapi")
+ }
+ if port, err := jsonconf.Int("httpport"); err != nil || port != 8080 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if port, err := jsonconf.Int64("mysqlport"); err != nil || port != 3600 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if pi, err := jsonconf.Float("PI"); err != nil || pi != 3.1415976 {
+ t.Error(pi)
+ t.Fatal(err)
+ }
+ if jsonconf.String("runmode") != "dev" {
+ t.Fatal("runmode not equal to dev")
+ }
+ if v, err := jsonconf.Bool("autorender"); err != nil || v != false {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if v, err := jsonconf.Bool("copyrequestbody"); err != nil || v != true {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if err = jsonconf.Set("name", "astaxie"); err != nil {
+ t.Fatal(err)
+ }
+ if jsonconf.String("name") != "astaxie" {
+ t.Fatal("get name error")
+ }
+}
diff --git a/config/xml.go b/config/xml.go
new file mode 100644
index 00000000..a4f9735c
--- /dev/null
+++ b/config/xml.go
@@ -0,0 +1,82 @@
+//xml parse should incluce in tags
+
+package config
+
+import (
+ "errors"
+ "github.com/clbanning/x2j"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "sync"
+)
+
+type XMLConfig struct {
+}
+
+func (xmls *XMLConfig) Parse(filename string) (ConfigContainer, error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ x := &XMLConfigContainer{
+ data: make(map[string]interface{}),
+ }
+ content, err := ioutil.ReadAll(file)
+ if err != nil {
+ return nil, err
+ }
+ d, err := x2j.DocToMap(string(content))
+ if err != nil {
+ return nil, err
+ }
+ x.data = d["config"].(map[string]interface{})
+ return x, nil
+}
+
+type XMLConfigContainer struct {
+ data map[string]interface{}
+ sync.Mutex
+}
+
+func (c *XMLConfigContainer) Bool(key string) (bool, error) {
+ return strconv.ParseBool(c.data[key].(string))
+}
+
+func (c *XMLConfigContainer) Int(key string) (int, error) {
+ return strconv.Atoi(c.data[key].(string))
+}
+
+func (c *XMLConfigContainer) Int64(key string) (int64, error) {
+ return strconv.ParseInt(c.data[key].(string), 10, 64)
+}
+
+func (c *XMLConfigContainer) Float(key string) (float64, error) {
+ return strconv.ParseFloat(c.data[key].(string), 64)
+}
+
+func (c *XMLConfigContainer) String(key string) string {
+ if v, ok := c.data[key].(string); ok {
+ return v
+ }
+ return ""
+}
+
+func (c *XMLConfigContainer) Set(key, val string) error {
+ c.Lock()
+ defer c.Unlock()
+ c.data[key] = val
+ return nil
+}
+
+func (c *XMLConfigContainer) DIY(key string) (v interface{}, err error) {
+ if v, ok := c.data[key]; ok {
+ return v, nil
+ }
+ return nil, errors.New("not exist key")
+}
+
+func init() {
+ Register("xml", &XMLConfig{})
+}
diff --git a/config/xml_test.go b/config/xml_test.go
new file mode 100644
index 00000000..1e429e06
--- /dev/null
+++ b/config/xml_test.go
@@ -0,0 +1,69 @@
+package config
+
+import (
+ "os"
+ "testing"
+)
+
+//xml parse should incluce in tags
+var xmlcontext = `
+
+beeapi
+8080
+3600
+3.1415976
+dev
+false
+true
+
+`
+
+func TestXML(t *testing.T) {
+ f, err := os.Create("testxml.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = f.WriteString(xmlcontext)
+ if err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ f.Close()
+ defer os.Remove("testxml.conf")
+ xmlconf, err := NewConfig("xml", "testxml.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if xmlconf.String("appname") != "beeapi" {
+ t.Fatal("appname not equal to beeapi")
+ }
+ if port, err := xmlconf.Int("httpport"); err != nil || port != 8080 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if port, err := xmlconf.Int64("mysqlport"); err != nil || port != 3600 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if pi, err := xmlconf.Float("PI"); err != nil || pi != 3.1415976 {
+ t.Error(pi)
+ t.Fatal(err)
+ }
+ if xmlconf.String("runmode") != "dev" {
+ t.Fatal("runmode not equal to dev")
+ }
+ if v, err := xmlconf.Bool("autorender"); err != nil || v != false {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if v, err := xmlconf.Bool("copyrequestbody"); err != nil || v != true {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if err = xmlconf.Set("name", "astaxie"); err != nil {
+ t.Fatal(err)
+ }
+ if xmlconf.String("name") != "astaxie" {
+ t.Fatal("get name error")
+ }
+}
diff --git a/config/yaml.go b/config/yaml.go
new file mode 100644
index 00000000..8ba3ca58
--- /dev/null
+++ b/config/yaml.go
@@ -0,0 +1,126 @@
+package config
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "github.com/wendal/goyaml2"
+ "io/ioutil"
+ "log"
+ "os"
+ "sync"
+)
+
+type YAMLConfig struct {
+}
+
+func (yaml *YAMLConfig) Parse(filename string) (ConfigContainer, error) {
+ y := &YAMLConfigContainer{
+ data: make(map[string]interface{}),
+ }
+ cnf, err := ReadYmlReader(filename)
+ if err != nil {
+ return nil, err
+ }
+ y.data = cnf
+ return y, nil
+}
+
+// 从Reader读取YAML
+func ReadYmlReader(path string) (cnf map[string]interface{}, err error) {
+ err = nil
+ f, err := os.Open(path)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ err = nil
+ buf, err := ioutil.ReadAll(f)
+ if err != nil || len(buf) < 3 {
+ return
+ }
+
+ if string(buf[0:1]) == "{" {
+ log.Println("Look lile a Json, try it")
+ err = json.Unmarshal(buf, &cnf)
+ if err == nil {
+ log.Println("It is Json Map")
+ return
+ }
+ }
+
+ _map, _err := goyaml2.Read(bytes.NewBuffer(buf))
+ if _err != nil {
+ log.Println("Goyaml2 ERR>", string(buf), _err)
+ //err = goyaml.Unmarshal(buf, &cnf)
+ err = _err
+ return
+ }
+ if _map == nil {
+ log.Println("Goyaml2 output nil? Pls report bug\n" + string(buf))
+ }
+ cnf, ok := _map.(map[string]interface{})
+ if !ok {
+ log.Println("Not a Map? >> ", string(buf), _map)
+ cnf = nil
+ }
+ return
+}
+
+type YAMLConfigContainer struct {
+ data map[string]interface{}
+ sync.Mutex
+}
+
+func (c *YAMLConfigContainer) Bool(key string) (bool, error) {
+ if v, ok := c.data[key].(bool); ok {
+ return v, nil
+ }
+ return false, errors.New("not bool value")
+}
+
+func (c *YAMLConfigContainer) Int(key string) (int, error) {
+ if v, ok := c.data[key].(int); ok {
+ return v, nil
+ }
+ return 0, errors.New("not int value")
+}
+
+func (c *YAMLConfigContainer) Int64(key string) (int64, error) {
+ if v, ok := c.data[key].(int64); ok {
+ return v, nil
+ }
+ return 0, errors.New("not bool value")
+}
+
+func (c *YAMLConfigContainer) Float(key string) (float64, error) {
+ if v, ok := c.data[key].(float64); ok {
+ return v, nil
+ }
+ return 0.0, errors.New("not float64 value")
+}
+
+func (c *YAMLConfigContainer) String(key string) string {
+ if v, ok := c.data[key].(string); ok {
+ return v
+ }
+ return ""
+}
+
+func (c *YAMLConfigContainer) Set(key, val string) error {
+ c.Lock()
+ defer c.Unlock()
+ c.data[key] = val
+ return nil
+}
+
+func (c *YAMLConfigContainer) DIY(key string) (v interface{}, err error) {
+ if v, ok := c.data[key]; ok {
+ return v, nil
+ }
+ return nil, errors.New("not exist key")
+}
+
+func init() {
+ Register("yaml", &YAMLConfig{})
+}
diff --git a/config/yaml_tast.go b/config/yaml_tast.go
new file mode 100644
index 00000000..45142e76
--- /dev/null
+++ b/config/yaml_tast.go
@@ -0,0 +1,66 @@
+package config
+
+import (
+ "os"
+ "testing"
+)
+
+var yamlcontext = `
+"appname": "beeapi",
+"httpport": 8080,
+"mysqlport": 3600,
+"PI": 3.1415976,
+"runmode": "dev",
+"autorender": false,
+"copyrequestbody": true
+`
+
+func TestYaml(t *testing.T) {
+ f, err := os.Create("testyaml.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = f.WriteString(yamlcontext)
+ if err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ f.Close()
+ defer os.Remove("testyaml.conf")
+ yamlconf, err := NewConfig("yaml", "testyaml.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if yamlconf.String("appname") != "beeapi" {
+ t.Fatal("appname not equal to beeapi")
+ }
+ if port, err := yamlconf.Int("httpport"); err != nil || port != 8080 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if port, err := yamlconf.Int64("mysqlport"); err != nil || port != 3600 {
+ t.Error(port)
+ t.Fatal(err)
+ }
+ if pi, err := yamlconf.Float("PI"); err != nil || pi != 3.1415976 {
+ t.Error(pi)
+ t.Fatal(err)
+ }
+ if yamlconf.String("runmode") != "dev" {
+ t.Fatal("runmode not equal to dev")
+ }
+ if v, err := yamlconf.Bool("autorender"); err != nil || v != false {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if v, err := yamlconf.Bool("copyrequestbody"); err != nil || v != true {
+ t.Error(v)
+ t.Fatal(err)
+ }
+ if err = yamlconf.Set("name", "astaxie"); err != nil {
+ t.Fatal(err)
+ }
+ if yamlconf.String("name") != "astaxie" {
+ t.Fatal("get name error")
+ }
+}