support section
if iniconf.String("demo.key1") != "asta" {
+ t.Fatal("get demo.key1 error")
+ }
+ if iniconf.String("demo.key2") != "xie" {
+ t.Fatal("get demo.key2 error")
+ }
Showing
2 changed files
with
94 additions
and
25 deletions
| ... | @@ -13,10 +13,14 @@ import ( | ... | @@ -13,10 +13,14 @@ import ( |
| 13 | ) | 13 | ) |
| 14 | 14 | ||
| 15 | var ( | 15 | var ( |
| 16 | DEFAULT_SECTION = "DEFAULT" | ||
| 16 | bComment = []byte{'#'} | 17 | bComment = []byte{'#'} |
| 18 | alterComment = []byte{';'} | ||
| 17 | bEmpty = []byte{} | 19 | bEmpty = []byte{} |
| 18 | bEqual = []byte{'='} | 20 | bEqual = []byte{'='} |
| 19 | bDQuote = []byte{'"'} | 21 | bDQuote = []byte{'"'} |
| 22 | sectionStart = []byte{'['} | ||
| 23 | sectionEnd = []byte{']'} | ||
| 20 | ) | 24 | ) |
| 21 | 25 | ||
| 22 | type IniConfig struct { | 26 | type IniConfig struct { |
| ... | @@ -32,9 +36,9 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { | ... | @@ -32,9 +36,9 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { |
| 32 | 36 | ||
| 33 | cfg := &IniConfigContainer{ | 37 | cfg := &IniConfigContainer{ |
| 34 | file.Name(), | 38 | file.Name(), |
| 35 | make(map[int][]string), | 39 | make(map[string]map[string]string), |
| 40 | make(map[string]string), | ||
| 36 | make(map[string]string), | 41 | make(map[string]string), |
| 37 | make(map[string]int64), | ||
| 38 | sync.RWMutex{}, | 42 | sync.RWMutex{}, |
| 39 | } | 43 | } |
| 40 | cfg.Lock() | 44 | cfg.Lock() |
| ... | @@ -43,8 +47,8 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { | ... | @@ -43,8 +47,8 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { |
| 43 | 47 | ||
| 44 | var comment bytes.Buffer | 48 | var comment bytes.Buffer |
| 45 | buf := bufio.NewReader(file) | 49 | buf := bufio.NewReader(file) |
| 46 | 50 | section := DEFAULT_SECTION | |
| 47 | for nComment, off := 0, int64(1); ; { | 51 | for { |
| 48 | line, _, err := buf.ReadLine() | 52 | line, _, err := buf.ReadLine() |
| 49 | if err == io.EOF { | 53 | if err == io.EOF { |
| 50 | break | 54 | break |
| ... | @@ -52,8 +56,7 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { | ... | @@ -52,8 +56,7 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { |
| 52 | if bytes.Equal(line, bEmpty) { | 56 | if bytes.Equal(line, bEmpty) { |
| 53 | continue | 57 | continue |
| 54 | } | 58 | } |
| 55 | 59 | line = bytes.TrimSpace(line) | |
| 56 | off += int64(len(line)) | ||
| 57 | 60 | ||
| 58 | if bytes.HasPrefix(line, bComment) { | 61 | if bytes.HasPrefix(line, bComment) { |
| 59 | line = bytes.TrimLeft(line, "#") | 62 | line = bytes.TrimLeft(line, "#") |
| ... | @@ -62,21 +65,40 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { | ... | @@ -62,21 +65,40 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { |
| 62 | comment.WriteByte('\n') | 65 | comment.WriteByte('\n') |
| 63 | continue | 66 | continue |
| 64 | } | 67 | } |
| 65 | if comment.Len() != 0 { | 68 | if bytes.HasPrefix(line, alterComment) { |
| 66 | cfg.comment[nComment] = []string{comment.String()} | 69 | line = bytes.TrimLeft(line, ";") |
| 70 | line = bytes.TrimLeftFunc(line, unicode.IsSpace) | ||
| 71 | comment.Write(line) | ||
| 72 | comment.WriteByte('\n') | ||
| 73 | continue | ||
| 74 | } | ||
| 75 | if bytes.HasPrefix(line, sectionStart) && bytes.HasSuffix(line, sectionEnd) { | ||
| 76 | section = string(line[1 : len(line)-1]) | ||
| 77 | if comment.Len() > 0 { | ||
| 78 | cfg.sectionComment[section] = comment.String() | ||
| 67 | comment.Reset() | 79 | comment.Reset() |
| 68 | nComment++ | 80 | } |
| 81 | if _, ok := cfg.data[section]; !ok { | ||
| 82 | cfg.data[section] = make(map[string]string) | ||
| 83 | } | ||
| 84 | } else { | ||
| 85 | if _, ok := cfg.data[section]; !ok { | ||
| 86 | cfg.data[section] = make(map[string]string) | ||
| 87 | } | ||
| 88 | keyval := bytes.SplitN(line, bEqual, 2) | ||
| 89 | val := bytes.TrimSpace(keyval[1]) | ||
| 90 | if bytes.HasPrefix(val, bDQuote) { | ||
| 91 | val = bytes.Trim(val, `"`) | ||
| 69 | } | 92 | } |
| 70 | 93 | ||
| 71 | val := bytes.SplitN(line, bEqual, 2) | 94 | key := string(bytes.TrimSpace(keyval[0])) |
| 72 | if bytes.HasPrefix([]byte(strings.TrimSpace(string(val[1]))), bDQuote) { | 95 | cfg.data[section][key] = string(val) |
| 73 | val[1] = bytes.Trim([]byte(strings.TrimSpace(string(val[1]))), `"`) | 96 | if comment.Len() > 0 { |
| 97 | cfg.keycomment[section+"."+key] = comment.String() | ||
| 98 | comment.Reset() | ||
| 99 | } | ||
| 74 | } | 100 | } |
| 75 | 101 | ||
| 76 | key := strings.TrimSpace(string(val[0])) | ||
| 77 | cfg.comment[nComment-1] = append(cfg.comment[nComment-1], key) | ||
| 78 | cfg.data[key] = strings.TrimSpace(string(val[1])) | ||
| 79 | cfg.offset[key] = off | ||
| 80 | } | 102 | } |
| 81 | return cfg, nil | 103 | return cfg, nil |
| 82 | } | 104 | } |
| ... | @@ -84,41 +106,53 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { | ... | @@ -84,41 +106,53 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { |
| 84 | // A Config represents the configuration. | 106 | // A Config represents the configuration. |
| 85 | type IniConfigContainer struct { | 107 | type IniConfigContainer struct { |
| 86 | filename string | 108 | filename string |
| 87 | comment map[int][]string // id: []{comment, key...}; id 1 is for main comment. | 109 | data map[string]map[string]string //section=> key:val |
| 88 | data map[string]string // key: value | 110 | sectionComment map[string]string //sction : comment |
| 89 | offset map[string]int64 // key: offset; for editing. | 111 | keycomment map[string]string // id: []{comment, key...}; id 1 is for main comment. |
| 90 | sync.RWMutex | 112 | sync.RWMutex |
| 91 | } | 113 | } |
| 92 | 114 | ||
| 93 | // Bool returns the boolean value for a given key. | 115 | // Bool returns the boolean value for a given key. |
| 94 | func (c *IniConfigContainer) Bool(key string) (bool, error) { | 116 | func (c *IniConfigContainer) Bool(key string) (bool, error) { |
| 95 | return strconv.ParseBool(c.data[key]) | 117 | return strconv.ParseBool(c.getdata(key)) |
| 96 | } | 118 | } |
| 97 | 119 | ||
| 98 | // Int returns the integer value for a given key. | 120 | // Int returns the integer value for a given key. |
| 99 | func (c *IniConfigContainer) Int(key string) (int, error) { | 121 | func (c *IniConfigContainer) Int(key string) (int, error) { |
| 100 | return strconv.Atoi(c.data[key]) | 122 | return strconv.Atoi(c.getdata(key)) |
| 101 | } | 123 | } |
| 102 | 124 | ||
| 103 | func (c *IniConfigContainer) Int64(key string) (int64, error) { | 125 | func (c *IniConfigContainer) Int64(key string) (int64, error) { |
| 104 | return strconv.ParseInt(c.data[key], 10, 64) | 126 | return strconv.ParseInt(c.getdata(key), 10, 64) |
| 105 | } | 127 | } |
| 106 | 128 | ||
| 107 | // Float returns the float value for a given key. | 129 | // Float returns the float value for a given key. |
| 108 | func (c *IniConfigContainer) Float(key string) (float64, error) { | 130 | func (c *IniConfigContainer) Float(key string) (float64, error) { |
| 109 | return strconv.ParseFloat(c.data[key], 64) | 131 | return strconv.ParseFloat(c.getdata(key), 64) |
| 110 | } | 132 | } |
| 111 | 133 | ||
| 112 | // String returns the string value for a given key. | 134 | // String returns the string value for a given key. |
| 113 | func (c *IniConfigContainer) String(key string) string { | 135 | func (c *IniConfigContainer) String(key string) string { |
| 114 | return c.data[key] | 136 | return c.getdata(key) |
| 115 | } | 137 | } |
| 116 | 138 | ||
| 117 | // WriteValue writes a new value for key. | 139 | // WriteValue writes a new value for key. |
| 118 | func (c *IniConfigContainer) Set(key, value string) error { | 140 | func (c *IniConfigContainer) Set(key, value string) error { |
| 119 | c.Lock() | 141 | c.Lock() |
| 120 | defer c.Unlock() | 142 | defer c.Unlock() |
| 121 | c.data[key] = value | 143 | if len(key) == 0 { |
| 144 | return errors.New("key is empty") | ||
| 145 | } | ||
| 146 | var section, k string | ||
| 147 | sectionkey := strings.Split(key, ".") | ||
| 148 | if len(sectionkey) >= 2 { | ||
| 149 | section = sectionkey[0] | ||
| 150 | k = sectionkey[1] | ||
| 151 | } else { | ||
| 152 | section = DEFAULT_SECTION | ||
| 153 | k = sectionkey[0] | ||
| 154 | } | ||
| 155 | c.data[section][k] = value | ||
| 122 | return nil | 156 | return nil |
| 123 | } | 157 | } |
| 124 | 158 | ||
| ... | @@ -129,6 +163,30 @@ func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) { | ... | @@ -129,6 +163,30 @@ func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) { |
| 129 | return v, errors.New("key not find") | 163 | return v, errors.New("key not find") |
| 130 | } | 164 | } |
| 131 | 165 | ||
| 166 | //section.key or key | ||
| 167 | func (c *IniConfigContainer) getdata(key string) string { | ||
| 168 | c.RLock() | ||
| 169 | defer c.RUnlock() | ||
| 170 | if len(key) == 0 { | ||
| 171 | return "" | ||
| 172 | } | ||
| 173 | var section, k string | ||
| 174 | sectionkey := strings.Split(key, ".") | ||
| 175 | if len(sectionkey) >= 2 { | ||
| 176 | section = sectionkey[0] | ||
| 177 | k = sectionkey[1] | ||
| 178 | } else { | ||
| 179 | section = DEFAULT_SECTION | ||
| 180 | k = sectionkey[0] | ||
| 181 | } | ||
| 182 | if v, ok := c.data[section]; ok { | ||
| 183 | if vv, o := v[k]; o { | ||
| 184 | return vv | ||
| 185 | } | ||
| 186 | } | ||
| 187 | return "" | ||
| 188 | } | ||
| 189 | |||
| 132 | func init() { | 190 | func init() { |
| 133 | Register("ini", &IniConfig{}) | 191 | Register("ini", &IniConfig{}) |
| 134 | } | 192 | } | ... | ... |
| ... | @@ -6,6 +6,8 @@ import ( | ... | @@ -6,6 +6,8 @@ import ( |
| 6 | ) | 6 | ) |
| 7 | 7 | ||
| 8 | var inicontext = ` | 8 | var inicontext = ` |
| 9 | ;comment one | ||
| 10 | #comment two | ||
| 9 | appname = beeapi | 11 | appname = beeapi |
| 10 | httpport = 8080 | 12 | httpport = 8080 |
| 11 | mysqlport = 3600 | 13 | mysqlport = 3600 |
| ... | @@ -13,6 +15,9 @@ PI = 3.1415976 | ... | @@ -13,6 +15,9 @@ PI = 3.1415976 |
| 13 | runmode = "dev" | 15 | runmode = "dev" |
| 14 | autorender = false | 16 | autorender = false |
| 15 | copyrequestbody = true | 17 | copyrequestbody = true |
| 18 | [demo] | ||
| 19 | key1="asta" | ||
| 20 | key2 = "xie" | ||
| 16 | ` | 21 | ` |
| 17 | 22 | ||
| 18 | func TestIni(t *testing.T) { | 23 | func TestIni(t *testing.T) { |
| ... | @@ -63,4 +68,10 @@ func TestIni(t *testing.T) { | ... | @@ -63,4 +68,10 @@ func TestIni(t *testing.T) { |
| 63 | if iniconf.String("name") != "astaxie" { | 68 | if iniconf.String("name") != "astaxie" { |
| 64 | t.Fatal("get name error") | 69 | t.Fatal("get name error") |
| 65 | } | 70 | } |
| 71 | if iniconf.String("demo.key1") != "asta" { | ||
| 72 | t.Fatal("get demo.key1 error") | ||
| 73 | } | ||
| 74 | if iniconf.String("demo.key2") != "xie" { | ||
| 75 | t.Fatal("get demo.key2 error") | ||
| 76 | } | ||
| 66 | } | 77 | } | ... | ... |
-
Please register or sign in to post a comment