From 4b84ed0055ff82ca160ba239e1d04b241f988ed8 Mon Sep 17 00:00:00 2001 From: George Vilches Date: Wed, 21 Dec 2016 06:26:37 -0500 Subject: [PATCH] Added support for unparseable sections. (#72) * Added support for unparseable sections. * Add write support for unparseable sections. * Slightly changed test files to make test results clearer with tools/platforms that handle whitespace differently. --- ini.go | 17 +++++++++++++++++ ini_test.go | 26 ++++++++++++++++++++++++++ parser.go | 17 +++++++++++++++++ section.go | 9 ++++++++- section_test.go | 28 ++++++++++++++++++++++++++++ testdata/aicc.ini | 11 +++++++++++ 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 testdata/aicc.ini diff --git a/ini.go b/ini.go index e812819..44f8999 100644 --- a/ini.go +++ b/ini.go @@ -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) { @@ -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 { @@ -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. diff --git a/ini_test.go b/ini_test.go index 66a5098..fef7035 100644 --- a/ini_test.go +++ b/ini_test.go @@ -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> This slide has the fuel listed in the wrong units +`) + }) +} + // Helpers for slice tests. func float64sEqual(values []float64, expected ...float64) { So(values, ShouldHaveLength, len(expected)) diff --git a/parser.go b/parser.go index 8ddf4e2..7dd18bd 100644 --- a/parser.go +++ b/parser.go @@ -250,6 +250,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 { @@ -295,6 +296,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 } diff --git a/section.go b/section.go index bbb73ca..e3b58d5 100644 --- a/section.go +++ b/section.go @@ -28,10 +28,11 @@ 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. @@ -39,6 +40,12 @@ 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 { diff --git a/section_test.go b/section_test.go index c6efb2c..80282c1 100644 --- a/section_test.go +++ b/section_test.go @@ -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`) + }) + }) +} \ No newline at end of file diff --git a/testdata/aicc.ini b/testdata/aicc.ini new file mode 100644 index 0000000..59a6197 --- /dev/null +++ b/testdata/aicc.ini @@ -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> This slide has the fuel listed in the wrong units