#36 support case insensitive load

- Use Travis CI
This commit is contained in:
Unknwon
2016-06-29 20:27:58 +08:00
parent 81d9577135
commit 2cd302aa78
8 changed files with 94 additions and 13 deletions
+1
View File
@@ -2,3 +2,4 @@ testdata/conf_out.ini
ini.sublime-project
ini.sublime-workspace
testdata/conf_reflect.ini
.idea
+14
View File
@@ -0,0 +1,14 @@
sudo: false
language: go
go:
- 1.4
- 1.5
- 1.6
- tip
script: go test -v -cover -race
notifications:
email:
- u@gogs.io
+16 -1
View File
@@ -1,4 +1,4 @@
ini [![Build Status](https://drone.io/github.com/go-ini/ini/status.png)](https://drone.io/github.com/go-ini/ini/latest) [![](http://gocover.io/_badge/github.com/go-ini/ini)](http://gocover.io/github.com/go-ini/ini)
INI [![Build Status](https://travis-ci.org/go-ini/ini.svg?branch=master)](https://travis-ci.org/go-ini/ini)
===
![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
@@ -68,6 +68,21 @@ If you have a list of files with possibilities that some of them may not availab
cfg, err := ini.LooseLoad("filename", "filename_404")
```
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.
```go
cfg, err := ini.InsensitiveLoad("filename")
//...
// sec1 and sec2 are the exactly same section object
sec1, err := cfg.GetSection("Section")
sec2, err := cfg.GetSection("SecTIOn")
// key1 and key2 are the exactly same key object
key1, err := cfg.GetKey("Key")
key2, err := cfg.GetKey("KeY")
```
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
+15
View File
@@ -61,6 +61,21 @@ err := cfg.Append("other file", []byte("other raw data"))
cfg, err := ini.LooseLoad("filename", "filename_404")
```
有时候分区和键的名称大小写混合非常烦人,这个时候就可以通过 `InsensitiveLoad` 将所有分区和键名在读取里强制转换为小写:
```go
cfg, err := ini.InsensitiveLoad("filename")
//...
// sec1 和 sec2 指向同一个分区对象
sec1, err := cfg.GetSection("Section")
sec2, err := cfg.GetSection("SecTIOn")
// key1 和 key2 指向同一个键对象
key1, err := cfg.GetKey("Key")
key2, err := cfg.GetKey("KeY")
```
更牛逼的是,当那些之前不存在的文件在重新调用 `Reload` 方法的时候突然出现了,那么它们会被正常加载。
### 操作分区(Section)
+26 -10
View File
@@ -36,7 +36,7 @@ const (
// Maximum allowed depth when recursively substituing variable names.
_DEPTH_VALUES = 99
_VERSION = "1.13.0"
_VERSION = "1.14.0"
)
// Version returns current package version literal.
@@ -126,20 +126,19 @@ type File struct {
// To keep data in order.
sectionList []string
// Whether the parser should ignore nonexistent files or return error.
looseMode bool
options LoadOptions
NameMapper
}
// newFile initializes File object with given data sources.
func newFile(dataSources []dataSource, looseMode bool) *File {
func newFile(dataSources []dataSource, opts LoadOptions) *File {
return &File{
BlockMode: true,
dataSources: dataSources,
sections: make(map[string]*Section),
sectionList: make([]string, 0, 10),
looseMode: looseMode,
options: opts,
}
}
@@ -154,7 +153,14 @@ func parseDataSource(source interface{}) (dataSource, error) {
}
}
func loadSources(looseMode bool, source interface{}, others ...interface{}) (_ *File, err error) {
type LoadOptions struct {
// Loose indicats whether the parser should ignore nonexistent files or return error.
Loose bool
// Insensitive indicates whether the parser forces all section and key names to lowercase.
Insensitive bool
}
func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
sources := make([]dataSource, len(others)+1)
sources[0], err = parseDataSource(source)
if err != nil {
@@ -166,7 +172,7 @@ func loadSources(looseMode bool, source interface{}, others ...interface{}) (_ *
return nil, err
}
}
f := newFile(sources, looseMode)
f := newFile(sources, opts)
if err = f.Reload(); err != nil {
return nil, err
}
@@ -177,13 +183,19 @@ func loadSources(looseMode bool, source interface{}, others ...interface{}) (_ *
// Arguments can be mixed of file name with string type, or raw data in []byte.
// It will return error if list contains nonexistent files.
func Load(source interface{}, others ...interface{}) (*File, error) {
return loadSources(false, source, others...)
return LoadSources(LoadOptions{}, source, others...)
}
// LooseLoad has exactly same functionality as Load function
// except it ignores nonexistent files instead of returning error.
func LooseLoad(source interface{}, others ...interface{}) (*File, error) {
return loadSources(true, source, others...)
return LoadSources(LoadOptions{Loose: true}, source, others...)
}
// InsensitiveLoad has exactly same functionality as Load function
// except it forces all section and key names to be lowercased.
func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
return LoadSources(LoadOptions{Insensitive: true}, source, others...)
}
// Empty returns an empty file object.
@@ -197,6 +209,8 @@ func Empty() *File {
func (f *File) NewSection(name string) (*Section, error) {
if len(name) == 0 {
return nil, errors.New("error creating new section: empty section name")
} else if f.options.Insensitive && name != DEFAULT_SECTION {
name = strings.ToLower(name)
}
if f.BlockMode {
@@ -227,6 +241,8 @@ func (f *File) NewSections(names ...string) (err error) {
func (f *File) GetSection(name string) (*Section, error) {
if len(name) == 0 {
name = DEFAULT_SECTION
} else if f.options.Insensitive {
name = strings.ToLower(name)
}
if f.BlockMode {
@@ -304,7 +320,7 @@ func (f *File) Reload() (err error) {
for _, s := range f.dataSources {
if err = f.reload(s); err != nil {
// In loose mode, we create an empty default section for nonexistent files.
if os.IsNotExist(err) && f.looseMode {
if os.IsNotExist(err) && f.options.Loose {
f.parse(bytes.NewBuffer(nil))
continue
}
+14
View File
@@ -170,6 +170,20 @@ func Test_Load(t *testing.T) {
So(err, ShouldNotBeNil)
})
})
Convey("Get section and key insensitively", t, func() {
cfg, err := InsensitiveLoad([]byte(_CONF_DATA), "testdata/conf.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
sec, err := cfg.GetSection("Author")
So(err, ShouldBeNil)
So(sec, ShouldNotBeNil)
key, err := sec.GetKey("E-mail")
So(err, ShouldBeNil)
So(key, ShouldNotBeNil)
})
}
func Test_LooseLoad(t *testing.T) {
+2 -1
View File
@@ -263,7 +263,8 @@ func (f *File) parse(reader io.Reader) (err error) {
return fmt.Errorf("unclosed section: %s", line)
}
section, err = f.NewSection(string(line[1:closeIdx]))
name := string(line[1:closeIdx])
section, err = f.NewSection(name)
if err != nil {
return err
}
+6 -1
View File
@@ -43,6 +43,8 @@ func (s *Section) Name() string {
func (s *Section) NewKey(name, val string) (*Key, error) {
if len(name) == 0 {
return nil, errors.New("error creating new key: empty key name")
} else if s.f.options.Insensitive {
name = strings.ToLower(name)
}
if s.f.BlockMode {
@@ -67,6 +69,9 @@ func (s *Section) GetKey(name string) (*Key, error) {
if s.f.BlockMode {
s.f.lock.RLock()
}
if s.f.options.Insensitive {
name = strings.ToLower(name)
}
key := s.keys[name]
if s.f.BlockMode {
s.f.lock.RUnlock()
@@ -140,7 +145,7 @@ func (s *Section) Keys() []*Key {
}
// ParentKeys returns list of keys of parent section.
func (s * Section) ParentKeys() []*Key {
func (s *Section) ParentKeys() []*Key {
var parentKeys []*Key
sname := s.name
for {