From c58d639bf5c4d4ff78f8c8e7e5592221fe1c9d03 Mon Sep 17 00:00:00 2001 From: Yvonnick Esnault Date: Mon, 11 Sep 2017 23:41:21 +0200 Subject: [PATCH 1/5] feat: comment annotation on struct Signed-off-by: Yvonnick Esnault --- marshal.go | 11 ++++++++--- marshal_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++ parser.go | 6 +++--- parser_test.go | 2 +- toml.go | 12 ++++++++---- tomltree_create.go | 6 +++--- tomltree_write.go | 23 +++++++++++++++++++++- tomltree_write_test.go | 4 ++-- 8 files changed, 91 insertions(+), 17 deletions(-) diff --git a/marshal.go b/marshal.go index 1a3176f9..42fb3cfb 100644 --- a/marshal.go +++ b/marshal.go @@ -11,6 +11,7 @@ import ( type tomlOpts struct { name string + comment *string include bool omitempty bool } @@ -147,7 +148,7 @@ func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { if err != nil { return nil, err } - tval.Set(opts.name, val) + tval.Set(opts.name, opts.comment, val) } } case reflect.Map: @@ -157,7 +158,7 @@ func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { if err != nil { return nil, err } - tval.Set(key.String(), val) + tval.Set(key.String(), nil, val) } } return tval, nil @@ -448,7 +449,11 @@ func unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) func tomlOptions(vf reflect.StructField) tomlOpts { tag := vf.Tag.Get("toml") parse := strings.Split(tag, ",") - result := tomlOpts{vf.Name, true, false} + var comment *string + if c := vf.Tag.Get("comment"); c != "" { + comment = &c + } + result := tomlOpts{vf.Name, comment, true, false} if parse[0] != "" { if parse[0] == "-" && len(parse) == 1 { result.include = false diff --git a/marshal_test.go b/marshal_test.go index dbfc7c1d..1dd7be65 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -598,3 +598,47 @@ func TestNestedCustomMarshaler(t *testing.T) { t.Errorf("Bad nested custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) } } + +var commentTestToml = []byte(` +# postgres it's a comment on type +[postgres] + noComment = "cvalue" + + # A comment on AttrB + password = "bvalue" + + # A comment on AttrA + user = "avalue" + + # a comment on My + [[postgres.My]] + + # a comment on My + [[postgres.My]] +`) + +func TestMarshalComment(t *testing.T) { + type TypeC struct { + my string + } + type TypeB struct { + AttrA string `toml:"user" comment:"A comment on AttrA"` + AttrB string `toml:"password" comment:"A comment on AttrB"` + AttrC string `toml:"noComment"` + My []TypeC `comment:"a comment on My"` + } + type TypeA struct { + TypeB TypeB `toml:"postgres" comment:"it's a comment on type"` + } + + ta := []TypeC{{my: "Foo"}, {my: "Baar"}} + config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", My: ta}} + result, err := Marshal(config) + if err != nil { + t.Fatal(err) + } + expected := commentTestToml + if !bytes.Equal(result, expected) { + t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) + } +} diff --git a/parser.go b/parser.go index 8ee49cb5..5ca65dff 100644 --- a/parser.go +++ b/parser.go @@ -110,7 +110,7 @@ func (p *tomlParser) parseGroupArray() tomlParserStateFn { newTree := newTree() newTree.position = startToken.Position array = append(array, newTree) - p.tree.SetPath(p.currentTable, array) + p.tree.SetPath(p.currentTable, nil, array) // remove all keys that were children of this table array prefix := key.val + "." @@ -205,7 +205,7 @@ func (p *tomlParser) parseAssign() tomlParserStateFn { case *Tree, []*Tree: toInsert = value default: - toInsert = &tomlValue{value, key.Position} + toInsert = &tomlValue{value: value, position: key.Position} } targetNode.values[keyVal] = toInsert return p.parseStart @@ -299,7 +299,7 @@ Loop: key := p.getToken() p.assume(tokenEqual) value := p.parseRvalue() - tree.Set(key.val, value) + tree.Set(key.val, nil, value) case tokenComma: if previous == nil { p.raiseError(follow, "inline table cannot start with a comma") diff --git a/parser_test.go b/parser_test.go index 508cb65f..7a6c21cd 100644 --- a/parser_test.go +++ b/parser_test.go @@ -46,7 +46,7 @@ func assertTree(t *testing.T, tree *Tree, err error, ref map[string]interface{}) func TestCreateSubTree(t *testing.T) { tree := newTree() tree.createSubTree([]string{"a", "b", "c"}, Position{}) - tree.Set("a.b.c", 42) + tree.Set("a.b.c", nil, 42) if tree.Get("a.b.c") != 42 { t.Fail() } diff --git a/toml.go b/toml.go index 64f19ed3..4da92478 100644 --- a/toml.go +++ b/toml.go @@ -12,12 +12,14 @@ import ( type tomlValue struct { value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list + comment *string position Position } // Tree is the result of the parsing of a TOML file. type Tree struct { values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree + comment *string position Position } @@ -177,14 +179,14 @@ func (t *Tree) GetDefault(key string, def interface{}) interface{} { // Set an element in the tree. // Key is a dot-separated path (e.g. a.b.c). // Creates all necessary intermediate trees, if needed. -func (t *Tree) Set(key string, value interface{}) { - t.SetPath(strings.Split(key, "."), value) +func (t *Tree) Set(key string, comment *string, value interface{}) { + t.SetPath(strings.Split(key, "."), comment, value) } // SetPath sets an element in the tree. // Keys is an array of path elements (e.g. {"a","b","c"}). // Creates all necessary intermediate trees, if needed. -func (t *Tree) SetPath(keys []string, value interface{}) { +func (t *Tree) SetPath(keys []string, comment *string, value interface{}) { subtree := t for _, intermediateKey := range keys[:len(keys)-1] { nextTree, exists := subtree.values[intermediateKey] @@ -210,12 +212,14 @@ func (t *Tree) SetPath(keys []string, value interface{}) { switch value.(type) { case *Tree: toInsert = value + subtree.comment = comment case []*Tree: toInsert = value + subtree.comment = comment case *tomlValue: toInsert = value default: - toInsert = &tomlValue{value: value} + toInsert = &tomlValue{value: value, comment: comment} } subtree.values[keys[len(keys)-1]] = toInsert diff --git a/tomltree_create.go b/tomltree_create.go index 19d1c0dc..79610e9b 100644 --- a/tomltree_create.go +++ b/tomltree_create.go @@ -104,7 +104,7 @@ func sliceToTree(object interface{}) (interface{}, error) { } arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue)) } - return &tomlValue{arrayValue.Interface(), Position{}}, nil + return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil } func toTree(object interface{}) (interface{}, error) { @@ -127,7 +127,7 @@ func toTree(object interface{}) (interface{}, error) { } values[key.String()] = newValue } - return &Tree{values, Position{}}, nil + return &Tree{values: values, position: Position{}}, nil } if value.Kind() == reflect.Array || value.Kind() == reflect.Slice { @@ -138,5 +138,5 @@ func toTree(object interface{}) (interface{}, error) { if err != nil { return nil, err } - return &tomlValue{simpleValue, Position{}}, nil + return &tomlValue{value: simpleValue, position: Position{}}, nil } diff --git a/tomltree_write.go b/tomltree_write.go index ca763ed5..233b1c55 100644 --- a/tomltree_write.go +++ b/tomltree_write.go @@ -118,6 +118,14 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( return bytesCount, err } + if v.comment != nil { + writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", *v.comment, "\n") + bytesCount += int64(writtenBytesCountComment) + if errc != nil { + return bytesCount, errc + } + } + writtenBytesCount, err := writeStrings(w, indent, k, " = ", repr, "\n") bytesCount += int64(writtenBytesCount) if err != nil { @@ -132,10 +140,16 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( if keyspace != "" { combinedKey = keyspace + "." + combinedKey } - switch node := v.(type) { // node has to be of those two types given how keys are sorted above case *Tree: + if t.comment != nil { + writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", combinedKey, " ", *t.comment) + bytesCount += int64(writtenBytesCountComment) + if errc != nil { + return bytesCount, errc + } + } writtenBytesCount, err := writeStrings(w, "\n", indent, "[", combinedKey, "]\n") bytesCount += int64(writtenBytesCount) if err != nil { @@ -147,6 +161,13 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( } case []*Tree: for _, subTree := range node { + if t.comment != nil { + writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", *t.comment) + bytesCount += int64(writtenBytesCountComment) + if errc != nil { + return bytesCount, errc + } + } writtenBytesCount, err := writeStrings(w, "\n", indent, "[[", combinedKey, "]]\n") bytesCount += int64(writtenBytesCount) if err != nil { diff --git a/tomltree_write_test.go b/tomltree_write_test.go index c2a1ce3b..2d4d6989 100644 --- a/tomltree_write_test.go +++ b/tomltree_write_test.go @@ -161,13 +161,13 @@ func TestTreeWriteToInvalidTreeSimpleValue(t *testing.T) { } func TestTreeWriteToInvalidTreeTomlValue(t *testing.T) { - tree := Tree{values: map[string]interface{}{"foo": &tomlValue{int8(1), Position{}}}} + tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: nil, position: Position{}}}} _, err := tree.ToTomlString() assertErrorString(t, "unsupported value type int8: 1", err) } func TestTreeWriteToInvalidTreeTomlValueArray(t *testing.T) { - tree := Tree{values: map[string]interface{}{"foo": &tomlValue{[]interface{}{int8(1)}, Position{}}}} + tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: nil, position: Position{}}}} _, err := tree.ToTomlString() assertErrorString(t, "unsupported value type int8: 1", err) } From a421401cc8c6a1cb18bb6602723ead2ca2120a38 Mon Sep 17 00:00:00 2001 From: Yvonnick Esnault Date: Tue, 12 Sep 2017 10:07:09 +0200 Subject: [PATCH 2/5] feat: commented annotation Signed-off-by: Yvonnick Esnault --- marshal.go | 9 ++++++--- marshal_test.go | 7 ++++--- parser.go | 4 ++-- parser_test.go | 2 +- toml.go | 24 ++++++++++++++---------- tomltree_write.go | 41 +++++++++++++++++++++++------------------ 6 files changed, 50 insertions(+), 37 deletions(-) diff --git a/marshal.go b/marshal.go index 42fb3cfb..1dbec2dc 100644 --- a/marshal.go +++ b/marshal.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "reflect" + "strconv" "strings" "time" ) @@ -12,6 +13,7 @@ import ( type tomlOpts struct { name string comment *string + commented bool include bool omitempty bool } @@ -148,7 +150,7 @@ func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { if err != nil { return nil, err } - tval.Set(opts.name, opts.comment, val) + tval.Set(opts.name, opts.comment, opts.commented, val) } } case reflect.Map: @@ -158,7 +160,7 @@ func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { if err != nil { return nil, err } - tval.Set(key.String(), nil, val) + tval.Set(key.String(), nil, false, val) } } return tval, nil @@ -453,7 +455,8 @@ func tomlOptions(vf reflect.StructField) tomlOpts { if c := vf.Tag.Get("comment"); c != "" { comment = &c } - result := tomlOpts{vf.Name, comment, true, false} + commented, _ := strconv.ParseBool(vf.Tag.Get("commented")) + result := tomlOpts{name: vf.Name, comment: comment, commented: commented, include: true, omitempty: false} if parse[0] != "" { if parse[0] == "-" && len(parse) == 1 { result.include = false diff --git a/marshal_test.go b/marshal_test.go index 1dd7be65..a44490e3 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -600,8 +600,9 @@ func TestNestedCustomMarshaler(t *testing.T) { } var commentTestToml = []byte(` -# postgres it's a comment on type +# it's a comment on type [postgres] + # isCommented = "dvalue" noComment = "cvalue" # A comment on AttrB @@ -613,7 +614,6 @@ var commentTestToml = []byte(` # a comment on My [[postgres.My]] - # a comment on My [[postgres.My]] `) @@ -625,6 +625,7 @@ func TestMarshalComment(t *testing.T) { AttrA string `toml:"user" comment:"A comment on AttrA"` AttrB string `toml:"password" comment:"A comment on AttrB"` AttrC string `toml:"noComment"` + AttrD string `toml:"isCommented" commented:"true"` My []TypeC `comment:"a comment on My"` } type TypeA struct { @@ -632,7 +633,7 @@ func TestMarshalComment(t *testing.T) { } ta := []TypeC{{my: "Foo"}, {my: "Baar"}} - config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", My: ta}} + config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", AttrD: "dvalue", My: ta}} result, err := Marshal(config) if err != nil { t.Fatal(err) diff --git a/parser.go b/parser.go index 5ca65dff..85feafeb 100644 --- a/parser.go +++ b/parser.go @@ -110,7 +110,7 @@ func (p *tomlParser) parseGroupArray() tomlParserStateFn { newTree := newTree() newTree.position = startToken.Position array = append(array, newTree) - p.tree.SetPath(p.currentTable, nil, array) + p.tree.SetPath(p.currentTable, nil, false, array) // remove all keys that were children of this table array prefix := key.val + "." @@ -299,7 +299,7 @@ Loop: key := p.getToken() p.assume(tokenEqual) value := p.parseRvalue() - tree.Set(key.val, nil, value) + tree.Set(key.val, nil, false, value) case tokenComma: if previous == nil { p.raiseError(follow, "inline table cannot start with a comma") diff --git a/parser_test.go b/parser_test.go index 7a6c21cd..81cbb21a 100644 --- a/parser_test.go +++ b/parser_test.go @@ -46,7 +46,7 @@ func assertTree(t *testing.T, tree *Tree, err error, ref map[string]interface{}) func TestCreateSubTree(t *testing.T) { tree := newTree() tree.createSubTree([]string{"a", "b", "c"}, Position{}) - tree.Set("a.b.c", nil, 42) + tree.Set("a.b.c", nil, false, 42) if tree.Get("a.b.c") != 42 { t.Fail() } diff --git a/toml.go b/toml.go index 4da92478..75df92e9 100644 --- a/toml.go +++ b/toml.go @@ -11,16 +11,18 @@ import ( ) type tomlValue struct { - value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list - comment *string - position Position + value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list + comment *string + commented bool + position Position } // Tree is the result of the parsing of a TOML file. type Tree struct { - values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree - comment *string - position Position + values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree + comment *string + commented bool + position Position } func newTree() *Tree { @@ -179,14 +181,14 @@ func (t *Tree) GetDefault(key string, def interface{}) interface{} { // Set an element in the tree. // Key is a dot-separated path (e.g. a.b.c). // Creates all necessary intermediate trees, if needed. -func (t *Tree) Set(key string, comment *string, value interface{}) { - t.SetPath(strings.Split(key, "."), comment, value) +func (t *Tree) Set(key string, comment *string, commented bool, value interface{}) { + t.SetPath(strings.Split(key, "."), comment, commented, value) } // SetPath sets an element in the tree. // Keys is an array of path elements (e.g. {"a","b","c"}). // Creates all necessary intermediate trees, if needed. -func (t *Tree) SetPath(keys []string, comment *string, value interface{}) { +func (t *Tree) SetPath(keys []string, comment *string, commented bool, value interface{}) { subtree := t for _, intermediateKey := range keys[:len(keys)-1] { nextTree, exists := subtree.values[intermediateKey] @@ -213,13 +215,15 @@ func (t *Tree) SetPath(keys []string, comment *string, value interface{}) { case *Tree: toInsert = value subtree.comment = comment + subtree.commented = commented case []*Tree: toInsert = value subtree.comment = comment + subtree.commented = commented case *tomlValue: toInsert = value default: - toInsert = &tomlValue{value: value, comment: comment} + toInsert = &tomlValue{value: value, comment: comment, commented: commented} } subtree.values[keys[len(keys)-1]] = toInsert diff --git a/tomltree_write.go b/tomltree_write.go index 233b1c55..85568907 100644 --- a/tomltree_write.go +++ b/tomltree_write.go @@ -119,14 +119,19 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( } if v.comment != nil { - writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", *v.comment, "\n") + comment := strings.Replace(*v.comment, "\n", "\n"+indent, -1) + writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", comment, "\n") bytesCount += int64(writtenBytesCountComment) if errc != nil { return bytesCount, errc } } - writtenBytesCount, err := writeStrings(w, indent, k, " = ", repr, "\n") + var commented string + if v.commented { + commented = "# " + } + writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n") bytesCount += int64(writtenBytesCount) if err != nil { return bytesCount, err @@ -140,17 +145,24 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( if keyspace != "" { combinedKey = keyspace + "." + combinedKey } + var commented string + if t.commented { + commented = "# " + } + + if t.comment != nil { + comment := strings.Replace(*t.comment, "\n", "\n"+indent, -1) + writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", comment) + bytesCount += int64(writtenBytesCountComment) + if errc != nil { + return bytesCount, errc + } + } + switch node := v.(type) { // node has to be of those two types given how keys are sorted above case *Tree: - if t.comment != nil { - writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", combinedKey, " ", *t.comment) - bytesCount += int64(writtenBytesCountComment) - if errc != nil { - return bytesCount, errc - } - } - writtenBytesCount, err := writeStrings(w, "\n", indent, "[", combinedKey, "]\n") + writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n") bytesCount += int64(writtenBytesCount) if err != nil { return bytesCount, err @@ -161,14 +173,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( } case []*Tree: for _, subTree := range node { - if t.comment != nil { - writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", *t.comment) - bytesCount += int64(writtenBytesCountComment) - if errc != nil { - return bytesCount, errc - } - } - writtenBytesCount, err := writeStrings(w, "\n", indent, "[[", combinedKey, "]]\n") + writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n") bytesCount += int64(writtenBytesCount) if err != nil { return bytesCount, err From e47763c0d955fb075f6e4c05f0c678a4b988c420 Mon Sep 17 00:00:00 2001 From: Yvonnick Esnault Date: Tue, 12 Sep 2017 10:21:15 +0200 Subject: [PATCH 3/5] fix: test with comment Signed-off-by: Yvonnick Esnault --- marshal_test.go | 15 +++++++++++---- tomltree_write.go | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/marshal_test.go b/marshal_test.go index a44490e3..752fd532 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -605,7 +605,8 @@ var commentTestToml = []byte(` # isCommented = "dvalue" noComment = "cvalue" - # A comment on AttrB + # A comment on AttrB with a + # break line password = "bvalue" # A comment on AttrA @@ -614,16 +615,22 @@ var commentTestToml = []byte(` # a comment on My [[postgres.My]] + # a comment on my on typeC + My = "Foo" + [[postgres.My]] + + # a comment on my on typeC + My = "Baar" `) func TestMarshalComment(t *testing.T) { type TypeC struct { - my string + My string `comment:"a comment on my on typeC"` } type TypeB struct { AttrA string `toml:"user" comment:"A comment on AttrA"` - AttrB string `toml:"password" comment:"A comment on AttrB"` + AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"` AttrC string `toml:"noComment"` AttrD string `toml:"isCommented" commented:"true"` My []TypeC `comment:"a comment on My"` @@ -632,7 +639,7 @@ func TestMarshalComment(t *testing.T) { TypeB TypeB `toml:"postgres" comment:"it's a comment on type"` } - ta := []TypeC{{my: "Foo"}, {my: "Baar"}} + ta := []TypeC{{My: "Foo"}, {My: "Baar"}} config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", AttrD: "dvalue", My: ta}} result, err := Marshal(config) if err != nil { diff --git a/tomltree_write.go b/tomltree_write.go index 85568907..8a5e7e49 100644 --- a/tomltree_write.go +++ b/tomltree_write.go @@ -119,7 +119,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( } if v.comment != nil { - comment := strings.Replace(*v.comment, "\n", "\n"+indent, -1) + comment := strings.Replace(*v.comment, "\n", "\n"+indent+"#", -1) writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", comment, "\n") bytesCount += int64(writtenBytesCountComment) if errc != nil { @@ -151,7 +151,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( } if t.comment != nil { - comment := strings.Replace(*t.comment, "\n", "\n"+indent, -1) + comment := strings.Replace(*t.comment, "\n", "\n"+indent+"#", -1) writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", comment) bytesCount += int64(writtenBytesCountComment) if errc != nil { From 9a5282f852773c55c1be7488cf7091ad2084ee5a Mon Sep 17 00:00:00 2001 From: Yvonnick Esnault Date: Wed, 20 Sep 2017 16:40:45 +0200 Subject: [PATCH 4/5] fix: comment on tree Signed-off-by: Yvonnick Esnault --- marshal.go | 8 ++++---- parser.go | 4 ++-- toml.go | 18 +++++++++--------- tomltree_write.go | 35 +++++++++++++++++++++++------------ 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/marshal.go b/marshal.go index 1dbec2dc..2a1cecf0 100644 --- a/marshal.go +++ b/marshal.go @@ -12,7 +12,7 @@ import ( type tomlOpts struct { name string - comment *string + comment string commented bool include bool omitempty bool @@ -160,7 +160,7 @@ func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { if err != nil { return nil, err } - tval.Set(key.String(), nil, false, val) + tval.Set(key.String(), "", false, val) } } return tval, nil @@ -451,9 +451,9 @@ func unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) func tomlOptions(vf reflect.StructField) tomlOpts { tag := vf.Tag.Get("toml") parse := strings.Split(tag, ",") - var comment *string + var comment string if c := vf.Tag.Get("comment"); c != "" { - comment = &c + comment = c } commented, _ := strconv.ParseBool(vf.Tag.Get("commented")) result := tomlOpts{name: vf.Name, comment: comment, commented: commented, include: true, omitempty: false} diff --git a/parser.go b/parser.go index 85feafeb..d492a1e6 100644 --- a/parser.go +++ b/parser.go @@ -110,7 +110,7 @@ func (p *tomlParser) parseGroupArray() tomlParserStateFn { newTree := newTree() newTree.position = startToken.Position array = append(array, newTree) - p.tree.SetPath(p.currentTable, nil, false, array) + p.tree.SetPath(p.currentTable, "", false, array) // remove all keys that were children of this table array prefix := key.val + "." @@ -299,7 +299,7 @@ Loop: key := p.getToken() p.assume(tokenEqual) value := p.parseRvalue() - tree.Set(key.val, nil, false, value) + tree.Set(key.val, "", false, value) case tokenComma: if previous == nil { p.raiseError(follow, "inline table cannot start with a comma") diff --git a/toml.go b/toml.go index 75df92e9..c3e32437 100644 --- a/toml.go +++ b/toml.go @@ -12,7 +12,7 @@ import ( type tomlValue struct { value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list - comment *string + comment string commented bool position Position } @@ -20,7 +20,7 @@ type tomlValue struct { // Tree is the result of the parsing of a TOML file. type Tree struct { values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree - comment *string + comment string commented bool position Position } @@ -181,14 +181,14 @@ func (t *Tree) GetDefault(key string, def interface{}) interface{} { // Set an element in the tree. // Key is a dot-separated path (e.g. a.b.c). // Creates all necessary intermediate trees, if needed. -func (t *Tree) Set(key string, comment *string, commented bool, value interface{}) { +func (t *Tree) Set(key string, comment string, commented bool, value interface{}) { t.SetPath(strings.Split(key, "."), comment, commented, value) } // SetPath sets an element in the tree. // Keys is an array of path elements (e.g. {"a","b","c"}). // Creates all necessary intermediate trees, if needed. -func (t *Tree) SetPath(keys []string, comment *string, commented bool, value interface{}) { +func (t *Tree) SetPath(keys []string, comment string, commented bool, value interface{}) { subtree := t for _, intermediateKey := range keys[:len(keys)-1] { nextTree, exists := subtree.values[intermediateKey] @@ -213,15 +213,15 @@ func (t *Tree) SetPath(keys []string, comment *string, commented bool, value int switch value.(type) { case *Tree: + tt := value.(*Tree) + tt.comment = comment toInsert = value - subtree.comment = comment - subtree.commented = commented case []*Tree: toInsert = value - subtree.comment = comment - subtree.commented = commented case *tomlValue: - toInsert = value + tt := value.(*tomlValue) + tt.comment = comment + toInsert = tt default: toInsert = &tomlValue{value: value, comment: comment, commented: commented} } diff --git a/tomltree_write.go b/tomltree_write.go index 8a5e7e49..449f35a4 100644 --- a/tomltree_write.go +++ b/tomltree_write.go @@ -118,9 +118,13 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( return bytesCount, err } - if v.comment != nil { - comment := strings.Replace(*v.comment, "\n", "\n"+indent+"#", -1) - writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", comment, "\n") + if v.comment != "" { + comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1) + start := "# " + if strings.HasPrefix(comment, "#") { + start = "" + } + writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n") bytesCount += int64(writtenBytesCountComment) if errc != nil { return bytesCount, errc @@ -150,18 +154,25 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) ( commented = "# " } - if t.comment != nil { - comment := strings.Replace(*t.comment, "\n", "\n"+indent+"#", -1) - writtenBytesCountComment, errc := writeStrings(w, "\n", indent, "# ", comment) - bytesCount += int64(writtenBytesCountComment) - if errc != nil { - return bytesCount, errc - } - } - switch node := v.(type) { // node has to be of those two types given how keys are sorted above case *Tree: + tv, ok := t.values[k].(*Tree) + if !ok { + return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) + } + if tv.comment != "" { + comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1) + start := "# " + if strings.HasPrefix(comment, "#") { + start = "" + } + writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment) + bytesCount += int64(writtenBytesCountComment) + if errc != nil { + return bytesCount, errc + } + } writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n") bytesCount += int64(writtenBytesCount) if err != nil { From ce6702f5101e773410e4497b8ea13dde7077af29 Mon Sep 17 00:00:00 2001 From: Yvonnick Esnault Date: Wed, 20 Sep 2017 17:58:02 +0200 Subject: [PATCH 5/5] fix : no comment on []Tree Signed-off-by: Yvonnick Esnault --- marshal_test.go | 11 +++++------ parser_test.go | 2 +- tomltree_write_test.go | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/marshal_test.go b/marshal_test.go index 752fd532..f9196cd0 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -612,7 +612,6 @@ var commentTestToml = []byte(` # A comment on AttrA user = "avalue" - # a comment on My [[postgres.My]] # a comment on my on typeC @@ -629,11 +628,11 @@ func TestMarshalComment(t *testing.T) { My string `comment:"a comment on my on typeC"` } type TypeB struct { - AttrA string `toml:"user" comment:"A comment on AttrA"` - AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"` - AttrC string `toml:"noComment"` - AttrD string `toml:"isCommented" commented:"true"` - My []TypeC `comment:"a comment on My"` + AttrA string `toml:"user" comment:"A comment on AttrA"` + AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"` + AttrC string `toml:"noComment"` + AttrD string `toml:"isCommented" commented:"true"` + My []TypeC } type TypeA struct { TypeB TypeB `toml:"postgres" comment:"it's a comment on type"` diff --git a/parser_test.go b/parser_test.go index 81cbb21a..029bc52c 100644 --- a/parser_test.go +++ b/parser_test.go @@ -46,7 +46,7 @@ func assertTree(t *testing.T, tree *Tree, err error, ref map[string]interface{}) func TestCreateSubTree(t *testing.T) { tree := newTree() tree.createSubTree([]string{"a", "b", "c"}, Position{}) - tree.Set("a.b.c", nil, false, 42) + tree.Set("a.b.c", "", false, 42) if tree.Get("a.b.c") != 42 { t.Fail() } diff --git a/tomltree_write_test.go b/tomltree_write_test.go index 2d4d6989..43053904 100644 --- a/tomltree_write_test.go +++ b/tomltree_write_test.go @@ -161,13 +161,13 @@ func TestTreeWriteToInvalidTreeSimpleValue(t *testing.T) { } func TestTreeWriteToInvalidTreeTomlValue(t *testing.T) { - tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: nil, position: Position{}}}} + tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: "", position: Position{}}}} _, err := tree.ToTomlString() assertErrorString(t, "unsupported value type int8: 1", err) } func TestTreeWriteToInvalidTreeTomlValueArray(t *testing.T) { - tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: nil, position: Position{}}}} + tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: "", position: Position{}}}} _, err := tree.ToTomlString() assertErrorString(t, "unsupported value type int8: 1", err) }