package settings import ( "bufio" "fmt" "io" "strings" ) var ( KeyValueDelimiterChars = []byte{':', '='} ErrNoValidDelimiter = fmt.Errorf("no valid delimiter found (use %s)", KeyValueDelimiterCharsToString()) ) func KeyValueDelimiterCharsToString() string { var builder strings.Builder for i, char := range KeyValueDelimiterChars { builder.WriteString(fmt.Sprintf("'%c'", char)) if i < len(KeyValueDelimiterChars)-1 { builder.WriteString(" or ") } } return builder.String() } func LineIsComment(line string) bool { return strings.HasPrefix(line, "//") || strings.HasPrefix(line, "#") } type Scanner struct { lineNumber int scanner *bufio.Scanner } // returns errors without any key/value. // does not clone the strings it extracted. func (s *Scanner) ReadNextLine() (key, value string, err error) { for s.scanner.Scan() { s.lineNumber++ line := strings.TrimSpace(s.scanner.Text()) if len(line) == 0 || LineIsComment(line) { continue } splitIndex := strings.IndexAny(line, string(KeyValueDelimiterChars)) if splitIndex == -1 { return "", "", fmt.Errorf("line %d: %w", s.lineNumber, ErrNoValidDelimiter) } key = strings.TrimSpace(line[:splitIndex]) value = strings.TrimSpace(line[splitIndex+1:]) return } return "", "", io.EOF } func NewScanner(r io.Reader) *Scanner { return &Scanner{ lineNumber: 0, scanner: bufio.NewScanner(r), } }