]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/go-ini/ini/README.md
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / go-ini / ini / README.md
1 INI [![Build Status](https://travis-ci.org/go-ini/ini.svg?branch=master)](https://travis-ci.org/go-ini/ini)
2 ===
3
4 ![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
5
6 Package ini provides INI file read and write functionality in Go.
7
8 [简体中文](README_ZH.md)
9
10 ## Feature
11
12 - Load multiple data sources(`[]byte`, file and `io.ReadCloser`) with overwrites.
13 - Read with recursion values.
14 - Read with parent-child sections.
15 - Read with auto-increment key names.
16 - Read with multiple-line values.
17 - Read with tons of helper methods.
18 - Read and convert values to Go types.
19 - Read and **WRITE** comments of sections and keys.
20 - Manipulate sections, keys and comments with ease.
21 - Keep sections and keys in order as you parse and save.
22
23 ## Installation
24
25 To use a tagged revision:
26
27 go get gopkg.in/ini.v1
28
29 To use with latest changes:
30
31 go get github.com/go-ini/ini
32
33 Please add `-u` flag to update in the future.
34
35 ### Testing
36
37 If you want to test on your machine, please apply `-t` flag:
38
39 go get -t gopkg.in/ini.v1
40
41 Please add `-u` flag to update in the future.
42
43 ## Getting Started
44
45 ### Loading from data sources
46
47 A **Data Source** is either raw data in type `[]byte`, a file name with type `string` or `io.ReadCloser`. You can load **as many data sources as you want**. Passing other types will simply return an error.
48
49 ```go
50 cfg, err := ini.Load([]byte("raw data"), "filename", ioutil.NopCloser(bytes.NewReader([]byte("some other data"))))
51 ```
52
53 Or start with an empty object:
54
55 ```go
56 cfg := ini.Empty()
57 ```
58
59 When you cannot decide how many data sources to load at the beginning, you will still be able to **Append()** them later.
60
61 ```go
62 err := cfg.Append("other file", []byte("other raw data"))
63 ```
64
65 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.
66
67 ```go
68 cfg, err := ini.LooseLoad("filename", "filename_404")
69 ```
70
71 The cool thing is, whenever the file is available to load while you're calling `Reload` method, it will be counted as usual.
72
73 #### Ignore cases of key name
74
75 When you do not care about cases of section and key names, you can use `InsensitiveLoad` to force all names to be lowercased while parsing.
76
77 ```go
78 cfg, err := ini.InsensitiveLoad("filename")
79 //...
80
81 // sec1 and sec2 are the exactly same section object
82 sec1, err := cfg.GetSection("Section")
83 sec2, err := cfg.GetSection("SecTIOn")
84
85 // key1 and key2 are the exactly same key object
86 key1, err := cfg.GetKey("Key")
87 key2, err := cfg.GetKey("KeY")
88 ```
89
90 #### MySQL-like boolean key
91
92 MySQL's configuration allows a key without value as follows:
93
94 ```ini
95 [mysqld]
96 ...
97 skip-host-cache
98 skip-name-resolve
99 ```
100
101 By default, this is considered as missing value. But if you know you're going to deal with those cases, you can assign advanced load options:
102
103 ```go
104 cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf"))
105 ```
106
107 The value of those keys are always `true`, and when you save to a file, it will keep in the same foramt as you read.
108
109 #### Comment
110
111 Take care that following format will be treated as comment:
112
113 1. Line begins with `#` or `;`
114 2. Words after `#` or `;`
115 3. Words after section name (i.e words after `[some section name]`)
116
117 If you want to save a value with `#` or `;`, please quote them with ``` ` ``` or ``` """ ```.
118
119 ### Working with sections
120
121 To get a section, you would need to:
122
123 ```go
124 section, err := cfg.GetSection("section name")
125 ```
126
127 For a shortcut for default section, just give an empty string as name:
128
129 ```go
130 section, err := cfg.GetSection("")
131 ```
132
133 When you're pretty sure the section exists, following code could make your life easier:
134
135 ```go
136 section := cfg.Section("section name")
137 ```
138
139 What happens when the section somehow does not exist? Don't panic, it automatically creates and returns a new section to you.
140
141 To create a new section:
142
143 ```go
144 err := cfg.NewSection("new section")
145 ```
146
147 To get a list of sections or section names:
148
149 ```go
150 sections := cfg.Sections()
151 names := cfg.SectionStrings()
152 ```
153
154 ### Working with keys
155
156 To get a key under a section:
157
158 ```go
159 key, err := cfg.Section("").GetKey("key name")
160 ```
161
162 Same rule applies to key operations:
163
164 ```go
165 key := cfg.Section("").Key("key name")
166 ```
167
168 To check if a key exists:
169
170 ```go
171 yes := cfg.Section("").HasKey("key name")
172 ```
173
174 To create a new key:
175
176 ```go
177 err := cfg.Section("").NewKey("name", "value")
178 ```
179
180 To get a list of keys or key names:
181
182 ```go
183 keys := cfg.Section("").Keys()
184 names := cfg.Section("").KeyStrings()
185 ```
186
187 To get a clone hash of keys and corresponding values:
188
189 ```go
190 hash := cfg.Section("").KeysHash()
191 ```
192
193 ### Working with values
194
195 To get a string value:
196
197 ```go
198 val := cfg.Section("").Key("key name").String()
199 ```
200
201 To validate key value on the fly:
202
203 ```go
204 val := cfg.Section("").Key("key name").Validate(func(in string) string {
205 if len(in) == 0 {
206 return "default"
207 }
208 return in
209 })
210 ```
211
212 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):
213
214 ```go
215 val := cfg.Section("").Key("key name").Value()
216 ```
217
218 To check if raw value exists:
219
220 ```go
221 yes := cfg.Section("").HasValue("test value")
222 ```
223
224 To get value with types:
225
226 ```go
227 // For boolean values:
228 // true when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On
229 // false when value is: 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off
230 v, err = cfg.Section("").Key("BOOL").Bool()
231 v, err = cfg.Section("").Key("FLOAT64").Float64()
232 v, err = cfg.Section("").Key("INT").Int()
233 v, err = cfg.Section("").Key("INT64").Int64()
234 v, err = cfg.Section("").Key("UINT").Uint()
235 v, err = cfg.Section("").Key("UINT64").Uint64()
236 v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
237 v, err = cfg.Section("").Key("TIME").Time() // RFC3339
238
239 v = cfg.Section("").Key("BOOL").MustBool()
240 v = cfg.Section("").Key("FLOAT64").MustFloat64()
241 v = cfg.Section("").Key("INT").MustInt()
242 v = cfg.Section("").Key("INT64").MustInt64()
243 v = cfg.Section("").Key("UINT").MustUint()
244 v = cfg.Section("").Key("UINT64").MustUint64()
245 v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
246 v = cfg.Section("").Key("TIME").MustTime() // RFC3339
247
248 // Methods start with Must also accept one argument for default value
249 // when key not found or fail to parse value to given type.
250 // Except method MustString, which you have to pass a default value.
251
252 v = cfg.Section("").Key("String").MustString("default")
253 v = cfg.Section("").Key("BOOL").MustBool(true)
254 v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
255 v = cfg.Section("").Key("INT").MustInt(10)
256 v = cfg.Section("").Key("INT64").MustInt64(99)
257 v = cfg.Section("").Key("UINT").MustUint(3)
258 v = cfg.Section("").Key("UINT64").MustUint64(6)
259 v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
260 v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339
261 ```
262
263 What if my value is three-line long?
264
265 ```ini
266 [advance]
267 ADDRESS = """404 road,
268 NotFound, State, 5000
269 Earth"""
270 ```
271
272 Not a problem!
273
274 ```go
275 cfg.Section("advance").Key("ADDRESS").String()
276
277 /* --- start ---
278 404 road,
279 NotFound, State, 5000
280 Earth
281 ------ end --- */
282 ```
283
284 That's cool, how about continuation lines?
285
286 ```ini
287 [advance]
288 two_lines = how about \
289 continuation lines?
290 lots_of_lines = 1 \
291 2 \
292 3 \
293 4
294 ```
295
296 Piece of cake!
297
298 ```go
299 cfg.Section("advance").Key("two_lines").String() // how about continuation lines?
300 cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4
301 ```
302
303 Well, I hate continuation lines, how do I disable that?
304
305 ```go
306 cfg, err := ini.LoadSources(ini.LoadOptions{
307 IgnoreContinuation: true,
308 }, "filename")
309 ```
310
311 Holy crap!
312
313 Note that single quotes around values will be stripped:
314
315 ```ini
316 foo = "some value" // foo: some value
317 bar = 'some value' // bar: some value
318 ```
319
320 That's all? Hmm, no.
321
322 #### Helper methods of working with values
323
324 To get value with given candidates:
325
326 ```go
327 v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
328 v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
329 v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
330 v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
331 v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9})
332 v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9})
333 v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
334 v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339
335 ```
336
337 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.
338
339 To validate value in a given range:
340
341 ```go
342 vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2)
343 vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20)
344 vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20)
345 vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9)
346 vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9)
347 vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime)
348 vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
349 ```
350
351 ##### Auto-split values into a slice
352
353 To use zero value of type for invalid inputs:
354
355 ```go
356 // Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
357 // Input: how, 2.2, are, you -> [0.0 2.2 0.0 0.0]
358 vals = cfg.Section("").Key("STRINGS").Strings(",")
359 vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
360 vals = cfg.Section("").Key("INTS").Ints(",")
361 vals = cfg.Section("").Key("INT64S").Int64s(",")
362 vals = cfg.Section("").Key("UINTS").Uints(",")
363 vals = cfg.Section("").Key("UINT64S").Uint64s(",")
364 vals = cfg.Section("").Key("TIMES").Times(",")
365 ```
366
367 To exclude invalid values out of result slice:
368
369 ```go
370 // Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
371 // Input: how, 2.2, are, you -> [2.2]
372 vals = cfg.Section("").Key("FLOAT64S").ValidFloat64s(",")
373 vals = cfg.Section("").Key("INTS").ValidInts(",")
374 vals = cfg.Section("").Key("INT64S").ValidInt64s(",")
375 vals = cfg.Section("").Key("UINTS").ValidUints(",")
376 vals = cfg.Section("").Key("UINT64S").ValidUint64s(",")
377 vals = cfg.Section("").Key("TIMES").ValidTimes(",")
378 ```
379
380 Or to return nothing but error when have invalid inputs:
381
382 ```go
383 // Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4]
384 // Input: how, 2.2, are, you -> error
385 vals = cfg.Section("").Key("FLOAT64S").StrictFloat64s(",")
386 vals = cfg.Section("").Key("INTS").StrictInts(",")
387 vals = cfg.Section("").Key("INT64S").StrictInt64s(",")
388 vals = cfg.Section("").Key("UINTS").StrictUints(",")
389 vals = cfg.Section("").Key("UINT64S").StrictUint64s(",")
390 vals = cfg.Section("").Key("TIMES").StrictTimes(",")
391 ```
392
393 ### Save your configuration
394
395 Finally, it's time to save your configuration to somewhere.
396
397 A typical way to save configuration is writing it to a file:
398
399 ```go
400 // ...
401 err = cfg.SaveTo("my.ini")
402 err = cfg.SaveToIndent("my.ini", "\t")
403 ```
404
405 Another way to save is writing to a `io.Writer` interface:
406
407 ```go
408 // ...
409 cfg.WriteTo(writer)
410 cfg.WriteToIndent(writer, "\t")
411 ```
412
413 By default, spaces are used to align "=" sign between key and values, to disable that:
414
415 ```go
416 ini.PrettyFormat = false
417 ```
418
419 ## Advanced Usage
420
421 ### Recursive Values
422
423 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.
424
425 ```ini
426 NAME = ini
427
428 [author]
429 NAME = Unknwon
430 GITHUB = https://github.com/%(NAME)s
431
432 [package]
433 FULL_NAME = github.com/go-ini/%(NAME)s
434 ```
435
436 ```go
437 cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon
438 cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini
439 ```
440
441 ### Parent-child Sections
442
443 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.
444
445 ```ini
446 NAME = ini
447 VERSION = v1
448 IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
449
450 [package]
451 CLONE_URL = https://%(IMPORT_PATH)s
452
453 [package.sub]
454 ```
455
456 ```go
457 cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1
458 ```
459
460 #### Retrieve parent keys available to a child section
461
462 ```go
463 cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"]
464 ```
465
466 ### Unparseable Sections
467
468 Sometimes, you have sections that do not contain key-value pairs but raw content, to handle such case, you can use `LoadOptions.UnparsableSections`:
469
470 ```go
471 cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS]
472 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`))
473
474 body := cfg.Section("COMMENTS").Body()
475
476 /* --- start ---
477 <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
478 ------ end --- */
479 ```
480
481 ### Auto-increment Key Names
482
483 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.
484
485 ```ini
486 [features]
487 -: Support read/write comments of keys and sections
488 -: Support auto-increment of key names
489 -: Support load multiple files to overwrite key values
490 ```
491
492 ```go
493 cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"}
494 ```
495
496 ### Map To Struct
497
498 Want more objective way to play with INI? Cool.
499
500 ```ini
501 Name = Unknwon
502 age = 21
503 Male = true
504 Born = 1993-01-01T20:17:05Z
505
506 [Note]
507 Content = Hi is a good man!
508 Cities = HangZhou, Boston
509 ```
510
511 ```go
512 type Note struct {
513 Content string
514 Cities []string
515 }
516
517 type Person struct {
518 Name string
519 Age int `ini:"age"`
520 Male bool
521 Born time.Time
522 Note
523 Created time.Time `ini:"-"`
524 }
525
526 func main() {
527 cfg, err := ini.Load("path/to/ini")
528 // ...
529 p := new(Person)
530 err = cfg.MapTo(p)
531 // ...
532
533 // Things can be simpler.
534 err = ini.MapTo(p, "path/to/ini")
535 // ...
536
537 // Just map a section? Fine.
538 n := new(Note)
539 err = cfg.Section("Note").MapTo(n)
540 // ...
541 }
542 ```
543
544 Can I have default value for field? Absolutely.
545
546 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.
547
548 ```go
549 // ...
550 p := &Person{
551 Name: "Joe",
552 }
553 // ...
554 ```
555
556 It's really cool, but what's the point if you can't give me my file back from struct?
557
558 ### Reflect From Struct
559
560 Why not?
561
562 ```go
563 type Embeded struct {
564 Dates []time.Time `delim:"|"`
565 Places []string `ini:"places,omitempty"`
566 None []int `ini:",omitempty"`
567 }
568
569 type Author struct {
570 Name string `ini:"NAME"`
571 Male bool
572 Age int
573 GPA float64
574 NeverMind string `ini:"-"`
575 *Embeded
576 }
577
578 func main() {
579 a := &Author{"Unknwon", true, 21, 2.8, "",
580 &Embeded{
581 []time.Time{time.Now(), time.Now()},
582 []string{"HangZhou", "Boston"},
583 []int{},
584 }}
585 cfg := ini.Empty()
586 err = ini.ReflectFrom(cfg, a)
587 // ...
588 }
589 ```
590
591 So, what do I get?
592
593 ```ini
594 NAME = Unknwon
595 Male = true
596 Age = 21
597 GPA = 2.8
598
599 [Embeded]
600 Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00
601 places = HangZhou,Boston
602 ```
603
604 #### Name Mapper
605
606 To save your time and make your code cleaner, this library supports [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) between struct field and actual section and key name.
607
608 There are 2 built-in name mappers:
609
610 - `AllCapsUnderscore`: it converts to format `ALL_CAPS_UNDERSCORE` then match section or key.
611 - `TitleUnderscore`: it converts to format `title_underscore` then match section or key.
612
613 To use them:
614
615 ```go
616 type Info struct {
617 PackageName string
618 }
619
620 func main() {
621 err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini"))
622 // ...
623
624 cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
625 // ...
626 info := new(Info)
627 cfg.NameMapper = ini.AllCapsUnderscore
628 err = cfg.MapTo(info)
629 // ...
630 }
631 ```
632
633 Same rules of name mapper apply to `ini.ReflectFromWithMapper` function.
634
635 #### Value Mapper
636
637 To expand values (e.g. from environment variables), you can use the `ValueMapper` to transform values:
638
639 ```go
640 type Env struct {
641 Foo string `ini:"foo"`
642 }
643
644 func main() {
645 cfg, err := ini.Load([]byte("[env]\nfoo = ${MY_VAR}\n")
646 cfg.ValueMapper = os.ExpandEnv
647 // ...
648 env := &Env{}
649 err = cfg.Section("env").MapTo(env)
650 }
651 ```
652
653 This would set the value of `env.Foo` to the value of the environment variable `MY_VAR`.
654
655 #### Other Notes On Map/Reflect
656
657 Any embedded struct is treated as a section by default, and there is no automatic parent-child relations in map/reflect feature:
658
659 ```go
660 type Child struct {
661 Age string
662 }
663
664 type Parent struct {
665 Name string
666 Child
667 }
668
669 type Config struct {
670 City string
671 Parent
672 }
673 ```
674
675 Example configuration:
676
677 ```ini
678 City = Boston
679
680 [Parent]
681 Name = Unknwon
682
683 [Child]
684 Age = 21
685 ```
686
687 What if, yes, I'm paranoid, I want embedded struct to be in the same section. Well, all roads lead to Rome.
688
689 ```go
690 type Child struct {
691 Age string
692 }
693
694 type Parent struct {
695 Name string
696 Child `ini:"Parent"`
697 }
698
699 type Config struct {
700 City string
701 Parent
702 }
703 ```
704
705 Example configuration:
706
707 ```ini
708 City = Boston
709
710 [Parent]
711 Name = Unknwon
712 Age = 21
713 ```
714
715 ## Getting Help
716
717 - [API Documentation](https://gowalker.org/gopkg.in/ini.v1)
718 - [File An Issue](https://github.com/go-ini/ini/issues/new)
719
720 ## FAQs
721
722 ### What does `BlockMode` field do?
723
724 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.
725
726 ### Why another INI library?
727
728 Many people are using my another INI library [goconfig](https://github.com/Unknwon/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.
729
730 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)
731
732 ## License
733
734 This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.