DataDog/ini

Name: ini

Owner: Datadog, Inc.

Description: Package ini provides INI file read and write functionality in Go.

Forked from: go-ini/ini

Created: 2016-05-05 23:23:29.0

Updated: 2016-05-20 03:56:19.0

Pushed: 2016-06-07 20:13:25.0

Homepage: http://go-ini.github.io/ini

Size: 286

Language: Go

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

ini Build Status

Package ini provides INI file read and write functionality in Go.

????

Feature
Installation

To use a tagged revision:

go get gopkg.in/ini.v1

To use with latest changes:

go get github.com/go-ini/ini

Please add -u flag to update in the future.

Testing

If you want to test on your machine, please apply -t flag:

go get -t gopkg.in/ini.v1

Please add -u flag to update in the future.

Getting Started
Loading from data sources

A Data Source is either raw data in type []byte or a file name with type string and you can load as many as data sources you want. Passing other types will simply return an error.

 err := ini.Load([]byte("raw data"), "filename")

Or start with an empty object:

:= ini.Empty()

When you cannot decide how many data sources to load at the beginning, you still able to Append() them later.

:= cfg.Append("other file", []byte("other raw data"))

If you have a list of files with possibilities that some of them may not available at the time, and you don't know exactly which ones, you can use LooseLoad to ignore nonexistent files without returning error.

 err := ini.LooseLoad("filename", "filename_404")

The cool thing is, whenever the file is available to load while you're calling Reload method, it will be counted as usual.

Working with sections

To get a section, you would need to:

ion, err := cfg.GetSection("section name")

For a shortcut for default section, just give an empty string as name:

ion, err := cfg.GetSection("")

When you're pretty sure the section exists, following code could make your life easier:

ion := cfg.Section("")

What happens when the section somehow does not exist? Don't panic, it automatically creates and returns a new section to you.

To create a new section:

:= cfg.NewSection("new section")

To get a list of sections or section names:

ions := cfg.Sections()
s := cfg.SectionStrings()
Working with keys

To get a key under a section:

 err := cfg.Section("").GetKey("key name")

Same rule applies to key operations:

:= cfg.Section("").Key("key name")

To check if a key exists:

:= cfg.Section("").HasKey("key name")

To create a new key:

:= cfg.Section("").NewKey("name", "value")

To get a list of keys or key names:

 := cfg.Section("").Keys()
s := cfg.Section("").KeyStrings()

To get a clone hash of keys and corresponding values:

 := cfg.GetSection("").KeysHash()
Working with values

To get a string value:

:= cfg.Section("").Key("key name").String()

To validate key value on the fly:

:= cfg.Section("").Key("key name").Validate(func(in string) string {
if len(in) == 0 {
    return "default"
}
return in

If you do not want any auto-transformation (such as recursive read) for the values, you can get raw value directly (this way you get much better performance):

:= cfg.Section("").Key("key name").Value()

To check if raw value exists:

:= cfg.Section("").HasValue("test value")

To get value with types:

or boolean values:
rue when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On
alse when value is: 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off
rr = cfg.Section("").Key("BOOL").Bool()
rr = cfg.Section("").Key("FLOAT64").Float64()
rr = cfg.Section("").Key("INT").Int()
rr = cfg.Section("").Key("INT64").Int64()
rr = cfg.Section("").Key("UINT").Uint()
rr = cfg.Section("").Key("UINT64").Uint64()
rr = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
rr = cfg.Section("").Key("TIME").Time() // RFC3339

cfg.Section("").Key("BOOL").MustBool()
cfg.Section("").Key("FLOAT64").MustFloat64()
cfg.Section("").Key("INT").MustInt()
cfg.Section("").Key("INT64").MustInt64()
cfg.Section("").Key("UINT").MustUint()
cfg.Section("").Key("UINT64").MustUint64()
cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
cfg.Section("").Key("TIME").MustTime() // RFC3339

ethods start with Must also accept one argument for default value
hen key not found or fail to parse value to given type.
xcept method MustString, which you have to pass a default value.

cfg.Section("").Key("String").MustString("default")
cfg.Section("").Key("BOOL").MustBool(true)
cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
cfg.Section("").Key("INT").MustInt(10)
cfg.Section("").Key("INT64").MustInt64(99)
cfg.Section("").Key("UINT").MustUint(3)
cfg.Section("").Key("UINT64").MustUint64(6)
cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339

What if my value is three-line long?

ance]
ESS = """404 road,
ound, State, 5000
h"""

Not a problem!

Section("advance").Key("ADDRESS").String()

-- start ---
road,
ound, State, 5000
h
--  end  --- */

That's cool, how about continuation lines?

ance]
lines = how about \
continuation lines?
_of_lines = 1 \
2 \
3 \
4

