Compare commits

...

3 Commits

Author SHA1 Message Date
Alan Murtagh c01d1270ff Multiline Marshal tag (#221)
The new multiline tag works just like the existing 'commented' tag (i.e.
`multiline:"true"`), and tells go-toml to marshal the value as a
multi-line string. The tag currently has no impact on any non-string
fields.
2018-06-05 13:47:19 -07:00
Cameron Moore 66540cf1fc Go 1.10 support (#223)
* Update Travis CI to use latest Go releases

* Fix go vet issues for Go 1.10

Starting in Go 1.10, the `go test` command now automatically runs `go
vet`. This commit fixes two issues flagged by vet that cause `go test`
to fail.

* Fix gofmt issue for Go 1.10

Go 1.10 introduced a small formatting change with comments in empty
multiline slice definitions.  This commit attempts to move the offending
comment in such a way that all version of gofmt will agree on its
location.

* Remove go-vet from test.sh

Starting in Go 1.10, the `go test` command automatically runs `go vet`,
so we don't need to run `go vet` explicitly from within test.sh.

Fixes #222
2018-03-23 11:52:43 -07:00
Chris 05bcc0fb0d Make multi-line arrays always use trailing commas (#217)
This makes ArraysWithOneElementPerLine output arrays with commas after every element.

```
A = [1,2,3]
```

Now becomes:

```
A = [
  1,
  2,
  3,
]
```
2018-02-28 15:36:31 -08:00
9 changed files with 141 additions and 34 deletions
+3 -2
View File
@@ -1,8 +1,9 @@
sudo: false sudo: false
language: go language: go
go: go:
- 1.8.5 - 1.8.x
- 1.9.2 - 1.9.x
- 1.10.x
- tip - tip
matrix: matrix:
allow_failures: allow_failures:
+6 -7
View File
@@ -17,13 +17,12 @@ import (
func main() { func main() {
flag.Usage = func() { flag.Usage = func() {
fmt.Fprintln(os.Stderr, `tomljson can be used in two ways: fmt.Fprintln(os.Stderr, "tomljson can be used in two ways:")
Writing to STDIN and reading from STDOUT: fmt.Fprintln(os.Stderr, "Writing to STDIN and reading from STDOUT:")
cat file.toml | tomljson > file.json fmt.Fprintln(os.Stderr, " cat file.toml | tomljson > file.json")
fmt.Fprintln(os.Stderr, "")
Reading from a file name: fmt.Fprintln(os.Stderr, "Reading from a file name:")
tomljson file.toml fmt.Fprintln(os.Stderr, " tomljson file.toml")
`)
} }
flag.Parse() flag.Parse()
os.Exit(processMain(flag.Args(), os.Stdin, os.Stdout, os.Stderr)) os.Exit(processMain(flag.Args(), os.Stdin, os.Stdout, os.Stderr))
+8 -9
View File
@@ -17,15 +17,14 @@ import (
func main() { func main() {
flag.Usage = func() { flag.Usage = func() {
fmt.Fprintln(os.Stderr, `tomll can be used in two ways: fmt.Fprintln(os.Stderr, "tomll can be used in two ways:")
Writing to STDIN and reading from STDOUT: fmt.Fprintln(os.Stderr, "Writing to STDIN and reading from STDOUT:")
cat file.toml | tomll > file.toml fmt.Fprintln(os.Stderr, " cat file.toml | tomll > file.toml")
fmt.Fprintln(os.Stderr, "")
Reading and updating a list of files: fmt.Fprintln(os.Stderr, "Reading and updating a list of files:")
tomll a.toml b.toml c.toml fmt.Fprintln(os.Stderr, " tomll a.toml b.toml c.toml")
fmt.Fprintln(os.Stderr, "")
When given a list of files, tomll will modify all files in place without asking. fmt.Fprintln(os.Stderr, "When given a list of files, tomll will modify all files in place without asking.")
`)
} }
flag.Parse() flag.Parse()
// read from stdin and print to stdout // read from stdin and print to stdout
+12 -3
View File
@@ -11,10 +11,13 @@ import (
"time" "time"
) )
const tagKeyMultiline = "multiline"
type tomlOpts struct { type tomlOpts struct {
name string name string
comment string comment string
commented bool commented bool
multiline bool
include bool include bool
omitempty bool omitempty bool
} }
@@ -187,7 +190,7 @@ func (e *Encoder) QuoteMapKeys(v bool) *Encoder {
// A = [ // A = [
// 1, // 1,
// 2, // 2,
// 3 // 3,
// ] // ]
func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder { func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder {
e.arraysOneElementPerLine = v e.arraysOneElementPerLine = v
@@ -230,7 +233,12 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
if err != nil { if err != nil {
return nil, err return nil, err
} }
tval.SetWithComment(opts.name, opts.comment, opts.commented, val)
tval.SetWithOptions(opts.name, SetOptions{
Comment: opts.comment,
Commented: opts.commented,
Multiline: opts.multiline,
}, val)
} }
} }
case reflect.Map: case reflect.Map:
@@ -559,7 +567,8 @@ func tomlOptions(vf reflect.StructField) tomlOpts {
comment = c comment = c
} }
commented, _ := strconv.ParseBool(vf.Tag.Get("commented")) commented, _ := strconv.ParseBool(vf.Tag.Get("commented"))
result := tomlOpts{name: vf.Name, comment: comment, commented: commented, include: true, omitempty: false} multiline, _ := strconv.ParseBool(vf.Tag.Get(tagKeyMultiline))
result := tomlOpts{name: vf.Name, comment: comment, commented: commented, multiline: multiline, include: true, omitempty: false}
if parse[0] != "" { if parse[0] != "" {
if parse[0] == "-" && len(parse) == 1 { if parse[0] == "-" && len(parse) == 1 {
result.include = false result.include = false
+1 -1
View File
@@ -775,7 +775,7 @@ func TestMarshalArrayOnePerLine(t *testing.T) {
B = [ B = [
1, 1,
2, 2,
3 3,
] ]
C = [1] C = [1]
`) `)
+3 -3
View File
@@ -2,12 +2,13 @@ package query
import ( import (
"fmt" "fmt"
"github.com/pelletier/go-toml"
"io/ioutil" "io/ioutil"
"sort" "sort"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/pelletier/go-toml"
) )
type queryTestNode struct { type queryTestNode struct {
@@ -406,8 +407,7 @@ func TestQueryFilterFn(t *testing.T) {
assertQueryPositions(t, string(buff), assertQueryPositions(t, string(buff),
"$..[?(float)]", "$..[?(float)]",
[]interface{}{ []interface{}{ // no float values in document
// no float values in document
}) })
tv, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") tv, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
-3
View File
@@ -23,9 +23,6 @@ function git_clone() {
# Remove potential previous runs # Remove potential previous runs
rm -rf src test_program_bin toml-test rm -rf src test_program_bin toml-test
# Run go vet
go vet ./...
go get github.com/pelletier/go-buffruneio go get github.com/pelletier/go-buffruneio
go get github.com/davecgh/go-spew/spew go get github.com/davecgh/go-spew/spew
go get gopkg.in/yaml.v2 go get gopkg.in/yaml.v2
+58
View File
@@ -14,6 +14,7 @@ type tomlValue struct {
value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
comment string comment string
commented bool commented bool
multiline bool
position Position position Position
} }
@@ -175,6 +176,63 @@ func (t *Tree) GetDefault(key string, def interface{}) interface{} {
return val return val
} }
// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour.
// The default values within the struct are valid default options.
type SetOptions struct {
Comment string
Commented bool
Multiline bool
}
// SetWithOptions is the same as Set, but allows you to provide formatting
// instructions to the key, that will be used by Marshal().
func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
t.SetPathWithOptions(strings.Split(key, "."), opts, value)
}
// SetPathWithOptions is the same as SetPath, but allows you to provide
// formatting instructions to the key, that will be reused by Marshal().
func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
subtree := t
for _, intermediateKey := range keys[:len(keys)-1] {
nextTree, exists := subtree.values[intermediateKey]
if !exists {
nextTree = newTree()
subtree.values[intermediateKey] = nextTree // add new element here
}
switch node := nextTree.(type) {
case *Tree:
subtree = node
case []*Tree:
// go to most recent element
if len(node) == 0 {
// create element if it does not exist
subtree.values[intermediateKey] = append(node, newTree())
}
subtree = node[len(node)-1]
}
}
var toInsert interface{}
switch value.(type) {
case *Tree:
tt := value.(*Tree)
tt.comment = opts.Comment
toInsert = value
case []*Tree:
toInsert = value
case *tomlValue:
tt := value.(*tomlValue)
tt.comment = opts.Comment
toInsert = tt
default:
toInsert = &tomlValue{value: value, comment: opts.Comment, commented: opts.Commented, multiline: opts.Multiline}
}
subtree.values[keys[len(keys)-1]] = toInsert
}
// Set an element in the tree. // Set an element in the tree.
// Key is a dot-separated path (e.g. a.b.c). // Key is a dot-separated path (e.g. a.b.c).
// Creates all necessary intermediate trees, if needed. // Creates all necessary intermediate trees, if needed.
+50 -6
View File
@@ -12,7 +12,41 @@ import (
"time" "time"
) )
// encodes a string to a TOML-compliant string value // Encodes a string to a TOML-compliant multi-line string value
// This function is a clone of the existing encodeTomlString function, except that whitespace characters
// are preserved. Quotation marks and backslashes are also not escaped.
func encodeMultilineTomlString(value string) string {
var b bytes.Buffer
for _, rr := range value {
switch rr {
case '\b':
b.WriteString(`\b`)
case '\t':
b.WriteString("\t")
case '\n':
b.WriteString("\n")
case '\f':
b.WriteString(`\f`)
case '\r':
b.WriteString("\r")
case '"':
b.WriteString(`"`)
case '\\':
b.WriteString(`\`)
default:
intRr := uint16(rr)
if intRr < 0x001F {
b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
} else {
b.WriteRune(rr)
}
}
}
return b.String()
}
// Encodes a string to a TOML-compliant string value
func encodeTomlString(value string) string { func encodeTomlString(value string) string {
var b bytes.Buffer var b bytes.Buffer
@@ -45,6 +79,15 @@ func encodeTomlString(value string) string {
} }
func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElementPerLine bool) (string, error) { func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElementPerLine bool) (string, error) {
// this interface check is added to dereference the change made in the writeTo function.
// That change was made to allow this function to see formatting options.
tv, ok := v.(*tomlValue)
if ok {
v = tv.value
} else {
tv = &tomlValue{}
}
switch value := v.(type) { switch value := v.(type) {
case uint64: case uint64:
return strconv.FormatUint(value, 10), nil return strconv.FormatUint(value, 10), nil
@@ -58,6 +101,9 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
} }
return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil
case string: case string:
if tv.multiline {
return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil
}
return "\"" + encodeTomlString(value) + "\"", nil return "\"" + encodeTomlString(value) + "\"", nil
case []byte: case []byte:
b, _ := v.([]byte) b, _ := v.([]byte)
@@ -91,12 +137,10 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
stringBuffer.WriteString("[\n") stringBuffer.WriteString("[\n")
for i, value := range values { for _, value := range values {
stringBuffer.WriteString(valueIndent) stringBuffer.WriteString(valueIndent)
stringBuffer.WriteString(value) stringBuffer.WriteString(value)
if i != len(values)-1 { stringBuffer.WriteString(`,`)
stringBuffer.WriteString(`,`)
}
stringBuffer.WriteString("\n") stringBuffer.WriteString("\n")
} }
@@ -132,7 +176,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, a
return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
} }
repr, err := tomlValueStringRepresentation(v.value, indent, arraysOneElementPerLine) repr, err := tomlValueStringRepresentation(v, indent, arraysOneElementPerLine)
if err != nil { if err != nil {
return bytesCount, err return bytesCount, err
} }