summaryrefslogtreecommitdiff
path: root/internal/ini/ini.go
blob: 36277e070f1661c2a8d77d5ec4c82553f876f5d3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// Package ini is a tiny dependency-free reader for the boring INI files lectio
// uses for config and calendar data: [sections] (including "a/b" subsections),
// "key = value" pairs (keys may be dotted, e.g. name.pl), and full-line
// "#"/";" comments. Comments are FULL-LINE only (a line whose first non-blank
// character is # or ;); values are kept verbatim so scripture citations may
// contain ";" and "#". It preserves order and never interprets values beyond
// trimming surrounding whitespace.
package ini

import (
	"bufio"
	"bytes"
	"fmt"
	"strings"
)

type Pair struct{ Key, Val string }

type Section struct {
	Name  string
	Pairs []Pair
}

// Parse reads INI bytes into ordered sections. Pairs before the first [section]
// go into a leading section with an empty Name.
func Parse(data []byte) ([]Section, error) {
	secs := []Section{{Name: ""}}
	sc := bufio.NewScanner(bytes.NewReader(data))
	sc.Buffer(make([]byte, 0, 64*1024), 1024*1024)
	line := 0
	for sc.Scan() {
		line++
		s := strings.TrimSpace(sc.Text())
		if s == "" || strings.HasPrefix(s, "#") || strings.HasPrefix(s, ";") {
			continue // blank or full-line comment
		}
		if strings.HasPrefix(s, "[") {
			if !strings.HasSuffix(s, "]") {
				return nil, fmt.Errorf("ini: line %d: unclosed section header %q", line, s)
			}
			name := strings.TrimSpace(s[1 : len(s)-1])
			secs = append(secs, Section{Name: name})
			continue
		}
		eq := strings.IndexByte(s, '=')
		if eq < 0 {
			return nil, fmt.Errorf("ini: line %d: expected key = value, got %q", line, s)
		}
		key := strings.TrimSpace(s[:eq])
		val := strings.TrimSpace(s[eq+1:]) // verbatim (may contain ';' or '#')
		cur := &secs[len(secs)-1]
		cur.Pairs = append(cur.Pairs, Pair{key, val})
	}
	return secs, sc.Err()
}

// List splits a comma-separated value, trimming spaces and dropping empties.
func List(val string) []string {
	var out []string
	for _, p := range strings.Split(val, ",") {
		if t := strings.TrimSpace(p); t != "" {
			out = append(out, t)
		}
	}
	return out
}