Giter VIP home page Giter VIP logo

toml's Introduction

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.

Compatible with TOML version v1.0.0.

Documentation: https://godocs.io/github.com/BurntSushi/toml

See the releases page for a changelog; this information is also in the git tag annotations (e.g. git show v0.4.0).

This library requires Go 1.18 or newer; add it to your go.mod with:

% go get github.com/BurntSushi/toml@latest

It also comes with a TOML validator CLI tool:

% go install github.com/BurntSushi/toml/cmd/tomlv@latest
% tomlv some-toml-file.toml

Examples

For the simplest example, consider some TOML file as just a list of keys and values:

Age = 25
Cats = [ "Cauchy", "Plato" ]
Pi = 3.14
Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00Z

Which can be decoded with:

type Config struct {
	Age        int
	Cats       []string
	Pi         float64
	Perfection []int
	DOB        time.Time
}

var conf Config
_, err := toml.Decode(tomlData, &conf)

You can also use struct tags if your struct field name doesn't map to a TOML key value directly:

some_key_NAME = "wat"
type TOML struct {
    ObscureKey string `toml:"some_key_NAME"`
}

Beware that like other decoders only exported fields are considered when encoding and decoding; private fields are silently ignored.

Using the Marshaler and encoding.TextUnmarshaler interfaces

Here's an example that automatically parses values in a mail.Address:

contacts = [
    "Donald Duck <[email protected]>",
    "Scrooge McDuck <[email protected]>",
]

Can be decoded with:

// Create address type which satisfies the encoding.TextUnmarshaler interface.
type address struct {
	*mail.Address
}

func (a *address) UnmarshalText(text []byte) error {
	var err error
	a.Address, err = mail.ParseAddress(string(text))
	return err
}

// Decode it.
func decode() {
	blob := `
		contacts = [
			"Donald Duck <[email protected]>",
			"Scrooge McDuck <[email protected]>",
		]
	`

	var contacts struct {
		Contacts []address
	}

	_, err := toml.Decode(blob, &contacts)
	if err != nil {
		log.Fatal(err)
	}

	for _, c := range contacts.Contacts {
		fmt.Printf("%#v\n", c.Address)
	}

	// Output:
	// &mail.Address{Name:"Donald Duck", Address:"[email protected]"}
	// &mail.Address{Name:"Scrooge McDuck", Address:"[email protected]"}
}

To target TOML specifically you can implement UnmarshalTOML TOML interface in a similar way.

More complex usage

See the _example/ directory for a more complex example.

toml's People

Contributors

anthonyfok avatar arp242 avatar bbuck avatar bep avatar burntsushi avatar cespare avatar dato avatar halostatue avatar husio avatar kkhaike avatar maddyblue avatar manunio avatar martinlindhe avatar mrdg avatar nathany avatar nobonobo avatar rjeczalik avatar samawise avatar samuel avatar samwhited avatar saracen avatar sethwklein avatar source-gisakulabs avatar spf13 avatar sqs avatar stapelberg avatar svendowideit avatar ttacon avatar usedbytes avatar zhsj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

toml's Issues

Inline Table Support

Hey, forgive me if I missed this. Recently, inline tables made it into the spec, is this on the roadmap for being supported in this package soon (meaning what kind of priority would supporting inline tables be, if it's one at all)? Just wondering! Also, awesome library!

Can't parse array value including "tokens without whitespaces" pattern

How to repeat:

package main

import (
    "github.com/BurntSushi/toml"
)

// include "separate token without spaces" pattern - "2,3"
const data = `
foo = [1, 2,3, 4]
`

type Config struct {
    Foo []int
}

func main() {
    var conf Config
    toml.Decode(data, &conf)
}

Result:

$ go run main.go
2014/04/09 00:29:23 BUG: Expected integer value, but got ',3'.

exit status 1

decoding table arrays need to be handled better

The following produces a runtime error:

package main

import (
    "fmt"
    "log"

    "github.com/BurntSushi/toml"
)

type Server struct {
    Url  string
    Port int
}

type Host struct {
    Servers []Server
}

var blob = `
[Host]
[[Servers]]
Url = "http://burntsushi.net"
Port = 8080

[[Servers]]
Url = "http://github.com"
Port = 1234
`

func main() {
    config := Host{
        Servers: []Server{
            {
                Url:  "http://google.com",
                Port: 80,
            },
        },
    }
    if _, err := toml.Decode(blob, &config); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%#v\n", config)
}

This is because (I think) the default value. The decoder is probably checking if it's not nil, and then just assuming that it is the right size. It should be smarter. (Just appending isn't quite right either, because I think it should overwrite the default value. But I should probably check the behavior of the standard library decoders before implementing that.)

