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 }