gcfg

package module
v0.0.0-...-46a135d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 6, 2017 License: BSD-2-Clause, BSD-3-Clause Imports: 14 Imported by: 0

README

gcfg

Fork of Péter Surányi's INI-style configuration file parser in Go.

Original project page: https://code.google.com/p/gcfg

Build Status (Linux)

Additional Features

Custom type parsers

Custom parsers can be registered with gcfg runtime. This is usually needed for "imported" types that do not implement text unmarshalling.

The below example enables parsing of time.Duration and url.URL types:

package main

import (
	"github.com/baobabus/gcfg"
	"net/url"
	"time"
)

func init() {
	var d time.Duration
	gcfg.RegisterTypeParser(reflect.TypeOf(d), func(blank bool, val string) (interface{}, error) {
		if blank {
			return nil, nil
		}
		return time.ParseDuration(val)
	})
	gcfg.RegisterTypeParser(reflect.TypeOf(url.URL{}), func(blank bool, val string) (interface{}, error) {
		if blank {
			return nil, nil
		}
		return url.Parse(val)
	})
}
[remote]
url = http://foo.com/bar/
timeout = 1m

Optional configuration sections

Configuration sections can be pointers to structs. The section structure will only be allocated and the reference updated if the input explicitly specifies the section.

In the below example, if Secondary is initially nil, it will remain nil unless [secondary] is explicitly specified in the input .ini file.

type Gateway struct {
	Url     *url.URL
	Timeout time.Duration
}

type Config struct {
	Primary   Gateway
	Secondary *Gateway
}

Constraints

Ordered types can have bounds specified:

type Gateway struct {
	Url     *url.URL
	Timeout time.Duration `min:"5s" max:"15m"`
}

Strings can have their length constrained:

type Config struct {
	Message string `minlen:"1" maxlen:"400"`
}

Protected fields

Configuration section fields can be locked down. This prevents the field from being set from .ini file.

The following definition:

type Config struct {
	Foo string `gcfg:"-"`
}

will produce an error when reading config:

[config]
foo = blah

Basic support for writing out the configuration

With certain limitations, runtime structures can be written out in .INI format.

	gcfg.Write(&myConfig, os.Stdout)

Documentation

Overview

Package gcfg reads "INI-style" text-based configuration files with "name=value" pairs grouped into sections (gcfg files).

This package is still a work in progress; see the sections below for planned changes.

Syntax