Piece of cake!

Section("advance").Key("two_lines").String() // how about continuation lines?
Section("advance").Key("lots_of_lines").String() // 1 2 3 4

Note that single quotes around values will be stripped:

= "some value" // foo: some value
= 'some value' // bar: some value

That's all? Hmm, no.

Helper methods of working with values

To get value with given candidates:

cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9})
cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9})
cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339

Default value will be presented if value of key is not in candidates you given, and default value does not need be one of candidates.

To validate value in a given range:

 = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2)
 = cfg.Section("").Key("INT").RangeInt(0, 10, 20)
 = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20)
 = cfg.Section("").Key("UINT").RangeUint(0, 3, 9)
 = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9)
 = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime)
 = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
Auto-split values into a slice

To use zero value of type for invalid inputs:

nput: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
nput: how, 2.2, are, you -> [0.0 2.2 0.0 0.0]
 = cfg.Section("").Key("STRINGS").Strings(",")
 = cfg.Section("").Key("FLOAT64S").Float64s(",")
 = cfg.Section("").Key("INTS").Ints(",")
 = cfg.Section("").Key("INT64S").Int64s(",")
 = cfg.Section("").Key("UINTS").Uints(",")
 = cfg.Section("").Key("UINT64S").Uint64s(",")
 = cfg.Section("").Key("TIMES").Times(",")

To exclude invalid values out of result slice:

nput: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
nput: how, 2.2, are, you -> [2.2]
 = cfg.Section("").Key("FLOAT64S").ValidFloat64s(",")
 = cfg.Section("").Key("INTS").ValidInts(",")
 = cfg.Section("").Key("INT64S").ValidInt64s(",")
 = cfg.Section("").Key("UINTS").ValidUints(",")
 = cfg.Section("").Key("UINT64S").ValidUint64s(",")
 = cfg.Section("").Key("TIMES").ValidTimes(",")

Or to return nothing but error when have invalid inputs:

nput: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
nput: how, 2.2, are, you -> error
 = cfg.Section("").Key("FLOAT64S").StrictFloat64s(",")
 = cfg.Section("").Key("INTS").StrictInts(",")
 = cfg.Section("").Key("INT64S").StrictInt64s(",")
 = cfg.Section("").Key("UINTS").StrictUints(",")
 = cfg.Section("").Key("UINT64S").StrictUint64s(",")
 = cfg.Section("").Key("TIMES").StrictTimes(",")
Save your configuration

Finally, it's time to save your configuration to somewhere.

A typical way to save configuration is writing it to a file:

..
= cfg.SaveTo("my.ini")
= cfg.SaveToIndent("my.ini", "\t")

Another way to save is writing to a io.Writer interface:

..
WriteTo(writer)
WriteToIndent(writer, "\t")
Advanced Usage
Recursive Values

For all value of keys, there is a special syntax %(<name>)s, where <name> is the key name in same section or default section, and %(<name>)s will be replaced by corresponding value(empty string if key not found). You can use this syntax at most 99 level of recursions.

 = ini

hor]
 = Unknwon
UB = https://github.com/%(NAME)s

kage]
_NAME = github.com/go-ini/%(NAME)s
o
Section("author").Key("GITHUB").String()        // https://github.com/Unknwon
Section("package").Key("FULL_NAME").String()    // github.com/go-ini/ini
Parent-child Sections

You can use . in section name to indicate parent-child relationship between two or more sections. If the key not found in the child section, library will try again on its parent section until there is no parent section.

 = ini
ION = v1
RT_PATH = gopkg.in/%(NAME)s.%(VERSION)s

kage]
E_URL = https://%(IMPORT_PATH)s

kage.sub]
o
Section("package.sub").Key("CLONE_URL").String()    // https://gopkg.in/ini.v1
Auto-increment Key Names

If key name is - in data source, then it would be seen as special syntax for auto-increment key name start from 1, and every section is independent on counter.

tures]
upport read/write comments of keys and sections
upport auto-increment of key names
upport load multiple files to overwrite key values
o
Section("features").KeyStrings()    // []{"#1", "#2", "#3"}
Map To Struct

Want more objective way to play with INI? Cool.

 = Unknwon
= 21
 = true
 = 1993-01-01T20:17:05Z