decode same toml at mac osx & linux os, the result is different

title = "subject"
active = "home"

mac osx decode with result:
map[meta:map[title:subject active:home]

linux decode with result:
map[meta:map[title:"subject" active:"home"]

so, when i get .meta.active at template, the value is different at different os....

Ignore unexported fields when encoding

type Example struct {
    Exported string
    unexported int
}

Having one or more unexported fields in a struct results in:

panic: reflect.Value.Interface: cannot return value obtained from unexported field or method

Shouldn't these fields simply be ignored, as in the encoding/ packages?

Structs not decoding

Am I doing this wrong? My toml:

debug = true

[categories]

  [serious]
  name = "Serious"
  folders = [ "Serious" ]

  [start]
  name = "Starters"
  folders = [ "Semi-serious", "Mood", "Dance", "Action" ]

  [long]
  name = "Long"
  folders = [ "Long" ]

  [christmas]
  name = "Christmas"
  folders = [ "Christmas" ]

  [halloween]
  name = "Halloween"
  folders = [ "Halloween" ]

  [meta]
  name = "Meta"
  folders = [ "Meta" ]

  [buffer]
  name = "Buffers"
  folder = [ "Buffer" ]

And my Go (simplified):

type Configuration struct {
    Debug             bool
    Categories        map[string]Category
}

type Category struct {
    Name    string
    Folders []string
}

configData := ...
var config Configuration = Configuration{...}
if _, err := toml.Decode(string(configData), &config); err != nil {
    fmt.Println("Error decoding config.toml:", err)
} else {
    fmt.Printf("Config: %#v\n", Config)
}

The output:

Config: config.Configuration{Debug:true, Categories:map[string]config.Category{}}

This looks very similar to your servers example, but the Categories value comes out empty. Am I doing something wrong?

Field 'val' is unexported, and therefore cannot be loaded with reflection.

Maybe I'm just tired, but I can't get around this ...
any idea ?

main.go

package main

import (
    "fmt"
    "log"
    "os"
    "github.com/BurntSushi/toml"
)

type tomlConfig struct {
    User tomlUser `toml:"user"`
}

type tomlUser struct {
    Name string   `toml:"name"`
}

var Config tomlConfig

func init() {
    confFile := "config.ini"

    // reading config
    _, err := toml.DecodeFile(confFile, Config)
    if err != nil { log.Fatalf("Error decoding config file: %s", err) }
}

func main() {
    fmt.Printf("Username: %s\n", Config.User.Name)
}

config file:

[user]
  name = "joe"

output:

2015/07/25 14:08:22 Error decoding config file: Field 'main.tomlConfig.user' is unexported, and therefore cannot be loaded with reflection.

toml.Primitive should encode correctly

If I have a struct like this:

type SomeStruct struct {
    PreserveData toml.Primitive
    SomeData *MyData
}

I would expect that I can read a toml file, decode it to a SomeStruct, change the SomeData, and encode it back into a file with the PreserveData basically staying the same. Instead, it encodes to nothing.

Can we have a toml.Primitive encode back into whatever it used to be? Sort of like how json.RawMessage works?

Advanced unmarshalling: two config values into a single struct field?

I have TOML that looks like

  beef = moo
  AWSKey = "abcdefg"
  AWSSecret = "1a2b3c"

And I'd like to parse this into a Go struct like this:

type Config struct {
  Beef string
  AWSAuth aws.Auth
}

The aws.Auth type needs both the AWSKey and the AWSSecret from the config. Is there any way that I can unmarshal two config values into a single field of the target struct? Essentially replace auth := aws.Auth{AccessKey: config.AWSKey, SecretKey: config.AWSSecret} with some unmarshalling magic?

Thanks for a great library!

can't run example

the example can't run with error 2014/07/18 17:37:00 Near line 2, key '[song': Near line 2: Expected a top-level item to end with a new line, comment or EOF, but got ']' instead.

Needs to support raw strings (sometimes you need a revolution)

the TOML spec itself makes no allowance for this, but there's also an issue for it that's been open for... oh, 11 months now with no response? I'm thinking spec be damned (for now), and we go with single quotes or backticks for raw strings.

regex_that_doesnt_suck = '^There, now I don\'t suck \d+ times more than YAML'
# or
i_am_okay_too = `I've got \d{2} problems, but backticks ain't one`

Who's with me?

net.IP values in file causes error

config error: Near line 14, key '': Near line 14: Expected a top-level item to end with a new line, comment or EOF, but got '.' instead.

related to the setInt issue

sorry, I didn't look hard at the time :/

Handling of empty fields

I have been trying to figure out whether handling of empty fields is proper. As far as I can tell, the real issue is that an empty field may otherwise be a float, string, what have you. Of course, this means you cannot just use nil or just blindly convert no value to an empty string. It seems like nil is implied in the spec if the value for a field is empty, but from what I see there is no handling for nil in this library.

Not sure I am offering anything useful here, but seems like some handling should exist. Maybe I am missing something?

type_fields.go references LICENSE file that doesn't exist

type_fields.go contains the following header:

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

However, there is no LICENSE file present and I suspect this code from the go authors is not actually covered by the WTFPL in the COPYING file.

Proposal: json-style custom marshalling

In the json package, encoding a type which implements the Marshaler interface will cause the encoder to call the MarshalJSON method in order to encode the value. Notably, this succeeds so long as the produced output is valid JSON.

This package allows similar functionality, but restricts it to string values. It would be very nice if this were expanded so that any valid TOML were supported. To provide some motivation: @ezrosent and I ran into this issue today in trying to encode numerical values, and ended up having to use a horrible hack to work around this limitation :P

panic: reflect: call of reflect.Value.SetInt on uint Value

The following code seems to be broken following #31 (14be9b2):

package main

import (
    "fmt"
    toml "github.com/BurntSushi/toml"
)

type thing struct {
    Something uint
}

func main() {
    var th thing
    if _, err := toml.Decode("something = 1234567", &th); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(th.Something)
    }
}

Result:

$ ./toml-test 
panic: reflect: call of reflect.Value.SetInt on uint Value

goroutine 1 [running]:
runtime.panic(0xbdb00, 0x2102b6080)
    /usr/local/Cellar/go/1.2.2/libexec/src/pkg/runtime/panic.c:266 +0xb6
reflect.Value.SetInt(0xa2540, 0x21026d018, 0x76, 0x12d687)
    /usr/local/Cellar/go/1.2.2/libexec/src/pkg/reflect/value.go:1457 +0xb7
github.com/BurntSushi/toml.unifyInt(0xa23c0, 0x12d687, 0xa2540, 0x21026d018, 0x76, ...)
    /Users/mikey/go/src/github.com/BurntSushi/toml/decode.go:384 +0x4c4
github.com/BurntSushi/toml.unify(0xa23c0, 0x12d687, 0xa2540, 0x21026d018, 0x76, ...)
    /Users/mikey/go/src/github.com/BurntSushi/toml/decode.go:171 +0x262
github.com/BurntSushi/toml.unifyStruct(0xa1d00, 0x21028b270, 0xbfd60, 0x21026d018, 0x196, ...)
    /Users/mikey/go/src/github.com/BurntSushi/toml/decode.go:241 +0x50f
github.com/BurntSushi/toml.unify(0xa1d00, 0x21028b270, 0xbfd60, 0x21026d018, 0x196, ...)
    /Users/mikey/go/src/github.com/BurntSushi/toml/decode.go:183 +0x804
github.com/BurntSushi/toml.Decode(0xebbf0, 0x13, 0x99be0, 0x21026d018, 0x1f382, ...)
    /Users/mikey/go/src/github.com/BurntSushi/toml/decode.go:121 +0x107
main.main()
    /Users/mikey/git/toml-test/test.go:14 +0x65

I'm new to TOML; is this a bug in my code, or the parser?

Cannot decode integer value to float64 type.

It seems like a struct containing a float64 field cannot be decoded from a TOML document containing an integer value. The following example demonstrates this.

package main

import (
    "log"

    "github.com/BurntSushi/toml"
)

func main() {
    raw := "Foo = 123"
    var s struct {
        Foo float64
    }
    _, err := toml.Decode(raw, &s)
    if err != nil {
        log.Fatal(err)
    }
    log.Print(s)
}

The error message printed looks like this

2014/09/09 13:42:51 Type mismatch for 'struct { Foo float64 }.Foo': Expected float but found 'int64'.

panic if decoding to an empty slice

Try the following code:

package main

import "github.com/BurntSushi/toml"
import "log

type Q struct {
    Test []string
}

func main() {
    q := Q{Test: []string{}}
    _, err := toml.Decode(`Test = ["test"]`, &q)
    if err != nil {
        log.Fatal(err)
    }
}

This results in a panic:

panic: reflect: slice index out of range

goroutine 1 [running]:
reflect.Value.Index(0x6c1b60, 0x12658d80, 0xd7, 0x0, 0x0, 0x0, 0x0)
        c:/go/src/reflect/value.go:818 +0x10f github.com/BurntSushi/toml.(*MetaData).unifySliceArray(0x12652ba0, 0x6c0d60, 0x12658dd0, 0x57, 0x6c1b60, 0x12658d80, 0xd7, 0x0, 0x0)
        %GOPATH%/src/github.com/BurntSushi/toml/decode.go:321 +0xde github.com/BurntSushi/toml.(*MetaData).unifySlice(0x12652ba0, 0x6c0d60, 0x12658dd0, 0x6c1b60, 0x12658d80, 0xd7, 0x0, 0x0)
        %GOPATH%/src/github.com/BurntSushi/toml/decode.go:314 +0x1b4
github.com/BurntSushi/toml.(*MetaData).unify(0x12652ba0, 0x6c0d60, 0x12658dd0, 0x6c1b60, 0x12658d80, 0xd7, 0x0, 0x0)
        %GOPATH%/src/github.com/BurntSushi/toml/decode.go:206 +0xb11 github.com/BurntSushi/toml.(*MetaData).unifyStruct(0x12652ba0, 0x6cc160, 0x126543c0, 0x6d3e80, 0x12658d80, 0xd9, 0x0, 0x0)
        %GOPATH%/src/github.com/BurntSushi/toml/decode.go:252 +0x65a github.com/BurntSushi/toml.(*MetaData).unify(0x12652ba0, 0x6cc160, 0x126543c0, 0
x6d3e80, 0x12658d80, 0xd9, 0x0, 0x0)
        %GOPATH%/src/github.com/BurntSushi/toml/decode.go:200 +0xb9f github.com/BurntSushi/toml.Decode(0x7adf28, 0xf, 0x6bcbe0, 0x12658d80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        %GOPATH%/src/github.com/BurntSushi/toml/decode.go:114 +0x1c5 main.NewStaticContext(0x12655947, 0xa, 0x12656900, 0x0, 0x0, 0x0)
        ...

This is only caused when the Test field is initialized to an empty slice.

If Test is nil or has at least one element, Decode works as expected.

Support for delayed parsing or unification of sections

I have some occasions there I might have a few sections of configuration that map to modules in a system that are not coupled with the top-level configuration loader. These sections could be considered to need somewhat dynamic unification.

With JSON it's rather easy since one can simply put placeholders using json.RawMessage. Which allows those sections of the document to be delayed. Would you consider accepting something like a toml.RawSection or similar?

In the meantime I've resorted to hardcoding these subsections but it's brittle and requires constant edits to ensure the compiled in packages that implement this special submodule interface are included properly in the configuration. I could also parse to a generic map and unify by hand but this results in a lot of ugly boilerplate code that has to repeated for each section type.

Version bump?

Any way we could get a v0.1.1 or v0.2.0, especially since some nice features like #61 have merged since the v0.1.0 tag? 😄

Encoder reorders elements, causing incorrect encoding.

Expected: toml package to encode data properly.
Actual: During encoding it seems like arrays of maps are given some sort of priority, the ordering of the fields is mixed up, and an incorrect toml representation of the object is output.

Given the following reproduce code:

package main

import (
    "bytes"
    "fmt"

    "github.com/BurntSushi/toml"
)

// The object we are trying to build.
var config = `
[map]
zero = 5
[[map.maparr]]
friend = 5
`

func main() {
    var toEncode = map[string]interface{}{
        "map": map[string]interface{}{
            "zero": 5,
            "maparr": []map[string]interface{}{
                map[string]interface{}{
                    "friend": 5,
                },
            },
        },
    }

    b := &bytes.Buffer{}
    enc := toml.NewEncoder(b)
    if err := enc.Encode(toEncode); err != nil {
        fmt.Println("Error encoding:", err)
        return
    }

    fmt.Println(b.String())
}

The expected output of this is:

[map]
  zero = 5
  [[map.maparr]]
    friend = 5

But the actual output is:

[map]

  [[map.maparr]]
    friend = 5
  zero = 5

Because of the ordering of the fields, the field "zero" belongs to the [[map.maparr]] entry rather to the [map] entry, meaning that the encoded toml is incorrect.

Unmarshal with default values

Hello there,

Would that be a bad idea to have defaults when no information is provided ? Something like :

type Server struct {
    Url     string
    Port    int `toml:port,default=80` 
}

Or would that default be done in the code ?

We could also have another toml file containing the default values to initialize the types with.

quoted keys decoded with errors

Here is the example:

package main

import "encoding/json"
import "fmt"
import "heaverd-mitm.build/src/.go/src/github.com/BurntSushi/toml"

const tomlString = `
"some.value" = "ang if this"
Key = "here it is"
`

func main() {
    const tomlString = `
key = "first"
"quoted.key" = "second"
["quoted.table"]
    key = "third"
`
    var conf interface{}
    toml.Decode(tomlString, &conf)
    result, _ := json.MarshalIndent(conf, "", "\t")
    fmt.Println("decoded:", string(result))
}

We got this:

decoded: {
    "\"quoted": {
        "table\"": {
            "key": "third"
        }
    },
    "\"quoted.key\"": "second",
    "key": "first"
}

But should get this:

decoded: {
    "quoted.table": {
        "key": "third"
    },
    "quoted.key": "second",
    "key": "first"
}

infinite loop in unexpected EOF pattern.

= Patterns =

pattern1

[hoge]
dEOF

pattern2

[hoge]
]EOF

pattern3

[hoge]
sample = "hogeEOF

or others...

= Simple solution! =

diff --git a/lex.go b/lex.go
index 3dcae49..e343bca 100644
--- a/lex.go
+++ b/lex.go
@@ -82,7 +82,7 @@ func (lx *lexer) nextItem() item {

 func lex(input string) *lexer {
        lx := &lexer{
-           input: input,
+         input: input + "\n",
                state: lexTop,
                line:  1,
                items: make(chan item, 10),

Support (Go) arrays

Please? :D

Presumably it'd be a parse error if the TOML array isn't the right length.

question about custom types

I'm using your example to Unmarshal a custom duration type from the README. I'm able to Unmarshal, but can you advise on how to Encode or Marshal?

I ran into problems with can't encode an anonymous field that is not a struct. In searching around, I figured I could avoid the headache by modifying the duration type to only include a time.Duration field like so:

type duration struct {
    d time.Duration
}
func (d *duration) MarshalText() ([]byte, error) {
    return []byte(d.d.String()), nil
}
func (d *duration) UnmarshalText(text []byte) error {
    var err error
    d.d, err = time.ParseDuration(string(text))
    return err
}

This got me farther in that the Encode succeeds, but the output is not useful. Here's a section with a duration type:

[Heka]
  Connect = "tcp://127.0.0.1:5565"
  LogPoolSize = 100
  [Heka.log_every]

When what I'm hoping to yield is:

[Heka]
  Connect = "tcp://127.0.0.1:5565"
  LogPoolSize = 100
  log_every = "15s"

Can you set me straight?

Any support for encoding strings as multi-line?

While i see that the capability to decode multi-line strings has been added I'm wondering if there is support for encoding strings in the multi-line format. The envisioned use case would be something like saving a markdown blog post and its metadata in a human readable format

Encoder reorders arrays in structs

The toml.Encoder reorders arrays in structs. I can understand why the main values need to be above, but not why the arrays need to be reordered?

type Conf struct {
    V string
    A Alpha
    B []Beta
}

type Alpha struct{ V string }
type Beta struct{ V string }

func main() {
    conf := &Conf{
        V: "main",
        A: Alpha{"Alpha"},
        B: []Beta{{"1"}},
    }

    var buf bytes.Buffer
    toml.NewEncoder(&buf).Encode(conf)
    fmt.Println(buf.String())
}

This prints:

V = "main"

[[B]]
  V = "1"

[A]
  V = "Alpha"

Instead of the expected:

V = "main"

[A]
  V = "Alpha"

[[B]]
  V = "1"

(Also, as a side note, I tried to use an empty table name, should it work? e.g. [servers.], to get an entry of servers: { "": { ... }})

support and examples for quoted keys?

Hi, does this support quoted strings such as

"127.0.0.1" = "value"
"character encoding" = "value"
"ʎǝʞ" = "value"

If so, is there an example on how that's used?

thx!

`omitempty` breaks Decode

With #81, toml supports the omitempty tag while encoding a struct. For example:

type S struct {
    V string `toml:"value,omitempty"`
}

will skip writing "V" during toml.Encode().

Unfortunately the reverse is not true. omitempty has no meaning for toml.Decode() and is treated as part of the field name. In the example above, toml.Decode() will attempt to decode the field named value,omitempty into "V" and will ignore the field named value.

Infinite Loop

I was running the tomlv which works wonderfully unless your error is the last thing in the file. try returning after the end of a valid toml and hitting d. Now when you run the tomlv it just infinite loops.

The problems in lex.go in the nextItem function. it gets into the default and just loops forever. I'm new at programming and although I could figure out where the problem was it was beyond my skills to fix it.

Integer values incorrectly decode into smaller integer types.

The following program prints: -91

type thing struct {
    Something int8 `toml:"something"`
}

func main() {
    var th thing
    if _, err := toml.Decode("something = 6565", &th); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(th.Something)
    }
}

What I'd expect is that there would be an error occurring at decode-time because the value cannot safely be coerced into an integer of that size.

What happens is that the value is incorrectly decoded into the struct with no errors.

The spec says that a 64-bit integer should be assumed, but I'd think that's in the absence of a schema. Since we have a schema shouldn't we be honoring that contract as well?

Support "omitempty" like JSON and XML

It would be handy to tag my go struct with "omitempty" so that the toml writer does not write the value if it is empty. The go standard libraries json and xml have something similar.

Example:

type Settings struct {
    Port string `toml:"port,omitempty"`
}

Cannot decode interface

Not being able to decode interface, is that a known behavior? See below, Storage is an interface:
Type mismatch for 'backend.ProxyBackend.Storage': Unsupported type 'interface'.

Cannot round trip strings containing "\u"

Steps to reproduce:

  1. Run this code:
package main

import (
    "bytes"
    "fmt"

    "github.com/BurntSushi/toml"
)

func main() {
    data := map[string]string{
        "k": `c:\users`,
    }

    buf := &bytes.Buffer{}
    enc := toml.NewEncoder(buf)
    if err := enc.Encode(data); err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(buf.String())
    data2 := make(map[string]string)
    if _, err := toml.DecodeReader(buf, &data2); err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(data2)
}

I expect it to run without error and result in data2 containing a copy of data.

Instead I get this output:

k = "c:\\users"

2015/01/16 11:05:00 BUG: Could not parse 'sers' as a hexadecimal number, but the lexer claims it's OK: strconv
.ParseUint: parsing "sers": invalid syntax

and the program aborts before completing.

Function insensitiveGet bug

Hi, i got a strange issue, function insensitiveGet() in decode.go seems can't work correctly.

This is the a.toml,a simpleness toml file only one line, and encode use "UTF-8 with BOM".

Name = "kenshin"

And here is my go code.

package main

import (
    "fmt"
    "github.com/BurntSushi/toml"
)

type Config struct {
    Name string
}

var C Config

func main() {
    _, err := toml.DecodeFile("a.toml", &C)
    if err != nil {
        fmt.Println("xxx") //didn't excute here
    }
    fmt.Println(C.Name) //printf nil
}

Obvious, There are some issues in toml.DecodeFile(). I follow the tracks of toml source code. At last, I found the broken code at decode.go function insensitiveGet(). I add some debug code at this function,and found some strang issue.

func insensitiveGet(
    tmap map[string]interface{}, kname string) (interface{}, bool) {
    fmt.Println(kname)
    fmt.Println(tmap)
    fmt.Println(tmap["Name"])
    if datum, ok := tmap[kname]; ok {
        fmt.Println("xxx")
        return datum, true
    }
    for k, v := range tmap {
        if strings.EqualFold(kname, k) {
            return v, true
        }
    }
    return nil, false
}

The next is the output of this function.

Name
map[Name:kenshin]
<nil>

tmap =map[Name:kenshin], but tmap["Name"] return nil. Please point out what's wrong here, thx

octal / hex integer literal

May I use Go's integer literal?
below is very simple, or we need to implement lexOctNumber / lexHexNumber?

I'm sorry for long line, I can't beg you to pull because I'm in proxy only env.

diff --git a/decode_test.go b/decode_test.go
index 456b985..a528074 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -71,6 +71,8 @@ func TestDecode(t *testing.T) {
 var caseToml = `
 tOpString = "string"
 tOpInt = 1
+tOpOint = 0101
+tOpXint = 0xab2
 tOpFloat = 1.1
 tOpBool = true
 tOpdate = 2006-01-02T15:04:05Z
@@ -87,6 +89,8 @@ nEstedString = "another string"
 type Insensitive struct {
        TopString string
        TopInt    int
+       TopOint    int
+       TopXint    int
        TopFloat  float64
        TopBool   bool
        TopDate   time.Time
@@ -115,6 +119,8 @@ func TestCase(t *testing.T) {
        expected := Insensitive{
                TopString: "string",
                TopInt:    1,
+               TopOint:   65,
+               TopXint:   2738,
                TopFloat:  1.1,
                TopBool:   true,
                TopDate:   tme,
diff --git a/lex.go b/lex.go
index 8c18e02..f289758 100644
--- a/lex.go
+++ b/lex.go
@@ -342,6 +342,8 @@ func lexValue(lx *lexer) stateFn {
                return lexFalse
        case r == '-':
                return lexNumberStart
+       case r == '0':
+               return lexNumberStart
        case isDigit(r):
                lx.backup() // avoid an extra state and use the same as above
                return lexNumberOrDateStart
@@ -530,14 +532,15 @@ func lexDateAfterYear(lx *lexer) stateFn {
 func lexNumberStart(lx *lexer) stateFn {
        // we MUST see a digit. Even floats have to start with a digit.
        r := lx.next()
-       if !isDigit(r) {
-               if r == '.' {
-                       return lx.errorf("Floats must start with a digit, not '.'.")
-               } else {
-                       return lx.errorf("Expected a digit but got '%s'.", r)
-               }
+       switch {
+       case r == '.':
+               return lx.errorf("Floats must start with a digit, not '.'.")
+       case r == 'x':
+               fallthrough
+       case isDigit(r):
+               return lexNumber
        }
-       return lexNumber
+       return lx.errorf("Expected a digit but got '%s'.", r)
 }

 // lexNumber consumes an integer or a float after seeing the first digit.
@@ -545,6 +548,8 @@ func lexNumber(lx *lexer) stateFn {
        r := lx.next()
        switch {
        case isDigit(r):
+               fallthrough
+       case isHexadecimal(r):
                return lexNumber
        case r == '.':
                return lexFloatStart
diff --git a/parse.go b/parse.go
index 5d37bc0..f335f9d 100644
--- a/parse.go
+++ b/parse.go
@@ -144,7 +144,7 @@ func (p *parser) value(it item) (interface{}, tomlType) {
                }
                p.bug("Expected boolean value, but got '%s'.", it.val)
        case itemInteger:
-               num, err := strconv.ParseInt(it.val, 10, 64)
+               num, err := strconv.ParseInt(it.val, 0, 64)
                if err != nil {
                        if e, ok := err.(*strconv.NumError); ok &&
                                e.Err == strconv.ErrRange {

Probable bug/typo

Hey Burntsushi, I was perusing the source today (kickass practical use of reflect, btw) and I came across what is certainly some kind of mistake.

    case reflect.Interface:
        // we only support empty interfaces.
        if rv.NumMethod() > 0 {
            e("Unsupported type '%s'.", rv.Kind())  // No return?
        }
        return unifyAnything(data, rv)

I was too lazy to find an actual breaking example, but it can't be what you intended.

Might be worth running errcheck over this code to see if there are any more.

Support comments in encoding

Given that TOML supports comments, it would be nice if there were some way to emit TOML which included comments. My initial inclination was to implement the TextMarshaler interface, but it seems that doesn't work for key/value pairs (so you can't use it to omit a custom line of TOML).

My suggestion is that something be added which could be used to support this. I'm not sure what it would look like (a more flexible encoding mechanism? specific support for comments?), but it would be great to have.

Export "Unify"-like Public API?

This may be related to #3. I have a TOML file that looks something like this:

[things]

[things.something]
type = "widget"
key = "value"
etc.

I can't decode the "things" until I know the "type." Ideally what I want to do is something like this:

type Top struct {
    Things map[string]interface{}
}

type Widget struct {
    Key string
}

// Later on...
var widget Widget
toml.Unify(top.Things["something"], &widget)

(I realize that isn't the type signature of Unify at the moment)

Is there a better way to do this?

[enhancement] support ISO8601 offsets

For example, if I type date --iso-8601=seconds using the GNU date program, I get the output

2014-08-13T16:46:08-0700

It would be nice if this was parsed properly into a Time object, instead the code generates:

Near line 4, key 'date': Near line 4: Expected 'Z' in ISO8601 datetime, but found '-' instead.

Here's the relevant piece of code:

func lexDateAfterYear(lx *lexer) stateFn {
    formats := []rune{
        // digits are '0'.
        // everything else is direct equality.
        '0', '0', '-', '0', '0',
        'T',
        '0', '0', ':', '0', '0', ':', '0', '0',
        'Z',
    }
    for _, f := range formats {
        r := lx.next()
        if f == '0' {
            if !isDigit(r) {
                return lx.errorf("Expected digit in ISO8601 datetime, "+
                    "but found %q instead.", r)
            }
        } else if f != r {
            return lx.errorf("Expected %q in ISO8601 datetime, "+
                "but found %q instead.", f, r)
        }
    }
    lx.emit(itemDatetime)
    return lx.pop()
}

Functions end without a return statement

Not sure if these are warnings or errors, but when downloading using "go get" I receive the following:

# github.com/BurntSushi/toml
../go/src/github.com/BurntSushi/toml/encode.go:248: function ends without a return statement
../go/src/github.com/BurntSushi/toml/encode.go:276: function ends without a return statement
../go/src/github.com/BurntSushi/toml/encode.go:421: function ends without a return statement
../go/src/github.com/BurntSushi/toml/encode.go:452: function ends without a return statement
../go/src/github.com/BurntSushi/toml/encode.go:490: function ends without a return statement
../go/src/github.com/BurntSushi/toml/lex.go:71: function ends without a return statement

Looking at a few of the above functions, it looks like it is typically caused by a function returning values within a switch, with the default case being last. I think it can be worked around by moving the logic for the default case outside of the switch statement.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.