Compare commits

...

6 Commits

Author SHA1 Message Date
Gregory Oschwald 728039f679 Handle other key types in Unmarshal (#276)
Previously, this would fail with:

```
panic: reflect.Value.SetMapIndex: value of type string is not assignable to type toml.letter [recovered]
panic: reflect.Value.SetMapIndex: value of type string is not assignable to type toml.letter
```

Now this only panics when the key type cannot be converted from a
string.
2019-04-29 20:50:10 -07:00
Gregory Oschwald 1d8903f1d0 Allow unmarshaling to top level maps (#273) 2019-04-24 23:15:40 -07:00
Brent DeSpain 65b27e6823 Order map keys alphabetically (#270)
This makes sure we have a stable output when marshaling
maps.

Fixes #268
2019-04-11 13:52:29 +01:00
Thomas Pelletier 6ea91ef590 Do not push Docker images for forked repositories (#272)
For security reasons, CircleCI does not make environment variables
available on forked repositories (often used in PRs). This will still
build the docker image, but won't try to push it to dockerhub.
2019-04-11 13:49:07 +01:00
Ceriath 51edd0ca49 Fix goreportcard issues (#271)
* Fixed misspell

* Fixed ineffassign

`user` and `password` always got overwritten
`orderedVals` was initialized with an empty array but always got overwritten by either `sortByLines()` or `sortAlphabetical`
`err` was assigned a `nil` value that was either overwritten or unused anyways

* Fix comment for DeletePath

The comment assumed the method was named Delete, i guess a rename happened at some point

* Update doc_test.go
2019-04-11 12:11:29 +01:00
Thomas Pelletier d95bfe020e Dockerfile (#269)
Provide docker images for go-toml tools.

Ref: https://github.com/pelletier/go-toml/pull/267
2019-04-10 13:43:12 +01:00
12 changed files with 232 additions and 19 deletions
+30
View File
@@ -88,6 +88,33 @@ jobs:
command: |
curl https://codecov.io/bash > codecov.sh
bash codecov.sh -v
docker:
docker:
- image: "circleci/golang:1.12"
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: true
- run: docker build -t pelletier/go-toml:$CIRCLE_SHA1 .
- run:
name: "Publish docker image"
command: |
if [ "${CIRCLE_PR_REPONAME}" == "" ]; then
IMAGE_NAME="pelletier/go-toml"
IMAGE_SHA_TAG="${IMAGE_NAME}:$CIRCLE_SHA1"
if [ "${CIRCLE_BRANCH}" = "master" ]; then
docker login -u $DOCKER_USER -p $DOCKER_PASS
docker tag ${IMAGE_SHA_TAG} ${IMAGE_NAME}:latest
docker push ${IMAGE_NAME}:latest
fi
if [ "${CIRCLE_TAG}" != "" ]; then
docker login -u $DOCKER_USER -p $DOCKER_PASS
docker tag ${IMAGE_SHA_TAG} ${IMAGE_NAME}:${CIRCLE_TAG}
docker push ${IMAGE_NAME}:${CIRCLE_TAG}
fi
else
echo "not pushing docker image for forked repo"
fi
workflows:
version: 2.1
@@ -138,3 +165,6 @@ workflows:
requires:
- go1_11
- go1_12
- docker:
requires:
- codecov
+2
View File
@@ -0,0 +1,2 @@
cmd/tomll/tomll
cmd/tomljson/tomljson
+3
View File
@@ -1,2 +1,5 @@
test_program/test_program_bin
fuzz/
cmd/tomll/tomll
cmd/tomljson/tomljson
cmd/tomltestgen/tomltestgen
+10
View File
@@ -0,0 +1,10 @@
FROM golang:1.12-alpine3.9 as builder
WORKDIR /go/src/github.com/pelletier/go-toml
COPY . .
ENV CGO_ENABLED=0
ENV GOOS=linux
RUN go install ./...
FROM scratch
COPY --from=builder /go/bin/tomll /usr/bin/tomll
COPY --from=builder /go/bin/tomljson /usr/bin/tomljson
+17
View File
@@ -101,6 +101,23 @@ Go-toml provides two handy command line tools:
tomljson --help
```
### Docker image
Those tools are also availble as a Docker image from
[dockerhub](https://hub.docker.com/r/pelletier/go-toml). For example, to
use `tomljson`:
```
docker run -v $PWD:/workdir pelletier/go-toml tomljson /workdir/example.toml
```
Only master (`latest`) and tagged versions are published to dockerhub. You
can build your own image as usual:
```
docker build -t go-toml .
```
## Contribute
Feel free to report bugs and patches using GitHub's pull requests system on
+5 -4
View File
@@ -16,13 +16,14 @@ func Example_tree() {
fmt.Println("Error ", err.Error())
} else {
// retrieve data directly
user := config.Get("postgres.user").(string)
password := config.Get("postgres.password").(string)
directUser := config.Get("postgres.user").(string)
directPassword := config.Get("postgres.password").(string)
fmt.Println("User is", directUser, " and password is", directPassword)
// or using an intermediate object
configTree := config.Get("postgres").(*toml.Tree)
user = configTree.Get("user").(string)
password = configTree.Get("password").(string)
user := configTree.Get("user").(string)
password := configTree.Get("password").(string)
fmt.Println("User is", user, " and password is", password)
// show where elements are in the file
+1 -1
View File
@@ -24,7 +24,7 @@ func testResult(t *testing.T, key string, expected []string) {
func testError(t *testing.T, key string, expectedError string) {
res, err := parseKey(key)
if err == nil {
t.Fatalf("Expected error, but succesfully parsed key %s", res)
t.Fatalf("Expected error, but successfully parsed key %s", res)
}
if fmt.Sprintf("%s", err) != expectedError {
t.Fatalf("Expected error \"%s\", but got \"%s\".", expectedError, err)
+35 -7
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"reflect"
"sort"
"strconv"
"strings"
"time"
@@ -329,7 +330,26 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
}
}
case reflect.Map:
for _, key := range mval.MapKeys() {
keys := mval.MapKeys()
if e.order == OrderPreserve && len(keys) > 0 {
// Sorting []reflect.Value is not straight forward.
//
// OrderPreserve will support deterministic results when string is used
// as the key to maps.
typ := keys[0].Type()
kind := keys[0].Kind()
if kind == reflect.String {
ikeys := make([]string, len(keys))
for i := range keys {
ikeys[i] = keys[i].Interface().(string)
}
sort.Strings(ikeys)
for i := range ikeys {
keys[i] = reflect.ValueOf(ikeys[i]).Convert(typ)
}
}
}
for _, key := range keys {
mvalf := mval.MapIndex(key)
val, err := e.valueToToml(mtype.Elem(), mvalf)
if err != nil {
@@ -494,11 +514,19 @@ func (d *Decoder) SetTagName(v string) *Decoder {
func (d *Decoder) unmarshal(v interface{}) error {
mtype := reflect.TypeOf(v)
if mtype.Kind() != reflect.Ptr || mtype.Elem().Kind() != reflect.Struct {
return errors.New("Only a pointer to struct can be unmarshaled from TOML")
if mtype.Kind() != reflect.Ptr {
return errors.New("only a pointer to struct or map can be unmarshaled from TOML")
}
sval, err := d.valueFromTree(mtype.Elem(), d.tval)
elem := mtype.Elem()
switch elem.Kind() {
case reflect.Struct, reflect.Map:
default:
return errors.New("only a pointer to struct or map can be unmarshaled from TOML")
}
sval, err := d.valueFromTree(elem, d.tval)
if err != nil {
return err
}
@@ -546,8 +574,8 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value,
if !found && opts.defaultValue != "" {
mvalf := mval.Field(i)
var val interface{} = nil
var err error = nil
var val interface{}
var err error
switch mvalf.Kind() {
case reflect.Bool:
val, err = strconv.ParseBool(opts.defaultValue)
@@ -587,7 +615,7 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value,
if err != nil {
return mval, formatError(err, tval.GetPosition(key))
}
mval.SetMapIndex(reflect.ValueOf(key), mvalf)
mval.SetMapIndex(reflect.ValueOf(key).Convert(mtype.Key()), mvalf)
}
}
return mval, nil
+17
View File
@@ -0,0 +1,17 @@
title = "TOML Marshal Testing"
[basic_map]
one = "one"
two = "two"
[long_map]
a7 = "1"
b3 = "2"
c8 = "3"
d4 = "4"
e6 = "5"
f5 = "6"
g10 = "7"
h1 = "8"
i2 = "9"
j9 = "10"
+110 -5
View File
@@ -126,6 +126,12 @@ type testDoc struct {
Unexported2 int `toml:"-"`
}
type testMapDoc struct {
Title string `toml:"title"`
BasicMap map[string]string `toml:"basic_map"`
LongMap map[string]string `toml:"long_map"`
}
type testDocBasics struct {
Uint uint `toml:"uint"`
Bool bool `toml:"bool"`
@@ -200,6 +206,26 @@ var docData = testDoc{
SubDocPtrs: []*testSubDoc{&subdoc},
}
var mapTestDoc = testMapDoc{
Title: "TOML Marshal Testing",
BasicMap: map[string]string{
"one": "one",
"two": "two",
},
LongMap: map[string]string{
"h1": "8",
"i2": "9",
"b3": "2",
"d4": "4",
"f5": "6",
"e6": "5",
"a7": "1",
"c8": "3",
"j9": "10",
"g10": "7",
},
}
func TestDocMarshal(t *testing.T) {
result, err := Marshal(docData)
if err != nil {
@@ -223,6 +249,29 @@ func TestDocMarshalOrdered(t *testing.T) {
}
}
func TestDocMarshalMaps(t *testing.T) {
result, err := Marshal(mapTestDoc)
if err != nil {
t.Fatal(err)
}
expected, _ := ioutil.ReadFile("marshal_OrderPreserve_Map_test.toml")
if !bytes.Equal(result, expected) {
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
}
}
func TestDocMarshalOrderedMaps(t *testing.T) {
var result bytes.Buffer
err := NewEncoder(&result).Order(OrderPreserve).Encode(mapTestDoc)
if err != nil {
t.Fatal(err)
}
expected, _ := ioutil.ReadFile("marshal_OrderPreserve_Map_test.toml")
if !bytes.Equal(result.Bytes(), expected) {
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
}
}
func TestDocMarshalPointer(t *testing.T) {
result, err := Marshal(&docData)
if err != nil {
@@ -1054,12 +1103,68 @@ func TestUnmarshalCustomTag(t *testing.T) {
}
func TestUnmarshalMap(t *testing.T) {
m := make(map[string]int)
m["a"] = 1
testToml := []byte(`
a = 1
b = 2
c = 3
`)
var result map[string]int
err := Unmarshal(testToml, &result)
if err != nil {
t.Errorf("Received unexpected error: %s", err)
return
}
err := Unmarshal(basicTestToml, m)
if err.Error() != "Only a pointer to struct can be unmarshaled from TOML" {
t.Fail()
expected := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
}
}
func TestUnmarshalMapWithTypedKey(t *testing.T) {
testToml := []byte(`
a = 1
b = 2
c = 3
`)
type letter string
var result map[letter]int
err := Unmarshal(testToml, &result)
if err != nil {
t.Errorf("Received unexpected error: %s", err)
return
}
expected := map[letter]int{
"a": 1,
"b": 2,
"c": 3,
}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
}
}
func TestUnmarshalNonPointer(t *testing.T) {
a := 1
err := Unmarshal([]byte{}, a)
if err == nil {
t.Fatal("unmarshal should err when given a non pointer")
}
}
func TestUnmarshalInvalidPointerKind(t *testing.T) {
a := 1
err := Unmarshal([]byte{}, &a)
if err == nil {
t.Fatal("unmarshal should err when given an invalid pointer type")
}
}
+1 -1
View File
@@ -275,7 +275,7 @@ func (t *Tree) Delete(key string) error {
return t.DeletePath(keys)
}
// Delete removes a key from the tree.
// DeletePath removes a key from the tree.
// Keys is an array of path elements (e.g. {"a","b","c"}).
func (t *Tree) DeletePath(keys []string) error {
keyLen := len(keys)
+1 -1
View File
@@ -259,7 +259,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, a
}
func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder) (int64, error) {
orderedVals := make([]sortNode, 0)
var orderedVals []sortNode
switch ord {
case OrderPreserve: