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 } // Returns `true` if `parser` has reached the end of parsable tokens, either because the stream has // ended or it has encountered an error. func (parser *Parser) IsEnd() bool { return parser.isEnd } // Creates and returns a new [Parser] by reference from `reader` func NewParser(reader io.Reader) *Parser { return &Parser{ scanner: bufio.NewScanner(reader), isEnd: false, } } // 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 { 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. return Entry{ Section: parser.section, Key: unquote(strings.TrimSpace(line[0:assignmentIndex])), Value: unquote(strings.TrimSpace(line[assignmentIndex+1:])), } } // Key which is its own value. var keyValue = unquote(line[1:lineTail]) return Entry{ Section: parser.section, Key: keyValue, Value: keyValue, } } parser.err = parser.scanner.Err() parser.isEnd = true return Entry{} } // State machine for parsing streamable INI file sources. type Parser struct { scanner *bufio.Scanner err error section string isEnd bool } // Returns a string with the the outer-most quotes surrounding `s` trimmed, should they exist. func unquote(s string) string { var sLen = len(s) if sLen != 0 { var sTail = sLen - 1 if (s[0] == '"') && (s[sTail] == '"') { return s[1:sTail] } } return s }