aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--simpleconf.go168
1 files changed, 168 insertions, 0 deletions
diff --git a/simpleconf.go b/simpleconf.go
new file mode 100644
index 0000000..777cd53
--- /dev/null
+++ b/simpleconf.go
@@ -0,0 +1,168 @@
+package simpleconf
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+)
+
+type Config map[string]Section
+type Section map[string]string
+
+func (c Config) addSection(section Section, name string) {
+ if section != nil {
+ c[name] = section
+ }
+}
+
+func Load(r io.Reader) (config Config, outerr error) {
+ config = make(Config)
+ scanner := bufio.NewScanner(r)
+
+ var section Section
+ var sectName string
+
+ for l := 1; scanner.Scan(); l++ {
+ line := strings.TrimSpace(scanner.Text())
+ if len(line) == 0 {
+ continue
+ }
+
+ switch line[0] {
+ case ';', '#':
+ continue
+ case '[':
+ parts := strings.SplitN(line, "[", 2)
+ parts = strings.SplitN(parts[1], "]", 2)
+
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("Missing closing ']' in section header at line %d", l)
+ }
+ if len(parts[1]) != 0 {
+ return nil, fmt.Errorf("More data after closing ']' at line %d", l)
+ }
+
+ config.addSection(section, sectName)
+ section = make(Section)
+ default:
+ parts := strings.SplitN(line, "=", 2)
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("Couldn't neither find a comment, a section header nor a key-value pair at line %d", l)
+ }
+
+ key := strings.TrimSpace(parts[0])
+ val := strings.TrimSpace(parts[1])
+ if len(key) == 0 {
+ return nil, fmt.Errorf("Empty key at line %d", l)
+ }
+
+ if section == nil {
+ return nil, fmt.Errorf("Found key-value pair, but no section at line %d", l)
+ }
+
+ section[key] = val
+ }
+ }
+
+ config.addSection(section, sectName)
+
+ outerr = scanner.Err()
+ return
+}
+
+var (
+ NotFound = errors.New("Section or key not found.")
+ NotBool = errors.New("Could not interpret value as bool.")
+)
+
+func (c Config) GetString(section, key string) (string, error) {
+ s, ok := c[section]
+ if !ok {
+ return "", NotFound
+ }
+ rv, ok := s[key]
+ if !ok {
+ return "", NotFound
+ }
+ return rv, nil
+}
+
+func (c Config) GetStringDefault(d, section, key string) (string, error) {
+ rv, err := c.GetString(section, key)
+ if err == NotFound {
+ return d, nil
+ }
+ return rv, err
+}
+
+func (c Config) GetInt(section, key string) (int64, error) {
+ s, err := c.GetString(section, key)
+ if err != nil {
+ return 0, err
+ }
+ return strconv.ParseInt(s, 10, 64)
+}
+
+func (c Config) GetIntDefault(d int64, section, key string) (int64, error) {
+ rv, err := c.GetInt(section, key)
+ if err == NotFound {
+ return d, nil
+ }
+ return rv, err
+}
+
+func (c Config) GetFloat(section, key string) (float64, error) {
+ s, err := c.GetString(section, key)
+ if err != nil {
+ return 0, err
+ }
+ return strconv.ParseFloat(s, 64)
+}
+
+func (c Config) GetFloatDefault(d float64, section, key string) (float64, error) {
+ rv, err := c.GetFloat(section, key)
+ if err == NotFound {
+ return d, nil
+ }
+ return rv, err
+}
+
+func (c Config) GetBool(section, key string) (bool, error) {
+ s, err := c.GetString(section, key)
+ if err != nil {
+ return false, err
+ }
+ switch strings.ToLower(s) {
+ case "true", "on", "yes", "y", "1":
+ return true, nil
+ case "false", "off", "no", "n", "0":
+ return false, nil
+ default:
+ return false, NotBool
+ }
+}
+
+func (c Config) GetBoolDefault(d bool, section, key string) (bool, error) {
+ rv, err := c.GetBool(section, key)
+ if err == NotFound {
+ return d, nil
+ }
+ return rv, err
+}
+
+func (c Config) GetFile(flag int, perm os.FileMode, section, key string) (*os.File, error) {
+ s, err := c.GetString(section, key)
+ if err != nil {
+ return nil, err
+ }
+
+ return os.OpenFile(s, flag, perm)
+}
+
+func (c Config) GetFileReadonly(section, key string) (*os.File, error) {
+ return c.GetFile(os.O_RDONLY, 0, section, key)
+}