Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions ini.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ type LoadOptions struct {
// AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
// This type of keys are mostly used in my.cnf.
AllowBooleanKeys bool
// Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
// conform to key/value pairs. Specify the names of those blocks here.
UnparseableSections []string
}

func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
Expand Down Expand Up @@ -233,6 +236,13 @@ func (f *File) NewSection(name string) (*Section, error) {
return f.sections[name], nil
}

// NewRawSection creates a new section as an unparseable body.
func (f *File) NewRawSection(name string, body string) (*Section, error) {
section, err := f.NewSection(name)
section.rawBody = body
return section, err
}

// NewSections creates a list of sections.
func (f *File) NewSections(names ...string) (err error) {
for _, name := range names {
Expand Down Expand Up @@ -386,6 +396,13 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
}
}

if sec.rawBody != "" {
if _, err = buf.WriteString(sec.rawBody); err != nil {
return 0, err
}
continue
}

// Count and generate alignment length and buffer spaces using the
// longest key. Keys may be modifed if they contain certain characters so
// we need to take that into account in our calculation.
Expand Down
26 changes: 26 additions & 0 deletions ini_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,32 @@ NotFound, State, 50000"""
})
}

func Test_File_WriteTo_SectionRaw(t *testing.T) {
Convey("Write a INI with a raw section", t, func() {
var buf bytes.Buffer
cfg, err := LoadSources(
LoadOptions{
UnparseableSections: []string{"CORE_LESSON", "COMMENTS"},
},
"testdata/aicc.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
cfg.WriteToIndent(&buf, "\t")
So(buf.String(), ShouldEqual, `[Core]
Lesson_Location = 87
Lesson_Status = C
Score = 3
Time = 00:02:30

[CORE_LESSON]
my lesson state data – 1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000 – end my lesson state data
[COMMENTS]
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>
`)
})
}

// Helpers for slice tests.
func float64sEqual(values []float64, expected ...float64) {
So(values, ShouldHaveLength, len(expected))
Expand Down
17 changes: 17 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ func (f *File) parse(reader io.Reader) (err error) {
section, _ := f.NewSection(DEFAULT_SECTION)

var line []byte
var inUnparseableSection bool
for !p.isEOF {
line, err = p.readUntil('\n')
if err != nil {
Expand Down Expand Up @@ -280,6 +281,22 @@ func (f *File) parse(reader io.Reader) (err error) {
// Reset aotu-counter and comments
p.comment.Reset()
p.count = 1

inUnparseableSection = false
if len(f.options.UnparseableSections) > 0 {
for _, sectionName := range f.options.UnparseableSections {
if sectionName == name ||
(f.options.Insensitive && strings.ToLower(sectionName) == strings.ToLower(name)) {
inUnparseableSection = true
continue
}
}
}
continue
}

if inUnparseableSection {
section.rawBody += string(line)
continue
}

Expand Down
9 changes: 8 additions & 1 deletion section.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,24 @@ type Section struct {
keys map[string]*Key
keyList []string
keysHash map[string]string
rawBody string
}

func newSection(f *File, name string) *Section {
return &Section{f, "", name, make(map[string]*Key), make([]string, 0, 10), make(map[string]string)}
return &Section{f, "", name, make(map[string]*Key), make([]string, 0, 10), make(map[string]string), ""}
}

// Name returns name of Section.
func (s *Section) Name() string {
return s.name
}

// Body returns rawBody of Section if the section was marked as unparseable.
// It still follows the other rules of the INI format surrounding leading/trailing whitespace.
func (s *Section) Body() string {
return strings.TrimSpace(s.rawBody)
}

// NewKey creates a new key to given section.
func (s *Section) NewKey(name, val string) (*Key, error) {
if len(name) == 0 {
Expand Down
28 changes: 28 additions & 0 deletions section_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,31 @@ func Test_Section(t *testing.T) {
})
})
}

func Test_SectionRaw(t *testing.T) {
Convey("Test section raw string", t, func() {
cfg, err := LoadSources(
LoadOptions{
Insensitive: true,
UnparseableSections: []string{"core_lesson", "comments"},
},
"testdata/aicc.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)

Convey("Get section strings", func() {
So(strings.Join(cfg.SectionStrings(), ","), ShouldEqual, "DEFAULT,core,core_lesson,comments")
})

Convey("Validate non-raw section", func() {
val, err := cfg.Section("core").GetKey("lesson_status")
So(err, ShouldBeNil)
So(val.String(), ShouldEqual, "C")
})

Convey("Validate raw section", func() {
So(cfg.Section("core_lesson").Body(), ShouldEqual, `my lesson state data – 1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000 – end my lesson state data`)
})
})
}
11 changes: 11 additions & 0 deletions testdata/aicc.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Core]
Lesson_Location = 87
Lesson_Status = C
Score = 3
Time = 00:02:30

[CORE_LESSON]
my lesson state data – 1111111111111111111000000000000000001110000
111111111111111111100000000000111000000000 – end my lesson state data
[COMMENTS]
<1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>