diff options
-rw-r--r-- | simpleconf.go | 168 |
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) +} |