The syntax is based on that used by git config: http://git-scm.com/docs/git-config#_syntax . There are some (planned) differences compared to the git config format:

  • improve data portability:
  • must be encoded in UTF-8 (for now) and must not contain the 0 byte
  • include and "path" type is not supported (path type may be implementable as a user-defined type)
  • internationalization
  • section and variable names can contain unicode letters, unicode digits (as defined in http://golang.org/ref/spec#Characters ) and hyphens (U+002D), starting with a unicode letter
  • validation
  • mapping of data fields of ordered types can optionally specify minimum and maximum values
  • mapping of data fields of string types can optionally specify minimum and maximum length
  • (planned) mapping of data fields of slice types can optionally specify minimum and maximum element count
  • disallow potentially ambiguous or misleading definitions:
  • `[sec.sub]` format is not allowed (deprecated in gitconfig)
  • `[sec ""]` is not allowed
  • use `[sec]` for section name "sec" and empty subsection name
  • (planned) within a single file, definitions must be contiguous for each:
  • section: '[secA]' -> '[secB]' -> '[secA]' is an error
  • subsection: '[sec "A"]' -> '[sec "B"]' -> '[sec "A"]' is an error
  • multivalued variable: 'multi=a' -> 'other=x' -> 'multi=b' is an error

Data structure

The functions in this package read values into a user-defined struct. Each section corresponds to a struct field or a field that is a pointer to a struct in the config struct, and each variable in a section corresponds to a data field in the section struct. The mapping of each section or variable name to fields is done either based on the "gcfg" struct tag or by matching the name of the section or variable, ignoring case. In the latter case, hyphens '-' in section and variable names correspond to underscores '_' in field names. Fields must be exported; to use a section or variable name starting with a letter that is neither upper- or lower-case, prefix the field name with 'X'. (See https://code.google.com/p/go/issues/detail?id=5763#c4 .)

For sections with subsections, the corresponding field in config must be a map, rather than a struct, with string keys and pointer-to-struct values. Values for subsection variables are stored in the map with the subsection name used as the map key. (Note that unlike section and variable names, subsection names are case sensitive.) When using a map, and there is a section with the same section name but without a subsection name, its values are stored with the empty string used as the key.

The functions in this package panic if config is not a pointer to a struct, or when a field is not of a suitable type (either a struct or a map with string keys and pointer-to-struct values).

Parsing of values

The section structs in the config struct may contain single-valued or multi-valued variables. Variables of unnamed slice type (that is, a type starting with `[]`) are treated as multi-value; all others (including named slice types) are treated as single-valued variables.

Single-valued variables are handled based on the type as follows. Unnamed pointer types (that is, types starting with `*`) are dereferenced, and if necessary, a new instance is allocated.

For types implementing the encoding.TextUnmarshaler interface, the UnmarshalText method is used to set the value. Implementing this method is the recommended way for parsing user-defined types.

For fields of string kind, the value string is assigned to the field, after unquoting and unescaping as needed. For fields of bool kind, the field is set to true if the value is "true", "yes", "on" or "1", and set to false if the value is "false", "no", "off" or "0", ignoring case. In addition, single-valued bool fields can be specified with a "blank" value (variable name without equals sign and value); in such case the value is set to true.

Predefined integer types [u]int(|8|16|32|64) and big.Int are parsed as decimal or hexadecimal (if having '0x' prefix). (This is to prevent unintuitively handling zero-padded numbers as octal.) Other types having [u]int* as the underlying type, such as os.FileMode and uintptr allow decimal, hexadecimal, or octal values. Parsing mode for integer types can be overridden using the struct tag option ",int=mode" where mode is a combination of the 'd', 'h', and 'o' characters (each standing for decimal, hexadecimal, and octal, respectively.)

Custom parser can be registered using RegisterTypeParser() function. This is useful for types that require special handling and which don't implement encoding.TextUnmarshaler interface (such as time.Duration).

All other types are parsed using fmt.Sscanf with the "%v" verb.

For multi-valued variables, each individual value is parsed as above and appended to the slice. If the first value is specified as a "blank" value (variable name without equals sign and value), a new slice is allocated; that is any values previously set in the slice will be ignored.

The types subpackage for provides helpers for parsing "enum-like" and integer types.

TODO

The following is a list of changes under consideration:

  • documentation
  • self-contained syntax documentation
  • more practical examples
  • move TODOs to issue tracker (eventually)
  • syntax
  • reconsider valid escape sequences (gitconfig doesn't support \r in value, \t in subsection name, etc.)
  • reading / parsing gcfg files
  • define internal representation structure
  • support multiple inputs (readers, strings, files)
  • support declaring encoding (?)
  • support varying fields sets for subsections (?)
  • writing gcfg files
  • error handling
  • make error context accessible programmatically?
  • limit input size?

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ReadFileInto

func ReadFileInto(config interface{}, filename string) error

ReadFileInto reads gcfg formatted data from the file filename and sets the values into the corresponding fields in config.

func ReadInto

func ReadInto(config interface{}, reader io.Reader) error

ReadInto reads gcfg formatted data from reader and sets the values into the corresponding fields in config.

func ReadStringInto

func ReadStringInto(config interface{}, str string) error

ReadStringInto reads gcfg formatted data from str and sets the values into the corresponding fields in config.

Example
package main

import (
	"fmt"
	"log"

	"github.com/baobabus/gcfg"
)

func main() {
	cfgStr := `; Comment line
[section]
name=value # comment`
	cfg := struct {
		Section struct {
			Name string
		}
	}{}
	err := gcfg.ReadStringInto(&cfg, cfgStr)
	if err != nil {
		log.Fatalf("Failed to parse gcfg data: %s", err)
	}
	fmt.Println(cfg.Section.Name)
}
Output:

value
Example (Bool)
package main

import (
	"fmt"
	"log"

	"github.com/baobabus/gcfg"
)

func main() {
	cfgStr := `; Comment line
[section]
switch=on`
	cfg := struct {
		Section struct {
			Switch bool
		}
	}{}
	err := gcfg.ReadStringInto(&cfg, cfgStr)
	if err != nil {
		log.Fatalf("Failed to parse gcfg data: %s", err)
	}
	fmt.Println(cfg.Section.Switch)
}
Output:

true
Example (Hyphens)
package main

import (
	"fmt"
	"log"

	"github.com/baobabus/gcfg"
)

func main() {
	cfgStr := `; Comment line
[section-name]
variable-name=value # comment`
	cfg := struct {
		Section_Name struct {
			Variable_Name string
		}
	}{}
	err := gcfg.ReadStringInto(&cfg, cfgStr)
	if err != nil {
		log.Fatalf("Failed to parse gcfg data: %s", err)
	}
	fmt.Println(cfg.Section_Name.Variable_Name)
}
Output:

value
Example (Multivalue)
package main

import (
	"fmt"
	"log"

	"github.com/baobabus/gcfg"
)

func main() {
	cfgStr := `; Comment line
[section]
multi=value1
multi=value2`
	cfg := struct {
		Section struct {
			Multi []string
		}
	}{}
	err := gcfg.ReadStringInto(&cfg, cfgStr)
	if err != nil {
		log.Fatalf("Failed to parse gcfg data: %s", err)
	}
	fmt.Println(cfg.Section.Multi)
}
Output:

[value1 value2]
Example (Subsections)
package main

import (
	"fmt"
	"log"

	"github.com/baobabus/gcfg"
)

func main() {
	cfgStr := `; Comment line
[profile "A"]
color = white

[profile "B"]
color = black
`
	cfg := struct {
		Profile map[string]*struct {
			Color string
		}
	}{}
	err := gcfg.ReadStringInto(&cfg, cfgStr)
	if err != nil {
		log.Fatalf("Failed to parse gcfg data: %s", err)
	}
	fmt.Printf("%s %s\n", cfg.Profile["A"].Color, cfg.Profile["B"].Color)
}
Output:

white black
Example (Tags)
package main

import (
	"fmt"
	"log"

	"github.com/baobabus/gcfg"
)

func main() {
	cfgStr := `; Comment line
[section]
var-name=value # comment`
	cfg := struct {
		Section struct {
			FieldName string `gcfg:"var-name"`
		}
	}{}
	err := gcfg.ReadStringInto(&cfg, cfgStr)
	if err != nil {
		log.Fatalf("Failed to parse gcfg data: %s", err)
	}
	fmt.Println(cfg.Section.FieldName)
}
Output:

value
Example (Unicode)
package main

import (
	"fmt"
	"log"

	"github.com/baobabus/gcfg"
)

func main() {
	cfgStr := `; Comment line
[甲]
乙=丙 # comment`
	cfg := struct {
		X甲 struct {
			X乙 string
		}
	}{}
	err := gcfg.ReadStringInto(&cfg, cfgStr)
	if err != nil {
		log.Fatalf("Failed to parse gcfg data: %s", err)
	}
	fmt.Println(cfg.X甲.X乙)
}
Output:

func RegisterTypeFormatter

func RegisterTypeFormatter(tgtType reflect.Type, typeFormatter TypeFormatter) error

func RegisterTypeParser

func RegisterTypeParser(tgtType reflect.Type, typeParser TypeParser) error

Registers type parser function.

func Write

func Write(config interface{}, w io.Writer) error

Write writes config in gcfg formatted data.

Types

type TypeFormatter

type TypeFormatter func(interface{}) string

type TypeParser

type TypeParser func(blank bool, val string) (interface{}, error)

Directories

Path Synopsis
Package scanner implements a scanner for gcfg configuration text.
Package scanner implements a scanner for gcfg configuration text.
Package token defines constants representing the lexical tokens of the gcfg configuration syntax and basic operations on tokens (printing, predicates).
Package token defines constants representing the lexical tokens of the gcfg configuration syntax and basic operations on tokens (printing, predicates).
Package types defines helpers for type conversions.
Package types defines helpers for type conversions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL