commit b1b8c0140feb4e21c1c0d8512cca0ac0861728c4 Author: kayomn Date: Tue Dec 20 17:04:39 2022 +0000 Initial commit diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ea27136 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module sauce.pizzawednes.day/kayomn/ini-gusher + +go 1.19 diff --git a/ini.go b/ini.go new file mode 100644 index 0000000..5166836 --- /dev/null +++ b/ini.go @@ -0,0 +1,98 @@ +package ini + +import ( + "bufio" + "io" + "strings" +) + +// Singular key-value pair under the given section within an INI file. +type Entry struct { + Section string + Key string + Value string +} + +// Returns the last error that occured during parsing. +func (parser *Parser) Err() error { + return parser.err +} + +// Creates and returns a new [Parser] by reference from `reader` +func NewParser(reader io.Reader) *Parser { + return &Parser{ + scanner: bufio.NewScanner(reader), + } +} + +// Parses the next key-value pair in the `parser` stream, returning the parsed [Entry], or an empty +// one if nothing was parsed, and a `bool` representing whether or not there is any further data +// available to parse. +// +// Note that the `parser` does not guarantee any parse order for key-value pairs extracted from the +// `parser` stream. +func (parser *Parser) Parse() (Entry, bool) { + for parser.scanner.Scan() { + var line = strings.TrimSpace(parser.scanner.Text()) + var lineLen = len(line) + + if lineLen == 0 { + // Skip empty lines. + continue + } + + if (line[0] == '#') || (line[0] == ';') { + // Skip comment lines. + continue + } + + var lineTail = lineLen - 1 + + if (line[0] == '[') && (line[lineTail] == ']') { + // Section name. + parser.section = line[1:lineTail] + + continue + } + + if assignmentIndex := strings.Index(line, "="); assignmentIndex > -1 { + // Key with value. + var value = strings.TrimSpace(line[assignmentIndex+1:]) + var valueLen = len(value) + + if valueLen != 0 { + var valueTail = len(value) - 1 + + if (value[0] == '"') && (value[valueTail] == '"') { + value = value[1:valueTail] + } + } + + return Entry{ + Section: parser.section, + Key: strings.TrimSpace(line[0:assignmentIndex]), + Value: value, + }, true + } + + // Key which is its own value. + var keyValue = line[1:lineTail] + + return Entry{ + Section: parser.section, + Key: keyValue, + Value: keyValue, + }, true + } + + parser.err = parser.scanner.Err() + + return Entry{}, false +} + +// State machine for parsing streamable INI file sources. +type Parser struct { + scanner *bufio.Scanner + err error + section string +}