109 lines
2.1 KiB
Go
109 lines
2.1 KiB
Go
package conf
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
strComment = "#"
|
|
strEqSign = "="
|
|
)
|
|
|
|
var (
|
|
ErrFormat = errors.New("conf: line does not match \"key = value\" pattern")
|
|
ErrDuplicateKey = errors.New("conf: duplicate key found")
|
|
)
|
|
|
|
// Open reads values from file. It returns nil and error if os.Open(filename) fails.
|
|
// It would be wise to always check returned error.
|
|
// ParseFile captures two types of values: "key = value" and "option". Either key value pair or
|
|
// option must be put in its own line in the file.
|
|
// Key or option must be a single word. In example line:
|
|
//
|
|
// "option1 option2"
|
|
//
|
|
// only option1 will be captured. option2 needs to be in a separate line in the file to take effect.
|
|
// In line "key = value1,value2,value3" all of value1, value2, and value3 will be
|
|
// captured. They can be later accesed separately with Setting's Split() method.
|
|
func Open(filename string) (*Conf, error) {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c, err := parseReader(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c, file.Close()
|
|
}
|
|
|
|
// Read reads from r and returns Config.
|
|
func Read(r io.Reader) (*Conf, error) {
|
|
return parseReader(r)
|
|
}
|
|
|
|
func parseReader(r io.Reader) (*Conf, error) {
|
|
settings := make(map[string]string)
|
|
|
|
scanner := bufio.NewScanner(r)
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
|
|
line = trim(stripComment(line))
|
|
if line == "" {
|
|
continue
|
|
}
|
|
|
|
key, value, ok := toKV(line)
|
|
if !ok {
|
|
return nil, ErrFormat
|
|
}
|
|
|
|
if _, ok := settings[key]; ok {
|
|
return nil, fmt.Errorf("%w: %s", ErrDuplicateKey, key)
|
|
}
|
|
|
|
settings[key] = value
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Conf{
|
|
settings: settings,
|
|
}, nil
|
|
}
|
|
|
|
func toKV(s string) (string, string, bool) {
|
|
k, v, ok := strings.Cut(s, strEqSign)
|
|
if !ok || k == "" || v == "" {
|
|
return "", "", false
|
|
}
|
|
|
|
return trim(k), trim(v), true
|
|
}
|
|
|
|
func trim(s string) string {
|
|
s = strings.TrimSpace(s)
|
|
|
|
return s
|
|
}
|
|
|
|
func stripComment(s string) string {
|
|
idx := strings.Index(s, strComment)
|
|
if idx > -1 {
|
|
s = s[:idx]
|
|
}
|
|
|
|
return s
|
|
}
|