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