e]
ent = Hi is a good man!
es = HangZhou, Boston
o
 Note struct {
Content string
Cities  []string


 Person struct {
Name string
Age  int `ini:"age"`
Male bool
Born time.Time
Note
Created time.Time `ini:"-"`


 main() {
cfg, err := ini.Load("path/to/ini")
// ...
p := new(Person)
err = cfg.MapTo(p)
// ...

// Things can be simpler.
err = ini.MapTo(p, "path/to/ini")
// ...

// Just map a section? Fine.
n := new(Note)
err = cfg.Section("Note").MapTo(n)
// ...

Can I have default value for field? Absolutely.

Assign it before you map to struct. It will keep the value as it is if the key is not presented or got wrong type.

..
 &Person{
Name: "Joe",

..

It's really cool, but what's the point if you can't give me my file back from struct?

Reflect From Struct

Why not?

 Embeded struct {
Dates  []time.Time `delim:"|"`
Places []string
None   []int


 Author struct {
Name      string `ini:"NAME"`
Male      bool
Age       int
GPA       float64
NeverMind string `ini:"-"`
*Embeded


 main() {
a := &Author{"Unknwon", true, 21, 2.8, "",
    &Embeded{
        []time.Time{time.Now(), time.Now()},
        []string{"HangZhou", "Boston"},
        []int{},
    }}
cfg := ini.Empty()
err = ini.ReflectFrom(cfg, a)
// ...

So, what do I get?

 = Unknwon
 = true
= 21
= 2.8

eded]
s = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00
es = HangZhou,Boston
 =

What if I want to compose a struct template of other structs?

Struct pointers as field types
1
1]
2
2]
3
o
 Bar struct {
Baz int


 FullIni struct {
Foo int
Bar1 *Bar
Bar2 *Bar


 main() {
cfg, err := ini.Load("path/to/ini")
// ...
i := new(FullIni)
err = cfg.MapTo(i)
// ...

Name Mapper

To save your time and make your code cleaner, this library supports NameMapper between struct field and actual section and key name.

There are 2 built-in name mappers:

To use them:

 Info struct {
PackageName string


 main() {
err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini"))
// ...

cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
// ...
info := new(Info)
cfg.NameMapper = ini.AllCapsUnderscore
err = cfg.MapTo(info)
// ...

Same rules of name mapper apply to ini.ReflectFromWithMapper function.

Other Notes On Map/Reflect

Any embedded struct is treated as a section by default, and there is no automatic parent-child relations in map/reflect feature:

 Child struct {
Age string


 Parent struct {
Name string
Child


 Config struct {
City string
Parent

Example configuration:

 = Boston

ent]
 = Unknwon

ld]
= 21

What if, yes, I'm paranoid, I want embedded struct to be in the same section. Well, all roads lead to Rome.

 Child struct {
Age string


 Parent struct {
Name string
Child `ini:"Parent"`


 Config struct {
City string
Parent

Example configuration:

 = Boston

ent]
 = Unknwon
= 21
MapTo special flags

In addition to the flags mentioned above, you can specify strict and mustExist in a comma delimited list on the ini tag in a struct definition. strict will cause MapTo to return an error if a field does not parse to the type specified in the struct instead of silently failing. mustExist will cause an error to be returned if the struct field does not exist in the loaded ini data. The first value in the ini tag must be the field name override as described above. If no override is desired but other tags are needed, there must be a leading comma before the flag list.

 = Bob
= thirty
o
 Person struct {
Name string
Age int `ini:",strict"`

ill return error from MapTo (Field set strict, but Age cannot parse to int).
o
 Person struct {
Name string
Age int
Salary float64 `ini:",mustExist"`

ill return error from MapTo (Salary field set mustExist, but does not exist).
o
 Person struct {
Name string
Age int `ini:",strict,mustExist"`

ill return error from MapTo (Field set strict, but Age cannot parse to int).
o
 Person struct {
MyName string `ini:"Name,strict"`
Age int

ill map without error.
o
 Person struct {
Name string `ini:"MyName,mustExist,strict"`
Age int

ill return error from MapTo (No field MyName and mustExist).
Getting Help
FAQs
What does BlockMode field do?

By default, library lets you read and write values so we need a locker to make sure your data is safe. But in cases that you are very sure about only reading data through the library, you can set cfg.BlockMode = false to speed up read operations about 50-70% faster.

Why another INI library?

Many people are using my another INI library goconfig, so the reason for this one is I would like to make more Go style code. Also when you set cfg.BlockMode = false, this one is about 10-30% faster.

To make those changes I have to confirm API broken, so it's safer to keep it in another place and start using gopkg.in to version my package at this time.(PS: shorter import path)

License

This project is under Apache v2 License. See the LICENSE file for the full license text.


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.