You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2015/11/21 01:42:46 UTC
[21/42] incubator-mynewt-newt git commit: Move newt source into a
"newt" subdirectory.
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/README.md
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/README.md b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/README.md
new file mode 100644
index 0000000..5a5df63
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/README.md
@@ -0,0 +1,220 @@
+## TOML parser and encoder for Go with reflection
+
+TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
+reflection interface similar to Go's standard library `json` and `xml`
+packages. This package also supports the `encoding.TextUnmarshaler` and
+`encoding.TextMarshaler` interfaces so that you can define custom data
+representations. (There is an example of this below.)
+
+Spec: https://github.com/mojombo/toml
+
+Compatible with TOML version
+[v0.2.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.2.0.md)
+
+Documentation: http://godoc.org/github.com/BurntSushi/toml
+
+Installation:
+
+```bash
+go get github.com/BurntSushi/toml
+```
+
+Try the toml validator:
+
+```bash
+go get github.com/BurntSushi/toml/cmd/tomlv
+tomlv some-toml-file.toml
+```
+
+[![Build status](https://api.travis-ci.org/BurntSushi/toml.png)](https://travis-ci.org/BurntSushi/toml)
+
+
+### Testing
+
+This package passes all tests in
+[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder
+and the encoder.
+
+### Examples
+
+This package works similarly to how the Go standard library handles `XML`
+and `JSON`. Namely, data is loaded into Go values via reflection.
+
+For the simplest example, consider some TOML file as just a list of keys
+and values:
+
+```toml
+Age = 25
+Cats = [ "Cauchy", "Plato" ]
+Pi = 3.14
+Perfection = [ 6, 28, 496, 8128 ]
+DOB = 1987-07-05T05:45:00Z
+```
+
+Which could be defined in Go as:
+
+```go
+type Config struct {
+ Age int
+ Cats []string
+ Pi float64
+ Perfection []int
+ DOB time.Time // requires `import time`
+}
+```
+
+And then decoded with:
+
+```go
+var conf Config
+if _, err := toml.Decode(tomlData, &conf); err != nil {
+ // handle error
+}
+```
+
+You can also use struct tags if your struct field name doesn't map to a TOML
+key value directly:
+
+```toml
+some_key_NAME = "wat"
+```
+
+```go
+type TOML struct {
+ ObscureKey string `toml:"some_key_NAME"`
+}
+```
+
+### Using the `encoding.TextUnmarshaler` interface
+
+Here's an example that automatically parses duration strings into
+`time.Duration` values:
+
+```toml
+[[song]]
+name = "Thunder Road"
+duration = "4m49s"
+
+[[song]]
+name = "Stairway to Heaven"
+duration = "8m03s"
+```
+
+Which can be decoded with:
+
+```go
+type song struct {
+ Name string
+ Duration duration
+}
+type songs struct {
+ Song []song
+}
+var favorites songs
+if _, err := toml.Decode(blob, &favorites); err != nil {
+ log.Fatal(err)
+}
+
+for _, s := range favorites.Song {
+ fmt.Printf("%s (%s)\n", s.Name, s.Duration)
+}
+```
+
+And you'll also need a `duration` type that satisfies the
+`encoding.TextUnmarshaler` interface:
+
+```go
+type duration struct {
+ time.Duration
+}
+
+func (d *duration) UnmarshalText(text []byte) error {
+ var err error
+ d.Duration, err = time.ParseDuration(string(text))
+ return err
+}
+```
+
+### More complex usage
+
+Here's an example of how to load the example from the official spec page:
+
+```toml
+# This is a TOML document. Boom.
+
+title = "TOML Example"
+
+[owner]
+name = "Tom Preston-Werner"
+organization = "GitHub"
+bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
+dob = 1979-05-27T07:32:00Z # First class dates? Why not?
+
+[database]
+server = "192.168.1.1"
+ports = [ 8001, 8001, 8002 ]
+connection_max = 5000
+enabled = true
+
+[servers]
+
+ # You can indent as you please. Tabs or spaces. TOML don't care.
+ [servers.alpha]
+ ip = "10.0.0.1"
+ dc = "eqdc10"
+
+ [servers.beta]
+ ip = "10.0.0.2"
+ dc = "eqdc10"
+
+[clients]
+data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
+
+# Line breaks are OK when inside arrays
+hosts = [
+ "alpha",
+ "omega"
+]
+```
+
+And the corresponding Go types are:
+
+```go
+type tomlConfig struct {
+ Title string
+ Owner ownerInfo
+ DB database `toml:"database"`
+ Servers map[string]server
+ Clients clients
+}
+
+type ownerInfo struct {
+ Name string
+ Org string `toml:"organization"`
+ Bio string
+ DOB time.Time
+}
+
+type database struct {
+ Server string
+ Ports []int
+ ConnMax int `toml:"connection_max"`
+ Enabled bool
+}
+
+type server struct {
+ IP string
+ DC string
+}
+
+type clients struct {
+ Data [][]interface{}
+ Hosts []string
+}
+```
+
+Note that a case insensitive match will be tried if an exact match can't be
+found.
+
+A working example of the above can be found in `_examples/example.{go,toml}`.
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
new file mode 100644
index 0000000..5a8e332
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
@@ -0,0 +1,14 @@
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2004 Sam Hocevar <sa...@hocevar.net>
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md
new file mode 100644
index 0000000..24421eb
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md
@@ -0,0 +1,14 @@
+# Implements the TOML test suite interface
+
+This is an implementation of the interface expected by
+[toml-test](https://github.com/BurntSushi/toml-test) for my
+[toml parser written in Go](https://github.com/BurntSushi/toml).
+In particular, it maps TOML data on `stdin` to a JSON format on `stdout`.
+
+
+Compatible with TOML version
+[v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md)
+
+Compatible with `toml-test` version
+[v0.2.0](https://github.com/BurntSushi/toml-test/tree/v0.2.0)
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go
new file mode 100644
index 0000000..14e7557
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go
@@ -0,0 +1,90 @@
+// Command toml-test-decoder satisfies the toml-test interface for testing
+// TOML decoders. Namely, it accepts TOML on stdin and outputs JSON on stdout.
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "path"
+ "time"
+
+ "github.com/BurntSushi/toml"
+)
+
+func init() {
+ log.SetFlags(0)
+
+ flag.Usage = usage
+ flag.Parse()
+}
+
+func usage() {
+ log.Printf("Usage: %s < toml-file\n", path.Base(os.Args[0]))
+ flag.PrintDefaults()
+
+ os.Exit(1)
+}
+
+func main() {
+ if flag.NArg() != 0 {
+ flag.Usage()
+ }
+
+ var tmp interface{}
+ if _, err := toml.DecodeReader(os.Stdin, &tmp); err != nil {
+ log.Fatalf("Error decoding TOML: %s", err)
+ }
+
+ typedTmp := translate(tmp)
+ if err := json.NewEncoder(os.Stdout).Encode(typedTmp); err != nil {
+ log.Fatalf("Error encoding JSON: %s", err)
+ }
+}
+
+func translate(tomlData interface{}) interface{} {
+ switch orig := tomlData.(type) {
+ case map[string]interface{}:
+ typed := make(map[string]interface{}, len(orig))
+ for k, v := range orig {
+ typed[k] = translate(v)
+ }
+ return typed
+ case []map[string]interface{}:
+ typed := make([]map[string]interface{}, len(orig))
+ for i, v := range orig {
+ typed[i] = translate(v).(map[string]interface{})
+ }
+ return typed
+ case []interface{}:
+ typed := make([]interface{}, len(orig))
+ for i, v := range orig {
+ typed[i] = translate(v)
+ }
+
+ // We don't really need to tag arrays, but let's be future proof.
+ // (If TOML ever supports tuples, we'll need this.)
+ return tag("array", typed)
+ case time.Time:
+ return tag("datetime", orig.Format("2006-01-02T15:04:05Z"))
+ case bool:
+ return tag("bool", fmt.Sprintf("%v", orig))
+ case int64:
+ return tag("integer", fmt.Sprintf("%d", orig))
+ case float64:
+ return tag("float", fmt.Sprintf("%v", orig))
+ case string:
+ return tag("string", orig)
+ }
+
+ panic(fmt.Sprintf("Unknown type: %T", tomlData))
+}
+
+func tag(typeName string, data interface{}) map[string]interface{} {
+ return map[string]interface{}{
+ "type": typeName,
+ "value": data,
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
new file mode 100644
index 0000000..5a8e332
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
@@ -0,0 +1,14 @@
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2004 Sam Hocevar <sa...@hocevar.net>
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md
new file mode 100644
index 0000000..45a603f
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md
@@ -0,0 +1,14 @@
+# Implements the TOML test suite interface for TOML encoders
+
+This is an implementation of the interface expected by
+[toml-test](https://github.com/BurntSushi/toml-test) for the
+[TOML encoder](https://github.com/BurntSushi/toml).
+In particular, it maps JSON data on `stdin` to a TOML format on `stdout`.
+
+
+Compatible with TOML version
+[v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md)
+
+Compatible with `toml-test` version
+[v0.2.0](https://github.com/BurntSushi/toml-test/tree/v0.2.0)
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go
new file mode 100644
index 0000000..092cc68
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go
@@ -0,0 +1,131 @@
+// Command toml-test-encoder satisfies the toml-test interface for testing
+// TOML encoders. Namely, it accepts JSON on stdin and outputs TOML on stdout.
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "log"
+ "os"
+ "path"
+ "strconv"
+ "time"
+
+ "github.com/BurntSushi/toml"
+)
+
+func init() {
+ log.SetFlags(0)
+
+ flag.Usage = usage
+ flag.Parse()
+}
+
+func usage() {
+ log.Printf("Usage: %s < json-file\n", path.Base(os.Args[0]))
+ flag.PrintDefaults()
+
+ os.Exit(1)
+}
+
+func main() {
+ if flag.NArg() != 0 {
+ flag.Usage()
+ }
+
+ var tmp interface{}
+ if err := json.NewDecoder(os.Stdin).Decode(&tmp); err != nil {
+ log.Fatalf("Error decoding JSON: %s", err)
+ }
+
+ tomlData := translate(tmp)
+ if err := toml.NewEncoder(os.Stdout).Encode(tomlData); err != nil {
+ log.Fatalf("Error encoding TOML: %s", err)
+ }
+}
+
+func translate(typedJson interface{}) interface{} {
+ switch v := typedJson.(type) {
+ case map[string]interface{}:
+ if len(v) == 2 && in("type", v) && in("value", v) {
+ return untag(v)
+ }
+ m := make(map[string]interface{}, len(v))
+ for k, v2 := range v {
+ m[k] = translate(v2)
+ }
+ return m
+ case []interface{}:
+ tabArray := make([]map[string]interface{}, len(v))
+ for i := range v {
+ if m, ok := translate(v[i]).(map[string]interface{}); ok {
+ tabArray[i] = m
+ } else {
+ log.Fatalf("JSON arrays may only contain objects. This " +
+ "corresponds to only tables being allowed in " +
+ "TOML table arrays.")
+ }
+ }
+ return tabArray
+ }
+ log.Fatalf("Unrecognized JSON format '%T'.", typedJson)
+ panic("unreachable")
+}
+
+func untag(typed map[string]interface{}) interface{} {
+ t := typed["type"].(string)
+ v := typed["value"]
+ switch t {
+ case "string":
+ return v.(string)
+ case "integer":
+ v := v.(string)
+ n, err := strconv.Atoi(v)
+ if err != nil {
+ log.Fatalf("Could not parse '%s' as integer: %s", v, err)
+ }
+ return n
+ case "float":
+ v := v.(string)
+ f, err := strconv.ParseFloat(v, 64)
+ if err != nil {
+ log.Fatalf("Could not parse '%s' as float64: %s", v, err)
+ }
+ return f
+ case "datetime":
+ v := v.(string)
+ t, err := time.Parse("2006-01-02T15:04:05Z", v)
+ if err != nil {
+ log.Fatalf("Could not parse '%s' as a datetime: %s", v, err)
+ }
+ return t
+ case "bool":
+ v := v.(string)
+ switch v {
+ case "true":
+ return true
+ case "false":
+ return false
+ }
+ log.Fatalf("Could not parse '%s' as a boolean.", v)
+ case "array":
+ v := v.([]interface{})
+ array := make([]interface{}, len(v))
+ for i := range v {
+ if m, ok := v[i].(map[string]interface{}); ok {
+ array[i] = untag(m)
+ } else {
+ log.Fatalf("Arrays may only contain other arrays or "+
+ "primitive values, but found a '%T'.", m)
+ }
+ }
+ return array
+ }
+ log.Fatalf("Unrecognized tag type '%s'.", t)
+ panic("unreachable")
+}
+
+func in(key string, m map[string]interface{}) bool {
+ _, ok := m[key]
+ return ok
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/COPYING
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/COPYING b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/COPYING
new file mode 100644
index 0000000..5a8e332
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/COPYING
@@ -0,0 +1,14 @@
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2004 Sam Hocevar <sa...@hocevar.net>
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/README.md
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/README.md b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/README.md
new file mode 100644
index 0000000..5df0dc3
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/README.md
@@ -0,0 +1,22 @@
+# TOML Validator
+
+If Go is installed, it's simple to try it out:
+
+```bash
+go get github.com/BurntSushi/toml/cmd/tomlv
+tomlv some-toml-file.toml
+```
+
+You can see the types of every key in a TOML file with:
+
+```bash
+tomlv -types some-toml-file.toml
+```
+
+At the moment, only one error message is reported at a time. Error messages
+include line numbers. No output means that the files given are valid TOML, or
+there is a bug in `tomlv`.
+
+Compatible with TOML version
+[v0.1.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.1.0.md)
+
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/main.go
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/main.go b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/main.go
new file mode 100644
index 0000000..c7d689a
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/cmd/tomlv/main.go
@@ -0,0 +1,61 @@
+// Command tomlv validates TOML documents and prints each key's type.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "path"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/BurntSushi/toml"
+)
+
+var (
+ flagTypes = false
+)
+
+func init() {
+ log.SetFlags(0)
+
+ flag.BoolVar(&flagTypes, "types", flagTypes,
+ "When set, the types of every defined key will be shown.")
+
+ flag.Usage = usage
+ flag.Parse()
+}
+
+func usage() {
+ log.Printf("Usage: %s toml-file [ toml-file ... ]\n",
+ path.Base(os.Args[0]))
+ flag.PrintDefaults()
+
+ os.Exit(1)
+}
+
+func main() {
+ if flag.NArg() < 1 {
+ flag.Usage()
+ }
+ for _, f := range flag.Args() {
+ var tmp interface{}
+ md, err := toml.DecodeFile(f, &tmp)
+ if err != nil {
+ log.Fatalf("Error in '%s': %s", f, err)
+ }
+ if flagTypes {
+ printTypes(md)
+ }
+ }
+}
+
+func printTypes(md toml.MetaData) {
+ tabw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
+ for _, key := range md.Keys() {
+ fmt.Fprintf(tabw, "%s%s\t%s\n",
+ strings.Repeat(" ", len(key)-1), key, md.Type(key...))
+ }
+ tabw.Flush()
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode.go
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode.go b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode.go
new file mode 100644
index 0000000..6c7d398
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode.go
@@ -0,0 +1,492 @@
+package toml
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "math"
+ "reflect"
+ "strings"
+ "time"
+)
+
+var e = fmt.Errorf
+
+// Unmarshaler is the interface implemented by objects that can unmarshal a
+// TOML description of themselves.
+type Unmarshaler interface {
+ UnmarshalTOML(interface{}) error
+}
+
+// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`.
+func Unmarshal(p []byte, v interface{}) error {
+ _, err := Decode(string(p), v)
+ return err
+}
+
+// Primitive is a TOML value that hasn't been decoded into a Go value.
+// When using the various `Decode*` functions, the type `Primitive` may
+// be given to any value, and its decoding will be delayed.
+//
+// A `Primitive` value can be decoded using the `PrimitiveDecode` function.
+//
+// The underlying representation of a `Primitive` value is subject to change.
+// Do not rely on it.
+//
+// N.B. Primitive values are still parsed, so using them will only avoid
+// the overhead of reflection. They can be useful when you don't know the
+// exact type of TOML data until run time.
+type Primitive struct {
+ undecoded interface{}
+ context Key
+}
+
+// DEPRECATED!
+//
+// Use MetaData.PrimitiveDecode instead.
+func PrimitiveDecode(primValue Primitive, v interface{}) error {
+ md := MetaData{decoded: make(map[string]bool)}
+ return md.unify(primValue.undecoded, rvalue(v))
+}
+
+// PrimitiveDecode is just like the other `Decode*` functions, except it
+// decodes a TOML value that has already been parsed. Valid primitive values
+// can *only* be obtained from values filled by the decoder functions,
+// including this method. (i.e., `v` may contain more `Primitive`
+// values.)
+//
+// Meta data for primitive values is included in the meta data returned by
+// the `Decode*` functions with one exception: keys returned by the Undecoded
+// method will only reflect keys that were decoded. Namely, any keys hidden
+// behind a Primitive will be considered undecoded. Executing this method will
+// update the undecoded keys in the meta data. (See the example.)
+func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
+ md.context = primValue.context
+ defer func() { md.context = nil }()
+ return md.unify(primValue.undecoded, rvalue(v))
+}
+
+// Decode will decode the contents of `data` in TOML format into a pointer
+// `v`.
+//
+// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be
+// used interchangeably.)
+//
+// TOML arrays of tables correspond to either a slice of structs or a slice
+// of maps.
+//
+// TOML datetimes correspond to Go `time.Time` values.
+//
+// All other TOML types (float, string, int, bool and array) correspond
+// to the obvious Go types.
+//
+// An exception to the above rules is if a type implements the
+// encoding.TextUnmarshaler interface. In this case, any primitive TOML value
+// (floats, strings, integers, booleans and datetimes) will be converted to
+// a byte string and given to the value's UnmarshalText method. See the
+// Unmarshaler example for a demonstration with time duration strings.
+//
+// Key mapping
+//
+// TOML keys can map to either keys in a Go map or field names in a Go
+// struct. The special `toml` struct tag may be used to map TOML keys to
+// struct fields that don't match the key name exactly. (See the example.)
+// A case insensitive match to struct names will be tried if an exact match
+// can't be found.
+//
+// The mapping between TOML values and Go values is loose. That is, there
+// may exist TOML values that cannot be placed into your representation, and
+// there may be parts of your representation that do not correspond to
+// TOML values. This loose mapping can be made stricter by using the IsDefined
+// and/or Undecoded methods on the MetaData returned.
+//
+// This decoder will not handle cyclic types. If a cyclic type is passed,
+// `Decode` will not terminate.
+func Decode(data string, v interface{}) (MetaData, error) {
+ p, err := parse(data)
+ if err != nil {
+ return MetaData{}, err
+ }
+ md := MetaData{
+ p.mapping, p.types, p.ordered,
+ make(map[string]bool, len(p.ordered)), nil,
+ }
+ return md, md.unify(p.mapping, rvalue(v))
+}
+
+// DecodeFile is just like Decode, except it will automatically read the
+// contents of the file at `fpath` and decode it for you.
+func DecodeFile(fpath string, v interface{}) (MetaData, error) {
+ bs, err := ioutil.ReadFile(fpath)
+ if err != nil {
+ return MetaData{}, err
+ }
+ return Decode(string(bs), v)
+}
+
+// DecodeReader is just like Decode, except it will consume all bytes
+// from the reader and decode it for you.
+func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
+ bs, err := ioutil.ReadAll(r)
+ if err != nil {
+ return MetaData{}, err
+ }
+ return Decode(string(bs), v)
+}
+
+// unify performs a sort of type unification based on the structure of `rv`,
+// which is the client representation.
+//
+// Any type mismatch produces an error. Finding a type that we don't know
+// how to handle produces an unsupported type error.
+func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
+
+ // Special case. Look for a `Primitive` value.
+ if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() {
+ // Save the undecoded data and the key context into the primitive
+ // value.
+ context := make(Key, len(md.context))
+ copy(context, md.context)
+ rv.Set(reflect.ValueOf(Primitive{
+ undecoded: data,
+ context: context,
+ }))
+ return nil
+ }
+
+ // Special case. Unmarshaler Interface support.
+ if rv.CanAddr() {
+ if v, ok := rv.Addr().Interface().(Unmarshaler); ok {
+ return v.UnmarshalTOML(data)
+ }
+ }
+
+ // Special case. Handle time.Time values specifically.
+ // TODO: Remove this code when we decide to drop support for Go 1.1.
+ // This isn't necessary in Go 1.2 because time.Time satisfies the encoding
+ // interfaces.
+ if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) {
+ return md.unifyDatetime(data, rv)
+ }
+
+ // Special case. Look for a value satisfying the TextUnmarshaler interface.
+ if v, ok := rv.Interface().(TextUnmarshaler); ok {
+ return md.unifyText(data, v)
+ }
+ // BUG(burntsushi)
+ // The behavior here is incorrect whenever a Go type satisfies the
+ // encoding.TextUnmarshaler interface but also corresponds to a TOML
+ // hash or array. In particular, the unmarshaler should only be applied
+ // to primitive TOML values. But at this point, it will be applied to
+ // all kinds of values and produce an incorrect error whenever those values
+ // are hashes or arrays (including arrays of tables).
+
+ k := rv.Kind()
+
+ // laziness
+ if k >= reflect.Int && k <= reflect.Uint64 {
+ return md.unifyInt(data, rv)
+ }
+ switch k {
+ case reflect.Ptr:
+ elem := reflect.New(rv.Type().Elem())
+ err := md.unify(data, reflect.Indirect(elem))
+ if err != nil {
+ return err
+ }
+ rv.Set(elem)
+ return nil
+ case reflect.Struct:
+ return md.unifyStruct(data, rv)
+ case reflect.Map:
+ return md.unifyMap(data, rv)
+ case reflect.Array:
+ return md.unifyArray(data, rv)
+ case reflect.Slice:
+ return md.unifySlice(data, rv)
+ case reflect.String:
+ return md.unifyString(data, rv)
+ case reflect.Bool:
+ return md.unifyBool(data, rv)
+ case reflect.Interface:
+ // we only support empty interfaces.
+ if rv.NumMethod() > 0 {
+ return e("Unsupported type '%s'.", rv.Kind())
+ }
+ return md.unifyAnything(data, rv)
+ case reflect.Float32:
+ fallthrough
+ case reflect.Float64:
+ return md.unifyFloat64(data, rv)
+ }
+ return e("Unsupported type '%s'.", rv.Kind())
+}
+
+func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
+ tmap, ok := mapping.(map[string]interface{})
+ if !ok {
+ return mismatch(rv, "map", mapping)
+ }
+
+ for key, datum := range tmap {
+ var f *field
+ fields := cachedTypeFields(rv.Type())
+ for i := range fields {
+ ff := &fields[i]
+ if ff.name == key {
+ f = ff
+ break
+ }
+ if f == nil && strings.EqualFold(ff.name, key) {
+ f = ff
+ }
+ }
+ if f != nil {
+ subv := rv
+ for _, i := range f.index {
+ subv = indirect(subv.Field(i))
+ }
+ if isUnifiable(subv) {
+ md.decoded[md.context.add(key).String()] = true
+ md.context = append(md.context, key)
+ if err := md.unify(datum, subv); err != nil {
+ return e("Type mismatch for '%s.%s': %s",
+ rv.Type().String(), f.name, err)
+ }
+ md.context = md.context[0 : len(md.context)-1]
+ } else if f.name != "" {
+ // Bad user! No soup for you!
+ return e("Field '%s.%s' is unexported, and therefore cannot "+
+ "be loaded with reflection.", rv.Type().String(), f.name)
+ }
+ }
+ }
+ return nil
+}
+
+func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
+ tmap, ok := mapping.(map[string]interface{})
+ if !ok {
+ return badtype("map", mapping)
+ }
+ if rv.IsNil() {
+ rv.Set(reflect.MakeMap(rv.Type()))
+ }
+ for k, v := range tmap {
+ md.decoded[md.context.add(k).String()] = true
+ md.context = append(md.context, k)
+
+ rvkey := indirect(reflect.New(rv.Type().Key()))
+ rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
+ if err := md.unify(v, rvval); err != nil {
+ return err
+ }
+ md.context = md.context[0 : len(md.context)-1]
+
+ rvkey.SetString(k)
+ rv.SetMapIndex(rvkey, rvval)
+ }
+ return nil
+}
+
+func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
+ datav := reflect.ValueOf(data)
+ if datav.Kind() != reflect.Slice {
+ return badtype("slice", data)
+ }
+ sliceLen := datav.Len()
+ if sliceLen != rv.Len() {
+ return e("expected array length %d; got TOML array of length %d",
+ rv.Len(), sliceLen)
+ }
+ return md.unifySliceArray(datav, rv)
+}
+
+func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
+ datav := reflect.ValueOf(data)
+ if datav.Kind() != reflect.Slice {
+ return badtype("slice", data)
+ }
+ sliceLen := datav.Len()
+ if rv.IsNil() {
+ rv.Set(reflect.MakeSlice(rv.Type(), sliceLen, sliceLen))
+ }
+ return md.unifySliceArray(datav, rv)
+}
+
+func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
+ sliceLen := data.Len()
+ for i := 0; i < sliceLen; i++ {
+ v := data.Index(i).Interface()
+ sliceval := indirect(rv.Index(i))
+ if err := md.unify(v, sliceval); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error {
+ if _, ok := data.(time.Time); ok {
+ rv.Set(reflect.ValueOf(data))
+ return nil
+ }
+ return badtype("time.Time", data)
+}
+
+func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
+ if s, ok := data.(string); ok {
+ rv.SetString(s)
+ return nil
+ }
+ return badtype("string", data)
+}
+
+func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
+ if num, ok := data.(float64); ok {
+ switch rv.Kind() {
+ case reflect.Float32:
+ fallthrough
+ case reflect.Float64:
+ rv.SetFloat(num)
+ default:
+ panic("bug")
+ }
+ return nil
+ }
+ return badtype("float", data)
+}
+
+func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
+ if num, ok := data.(int64); ok {
+ if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 {
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int64:
+ // No bounds checking necessary.
+ case reflect.Int8:
+ if num < math.MinInt8 || num > math.MaxInt8 {
+ return e("Value '%d' is out of range for int8.", num)
+ }
+ case reflect.Int16:
+ if num < math.MinInt16 || num > math.MaxInt16 {
+ return e("Value '%d' is out of range for int16.", num)
+ }
+ case reflect.Int32:
+ if num < math.MinInt32 || num > math.MaxInt32 {
+ return e("Value '%d' is out of range for int32.", num)
+ }
+ }
+ rv.SetInt(num)
+ } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 {
+ unum := uint64(num)
+ switch rv.Kind() {
+ case reflect.Uint, reflect.Uint64:
+ // No bounds checking necessary.
+ case reflect.Uint8:
+ if num < 0 || unum > math.MaxUint8 {
+ return e("Value '%d' is out of range for uint8.", num)
+ }
+ case reflect.Uint16:
+ if num < 0 || unum > math.MaxUint16 {
+ return e("Value '%d' is out of range for uint16.", num)
+ }
+ case reflect.Uint32:
+ if num < 0 || unum > math.MaxUint32 {
+ return e("Value '%d' is out of range for uint32.", num)
+ }
+ }
+ rv.SetUint(unum)
+ } else {
+ panic("unreachable")
+ }
+ return nil
+ }
+ return badtype("integer", data)
+}
+
+func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
+ if b, ok := data.(bool); ok {
+ rv.SetBool(b)
+ return nil
+ }
+ return badtype("boolean", data)
+}
+
+func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
+ rv.Set(reflect.ValueOf(data))
+ return nil
+}
+
+func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error {
+ var s string
+ switch sdata := data.(type) {
+ case TextMarshaler:
+ text, err := sdata.MarshalText()
+ if err != nil {
+ return err
+ }
+ s = string(text)
+ case fmt.Stringer:
+ s = sdata.String()
+ case string:
+ s = sdata
+ case bool:
+ s = fmt.Sprintf("%v", sdata)
+ case int64:
+ s = fmt.Sprintf("%d", sdata)
+ case float64:
+ s = fmt.Sprintf("%f", sdata)
+ default:
+ return badtype("primitive (string-like)", data)
+ }
+ if err := v.UnmarshalText([]byte(s)); err != nil {
+ return err
+ }
+ return nil
+}
+
+// rvalue returns a reflect.Value of `v`. All pointers are resolved.
+func rvalue(v interface{}) reflect.Value {
+ return indirect(reflect.ValueOf(v))
+}
+
+// indirect returns the value pointed to by a pointer.
+// Pointers are followed until the value is not a pointer.
+// New values are allocated for each nil pointer.
+//
+// An exception to this rule is if the value satisfies an interface of
+// interest to us (like encoding.TextUnmarshaler).
+func indirect(v reflect.Value) reflect.Value {
+ if v.Kind() != reflect.Ptr {
+ if v.CanAddr() {
+ pv := v.Addr()
+ if _, ok := pv.Interface().(TextUnmarshaler); ok {
+ return pv
+ }
+ }
+ return v
+ }
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ return indirect(reflect.Indirect(v))
+}
+
+func isUnifiable(rv reflect.Value) bool {
+ if rv.CanSet() {
+ return true
+ }
+ if _, ok := rv.Interface().(TextUnmarshaler); ok {
+ return true
+ }
+ return false
+}
+
+func badtype(expected string, data interface{}) error {
+ return e("Expected %s but found '%T'.", expected, data)
+}
+
+func mismatch(user reflect.Value, expected string, data interface{}) error {
+ return e("Type mismatch for %s. Expected %s but found '%T'.",
+ user.Type().String(), expected, data)
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_meta.go
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_meta.go b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_meta.go
new file mode 100644
index 0000000..ef6f545
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_meta.go
@@ -0,0 +1,122 @@
+package toml
+
+import "strings"
+
+// MetaData allows access to meta information about TOML data that may not
+// be inferrable via reflection. In particular, whether a key has been defined
+// and the TOML type of a key.
+type MetaData struct {
+ mapping map[string]interface{}
+ types map[string]tomlType
+ keys []Key
+ decoded map[string]bool
+ context Key // Used only during decoding.
+}
+
+// IsDefined returns true if the key given exists in the TOML data. The key
+// should be specified hierarchially. e.g.,
+//
+// // access the TOML key 'a.b.c'
+// IsDefined("a", "b", "c")
+//
+// IsDefined will return false if an empty key given. Keys are case sensitive.
+func (md *MetaData) IsDefined(key ...string) bool {
+ if len(key) == 0 {
+ return false
+ }
+
+ var hash map[string]interface{}
+ var ok bool
+ var hashOrVal interface{} = md.mapping
+ for _, k := range key {
+ if hash, ok = hashOrVal.(map[string]interface{}); !ok {
+ return false
+ }
+ if hashOrVal, ok = hash[k]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// Type returns a string representation of the type of the key specified.
+//
+// Type will return the empty string if given an empty key or a key that
+// does not exist. Keys are case sensitive.
+func (md *MetaData) Type(key ...string) string {
+ fullkey := strings.Join(key, ".")
+ if typ, ok := md.types[fullkey]; ok {
+ return typ.typeString()
+ }
+ return ""
+}
+
+// Key is the type of any TOML key, including key groups. Use (MetaData).Keys
+// to get values of this type.
+type Key []string
+
+func (k Key) String() string {
+ return strings.Join(k, ".")
+}
+
+func (k Key) maybeQuotedAll() string {
+ var ss []string
+ for i := range k {
+ ss = append(ss, k.maybeQuoted(i))
+ }
+ return strings.Join(ss, ".")
+}
+
+func (k Key) maybeQuoted(i int) string {
+ quote := false
+ for _, c := range k[i] {
+ if !isBareKeyChar(c) {
+ quote = true
+ break
+ }
+ }
+ if quote {
+ return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\""
+ } else {
+ return k[i]
+ }
+}
+
+func (k Key) add(piece string) Key {
+ newKey := make(Key, len(k)+1)
+ copy(newKey, k)
+ newKey[len(k)] = piece
+ return newKey
+}
+
+// Keys returns a slice of every key in the TOML data, including key groups.
+// Each key is itself a slice, where the first element is the top of the
+// hierarchy and the last is the most specific.
+//
+// The list will have the same order as the keys appeared in the TOML data.
+//
+// All keys returned are non-empty.
+func (md *MetaData) Keys() []Key {
+ return md.keys
+}
+
+// Undecoded returns all keys that have not been decoded in the order in which
+// they appear in the original TOML document.
+//
+// This includes keys that haven't been decoded because of a Primitive value.
+// Once the Primitive value is decoded, the keys will be considered decoded.
+//
+// Also note that decoding into an empty interface will result in no decoding,
+// and so no keys will be considered decoded.
+//
+// In this sense, the Undecoded keys correspond to keys in the TOML document
+// that do not have a concrete type in your representation.
+func (md *MetaData) Undecoded() []Key {
+ undecoded := make([]Key, 0, len(md.keys))
+ for _, key := range md.keys {
+ if !md.decoded[key.String()] {
+ undecoded = append(undecoded, key)
+ }
+ }
+ return undecoded
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_test.go
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_test.go b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_test.go
new file mode 100644
index 0000000..3805931
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/decode_test.go
@@ -0,0 +1,950 @@
+package toml
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func init() {
+ log.SetFlags(0)
+}
+
+func TestDecodeSimple(t *testing.T) {
+ var testSimple = `
+age = 250
+andrew = "gallant"
+kait = "brady"
+now = 1987-07-05T05:45:00Z
+yesOrNo = true
+pi = 3.14
+colors = [
+ ["red", "green", "blue"],
+ ["cyan", "magenta", "yellow", "black"],
+]
+
+[My.Cats]
+plato = "cat 1"
+cauchy = "cat 2"
+`
+
+ type cats struct {
+ Plato string
+ Cauchy string
+ }
+ type simple struct {
+ Age int
+ Colors [][]string
+ Pi float64
+ YesOrNo bool
+ Now time.Time
+ Andrew string
+ Kait string
+ My map[string]cats
+ }
+
+ var val simple
+ _, err := Decode(testSimple, &val)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ now, err := time.Parse("2006-01-02T15:04:05", "1987-07-05T05:45:00")
+ if err != nil {
+ panic(err)
+ }
+ var answer = simple{
+ Age: 250,
+ Andrew: "gallant",
+ Kait: "brady",
+ Now: now,
+ YesOrNo: true,
+ Pi: 3.14,
+ Colors: [][]string{
+ {"red", "green", "blue"},
+ {"cyan", "magenta", "yellow", "black"},
+ },
+ My: map[string]cats{
+ "Cats": cats{Plato: "cat 1", Cauchy: "cat 2"},
+ },
+ }
+ if !reflect.DeepEqual(val, answer) {
+ t.Fatalf("Expected\n-----\n%#v\n-----\nbut got\n-----\n%#v\n",
+ answer, val)
+ }
+}
+
+func TestDecodeEmbedded(t *testing.T) {
+ type Dog struct{ Name string }
+ type Age int
+
+ tests := map[string]struct {
+ input string
+ decodeInto interface{}
+ wantDecoded interface{}
+ }{
+ "embedded struct": {
+ input: `Name = "milton"`,
+ decodeInto: &struct{ Dog }{},
+ wantDecoded: &struct{ Dog }{Dog{"milton"}},
+ },
+ "embedded non-nil pointer to struct": {
+ input: `Name = "milton"`,
+ decodeInto: &struct{ *Dog }{},
+ wantDecoded: &struct{ *Dog }{&Dog{"milton"}},
+ },
+ "embedded nil pointer to struct": {
+ input: ``,
+ decodeInto: &struct{ *Dog }{},
+ wantDecoded: &struct{ *Dog }{nil},
+ },
+ "embedded int": {
+ input: `Age = -5`,
+ decodeInto: &struct{ Age }{},
+ wantDecoded: &struct{ Age }{-5},
+ },
+ }
+
+ for label, test := range tests {
+ _, err := Decode(test.input, test.decodeInto)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(test.wantDecoded, test.decodeInto) {
+ t.Errorf("%s: want decoded == %+v, got %+v",
+ label, test.wantDecoded, test.decodeInto)
+ }
+ }
+}
+
+func TestTableArrays(t *testing.T) {
+ var tomlTableArrays = `
+[[albums]]
+name = "Born to Run"
+
+ [[albums.songs]]
+ name = "Jungleland"
+
+ [[albums.songs]]
+ name = "Meeting Across the River"
+
+[[albums]]
+name = "Born in the USA"
+
+ [[albums.songs]]
+ name = "Glory Days"
+
+ [[albums.songs]]
+ name = "Dancing in the Dark"
+`
+
+ type Song struct {
+ Name string
+ }
+
+ type Album struct {
+ Name string
+ Songs []Song
+ }
+
+ type Music struct {
+ Albums []Album
+ }
+
+ expected := Music{[]Album{
+ {"Born to Run", []Song{{"Jungleland"}, {"Meeting Across the River"}}},
+ {"Born in the USA", []Song{{"Glory Days"}, {"Dancing in the Dark"}}},
+ }}
+ var got Music
+ if _, err := Decode(tomlTableArrays, &got); err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(expected, got) {
+ t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
+ }
+}
+
+// Case insensitive matching tests.
+// A bit more comprehensive than needed given the current implementation,
+// but implementations change.
+// Probably still missing demonstrations of some ugly corner cases regarding
+// case insensitive matching and multiple fields.
+func TestCase(t *testing.T) {
+ var caseToml = `
+tOpString = "string"
+tOpInt = 1
+tOpFloat = 1.1
+tOpBool = true
+tOpdate = 2006-01-02T15:04:05Z
+tOparray = [ "array" ]
+Match = "i should be in Match only"
+MatcH = "i should be in MatcH only"
+once = "just once"
+[nEst.eD]
+nEstedString = "another string"
+`
+
+ type InsensitiveEd struct {
+ NestedString string
+ }
+
+ type InsensitiveNest struct {
+ Ed InsensitiveEd
+ }
+
+ type Insensitive struct {
+ TopString string
+ TopInt int
+ TopFloat float64
+ TopBool bool
+ TopDate time.Time
+ TopArray []string
+ Match string
+ MatcH string
+ Once string
+ OncE string
+ Nest InsensitiveNest
+ }
+
+ tme, err := time.Parse(time.RFC3339, time.RFC3339[:len(time.RFC3339)-5])
+ if err != nil {
+ panic(err)
+ }
+ expected := Insensitive{
+ TopString: "string",
+ TopInt: 1,
+ TopFloat: 1.1,
+ TopBool: true,
+ TopDate: tme,
+ TopArray: []string{"array"},
+ MatcH: "i should be in MatcH only",
+ Match: "i should be in Match only",
+ Once: "just once",
+ OncE: "",
+ Nest: InsensitiveNest{
+ Ed: InsensitiveEd{NestedString: "another string"},
+ },
+ }
+ var got Insensitive
+ if _, err := Decode(caseToml, &got); err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(expected, got) {
+ t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
+ }
+}
+
+func TestPointers(t *testing.T) {
+ type Object struct {
+ Type string
+ Description string
+ }
+
+ type Dict struct {
+ NamedObject map[string]*Object
+ BaseObject *Object
+ Strptr *string
+ Strptrs []*string
+ }
+ s1, s2, s3 := "blah", "abc", "def"
+ expected := &Dict{
+ Strptr: &s1,
+ Strptrs: []*string{&s2, &s3},
+ NamedObject: map[string]*Object{
+ "foo": {"FOO", "fooooo!!!"},
+ "bar": {"BAR", "ba-ba-ba-ba-barrrr!!!"},
+ },
+ BaseObject: &Object{"BASE", "da base"},
+ }
+
+ ex1 := `
+Strptr = "blah"
+Strptrs = ["abc", "def"]
+
+[NamedObject.foo]
+Type = "FOO"
+Description = "fooooo!!!"
+
+[NamedObject.bar]
+Type = "BAR"
+Description = "ba-ba-ba-ba-barrrr!!!"
+
+[BaseObject]
+Type = "BASE"
+Description = "da base"
+`
+ dict := new(Dict)
+ _, err := Decode(ex1, dict)
+ if err != nil {
+ t.Errorf("Decode error: %v", err)
+ }
+ if !reflect.DeepEqual(expected, dict) {
+ t.Fatalf("\n%#v\n!=\n%#v\n", expected, dict)
+ }
+}
+
+type sphere struct {
+ Center [3]float64
+ Radius float64
+}
+
+func TestDecodeSimpleArray(t *testing.T) {
+ var s1 sphere
+ if _, err := Decode(`center = [0.0, 1.5, 0.0]`, &s1); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestDecodeArrayWrongSize(t *testing.T) {
+ var s1 sphere
+ if _, err := Decode(`center = [0.1, 2.3]`, &s1); err == nil {
+ t.Fatal("Expected array type mismatch error")
+ }
+}
+
+func TestDecodeLargeIntoSmallInt(t *testing.T) {
+ type table struct {
+ Value int8
+ }
+ var tab table
+ if _, err := Decode(`value = 500`, &tab); err == nil {
+ t.Fatal("Expected integer out-of-bounds error.")
+ }
+}
+
+func TestDecodeSizedInts(t *testing.T) {
+ type table struct {
+ U8 uint8
+ U16 uint16
+ U32 uint32
+ U64 uint64
+ U uint
+ I8 int8
+ I16 int16
+ I32 int32
+ I64 int64
+ I int
+ }
+ answer := table{1, 1, 1, 1, 1, -1, -1, -1, -1, -1}
+ toml := `
+ u8 = 1
+ u16 = 1
+ u32 = 1
+ u64 = 1
+ u = 1
+ i8 = -1
+ i16 = -1
+ i32 = -1
+ i64 = -1
+ i = -1
+ `
+ var tab table
+ if _, err := Decode(toml, &tab); err != nil {
+ t.Fatal(err.Error())
+ }
+ if answer != tab {
+ t.Fatalf("Expected %#v but got %#v", answer, tab)
+ }
+}
+
+func TestUnmarshaler(t *testing.T) {
+
+ var tomlBlob = `
+[dishes.hamboogie]
+name = "Hamboogie with fries"
+price = 10.99
+
+[[dishes.hamboogie.ingredients]]
+name = "Bread Bun"
+
+[[dishes.hamboogie.ingredients]]
+name = "Lettuce"
+
+[[dishes.hamboogie.ingredients]]
+name = "Real Beef Patty"
+
+[[dishes.hamboogie.ingredients]]
+name = "Tomato"
+
+[dishes.eggsalad]
+name = "Egg Salad with rice"
+price = 3.99
+
+[[dishes.eggsalad.ingredients]]
+name = "Egg"
+
+[[dishes.eggsalad.ingredients]]
+name = "Mayo"
+
+[[dishes.eggsalad.ingredients]]
+name = "Rice"
+`
+ m := &menu{}
+ if _, err := Decode(tomlBlob, m); err != nil {
+ log.Fatal(err)
+ }
+
+ if len(m.Dishes) != 2 {
+ t.Log("two dishes should be loaded with UnmarshalTOML()")
+ t.Errorf("expected %d but got %d", 2, len(m.Dishes))
+ }
+
+ eggSalad := m.Dishes["eggsalad"]
+ if _, ok := interface{}(eggSalad).(dish); !ok {
+ t.Errorf("expected a dish")
+ }
+
+ if eggSalad.Name != "Egg Salad with rice" {
+ t.Errorf("expected the dish to be named 'Egg Salad with rice'")
+ }
+
+ if len(eggSalad.Ingredients) != 3 {
+ t.Log("dish should be loaded with UnmarshalTOML()")
+ t.Errorf("expected %d but got %d", 3, len(eggSalad.Ingredients))
+ }
+
+ found := false
+ for _, i := range eggSalad.Ingredients {
+ if i.Name == "Rice" {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Error("Rice was not loaded in UnmarshalTOML()")
+ }
+
+ // test on a value - must be passed as *
+ o := menu{}
+ if _, err := Decode(tomlBlob, &o); err != nil {
+ log.Fatal(err)
+ }
+
+}
+
+type menu struct {
+ Dishes map[string]dish
+}
+
+func (m *menu) UnmarshalTOML(p interface{}) error {
+ m.Dishes = make(map[string]dish)
+ data, _ := p.(map[string]interface{})
+ dishes := data["dishes"].(map[string]interface{})
+ for n, v := range dishes {
+ if d, ok := v.(map[string]interface{}); ok {
+ nd := dish{}
+ nd.UnmarshalTOML(d)
+ m.Dishes[n] = nd
+ } else {
+ return fmt.Errorf("not a dish")
+ }
+ }
+ return nil
+}
+
+type dish struct {
+ Name string
+ Price float32
+ Ingredients []ingredient
+}
+
+func (d *dish) UnmarshalTOML(p interface{}) error {
+ data, _ := p.(map[string]interface{})
+ d.Name, _ = data["name"].(string)
+ d.Price, _ = data["price"].(float32)
+ ingredients, _ := data["ingredients"].([]map[string]interface{})
+ for _, e := range ingredients {
+ n, _ := interface{}(e).(map[string]interface{})
+ name, _ := n["name"].(string)
+ i := ingredient{name}
+ d.Ingredients = append(d.Ingredients, i)
+ }
+ return nil
+}
+
+type ingredient struct {
+ Name string
+}
+
+func ExampleMetaData_PrimitiveDecode() {
+ var md MetaData
+ var err error
+
+ var tomlBlob = `
+ranking = ["Springsteen", "J Geils"]
+
+[bands.Springsteen]
+started = 1973
+albums = ["Greetings", "WIESS", "Born to Run", "Darkness"]
+
+[bands."J Geils"]
+started = 1970
+albums = ["The J. Geils Band", "Full House", "Blow Your Face Out"]
+`
+
+ type band struct {
+ Started int
+ Albums []string
+ }
+ type classics struct {
+ Ranking []string
+ Bands map[string]Primitive
+ }
+
+ // Do the initial decode. Reflection is delayed on Primitive values.
+ var music classics
+ if md, err = Decode(tomlBlob, &music); err != nil {
+ log.Fatal(err)
+ }
+
+ // MetaData still includes information on Primitive values.
+ fmt.Printf("Is `bands.Springsteen` defined? %v\n",
+ md.IsDefined("bands", "Springsteen"))
+
+ // Decode primitive data into Go values.
+ for _, artist := range music.Ranking {
+ // A band is a primitive value, so we need to decode it to get a
+ // real `band` value.
+ primValue := music.Bands[artist]
+
+ var aBand band
+ if err = md.PrimitiveDecode(primValue, &aBand); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("%s started in %d.\n", artist, aBand.Started)
+ }
+ // Check to see if there were any fields left undecoded.
+ // Note that this won't be empty before decoding the Primitive value!
+ fmt.Printf("Undecoded: %q\n", md.Undecoded())
+
+ // Output:
+ // Is `bands.Springsteen` defined? true
+ // Springsteen started in 1973.
+ // J Geils started in 1970.
+ // Undecoded: []
+}
+
+func ExampleDecode() {
+ var tomlBlob = `
+# Some comments.
+[alpha]
+ip = "10.0.0.1"
+
+ [alpha.config]
+ Ports = [ 8001, 8002 ]
+ Location = "Toronto"
+ Created = 1987-07-05T05:45:00Z
+
+[beta]
+ip = "10.0.0.2"
+
+ [beta.config]
+ Ports = [ 9001, 9002 ]
+ Location = "New Jersey"
+ Created = 1887-01-05T05:55:00Z
+`
+
+ type serverConfig struct {
+ Ports []int
+ Location string
+ Created time.Time
+ }
+
+ type server struct {
+ IP string `toml:"ip"`
+ Config serverConfig `toml:"config"`
+ }
+
+ type servers map[string]server
+
+ var config servers
+ if _, err := Decode(tomlBlob, &config); err != nil {
+ log.Fatal(err)
+ }
+
+ for _, name := range []string{"alpha", "beta"} {
+ s := config[name]
+ fmt.Printf("Server: %s (ip: %s) in %s created on %s\n",
+ name, s.IP, s.Config.Location,
+ s.Config.Created.Format("2006-01-02"))
+ fmt.Printf("Ports: %v\n", s.Config.Ports)
+ }
+
+ // Output:
+ // Server: alpha (ip: 10.0.0.1) in Toronto created on 1987-07-05
+ // Ports: [8001 8002]
+ // Server: beta (ip: 10.0.0.2) in New Jersey created on 1887-01-05
+ // Ports: [9001 9002]
+}
+
+type duration struct {
+ time.Duration
+}
+
+func (d *duration) UnmarshalText(text []byte) error {
+ var err error
+ d.Duration, err = time.ParseDuration(string(text))
+ return err
+}
+
+// Example Unmarshaler shows how to decode TOML strings into your own
+// custom data type.
+func Example_unmarshaler() {
+ blob := `
+[[song]]
+name = "Thunder Road"
+duration = "4m49s"
+
+[[song]]
+name = "Stairway to Heaven"
+duration = "8m03s"
+`
+ type song struct {
+ Name string
+ Duration duration
+ }
+ type songs struct {
+ Song []song
+ }
+ var favorites songs
+ if _, err := Decode(blob, &favorites); err != nil {
+ log.Fatal(err)
+ }
+
+ // Code to implement the TextUnmarshaler interface for `duration`:
+ //
+ // type duration struct {
+ // time.Duration
+ // }
+ //
+ // func (d *duration) UnmarshalText(text []byte) error {
+ // var err error
+ // d.Duration, err = time.ParseDuration(string(text))
+ // return err
+ // }
+
+ for _, s := range favorites.Song {
+ fmt.Printf("%s (%s)\n", s.Name, s.Duration)
+ }
+ // Output:
+ // Thunder Road (4m49s)
+ // Stairway to Heaven (8m3s)
+}
+
+// Example StrictDecoding shows how to detect whether there are keys in the
+// TOML document that weren't decoded into the value given. This is useful
+// for returning an error to the user if they've included extraneous fields
+// in their configuration.
+func Example_strictDecoding() {
+ var blob = `
+key1 = "value1"
+key2 = "value2"
+key3 = "value3"
+`
+ type config struct {
+ Key1 string
+ Key3 string
+ }
+
+ var conf config
+ md, err := Decode(blob, &conf)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Undecoded keys: %q\n", md.Undecoded())
+ // Output:
+ // Undecoded keys: ["key2"]
+}
+
+// Example UnmarshalTOML shows how to implement a struct type that knows how to
+// unmarshal itself. The struct must take full responsibility for mapping the
+// values passed into the struct. The method may be used with interfaces in a
+// struct in cases where the actual type is not known until the data is
+// examined.
+func Example_unmarshalTOML() {
+
+ var blob = `
+[[parts]]
+type = "valve"
+id = "valve-1"
+size = 1.2
+rating = 4
+
+[[parts]]
+type = "valve"
+id = "valve-2"
+size = 2.1
+rating = 5
+
+[[parts]]
+type = "pipe"
+id = "pipe-1"
+length = 2.1
+diameter = 12
+
+[[parts]]
+type = "cable"
+id = "cable-1"
+length = 12
+rating = 3.1
+`
+ o := &order{}
+ err := Unmarshal([]byte(blob), o)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Println(len(o.parts))
+
+ for _, part := range o.parts {
+ fmt.Println(part.Name())
+ }
+
+ // Code to implement UmarshalJSON.
+
+ // type order struct {
+ // // NOTE `order.parts` is a private slice of type `part` which is an
+ // // interface and may only be loaded from toml using the
+ // // UnmarshalTOML() method of the Umarshaler interface.
+ // parts parts
+ // }
+
+ // func (o *order) UnmarshalTOML(data interface{}) error {
+
+ // // NOTE the example below contains detailed type casting to show how
+ // // the 'data' is retrieved. In operational use, a type cast wrapper
+ // // may be prefered e.g.
+ // //
+ // // func AsMap(v interface{}) (map[string]interface{}, error) {
+ // // return v.(map[string]interface{})
+ // // }
+ // //
+ // // resulting in:
+ // // d, _ := AsMap(data)
+ // //
+
+ // d, _ := data.(map[string]interface{})
+ // parts, _ := d["parts"].([]map[string]interface{})
+
+ // for _, p := range parts {
+
+ // typ, _ := p["type"].(string)
+ // id, _ := p["id"].(string)
+
+ // // detect the type of part and handle each case
+ // switch p["type"] {
+ // case "valve":
+
+ // size := float32(p["size"].(float64))
+ // rating := int(p["rating"].(int64))
+
+ // valve := &valve{
+ // Type: typ,
+ // ID: id,
+ // Size: size,
+ // Rating: rating,
+ // }
+
+ // o.parts = append(o.parts, valve)
+
+ // case "pipe":
+
+ // length := float32(p["length"].(float64))
+ // diameter := int(p["diameter"].(int64))
+
+ // pipe := &pipe{
+ // Type: typ,
+ // ID: id,
+ // Length: length,
+ // Diameter: diameter,
+ // }
+
+ // o.parts = append(o.parts, pipe)
+
+ // case "cable":
+
+ // length := int(p["length"].(int64))
+ // rating := float32(p["rating"].(float64))
+
+ // cable := &cable{
+ // Type: typ,
+ // ID: id,
+ // Length: length,
+ // Rating: rating,
+ // }
+
+ // o.parts = append(o.parts, cable)
+
+ // }
+ // }
+
+ // return nil
+ // }
+
+ // type parts []part
+
+ // type part interface {
+ // Name() string
+ // }
+
+ // type valve struct {
+ // Type string
+ // ID string
+ // Size float32
+ // Rating int
+ // }
+
+ // func (v *valve) Name() string {
+ // return fmt.Sprintf("VALVE: %s", v.ID)
+ // }
+
+ // type pipe struct {
+ // Type string
+ // ID string
+ // Length float32
+ // Diameter int
+ // }
+
+ // func (p *pipe) Name() string {
+ // return fmt.Sprintf("PIPE: %s", p.ID)
+ // }
+
+ // type cable struct {
+ // Type string
+ // ID string
+ // Length int
+ // Rating float32
+ // }
+
+ // func (c *cable) Name() string {
+ // return fmt.Sprintf("CABLE: %s", c.ID)
+ // }
+
+ // Output:
+ // 4
+ // VALVE: valve-1
+ // VALVE: valve-2
+ // PIPE: pipe-1
+ // CABLE: cable-1
+
+}
+
+type order struct {
+ // NOTE `order.parts` is a private slice of type `part` which is an
+ // interface and may only be loaded from toml using the UnmarshalTOML()
+ // method of the Umarshaler interface.
+ parts parts
+}
+
+func (o *order) UnmarshalTOML(data interface{}) error {
+
+ // NOTE the example below contains detailed type casting to show how
+ // the 'data' is retrieved. In operational use, a type cast wrapper
+ // may be prefered e.g.
+ //
+ // func AsMap(v interface{}) (map[string]interface{}, error) {
+ // return v.(map[string]interface{})
+ // }
+ //
+ // resulting in:
+ // d, _ := AsMap(data)
+ //
+
+ d, _ := data.(map[string]interface{})
+ parts, _ := d["parts"].([]map[string]interface{})
+
+ for _, p := range parts {
+
+ typ, _ := p["type"].(string)
+ id, _ := p["id"].(string)
+
+ // detect the type of part and handle each case
+ switch p["type"] {
+ case "valve":
+
+ size := float32(p["size"].(float64))
+ rating := int(p["rating"].(int64))
+
+ valve := &valve{
+ Type: typ,
+ ID: id,
+ Size: size,
+ Rating: rating,
+ }
+
+ o.parts = append(o.parts, valve)
+
+ case "pipe":
+
+ length := float32(p["length"].(float64))
+ diameter := int(p["diameter"].(int64))
+
+ pipe := &pipe{
+ Type: typ,
+ ID: id,
+ Length: length,
+ Diameter: diameter,
+ }
+
+ o.parts = append(o.parts, pipe)
+
+ case "cable":
+
+ length := int(p["length"].(int64))
+ rating := float32(p["rating"].(float64))
+
+ cable := &cable{
+ Type: typ,
+ ID: id,
+ Length: length,
+ Rating: rating,
+ }
+
+ o.parts = append(o.parts, cable)
+
+ }
+ }
+
+ return nil
+}
+
+type parts []part
+
+type part interface {
+ Name() string
+}
+
+type valve struct {
+ Type string
+ ID string
+ Size float32
+ Rating int
+}
+
+func (v *valve) Name() string {
+ return fmt.Sprintf("VALVE: %s", v.ID)
+}
+
+type pipe struct {
+ Type string
+ ID string
+ Length float32
+ Diameter int
+}
+
+func (p *pipe) Name() string {
+ return fmt.Sprintf("PIPE: %s", p.ID)
+}
+
+type cable struct {
+ Type string
+ ID string
+ Length int
+ Rating float32
+}
+
+func (c *cable) Name() string {
+ return fmt.Sprintf("CABLE: %s", c.ID)
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/doc.go
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/doc.go b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/doc.go
new file mode 100644
index 0000000..fe26800
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/doc.go
@@ -0,0 +1,27 @@
+/*
+Package toml provides facilities for decoding and encoding TOML configuration
+files via reflection. There is also support for delaying decoding with
+the Primitive type, and querying the set of keys in a TOML document with the
+MetaData type.
+
+The specification implemented: https://github.com/mojombo/toml
+
+The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
+whether a file is a valid TOML document. It can also be used to print the
+type of each key in a TOML document.
+
+Testing
+
+There are two important types of tests used for this package. The first is
+contained inside '*_test.go' files and uses the standard Go unit testing
+framework. These tests are primarily devoted to holistically testing the
+decoder and encoder.
+
+The second type of testing is used to verify the implementation's adherence
+to the TOML specification. These tests have been factored into their own
+project: https://github.com/BurntSushi/toml-test
+
+The reason the tests are in a separate project is so that they can be used by
+any implementation of TOML. Namely, it is language agnostic.
+*/
+package toml
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/encode.go
----------------------------------------------------------------------
diff --git a/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/encode.go b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/encode.go
new file mode 100644
index 0000000..c7e227c
--- /dev/null
+++ b/newt/Godeps/_workspace/src/github.com/BurntSushi/toml/encode.go
@@ -0,0 +1,551 @@
+package toml
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type tomlEncodeError struct{ error }
+
+var (
+ errArrayMixedElementTypes = errors.New(
+ "can't encode array with mixed element types")
+ errArrayNilElement = errors.New(
+ "can't encode array with nil element")
+ errNonString = errors.New(
+ "can't encode a map with non-string key type")
+ errAnonNonStruct = errors.New(
+ "can't encode an anonymous field that is not a struct")
+ errArrayNoTable = errors.New(
+ "TOML array element can't contain a table")
+ errNoKey = errors.New(
+ "top-level values must be a Go map or struct")
+ errAnything = errors.New("") // used in testing
+)
+
+var quotedReplacer = strings.NewReplacer(
+ "\t", "\\t",
+ "\n", "\\n",
+ "\r", "\\r",
+ "\"", "\\\"",
+ "\\", "\\\\",
+)
+
+// Encoder controls the encoding of Go values to a TOML document to some
+// io.Writer.
+//
+// The indentation level can be controlled with the Indent field.
+type Encoder struct {
+ // A single indentation level. By default it is two spaces.
+ Indent string
+
+ // hasWritten is whether we have written any output to w yet.
+ hasWritten bool
+ w *bufio.Writer
+}
+
+// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer
+// given. By default, a single indentation level is 2 spaces.
+func NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{
+ w: bufio.NewWriter(w),
+ Indent: " ",
+ }
+}
+
+// Encode writes a TOML representation of the Go value to the underlying
+// io.Writer. If the value given cannot be encoded to a valid TOML document,
+// then an error is returned.
+//
+// The mapping between Go values and TOML values should be precisely the same
+// as for the Decode* functions. Similarly, the TextMarshaler interface is
+// supported by encoding the resulting bytes as strings. (If you want to write
+// arbitrary binary data then you will need to use something like base64 since
+// TOML does not have any binary types.)
+//
+// When encoding TOML hashes (i.e., Go maps or structs), keys without any
+// sub-hashes are encoded first.
+//
+// If a Go map is encoded, then its keys are sorted alphabetically for
+// deterministic output. More control over this behavior may be provided if
+// there is demand for it.
+//
+// Encoding Go values without a corresponding TOML representation---like map
+// types with non-string keys---will cause an error to be returned. Similarly
+// for mixed arrays/slices, arrays/slices with nil elements, embedded
+// non-struct types and nested slices containing maps or structs.
+// (e.g., [][]map[string]string is not allowed but []map[string]string is OK
+// and so is []map[string][]string.)
+func (enc *Encoder) Encode(v interface{}) error {
+ rv := eindirect(reflect.ValueOf(v))
+ if err := enc.safeEncode(Key([]string{}), rv); err != nil {
+ return err
+ }
+ return enc.w.Flush()
+}
+
+func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ if terr, ok := r.(tomlEncodeError); ok {
+ err = terr.error
+ return
+ }
+ panic(r)
+ }
+ }()
+ enc.encode(key, rv)
+ return nil
+}
+
+func (enc *Encoder) encode(key Key, rv reflect.Value) {
+ // Special case. Time needs to be in ISO8601 format.
+ // Special case. If we can marshal the type to text, then we used that.
+ // Basically, this prevents the encoder for handling these types as
+ // generic structs (or whatever the underlying type of a TextMarshaler is).
+ switch rv.Interface().(type) {
+ case time.Time, TextMarshaler:
+ enc.keyEqElement(key, rv)
+ return
+ }
+
+ k := rv.Kind()
+ switch k {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
+ reflect.Uint64,
+ reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
+ enc.keyEqElement(key, rv)
+ case reflect.Array, reflect.Slice:
+ if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
+ enc.eArrayOfTables(key, rv)
+ } else {
+ enc.keyEqElement(key, rv)
+ }
+ case reflect.Interface:
+ if rv.IsNil() {
+ return
+ }
+ enc.encode(key, rv.Elem())
+ case reflect.Map:
+ if rv.IsNil() {
+ return
+ }
+ enc.eTable(key, rv)
+ case reflect.Ptr:
+ if rv.IsNil() {
+ return
+ }
+ enc.encode(key, rv.Elem())
+ case reflect.Struct:
+ enc.eTable(key, rv)
+ default:
+ panic(e("Unsupported type for key '%s': %s", key, k))
+ }
+}
+
+// eElement encodes any value that can be an array element (primitives and
+// arrays).
+func (enc *Encoder) eElement(rv reflect.Value) {
+ switch v := rv.Interface().(type) {
+ case time.Time:
+ // Special case time.Time as a primitive. Has to come before
+ // TextMarshaler below because time.Time implements
+ // encoding.TextMarshaler, but we need to always use UTC.
+ enc.wf(v.In(time.FixedZone("UTC", 0)).Format("2006-01-02T15:04:05Z"))
+ return
+ case TextMarshaler:
+ // Special case. Use text marshaler if it's available for this value.
+ if s, err := v.MarshalText(); err != nil {
+ encPanic(err)
+ } else {
+ enc.writeQuoted(string(s))
+ }
+ return
+ }
+ switch rv.Kind() {
+ case reflect.Bool:
+ enc.wf(strconv.FormatBool(rv.Bool()))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64:
+ enc.wf(strconv.FormatInt(rv.Int(), 10))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16,
+ reflect.Uint32, reflect.Uint64:
+ enc.wf(strconv.FormatUint(rv.Uint(), 10))
+ case reflect.Float32:
+ enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32)))
+ case reflect.Float64:
+ enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64)))
+ case reflect.Array, reflect.Slice:
+ enc.eArrayOrSliceElement(rv)
+ case reflect.Interface:
+ enc.eElement(rv.Elem())
+ case reflect.String:
+ enc.writeQuoted(rv.String())
+ default:
+ panic(e("Unexpected primitive type: %s", rv.Kind()))
+ }
+}
+
+// By the TOML spec, all floats must have a decimal with at least one
+// number on either side.
+func floatAddDecimal(fstr string) string {
+ if !strings.Contains(fstr, ".") {
+ return fstr + ".0"
+ }
+ return fstr
+}
+
+func (enc *Encoder) writeQuoted(s string) {
+ enc.wf("\"%s\"", quotedReplacer.Replace(s))
+}
+
+func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
+ length := rv.Len()
+ enc.wf("[")
+ for i := 0; i < length; i++ {
+ elem := rv.Index(i)
+ enc.eElement(elem)
+ if i != length-1 {
+ enc.wf(", ")
+ }
+ }
+ enc.wf("]")
+}
+
+func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
+ if len(key) == 0 {
+ encPanic(errNoKey)
+ }
+ for i := 0; i < rv.Len(); i++ {
+ trv := rv.Index(i)
+ if isNil(trv) {
+ continue
+ }
+ panicIfInvalidKey(key)
+ enc.newline()
+ enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll())
+ enc.newline()
+ enc.eMapOrStruct(key, trv)
+ }
+}
+
+func (enc *Encoder) eTable(key Key, rv reflect.Value) {
+ panicIfInvalidKey(key)
+ if len(key) == 1 {
+ // Output an extra new line between top-level tables.
+ // (The newline isn't written if nothing else has been written though.)
+ enc.newline()
+ }
+ if len(key) > 0 {
+ enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll())
+ enc.newline()
+ }
+ enc.eMapOrStruct(key, rv)
+}
+
+func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) {
+ switch rv := eindirect(rv); rv.Kind() {
+ case reflect.Map:
+ enc.eMap(key, rv)
+ case reflect.Struct:
+ enc.eStruct(key, rv)
+ default:
+ panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
+ }
+}
+
+func (enc *Encoder) eMap(key Key, rv reflect.Value) {
+ rt := rv.Type()
+ if rt.Key().Kind() != reflect.String {
+ encPanic(errNonString)
+ }
+
+ // Sort keys so that we have deterministic output. And write keys directly
+ // underneath this key first, before writing sub-structs or sub-maps.
+ var mapKeysDirect, mapKeysSub []string
+ for _, mapKey := range rv.MapKeys() {
+ k := mapKey.String()
+ if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) {
+ mapKeysSub = append(mapKeysSub, k)
+ } else {
+ mapKeysDirect = append(mapKeysDirect, k)
+ }
+ }
+
+ var writeMapKeys = func(mapKeys []string) {
+ sort.Strings(mapKeys)
+ for _, mapKey := range mapKeys {
+ mrv := rv.MapIndex(reflect.ValueOf(mapKey))
+ if isNil(mrv) {
+ // Don't write anything for nil fields.
+ continue
+ }
+ enc.encode(key.add(mapKey), mrv)
+ }
+ }
+ writeMapKeys(mapKeysDirect)
+ writeMapKeys(mapKeysSub)
+}
+
+func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
+ // Write keys for fields directly under this key first, because if we write
+ // a field that creates a new table, then all keys under it will be in that
+ // table (not the one we're writing here).
+ rt := rv.Type()
+ var fieldsDirect, fieldsSub [][]int
+ var addFields func(rt reflect.Type, rv reflect.Value, start []int)
+ addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
+ for i := 0; i < rt.NumField(); i++ {
+ f := rt.Field(i)
+ // skip unexporded fields
+ if f.PkgPath != "" {
+ continue
+ }
+ frv := rv.Field(i)
+ if f.Anonymous {
+ frv := eindirect(frv)
+ t := frv.Type()
+ if t.Kind() != reflect.Struct {
+ encPanic(errAnonNonStruct)
+ }
+ addFields(t, frv, f.Index)
+ } else if typeIsHash(tomlTypeOfGo(frv)) {
+ fieldsSub = append(fieldsSub, append(start, f.Index...))
+ } else {
+ fieldsDirect = append(fieldsDirect, append(start, f.Index...))
+ }
+ }
+ }
+ addFields(rt, rv, nil)
+
+ var writeFields = func(fields [][]int) {
+ for _, fieldIndex := range fields {
+ sft := rt.FieldByIndex(fieldIndex)
+ sf := rv.FieldByIndex(fieldIndex)
+ if isNil(sf) {
+ // Don't write anything for nil fields.
+ continue
+ }
+
+ keyName := sft.Tag.Get("toml")
+ if keyName == "-" {
+ continue
+ }
+ if keyName == "" {
+ keyName = sft.Name
+ }
+
+ keyName, opts := getOptions(keyName)
+ if _, ok := opts["omitempty"]; ok && isEmpty(sf) {
+ continue
+ } else if _, ok := opts["omitzero"]; ok && isZero(sf) {
+ continue
+ }
+
+ enc.encode(key.add(keyName), sf)
+ }
+ }
+ writeFields(fieldsDirect)
+ writeFields(fieldsSub)
+}
+
+// tomlTypeName returns the TOML type name of the Go value's type. It is
+// used to determine whether the types of array elements are mixed (which is
+// forbidden). If the Go value is nil, then it is illegal for it to be an array
+// element, and valueIsNil is returned as true.
+
+// Returns the TOML type of a Go value. The type may be `nil`, which means
+// no concrete TOML type could be found.
+func tomlTypeOfGo(rv reflect.Value) tomlType {
+ if isNil(rv) || !rv.IsValid() {
+ return nil
+ }
+ switch rv.Kind() {
+ case reflect.Bool:
+ return tomlBool
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
+ reflect.Uint64:
+ return tomlInteger
+ case reflect.Float32, reflect.Float64:
+ return tomlFloat
+ case reflect.Array, reflect.Slice:
+ if typeEqual(tomlHash, tomlArrayType(rv)) {
+ return tomlArrayHash
+ } else {
+ return tomlArray
+ }
+ case reflect.Ptr, reflect.Interface:
+ return tomlTypeOfGo(rv.Elem())
+ case reflect.String:
+ return tomlString
+ case reflect.Map:
+ return tomlHash
+ case reflect.Struct:
+ switch rv.Interface().(type) {
+ case time.Time:
+ return tomlDatetime
+ case TextMarshaler:
+ return tomlString
+ default:
+ return tomlHash
+ }
+ default:
+ panic("unexpected reflect.Kind: " + rv.Kind().String())
+ }
+}
+
+// tomlArrayType returns the element type of a TOML array. The type returned
+// may be nil if it cannot be determined (e.g., a nil slice or a zero length
+// slize). This function may also panic if it finds a type that cannot be
+// expressed in TOML (such as nil elements, heterogeneous arrays or directly
+// nested arrays of tables).
+func tomlArrayType(rv reflect.Value) tomlType {
+ if isNil(rv) || !rv.IsValid() || rv.Len() == 0 {
+ return nil
+ }
+ firstType := tomlTypeOfGo(rv.Index(0))
+ if firstType == nil {
+ encPanic(errArrayNilElement)
+ }
+
+ rvlen := rv.Len()
+ for i := 1; i < rvlen; i++ {
+ elem := rv.Index(i)
+ switch elemType := tomlTypeOfGo(elem); {
+ case elemType == nil:
+ encPanic(errArrayNilElement)
+ case !typeEqual(firstType, elemType):
+ encPanic(errArrayMixedElementTypes)
+ }
+ }
+ // If we have a nested array, then we must make sure that the nested
+ // array contains ONLY primitives.
+ // This checks arbitrarily nested arrays.
+ if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) {
+ nest := tomlArrayType(eindirect(rv.Index(0)))
+ if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) {
+ encPanic(errArrayNoTable)
+ }
+ }
+ return firstType
+}
+
+func getOptions(keyName string) (string, map[string]struct{}) {
+ opts := make(map[string]struct{})
+ ss := strings.Split(keyName, ",")
+ name := ss[0]
+ if len(ss) > 1 {
+ for _, opt := range ss {
+ opts[opt] = struct{}{}
+ }
+ }
+
+ return name, opts
+}
+
+func isZero(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ if rv.Int() == 0 {
+ return true
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ if rv.Uint() == 0 {
+ return true
+ }
+ case reflect.Float32, reflect.Float64:
+ if rv.Float() == 0.0 {
+ return true
+ }
+ }
+
+ return false
+}
+
+func isEmpty(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.String:
+ if len(strings.TrimSpace(rv.String())) == 0 {
+ return true
+ }
+ case reflect.Array, reflect.Slice, reflect.Map:
+ if rv.Len() == 0 {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (enc *Encoder) newline() {
+ if enc.hasWritten {
+ enc.wf("\n")
+ }
+}
+
+func (enc *Encoder) keyEqElement(key Key, val reflect.Value) {
+ if len(key) == 0 {
+ encPanic(errNoKey)
+ }
+ panicIfInvalidKey(key)
+ enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
+ enc.eElement(val)
+ enc.newline()
+}
+
+func (enc *Encoder) wf(format string, v ...interface{}) {
+ if _, err := fmt.Fprintf(enc.w, format, v...); err != nil {
+ encPanic(err)
+ }
+ enc.hasWritten = true
+}
+
+func (enc *Encoder) indentStr(key Key) string {
+ return strings.Repeat(enc.Indent, len(key)-1)
+}
+
+func encPanic(err error) {
+ panic(tomlEncodeError{err})
+}
+
+func eindirect(v reflect.Value) reflect.Value {
+ switch v.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ return eindirect(v.Elem())
+ default:
+ return v
+ }
+}
+
+func isNil(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return rv.IsNil()
+ default:
+ return false
+ }
+}
+
+func panicIfInvalidKey(key Key) {
+ for _, k := range key {
+ if len(k) == 0 {
+ encPanic(e("Key '%s' is not a valid table name. Key names "+
+ "cannot be empty.", key.maybeQuotedAll()))
+ }
+ }
+}
+
+func isValidKeyName(s string) bool {
+ return len(s) != 0
+}