package ini
import "git.sr.ht/~shulhan/pakakeh.go/lib/ini"
Package ini implement reading and writing INI text format as defined by Git configuration file syntax.
Features
Reading and writing on the same file should not change the content of file (including comment).
Unsupported features
Git "include" and "includeIf" directives.
In Git specification, an empty variable is equal to boolean true. This cause inconsistency between empty string and boolean true.
Syntax
The '#' and ';' characters begin comments to the end of line.
Blank lines are ignored.
Section
A section begins with the name of the section in square brackets.
A section continues until the next section begins.
Section name are case-insensitive.
Section name must start with an alphabetic character, no spaces before name or after '['.
Section name only allow alphanumeric characters, '-' and '.'.
Section can be further divided into subsections.
Section headers cannot span multiple lines.
You can have "[section]" if you have `[section "subsection"]`, but you don’t need to.
All the other lines (and the remainder of the line after the section header) are recognized as setting variables, in the form `name = value`.
Subsection
A subsection begin and end with double quotes, separated by space from the section name, in the section header, for example
[section "subsection"]
Subsection name is case sensitive and can contain any characters except newline and the null byte.
Subsection name can include doublequote `"` and backslash '\' by escaping them as in `\"` and `\\`, respectively.
Other backslashes preceding other characters are dropped when reading subsection name; for example, "\t" is read as "t" and "\0" is read as "0".
Variable
Variable name must start with an alphabetic character.
Variable must belong to some section, which means that there must be a section header before the first setting of a variable.
Variable name are case-insensitive.
Variable name allow only alphanumeric characters and '-'.
NOTE: This library add extension to allow dot ('.') and underscore ('_') characters on variable name.
Value
Value can be empty or not set. Variable name without value is a short-hand to set the value to the empty string value, for example
[section] thisisempty # equal to thisisempty=
Internal whitespaces within the value are retained verbatim. Leading and trailing whitespaces on value without double quote will be discarded.
key = multiple strings # equal to "multiple strings" key = " multiple strings " # equal to " multiple strings "
Value can be continued to the next line by ending it with a backslash '\' character, the backslash and the end-of-line are stripped.
key = multiple \ # equal to "multiple string" strings
Value can contain inline comment, for example
key = value # this is inline comment
Comment characters, '#' and ';', inside double quoted value will be read as content of value, not as comment,
key = "value # with hash"
Inside a value enclosed by double quotes, the following escape sequences are recognized: '\"' for doublequote, '\\' for backslash, '\n' for newline character (NL), '\t' for horizontal tabulation (HT, TAB) and '\b' for backspace (BS).
Other character escape sequences (including octal escape sequences) are invalid.
Marshaling
The container to be passed when marshaling must be struct type. Each exported field in the struct with "ini" tag is marshaled based on the section, subsection, and key in the tag's value.
The "ini" tag syntax is,
[SECTION] [':' SUBSECTION] [':' VAR]
At least one of the section, subsection, or key should be defined.
The subsection can contain colon and double quote. A colon ':' is escaped using double backslash '\\', for example "a:b\\:c:d" contains section "a", subsection "b:c", and variable "d". A double quote is escaped using triple backslash, for example `\\\"`.
If the field type is slice of primitive, for example "[]int", it will be marshaled into multiple key with the same name. Note that, marshaling "[]byte" does not supported, due to ambiguity between "byte" and "uint8" during reflection.
If the field type is struct, it will marshaled as new section and/or subsection based on tag on the struct field
If the field type is slice of struct, it will marshaled as multiple section-subsection with the same tags.
Map type is supported as long as the key is string, otherwise it will be ignored. The map key will be marshaled as key.
Other standard type that supported is time.Time, which will be rendered with the time format defined in "layout" tag.
Example,
type U struct { Int `ini:"::int"` } type T struct { String string `ini:"single::string" Time time.Time `ini:"single::time" layout:"2006-01-02"` SliceString []string `ini:"slice::string" Struct U `ini:"single:struct" SliceStruct []U `ini:"slice:struct" Map map[string]int `ini:"amap:" MapSub map[string]string `ini:"amap:sub" MapStruct map[string]U `ini:"mapstruct"` }
will be marshaled into
[single] string = <value of T.String> time = <value of T.Time with layout "YYYY-MM-DD"> [slice] string = <value of T.SliceString[0]> ... string = <value of T.SliceString[n]> [single "struct"] int = <value of T.U.Int> [slice "struct"] int = <value of T.SliceStruct[0].Int [slice "struct"] int = <value of T.SliceStruct[n].Int [amap] <T.Map.Key[0]> = <T.Map.Value[0]> ... <T.Map.Key[n]> = <T.Map.Value[n]> [amap "sub"] <T.MapSub.Key[0]> = <T.MapSub.Value[0]> ... <T.MapSub.Key[n]> = <T.MapSub.Value[n]> ## On map[string]struct, each key become a subsection. [mapstruct "<map.key[0]>"] <U.Field[0]> = <U.Value[0]> ... <U.Field[n]> = <U.Value[n]> ... [mapstruct "<map.key[n]>"] <U.Field[0]> = <U.Value[0]> ... <U.Field[n]> = <U.Value[n]>
Unmarshaling
The syntax and rules for unmarshaling is equal to the marshaling.
Index ¶
- func IsValidVarName(v string) bool
- func IsValueBoolTrue(v string) bool
- func Marshal(v any) (b []byte, err error)
- func ParseTag(in string) (tags []string)
- func Unmarshal(b []byte, v any) (err error)
- type Ini
- func Open(filename string) (in *Ini, err error)
- func Parse(text []byte) (in *Ini, err error)
- func (in *Ini) Add(secName, subName, key, value string) bool
- func (in *Ini) AsMap(sectionName, subName string) (out map[string][]string)
- func (in *Ini) Get(secName, subName, key, def string) (val string, ok bool)
- func (in *Ini) GetBool(secName, subName, key string, def bool) bool
- func (in *Ini) Gets(secName, subName, key string) (out []string)
- func (in *Ini) GetsUniq(secName, subName, key string, caseSensitive bool) (out []string)
- func (in *Ini) Keys() (keys []string)
- func (in *Ini) Prune()
- func (in *Ini) Rebase(other *Ini)
- func (in *Ini) Save(filename string) (err error)
- func (in *Ini) Section(secName, subName string) (sec *Section)
- func (in *Ini) Set(secName, subName, key, value string) bool
- func (in *Ini) Subs(secName string) (subs []*Section)
- func (in *Ini) Unmarshal(v any) (err error)
- func (in *Ini) Unset(secName, subName, key string) bool
- func (in *Ini) UnsetAll(secName, subName, key string)
- func (in *Ini) Val(keyPath string) (val string)
- func (in *Ini) Vals(keyPath string) (vals []string)
- func (in *Ini) ValsUniq(keyPath string, caseSensitive bool) (vals []string)
- func (in *Ini) Vars(sectionPath string) (vars map[string]string)
- func (in *Ini) Write(w io.Writer) (err error)
- type Section
Examples ¶
- Ini.Add
- Ini.AsMap
- Ini.Gets
- Ini.GetsUniq
- Ini.Keys
- Ini.Prune
- Ini.Rebase
- Ini.Section
- Ini.Set
- Ini.Subs
- Ini.Unset
- Ini.Val
- Ini.Vals
- Ini.ValsUniq
- Ini.Vars
- IsValidVarName
- Marshal
- Marshal (Map)
- Marshal (Struct)
- Unmarshal
- Unmarshal (Map)
- Unmarshal (Struct)
Functions ¶
func IsValidVarName ¶
IsValidVarName check if "v" is valid variable name, where the
first character must be a letter and the rest should contains only letter,
digit, period, hyphen, or underscore.
If "v" is valid it will return true.
Code:
Output:Example¶
{
fmt.Println(IsValidVarName(""))
fmt.Println(IsValidVarName("1abcd"))
fmt.Println(IsValidVarName("-abcd"))
fmt.Println(IsValidVarName("_abcd"))
fmt.Println(IsValidVarName(".abcd"))
fmt.Println(IsValidVarName("a@bcd"))
fmt.Println(IsValidVarName("a-b_c.d"))
// Output:
// false
// false
// false
// false
// false
// false
// true
}
false
false
false
false
false
false
true
func IsValueBoolTrue ¶
IsValueBoolTrue will return true if variable contains boolean value for true. The following conditions is boolean true for value: "" (empty string), "true", "yes", "ya", "t", "1" (all of string is case insensitive).
func Marshal ¶
Marshal encode the struct of v into stream of ini formatted string.
To encode a struct, each exported fields must have tagged with "ini" key; untagged field will not be exported.
Each exported field in the struct must have at least one tag: a section where the field's name (the key) and field's value will be saved. An optional subsection can be defined by adding a string separated by colon ":" after section's name. An optional key's name also can be defined by adding string after subsection name. If key's name is not defined it would be default to lowercase string of field's name.
An array or slice will be encoded as multiple keys. Marshaling "[]byte" does not supported, due to ambiguity between "byte" and "uint8" during reflection.
One exception to above rule is map type.
A map's key will override the key defined in tag.
Code:
Output: Code:
Output: Code:
Output:Example¶
{
ptrString := "b"
ptrInt := int(2)
ptrTime := time.Date(2021, 2, 28, 18, 44, 1, 0, time.UTC)
type U struct {
String string `ini:"::string"`
Int int `ini:"::int"`
}
type ADT struct {
Time time.Time `ini:"section::time" layout:"2006-01-02 15:04:05"`
PtrString *string `ini:"section:pointer:string"`
PtrInt *int `ini:"section:pointer:int"`
PtrTime *time.Time `ini:"section:pointer:time" layout:"2006-01-02 15:04:05"`
PtrStruct *U `ini:"pointer:struct"`
String string `ini:"section::string"`
SliceString []string `ini:"section:slice:string"`
SliceInt []int `ini:"section:slice:int"`
SliceUint []uint `ini:"section:slice:uint"`
SliceBool []bool `ini:"section:slice:bool"`
SliceStruct []U `ini:"slice:OfStruct"`
Struct U `ini:"section:struct"`
Duration time.Duration `ini:"section::duration"`
Int int `ini:"section::int"`
Bool bool `ini:"section::bool"`
}
t := ADT{
Time: time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC),
PtrString: &ptrString,
PtrInt: &ptrInt,
PtrTime: &ptrTime,
PtrStruct: &U{
String: "PtrStruct.String",
Int: 3,
},
String: "a",
SliceString: []string{"c", "d"},
SliceInt: []int{2, 3},
SliceUint: []uint{4, 5},
SliceBool: []bool{true, false},
SliceStruct: []U{{
String: "U.string 1",
Int: 1,
}, {
String: "U.string 2",
Int: 2,
}},
Struct: U{
String: "b",
Int: 2,
},
Duration: time.Minute,
Int: 1,
Bool: true,
}
iniText, err := Marshal(&t)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", iniText)
// Output:
// [section]
// time = 2006-01-02 15:04:05
// string = a
// duration = 1m0s
// int = 1
// bool = true
//
// [section "pointer"]
// string = b
// int = 2
// time = 2021-02-28 18:44:01
//
// [pointer "struct"]
// string = PtrStruct.String
// int = 3
//
// [section "slice"]
// string = c
// string = d
// int = 2
// int = 3
// uint = 4
// uint = 5
// bool = true
// bool = false
//
// [slice "OfStruct"]
// string = U.string 1
// int = 1
//
// [slice "OfStruct"]
// string = U.string 2
// int = 2
//
// [section "struct"]
// string = b
// int = 2
}
[section]
time = 2006-01-02 15:04:05
string = a
duration = 1m0s
int = 1
bool = true
[section "pointer"]
string = b
int = 2
time = 2021-02-28 18:44:01
[pointer "struct"]
string = PtrStruct.String
int = 3
[section "slice"]
string = c
string = d
int = 2
int = 3
uint = 4
uint = 5
bool = true
bool = false
[slice "OfStruct"]
string = U.string 1
int = 1
[slice "OfStruct"]
string = U.string 2
int = 2
[section "struct"]
string = b
int = 2
Example (Map)¶
{
type U struct {
String string `ini:"string"`
SliceString []string `ini:"::slice_string"`
Int int `ini:"int"`
}
type ADT struct {
MapString map[string]string `ini:"map:subString"`
MapPtrString map[string]*string `ini:"map:subPtrString"`
MapInt map[string]int `ini:"map:subInt"`
MapPtrInt map[string]*int `ini:"map:subPtrInt"`
MapStruct map[string]U `ini:"mapStruct"`
MapPtrStruct map[string]*U `ini:"mapPtrStruct"`
// This field should not marshaled.
unMapStruct map[string]U
}
var (
stringV = "v"
stringV2 = "v2"
intV = 6
t = ADT{
MapString: map[string]string{
"k": "v",
"k2": "v2",
},
MapPtrString: map[string]*string{
"k": &stringV,
"k2": &stringV2,
},
MapInt: map[string]int{
"keyInt": 6,
},
MapPtrInt: map[string]*int{
"keyInt": &intV,
},
MapStruct: map[string]U{
"struct-key-1": {
String: `struct-1-string`,
Int: 1,
SliceString: []string{`str-1`, `str-2`},
},
"struct-key-2": {
String: `struct-2-string`,
Int: 2,
SliceString: []string{`str-3`, `str-4`},
},
},
MapPtrStruct: map[string]*U{
"ptr-struct-key-1": {
String: `struct-1-string`,
Int: 1,
SliceString: []string{`str-5`, `str-6`},
},
"ptr-struct-key-2": {
String: `struct-2-string`,
Int: 2,
SliceString: []string{`str-7`, `str-8`},
},
},
unMapStruct: map[string]U{
"struct-key-1": {
String: "struct-1-string",
Int: 1,
},
},
}
iniText []byte
err error
)
iniText, err = Marshal(&t)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(iniText))
// Output:
// [map "subString"]
// k = v
// k2 = v2
//
// [map "subPtrString"]
// k = v
// k2 = v2
//
// [map "subInt"]
// keyint = 6
//
// [map "subPtrInt"]
// keyint = 6
//
// [mapstruct "struct-key-1"]
// string = struct-1-string
// slice_string = str-1
// slice_string = str-2
// int = 1
//
// [mapstruct "struct-key-2"]
// string = struct-2-string
// slice_string = str-3
// slice_string = str-4
// int = 2
//
// [mapptrstruct "ptr-struct-key-1"]
// string = struct-1-string
// slice_string = str-5
// slice_string = str-6
// int = 1
//
// [mapptrstruct "ptr-struct-key-2"]
// string = struct-2-string
// slice_string = str-7
// slice_string = str-8
// int = 2
}
[map "subString"]
k = v
k2 = v2
[map "subPtrString"]
k = v
k2 = v2
[map "subInt"]
keyint = 6
[map "subPtrInt"]
keyint = 6
[mapstruct "struct-key-1"]
string = struct-1-string
slice_string = str-1
slice_string = str-2
int = 1
[mapstruct "struct-key-2"]
string = struct-2-string
slice_string = str-3
slice_string = str-4
int = 2
[mapptrstruct "ptr-struct-key-1"]
string = struct-1-string
slice_string = str-5
slice_string = str-6
int = 1
[mapptrstruct "ptr-struct-key-2"]
string = struct-2-string
slice_string = str-7
slice_string = str-8
int = 2
Example (Struct)¶
{
type U struct {
String string `ini:"::string"`
Int int `ini:"::int"`
}
type ADT struct {
Time time.Time `ini:"section::time" layout:"2006-01-02 15:04:05"`
PtrStruct *U `ini:"pointer:struct"`
SliceStruct []U `ini:"slice:OfStruct"`
Struct U `ini:"section:struct"`
unexported U // This field should not be marshaled.
}
var (
t = ADT{
Time: time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC),
PtrStruct: &U{
String: "PtrStruct.String",
Int: 1,
},
SliceStruct: []U{{
String: "slice-struct-1",
Int: 2,
}, {
String: "slice-struct-2",
Int: 3,
}},
Struct: U{
String: "b",
Int: 4,
},
unexported: U{
String: "unexported",
Int: 5,
},
}
iniText []byte
err error
)
iniText, err = Marshal(&t)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(iniText))
// Output:
// [section]
// time = 2006-01-02 15:04:05
//
// [pointer "struct"]
// string = PtrStruct.String
// int = 1
//
// [slice "OfStruct"]
// string = slice-struct-1
// int = 2
//
// [slice "OfStruct"]
// string = slice-struct-2
// int = 3
//
// [section "struct"]
// string = b
// int = 4
}
[section]
time = 2006-01-02 15:04:05
[pointer "struct"]
string = PtrStruct.String
int = 1
[slice "OfStruct"]
string = slice-struct-1
int = 2
[slice "OfStruct"]
string = slice-struct-2
int = 3
[section "struct"]
string = b
int = 4
func ParseTag ¶
ParseTag parse the ini field tag as used in the struct's field. This returned slice always have 4 string element: section, subsection, key, and default value.
func Unmarshal ¶
Unmarshal parse the INI stream from slice of byte and store its value into
struct of `v`.
All the properties and specifications of field's tag follow the Marshal
function.
Code:
Output: Code:
Output: Code:
Output:Example¶
{
iniText := `
[section]
string = a
int = 1
bool = true
duration = 1s
time = 2006-01-02 15:04:05
[section "slice"]
string = c
string = d
int = 2
int = 3
bool = true
bool = false
uint = 4
uint = 5
[slice "OfStruct"]
string = U.string 1
int = 1
[slice "OfStruct"]
string = U.string 2
int = 2
[section "pointer"]
string = b
int = 2
`
type U struct {
String string `ini:"::string"`
Int int `ini:"::int"`
}
type ADT struct {
Time time.Time `ini:"section::time" layout:"2006-01-02 15:04:05"`
PtrString *string `ini:"section:pointer:string"`
PtrInt *int `ini:"section:pointer:int"`
PtrTime *time.Time `ini:"section:pointer:time" layout:"2006-01-02 15:04:05"`
PtrStruct *U `ini:"pointer:struct"`
String string `ini:"section::string"`
SliceString []string `ini:"section:slice:string"`
SliceInt []int `ini:"section:slice:int"`
SliceUint []uint `ini:"section:slice:uint"`
SliceBool []bool `ini:"section:slice:bool"`
SliceStruct []U `ini:"slice:OfStruct"`
Struct U `ini:"section:struct"`
Duration time.Duration `ini:"section::duration"`
Int int `ini:"section::int"`
Bool bool `ini:"section::bool"`
}
t := ADT{}
err := Unmarshal([]byte(iniText), &t)
if err != nil {
log.Fatal(err)
}
fmt.Printf("String: %v\n", t.String)
fmt.Printf("Int: %v\n", t.Int)
fmt.Printf("Bool: %v\n", t.Bool)
fmt.Printf("Duration: %v\n", t.Duration)
fmt.Printf("Time: %v\n", t.Time)
fmt.Printf("SliceString: %v\n", t.SliceString)
fmt.Printf("SliceInt: %v\n", t.SliceInt)
fmt.Printf("SliceUint: %v\n", t.SliceUint)
fmt.Printf("SliceBool: %v\n", t.SliceBool)
fmt.Printf("SliceStruct: %v\n", t.SliceStruct)
fmt.Printf("PtrString: %v\n", *t.PtrString)
fmt.Printf("PtrInt: %v\n", *t.PtrInt)
// Output:
// String: a
// Int: 1
// Bool: true
// Duration: 1s
// Time: 2006-01-02 15:04:05 +0000 UTC
// SliceString: [c d]
// SliceInt: [2 3]
// SliceUint: [4 5]
// SliceBool: [true false]
// SliceStruct: [{U.string 1 1} {U.string 2 2}]
// PtrString: b
// PtrInt: 2
}
String: a
Int: 1
Bool: true
Duration: 1s
Time: 2006-01-02 15:04:05 +0000 UTC
SliceString: [c d]
SliceInt: [2 3]
SliceUint: [4 5]
SliceBool: [true false]
SliceStruct: [{U.string 1 1} {U.string 2 2}]
PtrString: b
PtrInt: 2
Example (Map)¶
{
type U struct {
String string `ini:"string"`
SliceString []string `ini:"::slice_string"`
Int int `ini:"int"`
}
type ADT struct {
MapString map[string]string `ini:"map:string"`
MapInt map[string]int `ini:"map:int"`
MapStruct map[string]U `ini:"mapstruct"`
MapPtrStruct map[string]*U `ini:"mapptrstruct"`
}
var (
iniText = `
[map "string"]
k = v
k2 = v2
[map "int"]
k = 6
k2 = 7
[mapstruct "struct-key-1"]
string = struct-1-string
slice_string = str-1
slice_string = str-2
int = 1
[mapstruct "struct-key-2"]
string = struct-2-string
slice_string = str-3
slice_string = str-4
int = 2
[mapptrstruct "struct-key-1"]
string = struct-1-string
slice_string = str-5
slice_string = str-6
int = 1
[mapptrstruct "struct-key-2"]
string = struct-2-string
slice_string = str-7
slice_string = str-8
int = 2
`
t = ADT{}
err error
)
err = Unmarshal([]byte(iniText), &t)
if err != nil {
log.Fatal(err)
}
fmt.Printf("MapString: %v\n", t.MapString)
fmt.Printf("MapInt: %v\n", t.MapInt)
fmt.Printf("MapStruct: %v\n", t.MapStruct)
fmt.Printf("MapPtrStruct: struct-key-1: %v\n", t.MapPtrStruct["struct-key-1"])
fmt.Printf("MapPtrStruct: struct-key-2: %v\n", t.MapPtrStruct["struct-key-2"])
// Output:
// MapString: map[k:v k2:v2]
// MapInt: map[k:6 k2:7]
// MapStruct: map[struct-key-1:{struct-1-string [str-1 str-2] 1} struct-key-2:{struct-2-string [str-3 str-4] 2}]
// MapPtrStruct: struct-key-1: &{struct-1-string [str-5 str-6] 1}
// MapPtrStruct: struct-key-2: &{struct-2-string [str-7 str-8] 2}
}
MapString: map[k:v k2:v2]
MapInt: map[k:6 k2:7]
MapStruct: map[struct-key-1:{struct-1-string [str-1 str-2] 1} struct-key-2:{struct-2-string [str-3 str-4] 2}]
MapPtrStruct: struct-key-1: &{struct-1-string [str-5 str-6] 1}
MapPtrStruct: struct-key-2: &{struct-2-string [str-7 str-8] 2}
Example (Struct)¶
{
type U struct {
String string `ini:"::string"`
Int int `ini:"::int"`
}
type ADT struct {
Time time.Time `ini:"section::time" layout:"2006-01-02 15:04:05"`
PtrStruct *U `ini:"pointer:struct"`
SliceStruct []U `ini:"slice:OfStruct"`
Struct U `ini:"section:struct"`
unexported U // This field should not be unmarshaled.
}
var (
iniText = `
[section]
time = 2006-01-02 15:04:05
[pointer "struct"]
string = PtrStruct.String
int = 1
[slice "OfStruct"]
string = slice-struct-1
int = 2
[slice "OfStruct"]
string = slice-struct-2
int = 3
[section "struct"]
string = struct
int = 4
[unexported]
string = should not unmarshaled
int = 5
`
t = ADT{}
err error
)
err = Unmarshal([]byte(iniText), &t)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Time: %v\n", t.Time)
fmt.Printf("PtrStruct: %v\n", t.PtrStruct)
fmt.Printf("SliceStruct: %v\n", t.SliceStruct)
fmt.Printf("Struct: %v\n", t.Struct)
fmt.Printf("unexported: %v\n", t.unexported)
// Output:
// Time: 2006-01-02 15:04:05 +0000 UTC
// PtrStruct: &{PtrStruct.String 1}
// SliceStruct: [{slice-struct-1 2} {slice-struct-2 3}]
// Struct: {struct 4}
// unexported: { 0}
}
Time: 2006-01-02 15:04:05 +0000 UTC
PtrStruct: &{PtrStruct.String 1}
SliceStruct: [{slice-struct-1 2} {slice-struct-2 3}]
Struct: {struct 4}
unexported: { 0}
Types ¶
type Ini ¶
type Ini struct {
// contains filtered or unexported fields
}
Ini contains the parsed file.
func Open ¶
Open and parse INI formatted file. If the file is not exist, it will be created.
On fail it will return incomplete instance of Ini with an error.
func Parse ¶
Parse INI format from text.
func (*Ini) Add ¶
Add the new key and value to the last item in section and/or subsection.
If section or subsection is not exist it will create a new one. If section or key is empty, or value already exist it will not modify the INI object.
It will return true if new variable is added, otherwise it will return
false.
Code:
Output:Example¶
{
ini := new(Ini)
ini.Add("", "", "k1", "v1")
ini.Add("s1", "", "", "v2")
ini.Add("s1", "", "k1", "")
ini.Add("s1", "", "k1", "v1")
ini.Add("s1", "", "k1", "v2")
ini.Add("s1", "", "k1", "v1")
ini.Add("s1", "sub", "k1", "v1")
ini.Add("s1", "sub", "k1", "v1")
ini.Add("s2", "sub", "k1", "v1")
err := ini.Write(os.Stdout)
if err != nil {
log.Fatal(err)
}
// Output:
// [s1]
// k1 =
// k1 = v1
// k1 = v2
//
// [s1 "sub"]
// k1 = v1
//
// [s2 "sub"]
// k1 = v1
}
[s1]
k1 =
k1 = v1
k1 = v2
[s1 "sub"]
k1 = v1
[s2 "sub"]
k1 = v1
func (*Ini) AsMap ¶
AsMap return the INI contents as mapping of (section-name ":" subsection-name ":" variable-name) as key and the variable's values as slice of string.
If section name is not empty, only the keys will be listed in the map.
Code:
Output:Example¶
{
input := []byte(`
[section]
key=value1
key2=
[section "sub"]
key=value1
key2=
[section]
key=value2
key2=false
[section "sub"]
key=value2
key=value3
`)
inis, err := Parse(input)
if err != nil {
log.Fatal(err)
}
iniMap := inis.AsMap("", "")
for k, v := range iniMap {
fmt.Println(k, "=", v)
}
iniMap = inis.AsMap("section", "sub")
fmt.Println()
for k, v := range iniMap {
fmt.Println(k, "=", v)
}
// Unordered output:
// section::key = [value1 value2]
// section::key2 = [ false]
// section:sub:key = [value1 value2 value3]
// section:sub:key2 = []
//
// key = [value1 value2 value3]
// key2 = []
}
section::key = [value1 value2]
section::key2 = [ false]
section:sub:key = [value1 value2 value3]
section:sub:key2 = []
key = [value1 value2 value3]
key2 = []
func (*Ini) Get ¶
Get the last key on section and/or subsection.
If key found it will return its value and true; otherwise it will return default value in def and false.
func (*Ini) GetBool ¶
GetBool return key's value as boolean. If no key found it will return default value.
func (*Ini) Gets ¶
Gets key's values as slice of string in the same section and subsection.
Code:
Output:Example¶
{
input := []byte(`
[section]
key=value1
[section "sub"]
key=value2
[section]
key=value3
[section "sub"]
key=value4
key=value2
`)
inis, _ := Parse(input)
fmt.Println(inis.Gets("section", "", "key"))
fmt.Println(inis.Gets("section", "sub", "key"))
// Output:
// [value1 value3]
// [value2 value4 value2]
}
[value1 value3]
[value2 value4 value2]
func (*Ini) GetsUniq ¶
GetsUniq key's values as slice of string in the same section and
subsection.
Code:
Output:Example¶
{
input := []byte(`
[section]
key=value1
[section "sub"]
key=value2
[section]
key=value3
[section "sub"]
key=value4
key=value2
`)
inis, _ := Parse(input)
fmt.Println(inis.GetsUniq("section", "", "key", true))
fmt.Println(inis.GetsUniq("section", "sub", "key", true))
// Output:
// [value1 value3]
// [value2 value4]
}
[value1 value3]
[value2 value4]
func (*Ini) Keys ¶
Keys return sorted list of all section, subsection, and variables as
string where each of them separated by ":", for example
"section:sub:var".
Code:
Output:Example¶
{
const iniContent = `
[section1]
key3 = 3
key2 = 2
[section2 "sub 1"]
key3 = 3
key2 = 2
[section1]
key1 = 1
[section2 "sub 1"]
key2 = 2.2
key1 = 1
`
var (
ini *Ini
err error
)
ini, err = Parse([]byte(iniContent))
if err != nil {
log.Fatal(err)
}
var (
keys = ini.Keys()
gotkeys = strings.Join(keys, "\n")
)
fmt.Println(gotkeys)
// Output:
// section1::key1
// section1::key2
// section1::key3
// section2:sub 1:key1
// section2:sub 1:key2
// section2:sub 1:key3
}
section1::key1
section1::key2
section1::key3
section2:sub 1:key1
section2:sub 1:key2
section2:sub 1:key3
func (*Ini) Prune ¶
func (in *Ini) Prune()
Prune remove all empty lines, comments, and merge all section and
subsection with the same name into one group.
Code:
Output:Example¶
{
input := []byte(`
[section]
key=value1 # comment
key2= ; another comment
[section "sub"]
key=value1
; here is comment on section
[section]
key=value2
key2=false
[section "sub"]
key=value2
key=value1
`)
in, err := Parse(input)
if err != nil {
log.Fatal(err)
}
in.Prune()
err = in.Write(os.Stdout)
if err != nil {
log.Fatal(err)
}
// Output:
// [section]
// key = value1
// key2 = true
// key = value2
// key2 = false
//
// [section "sub"]
// key = value2
// key = value1
}
[section]
key = value1
key2 = true
key = value2
key2 = false
[section "sub"]
key = value2
key = value1
func (*Ini) Rebase ¶
Rebase merge the other INI sections into this INI sections.
Code:
Output:Example¶
{
input := []byte(`
[section]
key=value1
key2=
[section "sub"]
key=value1
`)
other := []byte(`
[section]
key=value2
key2=false
[section "sub"]
key=value2
key=value1
`)
in, err := Parse(input)
if err != nil {
log.Fatal(err)
}
in2, err := Parse(other)
if err != nil {
log.Fatal(err)
}
in.Prune()
in2.Prune()
in.Rebase(in2)
err = in.Write(os.Stdout)
if err != nil {
log.Fatal(err)
}
// Output:
// [section]
// key = value1
// key2 = true
// key = value2
// key2 = false
//
// [section "sub"]
// key = value2
// key = value1
}
[section]
key = value1
key2 = true
key = value2
key2 = false
[section "sub"]
key = value2
key = value1
func (*Ini) Save ¶
Save the current parsed Ini into file `filename`. It will overwrite the destination file if it's exist.
func (*Ini) Section ¶
Section given section and/or subsection name, return the Section object
that match with it.
If section name is empty, it will return nil.
If ini contains duplicate section (or subsection) it will merge all
of its variables into one section.
Code:
Output:Example¶
{
input := []byte(`
[section]
key=value1 # comment
key2= ; another comment
[section "sub"]
key=value1
[section] ; here is comment on section
key=value2
key2=false
[section "sub"]
key=value2
key=value1
`)
ini, err := Parse(input)
if err != nil {
log.Fatal(err)
}
sec := ini.Section("section", "")
for _, v := range sec.vars {
fmt.Printf("%s=%s\n", v.key, v.value)
}
// Output:
// key=value1
// key2=
// key=value2
// key2=false
}
key=value1
key2=
key=value2
key2=false
func (*Ini) Set ¶
Set the last variable's value in section-subsection that match with the key. If section or subsection is not found, the new section-subsection will be created. If key not found, the new key-value variable will be added to the section.
It will return true if new key added or updated; otherwise it will return
false.
Code:
Output:Example¶
{
input := []byte(`
[section]
key=value1 # comment
key2= ; another comment
[section "sub"]
key=value1
[section] ; here is comment on section
key=value2
key2=false
[section "sub"]
key=value2
key=value1
`)
ini, err := Parse(input)
if err != nil {
log.Fatal(err)
}
ini.Set("", "sub", "key", "value3")
ini.Set("sectionnotexist", "sub", "key", "value3")
ini.Set("section", "sub", "key", "value3")
ini.Set("section", "", "key", "value4")
ini.Set("section", "", "keynotexist", "value4")
err = ini.Write(os.Stdout)
if err != nil {
log.Fatal(err)
}
// Output:
// [section]
// key=value1 # comment
// key2= ; another comment
//
// [section "sub"]
// key=value1
//
// [section] ; here is comment on section
// key= value4
// key2=false
// keynotexist = value4
//
// [section "sub"]
// key=value2
// key= value3
//
// [sectionnotexist "sub"]
// key = value3
}
[section]
key=value1 # comment
key2= ; another comment
[section "sub"]
key=value1
[section] ; here is comment on section
key= value4
key2=false
keynotexist = value4
[section "sub"]
key=value2
key= value3
[sectionnotexist "sub"]
key = value3
func (*Ini) Subs ¶
Subs return all non empty subsections (and its variable) that have the same section name.
This function is shortcut to be used in templating.
Code:
Output:Example¶
{
input := []byte(`
[section]
key=value1 # comment
key2= ; another comment
[section "sub"]
key=value1
[section] ; here is comment on section
key=value2
key2=false
[section "sub"]
key=value2
key=value1
`)
ini, err := Parse(input)
if err != nil {
log.Fatal(err)
}
subs := ini.Subs("section")
for _, sub := range subs {
fmt.Println(sub.SubName(), sub.Vals("key"))
}
// Output:
// sub [value2 value1]
}
sub [value2 value1]
func (*Ini) Unmarshal ¶
Unmarshal store the value from configuration, based on `ini` tag, into a struct pointed by interface `v`.
func (*Ini) Unset ¶
Unset remove the last variable's in section and/or subsection that match
with the key.
If key found it will return true, otherwise it will return false.
Code:
Output:Example¶
{
input := []byte(`
[section]
key=value1 # comment
key2= ; another comment
[section "sub"]
key=value1
; here is comment on section
[section]
key=value2
key2=false
[section "sub"]
key=value2
key=value1
`)
ini, err := Parse(input)
if err != nil {
log.Fatal(err)
}
ini.Unset("", "sub", "keynotexist")
ini.Unset("sectionnotexist", "sub", "keynotexist")
ini.Unset("section", "sub", "keynotexist")
ini.Unset("section", "sub", "key")
ini.Unset("section", "", "keynotexist")
ini.Unset("section", "", "key")
err = ini.Write(os.Stdout)
if err != nil {
log.Fatal(err)
}
// Output:
// [section]
// key=value1 # comment
// key2= ; another comment
//
// [section "sub"]
// key=value1
//
// ; here is comment on section
// [section]
// key2=false
//
// [section "sub"]
// key=value2
}
[section]
key=value1 # comment
key2= ; another comment
[section "sub"]
key=value1
; here is comment on section
[section]
key2=false
[section "sub"]
key=value2
func (*Ini) UnsetAll ¶
UnsetAll remove all variables in section and/or subsection that match with the key. If key found it will return true, otherwise it will return false.
func (*Ini) Val ¶
Val return the last variable value using a string as combination of section, subsection, and key with ":" as separator. If key not found, it will return empty string.
For example, to get the value of key "k" in section "s" and subsection "sub", call
V("s:sub:k")
This function is shortcut to be used in templating.
Code:
Output:Example¶
{
input := `
[section]
key=value1
key2=
[section "sub"]
key=value1
[section]
key=value2
key2=false
[section "sub"]
key=value2
key=value3
`
ini, err := Parse([]byte(input))
if err != nil {
log.Fatal(err)
}
fmt.Println(ini.Val("section:sub:key"))
fmt.Println(ini.Val("section:sub:key2"))
fmt.Println(ini.Val("section::key"))
fmt.Println(ini.Val("section:key"))
// Output:
// value3
//
// value2
//
}
value3
value2
func (*Ini) Vals ¶
Vals return all values as slice of string. The keyPath is combination of section, subsection, and key using colon ":" as separator. If key not found, it will return an empty slice.
For example, to get all values of key "k" in section "s" and subsection "sub", call
Vals("s:sub:k")
This function is shortcut to be used in templating.
Code:
Output:Example¶
{
ini, err := Parse([]byte(testInput))
if err != nil {
log.Fatal(err)
}
fmt.Println(ini.Vals("section:key"))
fmt.Println(ini.Vals("section::key"))
fmt.Println(ini.Vals("section:sub:key2"))
fmt.Println(ini.Vals("section:sub:key"))
// Output:
// []
// [value1 value2]
// []
// [value1 value2 value2 value3]
}
[]
[value1 value2]
[]
[value1 value2 value2 value3]
func (*Ini) ValsUniq ¶
ValsUniq return all values as slice of string without any duplication.
Code:
Output:Example¶
{
ini, err := Parse([]byte(testInput))
if err != nil {
log.Fatal(err)
}
fmt.Println(ini.ValsUniq("section:key", true))
fmt.Println(ini.ValsUniq("section::key", true))
fmt.Println(ini.ValsUniq("section:sub:key2", true))
fmt.Println(ini.ValsUniq("section:sub:key", true))
// Output:
// []
// [value1 value2]
// []
// [value1 value2 value3]
}
[]
[value1 value2]
[]
[value1 value2 value3]
func (*Ini) Vars ¶
Vars return all variables in section and/or subsection as map of string. If there is a duplicate in key's name, only the last key value that will be store on map value.
This method is a shortcut that can be used in templating.
Code:
Output:Example¶
{
ini, err := Parse([]byte(testInput))
if err != nil {
log.Fatal(err)
}
for k, v := range ini.Vars("section:") {
fmt.Println(k, "=", v)
}
fmt.Println()
for k, v := range ini.Vars("section:sub") {
fmt.Println(k, "=", v)
}
// Unordered output:
// section::key = value2
// section::key2 = false
//
// key = value3
}
section::key = value2
section::key2 = false
key = value3
func (*Ini) Write ¶
Write the current parsed Ini into writer `w`.
type Section ¶
type Section struct {
// contains filtered or unexported fields
}
Section represent section header in INI file format and their variables.
Remember that section's name is case insensitive.
func (*Section) Name ¶
Name return the section's name.
func (*Section) String ¶
String return formatted INI section header.
func (*Section) SubName ¶
SubName return subsection's name.
func (*Section) Val ¶
Val return the last defined variable key in section.
func (*Section) Vals ¶
Vals return all variables in section as slice of string.
Source Files ¶
common.go doc.go ini.go ini_unmarshal.go linemode.go reader.go section.go struct_field.go tag_struct_field.go variable.go
- Version
- v0.60.0 (latest)
- Published
- Feb 1, 2025
- Platform
- linux/amd64
- Imports
- 13 packages
- Last checked
- 5 days ago –
Tools for package owners.