1313// limitations under the License.
1414//
1515// author: Cong Ding <dinggnu@gmail.com>
16- //
16+
1717package config
1818
1919import (
2020 "bufio"
2121 "errors"
22+ "fmt"
23+ "io/ioutil"
2224 "os"
2325 "strings"
2426)
2527
2628var commentPrefix = []string {"//" , "#" , ";" }
2729
28- func Read (filename string ) (map [string ]string , error ) {
29- var res = map [string ]string {}
30- in , err := os .Open (filename )
30+ // Config struct constructs a new configuration handler.
31+ type Config struct {
32+ filename string
33+ config map [string ]map [string ]string
34+ }
35+
36+ // NewConfig function cnstructs a new Config struct with filename. You have to
37+ // call Read() function to let it read from the file. Otherwise you will get
38+ // empty string (i.e., "") when you are calling Get() function. Another usage
39+ // is that you call NewConfig() function and then call Add()/Set() function to
40+ // add new key-values to the configuration. Finally you can call Write()
41+ // function to write the new configuration to the file.
42+ func NewConfig (filename string ) * Config {
43+ c := new (Config )
44+ c .filename = filename
45+ c .config = make (map [string ]map [string ]string )
46+ return c
47+ }
48+
49+ // Filename function returns the filename of the configuration.
50+ func (c * Config ) Filename () string {
51+ return c .filename
52+ }
53+
54+ // SetFilename function sets the filename of the configuration.
55+ func (c * Config ) SetFilename (filename string ) {
56+ c .filename = filename
57+ }
58+
59+ // Reset function reset the map in the configuration.
60+ func (c * Config ) Reset () {
61+ c .config = make (map [string ]map [string ]string )
62+ }
63+
64+ // Read function reads configurations from the file defined in
65+ // Config.filename.
66+ func (c * Config ) Read () error {
67+ in , err := os .Open (c .filename )
3168 if err != nil {
32- return res , err
69+ return err
3370 }
71+ defer in .Close ()
3472 scanner := bufio .NewScanner (in )
3573 line := ""
3674 section := ""
@@ -39,9 +77,9 @@ func Read(filename string) (map[string]string, error) {
3977 continue
4078 }
4179 if line == "" {
42- sec := checkSection (scanner .Text ())
43- if sec != "" {
44- section = sec + "."
80+ sec , ok := checkSection (scanner .Text ())
81+ if ok {
82+ section = sec
4583 continue
4684 }
4785 }
@@ -53,41 +91,103 @@ func Read(filename string) (map[string]string, error) {
5391 line = line [:len (line )- 1 ]
5492 continue
5593 }
56- key , value , err := checkLine (line )
57- if err != nil {
58- return res , errors .New ("WRONG: " + line )
94+ key , value , ok := checkLine (line )
95+ if ! ok {
96+ return errors .New ("WRONG: " + line )
5997 }
60- res [ section + key ] = value
98+ c . Set ( section , key , value )
6199 line = ""
62100 }
63- in .Close ()
64- return res , nil
101+ return nil
102+ }
103+
104+ // Get function returns the value of a key in the configuration. If the key
105+ // does not exist, it returns empty string (i.e., "").
106+ func (c * Config ) Get (section string , key string ) string {
107+ value , ok := c.config [section ][key ]
108+ if ! ok {
109+ return ""
110+ }
111+ return value
65112}
66113
67- func checkSection (line string ) string {
114+ // Set function updates the value of a key in the configuration. Function
115+ // Set() is exactly the same as function Add().
116+ func (c * Config ) Set (section string , key string , value string ) {
117+ _ , ok := c .config [section ]
118+ if ! ok {
119+ c .config [section ] = make (map [string ]string )
120+ }
121+ c.config [section ][key ] = value
122+ }
123+
124+ // Add function adds a new key to the configuration. Function Add() is exactly
125+ // the same as function Set().
126+ func (c * Config ) Add (section string , key string , value string ) {
127+ c .Set (section , key , value )
128+ }
129+
130+ // Del function deletes a key from the configuration.
131+ func (c * Config ) Del (section string , key string ) {
132+ _ , ok := c .config [section ]
133+ if ok {
134+ delete (c .config [section ], key )
135+ if len (c .config [section ]) == 0 {
136+ delete (c .config , section )
137+ }
138+ }
139+ }
140+
141+ // Write function writes the updated configuration back.
142+ func (c * Config ) Write () error {
143+ return nil
144+ }
145+
146+ // WriteTo function writes the configuration to a new file. This function
147+ // re-organizes the configuration and deletes all the comments.
148+ func (c * Config ) WriteTo (filename string ) error {
149+ content := ""
150+ for k , v := range c .config {
151+ format := "%v = %v\n "
152+ if k != "" {
153+ content += fmt .Sprintf ("[%v]\n " , k )
154+ format = "\t " + format
155+ }
156+ for key , value := range v {
157+ content += fmt .Sprintf (format , key , value )
158+ }
159+ }
160+ return ioutil .WriteFile (filename , []byte (content ), 0644 )
161+ }
162+
163+ // To check this line is a section or not. If it is not a section, it returns
164+ // "".
165+ func checkSection (line string ) (string , bool ) {
68166 line = strings .TrimSpace (line )
69167 lineLen := len (line )
70168 if lineLen < 2 {
71- return ""
169+ return "" , false
72170 }
73171 if line [0 ] == '[' && line [lineLen - 1 ] == ']' {
74- return line [1 : lineLen - 1 ]
172+ return line [1 : lineLen - 1 ], true
75173 }
76- return ""
174+ return "" , false
77175}
78176
79- func checkLine (line string ) (string , string , error ) {
177+ // To check this line is a valid key-value pair or not.
178+ func checkLine (line string ) (string , string , bool ) {
80179 key := ""
81180 value := ""
82181 sp := strings .SplitN (line , "=" , 2 )
83182 if len (sp ) != 2 {
84- return key , value , errors . New ( "WRONG: " + line )
183+ return key , value , false
85184 }
86185 key = strings .TrimSpace (sp [0 ])
87186 value = strings .TrimSpace (sp [1 ])
88- return key , value , nil
187+ return key , value , true
89188}
90189
190+ // To check this line is a whole line comment or not.
91191func checkComment (line string ) bool {
92192 line = strings .TrimSpace (line )
93193 for p := range commentPrefix {
0 commit comments