~nilium/go-ini

2b409f95484ac137a379a050af85585626f5e30a — Noel Cower 7 years ago 78ffead
Add comments for most exported names

Covers exported functions, types, variables, and constants.

This has one code change, in that it changes ReadINI to use
a bytes.Reader instead of a bytes.Buffer. This is primarily to avoid
giving the decoder only a reader type without also providing a writer,
but also because it just makes more sense.

Given that ReadINI is a pre-rewrite function kept around for
compatibility, it's not the most urgent function present. In spite of
that, test readers now include a bytes.Reader in addition to
bytes.Buffer, strings.Reader, and so on.

Change-Id: I6cc240f4ec1a9e57801d05d5f1eca8e65dad1b4b
3 files changed, 73 insertions(+), 8 deletions(-)

M errors.go
M ini.go
M ini_test.go
M errors.go => errors.go +17 -2
@@ 5,6 5,9 @@ import (
	"fmt"
)

// SyntaxError is an error returned when the INI parser encounters any syntax it does not
// understand. It contains the line, column, any other error encountered, and a description of the
// syntax error.
type SyntaxError struct {
	Line, Col int
	Err       error


@@ 18,8 21,13 @@ func (s *SyntaxError) Error() string {
	return fmt.Sprintf("ini: syntax error at %d:%d: %v -- %s", s.Line, s.Col, s.Err, s.Desc)
}

// UnclosedError is an error describing an unclosed bracket from {, (, [, and <. It is typically set
// as the Err field of a SyntaxError.
//
// Its value is expected to be one of the above opening braces.
type UnclosedError rune

// Expecting returns the rune that was expected but not found for the UnclosedError's rune value.
func (u UnclosedError) Expecting() rune {
	switch u := rune(u); u {
	case '{':


@@ 39,6 47,8 @@ func (u UnclosedError) Error() string {
	return fmt.Sprintf("ini: unclosed %c, expecting %c", rune(u), u.Expecting())
}

// BadCharError is an error describing an invalid character encountered during parsing. It is
// typically set as the Err field of a SyntaxError.
type BadCharError rune

func (r BadCharError) Error() string {


@@ 46,9 56,14 @@ func (r BadCharError) Error() string {
}

var (
	ErrSectionRawStr   = errors.New("ini: raw string not accepted in section")
	// ErrSectionRawStr is a syntax error seen when a raw string is present in a location that
	// is not valid.
	ErrSectionRawStr = errors.New("ini: raw string not accepted in section")
	// ErrUnclosedSection is a syntax error seen when a section name has not been closed.
	ErrUnclosedSection = errors.New("ini: section missing closing ]")
	ErrEmptyKey        = errors.New("ini: key is empty")
	// ErrEmptyKey is a syntax error seen if a key is empty.
	ErrEmptyKey = errors.New("ini: key is empty")

	// ErrBadNewline is a BadCharError for unexpected newlines.
	ErrBadNewline = BadCharError('\n')
)

M ini.go => ini.go +54 -6
@@ 42,14 42,18 @@ const (
// Values is any set of INI values. This may be used as a Recorder for a Reader.
type Values map[string][]string

// Set replaces key with a slice containing only value.
func (v Values) Set(key, value string) {
	v[key] = []string{value}
}

// Add adds a value to key's value slice (allocating one if none is present).
func (v Values) Add(key, value string) {
	v[key] = append(v[key], value)
}

// Get returns the first value for key. If key does not exist or has an empty value slice, Get
// returns an empty string.
func (v Values) Get(key string) string {
	if d := v[key]; len(d) > 0 {
		return d[0]


@@ 57,15 61,20 @@ func (v Values) Get(key string) string {
	return ""
}

// Del removes the key from the receiver.
func (v Values) Del(key string) {
	delete(v, key)
}

// Contains returns true if the key is defined in the Values map. This is an existence check, not
// a content check, so the key may be for a nil or empty slice.
func (v Values) Contains(key string) bool {
	_, ok := v[key]
	return ok
}

// Copy copies the values from the receiver to dst. If a key from the receiver exists in dst, the
// values from the receiver are appended to the values of dst[key].
func (v Values) Copy(dst Values) Values {
	if dst == nil {
		dst = make(Values, len(v))


@@ 76,7 85,10 @@ func (v Values) Copy(dst Values) Values {
	return dst
}

func (v Values) Matching(dst Values, fn func(string, []string) bool) Values {
// Matching iterates through the receiver's values, calling fn for each key and its values. If fn
// returns true, that key's values are returned in the resulting Values. fn must not modify the
// values slice it receives.
func (v Values) Matching(dst Values, fn func(key string, values []string) bool) Values {
	if dst == nil {
		dst = make(Values)
	}


@@ 88,6 100,7 @@ func (v Values) Matching(dst Values, fn func(string, []string) bool) Values {
	return dst
}

// WithPrefix returns a copy of Values containing only the keys with the given prefix string.
func (v Values) WithPrefix(dst Values, prefix string) Values {
	return v.Matching(dst, func(k string, _ []string) bool {
		return strings.HasPrefix(k, prefix)


@@ 98,6 111,9 @@ func (v Values) WithPrefix(dst Values, prefix string) Values {
// function. If nextfunc returns io.EOF, parsing is complete. Any other error halts parsing.
type nextfunc func() (nextfunc, error)

// decoder is a wrapper around an io.Reader for the purpose of doing by-rune parsing of INI file
// input. It also holds enough state to track line, column, key prefixes (from sections), and
// errors.
type decoder struct {
	true string



@@ 116,8 132,8 @@ type decoder struct {
	// Storage
	buffer  bytes.Buffer
	key     string
	prefix  []byte // prefix is prepended to all buffered keys
	prefix2 [32]byte
	prefix  []byte   // prefix is prepended to all buffered keys
	prefix2 [32]byte // prefix2 is a buffer to hold most key prefixes

	// peek / next state
	havenext bool


@@ 125,13 141,19 @@ type decoder struct {
	nexterr  error
}

// True is the default value provided to value-less keys in INI files. This is done to treat
// value-less keys as boolean-on flags in INI files.
const True string = "1"

// ReadINI reads an INI file from b, writing the results to the out Values. If out is nil, a new
// Values is allocated to store the results.
//
// ReadINI is a convenience function for calling DefaultDecoder.Read(bytes.NewReader(b), out).
func ReadINI(b []byte, out Values) (Values, error) {
	if out == nil {
		out = make(Values)
	}
	err := DefaultDecoder.Read(bytes.NewBuffer(b), out)
	err := DefaultDecoder.Read(bytes.NewReader(b), out)
	if err != nil {
		return nil, err
	}


@@ 568,6 590,13 @@ func (d *decoder) readElem() (next nextfunc, err error) {

var defaultSeparator = []byte{'.'}

// None is a value to force a Reader.True to indicate that empty keys should have no value or for
// Reader.Separator to indicate there should be no separator string between section keys and value
// keys.
//
// None is a specific sequence of garbage control characters, just due to it being unlikely that you
// would want it as a true or separator value. This is not guaranteed to be the same value between
// versions of the package.
const None = "\x00\x00\x13\x15\xff\x00\x12\x00\x13"

func (d *decoder) reset(cfg *Reader, dst Recorder, rd io.Reader) {


@@ 638,6 667,9 @@ func (d *decoder) read() (err error) {
	return err
}

// KeyCase is an option value to change how unquoted keys are handled. For example, to lowercase all
// unquoted portions of a key, you would use LowerCase (the default of new Readers and zero value).
// This allows for basic case normalization across files.
type KeyCase int

const (


@@ 650,6 682,8 @@ const (
	CaseSensitive
)

// DefaultDecoder is the default Reader. Its separator is a "." (period), its True value is the
// string "1", and keys are case-sensitive.
var DefaultDecoder = Reader{
	Separator: ".",
	Casing:    CaseSensitive,


@@ 663,12 697,26 @@ type Recorder interface {
	Add(key, value string)
}

// Reader is an INI reader configuration. It does not hold state and may be copied as needed.
// It is not safe to modify a Reader while Reading, however, as the internal decoder keeps a pointer
// to the Reader.
type Reader struct {
	// Separator is the string that is inserted between key segments (i.e., given a Separator of
	// ":", the string "[a b c]\nd = 5" evaluates out to a:b:c:d = 5). If Separator is None, not
	// the empty string, there is no separator. If Separator is the empty string, it defaults to
	// "." (period).
	Separator string
	Casing    KeyCase
	True      string
	// Casing controls how unquoted key segments are cased. If LowerCase (the default / zero
	// value), unquoted key segments are converted to lowercase. If UpperCase, they're made
	// uppercase. If CaseSensitive, key case is the same as the input.
	Casing KeyCase
	// True is the value string used for keys with no value. For example, if True is "T"
	// (assuming default Separator), given the input "[a b c]\nd", it evaluates to a.b.c.d = T.
	True string
}

// Read decodes INI file input from r and conveys it to dst. If an error occurs, it is returned. If
// the error is an EOF before parsing is finished, io.ErrUnexpectedEOF is returned.
func (d *Reader) Read(r io.Reader, dst Recorder) error {
	var dec decoder
	dec.reset(d, dst, r)

M ini_test.go => ini_test.go +2 -0
@@ 11,12 11,14 @@ import (

var succReaders = map[string]func(string) io.Reader{
	"bytes.Buffer":         func(s string) io.Reader { return bytes.NewBufferString(s) },
	"bytes.Reader":         func(s string) io.Reader { return bytes.NewReader([]byte(s)) },
	"strings.Reader":       func(s string) io.Reader { return strings.NewReader(s) },
	"iotest.OneByteReader": func(s string) io.Reader { return iotest.OneByteReader(strings.NewReader(s)) },
}

var failReaders = map[string]func(string) io.Reader{
	"bytes.Buffer":         func(s string) io.Reader { return bytes.NewBufferString(s) },
	"bytes.Reader":         func(s string) io.Reader { return bytes.NewReader([]byte(s)) },
	"strings.Reader":       func(s string) io.Reader { return strings.NewReader(s) },
	"iotest.OneByteReader": func(s string) io.Reader { return iotest.OneByteReader(strings.NewReader(s)) },
	"iotest.DataErrReader": func(s string) io.Reader { return iotest.DataErrReader(strings.NewReader(s)) },