Giter VIP home page Giter VIP logo

toml's Introduction

TOML parser and encoder library for Golang Build Status

TOML parser and encoder library for Golang.

This library is compatible with TOML version v0.4.0.

Installation

go get -u github.com/naoina/toml

Usage

The following TOML save as example.toml.

# This is a TOML document. Boom.

title = "TOML Example"

[owner]
name = "Lance Uppercut"
dob = 1979-05-27T07:32:00-08:00 # 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] ]

# Line breaks are OK when inside arrays
hosts = [
  "alpha",
  "omega"
]

Then above TOML will mapping to tomlConfig struct using toml.Unmarshal.

package main

import (
    "os"
    "time"

    "github.com/naoina/toml"
)

type tomlConfig struct {
    Title string
    Owner struct {
        Name string
        Dob  time.Time
    }
    Database struct {
        Server        string
        Ports         []int
        ConnectionMax uint
        Enabled       bool
    }
    Servers map[string]ServerInfo
    Clients struct {
        Data  [][]interface{}
        Hosts []string
    }
}

type ServerInfo struct {
    IP net.IP
    DC string
}

func main() {
    f, err := os.Open("example.toml")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    var config tomlConfig
    if err := toml.NewDecoder(f).Decode(&config); err != nil {
        panic(err)
    }

    // then to use the unmarshaled config...
    fmt.Println("IP of server 'alpha':", config.Servers["alpha"].IP)
}

Mappings

A key and value of TOML will map to the corresponding field. The fields of struct for mapping must be exported.

The rules of the mapping of key are following:

Exact matching

timeout_seconds = 256
type Config struct {
	Timeout_seconds int
}

Camelcase matching

server_name = "srv1"
type Config struct {
	ServerName string
}

Uppercase matching

ip = "10.0.0.1"
type Config struct {
	IP string
}

See the following examples for the value mappings.

String

val = "string"
type Config struct {
	Val string
}

Integer

val = 100
type Config struct {
	Val int
}

All types that can be used are following:

  • int8 (from -128 to 127)
  • int16 (from -32768 to 32767)
  • int32 (from -2147483648 to 2147483647)
  • int64 (from -9223372036854775808 to 9223372036854775807)
  • int (same as int32 on 32bit environment, or int64 on 64bit environment)
  • uint8 (from 0 to 255)
  • uint16 (from 0 to 65535)
  • uint32 (from 0 to 4294967295)
  • uint64 (from 0 to 18446744073709551615)
  • uint (same as uint32 on 32bit environment, or uint64 on 64bit environment)

Float

val = 3.1415
type Config struct {
	Val float32
}

All types that can be used are following:

  • float32
  • float64

Boolean

val = true
type Config struct {
	Val bool
}

Datetime

val = 2014-09-28T21:27:39Z
type Config struct {
	Val time.Time
}

Array

val = ["a", "b", "c"]
type Config struct {
	Val []string
}

Also following examples all can be mapped:

val1 = [1, 2, 3]
val2 = [["a", "b"], ["c", "d"]]
val3 = [[1, 2, 3], ["a", "b", "c"]]
val4 = [[1, 2, 3], [["a", "b"], [true, false]]]
type Config struct {
	Val1 []int
	Val2 [][]string
	Val3 [][]interface{}
	Val4 [][]interface{}
}

Table

[server]
type = "app"

  [server.development]
  ip = "10.0.0.1"

  [server.production]
  ip = "10.0.0.2"
type Config struct {
	Server map[string]Server
}

type Server struct {
	IP string
}

You can also use the following struct instead of map of struct.

type Config struct {
	Server struct {
		Development Server
		Production Server
	}
}

type Server struct {
	IP string
}

Array of Tables

[[fruit]]
  name = "apple"

  [fruit.physical]
    color = "red"
    shape = "round"

  [[fruit.variety]]
    name = "red delicious"

  [[fruit.variety]]
    name = "granny smith"

[[fruit]]
  name = "banana"

  [[fruit.variety]]
    name = "plantain"
type Config struct {
	Fruit []struct {
		Name string
		Physical struct {
			Color string
			Shape string
		}
		Variety []struct {
			Name string
		}
	}
}

Using the encoding.TextUnmarshaler interface

Package toml supports encoding.TextUnmarshaler (and encoding.TextMarshaler). You can use it to apply custom marshaling rules for certain types. The UnmarshalText method is called with the value text found in the TOML input. TOML strings are passed unquoted.

duration = "10s"
import time

type Duration time.Duration

// UnmarshalText implements encoding.TextUnmarshaler
func (d *Duration) UnmarshalText(data []byte) error {
    duration, err := time.ParseDuration(string(data))
    if err == nil {
        *d = Duration(duration)
    }
    return err
}

// MarshalText implements encoding.TextMarshaler
func (d Duration) MarshalText() ([]byte, error) {
    return []byte(time.Duration(d).String()), nil
}

type ConfigWithDuration struct {
    Duration Duration
}

Using the toml.UnmarshalerRec interface

You can also override marshaling rules specifically for TOML using the UnmarshalerRec and MarshalerRec interfaces. These are useful if you want to control how structs or arrays are handled. You can apply additional validation or set unexported struct fields.

Note: encoding.TextUnmarshaler and encoding.TextMarshaler should be preferred for simple (scalar) values because they're also compatible with other formats like JSON or YAML.

See the UnmarshalerRec example.

Using the toml.Unmarshaler interface

If you want to deal with raw TOML syntax, use the Unmarshaler and Marshaler interfaces. Their input and output is raw TOML syntax. As such, these interfaces are useful if you want to handle TOML at the syntax level.

See the Unmarshaler example.

API documentation

See Godoc.

License

MIT

toml's People

Contributors

aviau avatar corpix avatar evanphx avatar fjl avatar genofire avatar kestred avatar kynyka avatar meetme2meat avatar mind1949 avatar naoina avatar timraymond 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

toml's Issues

How modifying a key?

Does the library incorporate the possibility of modifying a key?
Eg:
[server]
addr = "127.0.0.1"
port = 8080
Update server set addr = ":: 1"
Update server set port = 3000

Given

[server]
addr = "::1"
port = 3000

Global tables create conflict with subtables

I'm getting an error parsing the [tags] sections in valid toml that looks like this:

# Global tags
[tags]
  shipment = "2a"

# Create list of tables with subtables

[[fruits.apple]]
  type = "red delicious"
  [fruits.apple.tags]
    PLU = 4555

[[fruits.apple]]
  type = "granny smith"
  [fruits.apple.tags]
    PLU = 4556

If I remove the global [tags] heading, or if I only set [fruits.apple.tags] once, the parsing works.

Below are some scripts for reproducing:

package main

import (
    "fmt"

    "github.com/naoina/toml"
)

// Global tags cause failure
const fruits = `
# Global tags
[tags]
  shipment = "2a"

# Create list of tables with subtables

[[fruits.apple]]
  type = "red delicious"
  [fruits.apple.tags]
    PLU = 4555

[[fruits.apple]]
  type = "granny smith"
  [fruits.apple.tags]
    PLU = 4556
`

func main() {
    _, err := toml.Parse([]byte(fruits))
    if err != nil {
        fmt.Println(err.Error())
        return
    }
}

it outputs:

% go run /tmp/config.go
toml: line 15: table `fruits.apple.tags' is in conflict with normal table in line 10

but if I take out the global [tags] table, it works:

package main

import (
    "fmt"

    "github.com/naoina/toml"
)

// This one works!
const fruits = `
# Create list of tables with subtables

[[fruits.apple]]
  type = "red delicious"
  [fruits.apple.tags]
    PLU = 4555

[[fruits.apple]]
  type = "granny smith"
  [fruits.apple.tags]
    PLU = 4556
`

func main() {
    _, err := toml.Parse([]byte(fruits))
    if err != nil {
        fmt.Println(err.Error())
        return
    }
}

or if I remove one of the [fruits.apple.tags] sections, it also parses OK:

package main

import (
    "fmt"

    "github.com/naoina/toml"
)

// This one works!
const fruits = `
# Create list of tables with subtables

[[fruits.apple]]
  type = "red delicious"

[[fruits.apple]]
  type = "granny smith"
  [fruits.apple.tags]
    PLU = 4556
`

func main() {
    _, err := toml.Parse([]byte(fruits))
    if err != nil {
        fmt.Println(err.Error())
        return
    }
}

Support AST manipulation

Say I wanted to load a TOML file from a program, change a value, and write it back to disk.

The current configuration makes that difficult. Say I wanted to change the value in testdata/example.toml. I would call toml.Parse() and eventually get an ast.Value, but there's no way to modify that, and updating the Value would not update the ast.Table.Data. In addition, it doesn't seem like there's a way to convert an *ast.Table back into a []byte.

Marshal doesn't handle multiple struct tag fields properly

If you have multiple struct field tags, and the first one isn't "toml" then it's ignored and the default logic kicks in for naming the fields in the serialized output.

Example:

package main

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

type cfg struct {
        MySetting string `json:"JSONMySetting",toml:"toml_my_setting"` // Does not work
        //MySetting string `toml:"toml_my_setting",json:"JSONMySetting"` // Works
}

func main() {
        c1 := cfg{"foo"}

        data, err := toml.Marshal(&c1)
        if err != nil {
                panic(err)
        }
        fmt.Printf("Serialized: %s", string(data))

        var c2 cfg
                
        if err := toml.Unmarshal(data, &c2); err != nil {
                panic(err)
        }
        fmt.Printf("Unmarshaled: %#v\n", c2)
}

Expected output:

Serialized: toml_my_setting="foo"
Unmarshaled: main.cfg{MySetting:"foo"}

Actual output:

Serialized: my_setting="foo"
Unmarshaled: main.cfg{MySetting:"foo"}

please tag and version this project

Hello,

Can you please tag and version this project?

I am the Debian Maintainer for toml and versioning would help Debian keep up with development.

unmarshalTOML function not called

Hello,
Thanks for your great library.

I would like to setup default values for my structure

// A Row is a dashboard Row it can contains multiple panels
type Row struct {
    Collapse bool    `json:"collapse"`
    Editable bool    `json:"editable"`
    Height   string  `json:"height"`
    Panels   []Panel `json:"panels" toml:"panel"`
    Title    string  `json:"title"`
}

So I defined this method :

func (row *Row) UnmarshalTOML(b interface{}) error {
    fmt.Printf("toto\n") //for debug

    r := NewRow()
    err := toml.Unmarshal(b.([]byte), &r)
    *row = r
    return err
}

But the method is not called when I do a toml.Unmarshal call.

Do you have any ideas about what I could do wrong here ?

Thanks,

Alain

Serialized struct cannot be unserialized when Using camel case

test code

package main

import (
    "log"

    "github.com/naoina/toml"
)

type Foo struct {
    APIKey string
}

func main() {
    foo := Foo{
        "hello world",
    }

    data, err := toml.Marshal(foo)
    if err != nil {
        log.Fatal(err)
    }

    bar := Foo{}
    if err := toml.Unmarshal(data, &bar); err != nil {
        log.Fatal(err)
    }

}

result

go run /tmp/test.go
2016/10/03 22:15:19 toml: unmarshal: line 1: field corresponding to `api_key' is not defined in `*main.Foo'
exit status 1

Ignore additional fields

My usecase is having a config file for multiple projects, and one project only uses a part of the structure of the full config file. The problem is that Unmarshal() fails if there are fields in the config file that are not defined in my struct.

Could this library ignore fields from the file that are not available in the target struct like (this is how for example encoding/json would handle this)?

Thanks

[Servers] should be a map, not a struct

Hi,

I really like your toml parser very much, as it is really simple to use.
I have just this issue that bothers me:

In https://github.com/toml-lang/toml, it is stated:

"Tables (also known as hash tables or dictionaries) are collections of key/value pairs."

Many times, the keys of the map can be represented as fields in a struct.

type tomlConfig struct {
    ...
    Servers struct {  <------ in your example, Servers is a struct
        Alpha Server
        Beta  Server
    }
    ...
}

type Server struct {
    IP string
    DC string
}

You consider that "Servers" is a struct, with "Alpha" and "Beta" as field names.

But in this case, how do you add new servers in the TOML config files ?
If you add more servers, like [servers.gamma], it means that you must change the definition of the struct in your program, and you must know the names of all servers beforehand:

type tomlConfig struct {
    ...
    Servers struct {
        Alpha Server
        Beta  Server
        Gamma Server
    }
    ...
}

I think that "Alpha" and "Beta" are not field names of a struct, but keys of a map.

type tomlConfig struct {
    ...
    Servers map[string]Server     <-------- I think that Servers should be a map, and not a struct
    ...
}

type Server struct {
    IP string
    DC string
}

It would be good if the parser could write in map, as well as in struct.

What is your optinion ?

cannot unmarshal a marshalled string

depending on the struct ordering, unmarshalling after a marshal does not work, for example
this works fine:

type appcfg struct {
  Debug  bool     `toml:"debug"`
  Admins []string `toml:"admins"`
  API   struct {
    URL       string `toml:"URL"`
    AuthToken string `toml:"authtoken"`
  }
}

this does not:

type appcfg struct {
  API   struct {
    URL       string `toml:"URL"`
    AuthToken string `toml:"authtoken"`
  }
  Debug  bool     `toml:"debug"`
  Admins []string `toml:"admins"`
}

Parsing Inline Tables

I'm trying to use TOML Inline tables and running into an issue. Given the following program:

package main

import (
    "fmt"

    "github.com/naoina/toml"
)

const good = `
[heading]
key1 = "value1"

[[heading.array]]
key2 = "value2"

[[heading.array]]
key2 = "value3"
  [heading.array.map]
  key3 = "value4"

[[heading.array]]
key2 = "value5"
  [heading.array.map]
  key3 = "value6"
`

const bad = `
[heading]
key1 = "value1"

[[heading.array]]
key2 = "value2"

[[heading.array]]
key2 = "value3"
map = { key3 = "value4" }

[[heading.array]]
key2 = "value5"
map = { key3 = "value6" }
`

func main() {
    if _, err := toml.Parse([]byte(good)); err != nil {
        fmt.Println(err)
    }
    if _, err := toml.Parse([]byte(bad)); err != nil {
        fmt.Println(err)
    }
}

Yields this error:

toml: line 14: table 'map' is in conflict with normal table in line 10

In the bad config, why does the second definition of map interfere with the first one? I can change the bad config to the following and it works fine:

const bad = `
[heading]
key1 = "value1"

[[heading.array]]
key2 = "value2"

[[heading.array]]
key2 = "value3"
map = { key3 = "value4" }

[[heading.array]]
key2 = "value5"
  [heading.array.map]
  key3 = "value6"
`

Wrong lines counting on Windows

Altough there are other problems on Windows resulting mainly from line ending difference (see the tests result bellow) one specific annoying issue is wrong lines counting occurring mainly in error strings, like:

       got:  line 15: key `b' is in conflict with table in line 7
       want: line 8: key `b' is in conflict with table in line 4

All tests passes on linux. But on Windows:

=== RUN   TestConfigNormField
--- PASS: TestConfigNormField (0.00s)
=== RUN   TestConfigFieldToKey
--- PASS: TestConfigFieldToKey (0.00s)
=== RUN   TestConfigMissingField
--- PASS: TestConfigMissingField (0.00s)
=== RUN   TestUnmarshal
--- FAIL: TestUnmarshal (0.00s)
    decode_test.go:321: Unmarshal value mismatch for input:
        # test.toml
        
        ################################################################################
        ## Comment
        
        # Speak your mind with the hash symbol. They go from the symbol to the end of
        # the line.
        
        
        ################################################################################
        ## Table
        
        # Tables (also known as hash tables or dictionaries) are collections of
        # key/value pairs. They appear in square brackets on a line by themselves.
        
        [table]
        
        key = "value" # Yeah, you can do this.
        
        # Nested tables are denoted by table names with dots in them. Name your tables
        # whatever crap you please, just don't use #, ., [ or ].
        
        [table.subtable]
        
        key = "another value"
        
        # You don't need to specify all the super-tables if you don't want to. TOML
        # knows how to do it for you.
        
        # [x] you
        # [x.y] don't
        # [x.y.z] need these
        [x.y.z.w] # for this to work
        
        
        ################################################################################
        ## Inline Table
        
        # Inline tables provide a more compact syntax for expressing tables. They are
        # especially useful for grouped data that can otherwise quickly become verbose.
        # Inline tables are enclosed in curly braces `{` and `}`. No newlines are
        # allowed between the curly braces unless they are valid within a value.
        
        [table.inline]
        
        name = { first = "Tom", last = "Preston-Werner" }
        point = { x = 1, y = 2 }
        
        
        ################################################################################
        ## String
        
        # There are four ways to express strings: basic, multi-line basic, literal, and
        # multi-line literal. All strings must contain only valid UTF-8 characters.
        
        [string.basic]
        
        basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
        
        [string.multiline]
        
        # The following strings are byte-for-byte equivalent:
        key1 = "One\nTwo"
        key2 = """One\nTwo"""
        key3 = """
        One
        Two"""
        
        [string.multiline.continued]
        
        # The following strings are byte-for-byte equivalent:
        key1 = "The quick brown fox jumps over the lazy dog."
        
        key2 = """
        The quick brown \
        
        
          fox jumps over \
            the lazy dog."""
        
        key3 = """\
               The quick brown \
               fox jumps over \
               the lazy dog.\
               """
        
        [string.literal]
        
        # What you see is what you get.
        winpath  = 'C:\Users\nodejs\templates'
        winpath2 = '\\ServerX\admin$\system32\'
        quoted   = 'Tom "Dubs" Preston-Werner'
        regex    = '<\i\c*\s*>'
        
        
        [string.literal.multiline]
        
        regex2 = '''I [dw]on't need \d{2} apples'''
        lines  = '''
        The first newline is
        trimmed in raw strings.
           All other whitespace
           is preserved.
        '''
        
        
        ################################################################################
        ## Integer
        
        # Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
        # Negative numbers are prefixed with a minus sign.
        
        [integer]
        
        key1 = +99
        key2 = 42
        key3 = 0
        key4 = -17
        
        [integer.underscores]
        
        # For large numbers, you may use underscores to enhance readability. Each
        # underscore must be surrounded by at least one digit.
        key1 = 1_000
        key2 = 5_349_221
        key3 = 1_2_3_4_5     # valid but inadvisable
        
        
        ################################################################################
        ## Float
        
        # A float consists of an integer part (which may be prefixed with a plus or
        # minus sign) followed by a fractional part and/or an exponent part.
        
        [float.fractional]
        
        key1 = +1.0
        key2 = 3.1415
        key3 = -0.01
        
        [float.exponent]
        
        key1 = 5e+22
        key2 = 1e6
        key3 = -2E-2
        
        [float.both]
        
        key = 6.626e-34
        
        [float.underscores]
        
        key1 = 9_224_617.445_991_228_313
        key2 = 1e1_00
        
        
        ################################################################################
        ## Boolean
        
        # Booleans are just the tokens you're used to. Always lowercase.
        
        [boolean]
        
        True = true
        False = false
        
        
        ################################################################################
        ## Datetime
        
        # Datetimes are RFC 3339 dates.
        
        [datetime]
        
        key1 = 1979-05-27T07:32:00Z
        key2 = 1979-05-27T00:32:00-07:00
        key3 = 1979-05-27T00:32:00.999999-07:00
        
        
        ################################################################################
        ## Array
        
        # Arrays are square brackets with other primitives inside. Whitespace is
        # ignored. Elements are separated by commas. Data types may not be mixed.
        
        [array]
        
        key1 = [ 1, 2, 3 ]
        key2 = [ "red", "yellow", "green" ]
        key3 = [ [ 1, 2 ], [3, 4, 5] ]
        key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
        
        # Arrays can also be multiline. So in addition to ignoring whitespace, arrays
        # also ignore newlines between the brackets.  Terminating commas are ok before
        # the closing bracket.
        
        key5 = [
          1, 2, 3
        ]
        key6 = [
          1,
          2, # this is ok
        ]
        
        
        ################################################################################
        ## Array of Tables
        
        # These can be expressed by using a table name in double brackets. Each table
        # with the same double bracketed name will be an element in the array. The
        # tables are inserted in the order encountered.
        
        [[products]]
        
        name = "Hammer"
        sku = 738594937
        
        [[products]]
        
        [[products]]
        
        name = "Nail"
        sku = 284758393
        color = "gray"
        
        
        # You can create nested arrays of tables as well.
        
        [[fruit]]
          name = "apple"
        
          [fruit.physical]
            color = "red"
            shape = "round"
        
          [[fruit.variety]]
            name = "red delicious"
        
          [[fruit.variety]]
            name = "granny smith"
        
        [[fruit]]
          name = "banana"
        
          [[fruit.variety]]
            name = "plantain"
        
        diff:
         {
          Table: {
           Key: "value",
           Subtable: {
            Key: "another value",
           },
           Inline: {
            Name: {
             First: "Tom",
             Last: "Preston-Werner",
            },
            Point: {
             x: 1,
             y: 2,
            },
           },
          },
          X: {
           Y: {
            Z: {
             W: {
             },
            },
           },
          },
          String: {
           Basic: {
            Basic: "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF.",
           },
           Multiline: {
            Key1: "One\nTwo",
            Key2: "One\nTwo",
        -   Key3: "One\r\nTwo",
        +   Key3: "One\nTwo",
            Continued: {
             Key1: "The quick brown fox jumps over the lazy dog.",
             Key2: "The quick brown fox jumps over the lazy dog.",
             Key3: "The quick brown fox jumps over the lazy dog.",
            },
           },
           Literal: {
            Winpath: "C:\\Users\\nodejs\\templates",
            Winpath2: "\\\\ServerX\\admin$\\system32\\",
            Quoted: "Tom \"Dubs\" Preston-Werner",
            Regex: "<\\i\\c*\\s*>",
            Multiline: {
             Regex2: "I [dw]on't need \\d{2} apples",
        -    Lines: "The first newline is\r\ntrimmed in raw strings.\r\n   All other whitespace\r\n   is preserved.\r\n",
        +    Lines: "The first newline is\ntrimmed in raw strings.\n   All other whitespace\n   is preserved.\n",
            },
           },
          },
          Integer: {
           Key1: 99,
           Key2: 42,
           Key3: 0,
           Key4: -17,
           Underscores: {
            Key1: 1000,
            Key2: 5349221,
            Key3: 12345,
           },
          },
          Float: {
           Fractional: {
            Key1: 1,
            Key2: 3.1415,
            Key3: -0.01,
           },
           Exponent: {
            Key1: 5e+22,
            Key2: 1e+06,
            Key3: -0.02,
           },
           Both: {
            Key: 6.626e-34,
           },
           Underscores: {
            Key1: 9.224617445991227e+06,
            Key2: 1e+100,
           },
          },
          Boolean: {
           True: true,
           False: false,
          },
          Datetime: {
           Key1: 1979-05-27 07:32:00 +0000 UTC,
           Key2: 1979-05-27 00:32:00 -0700 -0700,
           Key3: 1979-05-27 00:32:00.999999 -0700 -0700,
          },
          Array: {
           Key1: [1,2,3],
           Key2: ["red","yellow","green"],
           Key3: [[1,2],[3,4,5]],
           Key4: [[1,2],["a","b","c"]],
           Key5: [1,2,3],
           Key6: [1,2],
          },
          Products: [
           {
            Name: "Hammer",
            Sku: 738594937,
            Color: "",
           },
           {
            Name: "",
            Sku: 0,
            Color: "",
           },
           {
            Name: "Nail",
            Sku: 284758393,
            Color: "gray",
           },
          ],
          Fruit: [
           {
            Name: "apple",
            Physical: {
             Color: "red",
             Shape: "round",
            },
            Variety: [{Name:"red delicious"},{Name:"granny smith"}],
           },
           {
            Name: "banana",
            Physical: {
             Color: "",
             Shape: "",
            },
            Variety: [{Name:"plantain"}],
           },
          ],
         }
=== RUN   TestUnmarshal_WithString
--- FAIL: TestUnmarshal_WithString (0.00s)
    decode_test.go:321: Unmarshal value mismatch for input:
        # unmarshal-string-1.toml
        
        key1 = "One\nTwo"
        key2 = """One\nTwo"""
        key3 = """
        One
        Two"""
        
        diff:
         {
          Str: "",
          Key1: "One\nTwo",
          Key2: "One\nTwo",
        - Key3: "One\r\nTwo",
        + Key3: "One\nTwo",
          Winpath: "",
          Winpath2: "",
          Quoted: "",
          Regex: "",
          Regex2: "",
          Lines: "",
         }
    decode_test.go:321: Unmarshal value mismatch for input:
        # unmarshal-string-2.toml
        
        regex2 = '''I [dw]on't need \d{2} apples'''
        lines  = '''
        The first newline is
        trimmed in raw strings.
           All other whitespace
           is preserved.
        '''
        diff:
         {
          Str: "",
          Key1: "",
          Key2: "",
          Key3: "",
          Winpath: "",
          Winpath2: "",
          Quoted: "",
          Regex: "",
          Regex2: "I [dw]on't need \\d{2} apples",
        - Lines: "The first newline is\r\ntrimmed in raw strings.\r\n   All other whitespace\r\n   is preserved.\r\n",
        + Lines: "The first newline is\ntrimmed in raw strings.\n   All other whitespace\n   is preserved.\n",
         }
=== RUN   TestUnmarshal_WithInteger
--- PASS: TestUnmarshal_WithInteger (0.00s)
=== RUN   TestUnmarshal_WithFloat
--- PASS: TestUnmarshal_WithFloat (0.00s)
=== RUN   TestUnmarshal_WithBoolean
--- PASS: TestUnmarshal_WithBoolean (0.00s)
=== RUN   TestUnmarshal_WithDatetime
--- PASS: TestUnmarshal_WithDatetime (0.00s)
=== RUN   TestUnmarshal_WithArray
--- PASS: TestUnmarshal_WithArray (0.00s)
=== RUN   TestUnmarshal_WithTable
--- FAIL: TestUnmarshal_WithTable (0.00s)
    decode_test.go:318: Error mismatch for input:
        # unmarshal-table-conflict-1.toml
        # Table a is defined twice.
        
        [a]
        b = 1
        
        [a]
        c = 2
        
        got:  line 13: table `a' is in conflict with table in line 7
        want: line 7: table `a' is in conflict with table in line 4
    decode_test.go:318: Error mismatch for input:
        # unmarshal-table-conflict-2.toml
        # Key b conflicts with table a.b.
        
        [a]
        b = 1
        
        [a.b]
        c = 2
        
        got:  line 13: table `a.b' is in conflict with line 9
        want: line 7: table `a.b' is in conflict with line 5
    decode_test.go:318: Error mismatch for input:
        # unmarshal-table-conflict-3.toml
        # Key b conflicts with table a.b
        
        [a.b]
        c = 2
        
        [a]
        b = 1
        
        got:  line 15: key `b' is in conflict with table in line 7
        want: line 8: key `b' is in conflict with table in line 4
=== RUN   TestUnmarshal_WithEmbeddedStruct
--- PASS: TestUnmarshal_WithEmbeddedStruct (0.00s)
=== RUN   TestUnmarshal_WithArrayTable
--- FAIL: TestUnmarshal_WithArrayTable (0.00s)
    decode_test.go:318: Error mismatch for input:
        # unmarshal-arraytable-conflict-1.toml
        
        [[fruit]]
        name = "apple"
        
        [[fruit.variety]]
        name = "red delicious"
        
        # This table conflicts with the previous table.
        [fruit.variety]
        name = "granny smith"
        
        got:  line 19: table `fruit.variety' is in conflict with array table in line 11
        want: line 10: table `fruit.variety' is in conflict with array table in line 6
    decode_test.go:318: Error mismatch for input:
        # unmarshal-arraytable-conflict-2.toml
        
        [[fruit]]
        name = "apple"
        
        [fruit.variety]
        name = "granny smith"
        
        # This table conflicts with the previous table.
        [[fruit.variety]]
        name = "red delicious"
        
        got:  line 19: array table `fruit.variety' is in conflict with table in line 11
        want: line 10: array table `fruit.variety' is in conflict with table in line 6
    decode_test.go:318: Error mismatch for input:
        # unmarshal-arraytable-conflict-3.toml
        
        [[fruit]]
        name = "apple"
        variety = { name = "granny smith" }
        
        # conflicts with inline table above
        [[fruit.variety]]
        
        got:  line 15: array table `fruit.variety' is in conflict with table in line 9
        want: line 8: array table `fruit.variety' is in conflict with table in line 5
=== RUN   TestUnmarshal_WithUnmarshaler
--- FAIL: TestUnmarshal_WithUnmarshaler (0.00s)
    decode_test.go:998: toml.Unmarshal(data, &v); v => toml.testStruct{Title:"Unmarshaled: \"testtitle\"", MaxConn:"Unmarshaled: 777", Ports:"Unmarshaled: [8080, 8081, 8082]", Servers:"Unmarshaled: [1, 2, 3]", Table:"Unmarshaled: [table]\r\nname = \"alice\"", Arraytable:"Unmarshaled: [[arraytable]]\r\nname = \"alice\"\n[[arraytable]]\r\nname = \"bob\"", ArrayOfStruct:[]toml.testUnmarshalerStruct{toml.testUnmarshalerStruct{Title:"Unmarshaled: [[array_of_struct]]\r\ntitle = \"Alice's Adventures in Wonderland\"\r\nauthor = \"Lewis Carroll\"", Author:""}}}; want toml.testStruct{Title:"Unmarshaled: \"testtitle\"", MaxConn:"Unmarshaled: 777", Ports:"Unmarshaled: [8080, 8081, 8082]", Servers:"Unmarshaled: [1, 2, 3]", Table:"Unmarshaled: [table]\nname = \"alice\"", Arraytable:"Unmarshaled: [[arraytable]]\nname = \"alice\"\n[[arraytable]]\nname = \"bob\"", ArrayOfStruct:[]toml.testUnmarshalerStruct{toml.testUnmarshalerStruct{Title:"Unmarshaled: [[array_of_struct]]\ntitle = \"Alice's Adventures in Wonderland\"\nauthor = \"Lewis Carroll\"", Author:""}}}
=== RUN   TestUnmarshal_WithUnmarshalerForTopLevelStruct
--- PASS: TestUnmarshal_WithUnmarshalerForTopLevelStruct (0.00s)
=== RUN   TestUnmarshal_WithTextUnmarshaler
--- PASS: TestUnmarshal_WithTextUnmarshaler (0.00s)
=== RUN   TestUnmarshal_WithUnmarshalerRec
--- PASS: TestUnmarshal_WithUnmarshalerRec (0.00s)
=== RUN   TestUnmarshal_WithMultibyteString
--- PASS: TestUnmarshal_WithMultibyteString (0.00s)
=== RUN   TestUnmarshal_WithPointers
--- PASS: TestUnmarshal_WithPointers (0.00s)
=== RUN   TestUnmarshalMap
--- PASS: TestUnmarshalMap (0.00s)
=== RUN   TestUnmarshal_WithQuotedKeyValue
--- PASS: TestUnmarshal_WithQuotedKeyValue (0.00s)
=== RUN   TestUnmarshal_WithCustomPrimitiveType
--- PASS: TestUnmarshal_WithCustomPrimitiveType (0.00s)
=== RUN   TestUnmarshal_WithInterface
--- PASS: TestUnmarshal_WithInterface (0.00s)
=== RUN   TestMarshal
--- FAIL: TestMarshal (0.00s)
    encode_test.go:185: Output mismatch:
        Value:
        {Table:    {Key:      "value",
                    Subtable: {Key: "another value"},
                    Inline:   {Name:  {First: "Tom",
                                       Last:  "Preston-Werner"},
                               Point: {x: 1,
                                       y: 2}}},
         X:        {Y: {Z: {W: {}}}},
         String:   {Basic:     {Basic: "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."},
                    Multiline: {Key1:      "One\nTwo",
                                Key2:      "One\nTwo",
                                Key3:      "One\nTwo",
                                Continued: {Key1: "The quick brown fox jumps over the lazy dog.",
                                            Key2: "The quick brown fox jumps over the lazy dog.",
                                            Key3: "The quick brown fox jumps over the lazy dog."}},
                    Literal:   {Winpath:   "C:\\Users\\nodejs\\templates",
                                Winpath2:  "\\\\ServerX\\admin$\\system32\\",
                                Quoted:    "Tom \"Dubs\" Preston-Werner",
                                Regex:     "<\\i\\c*\\s*>",
                                Multiline: {Regex2: "I [dw]on't need \\d{2} apples",
                                            Lines:  "The first newline is\ntrimmed in raw strings.\n   All other whitespace\n   is preserved.\n"}}},
         Integer:  {Key1:        99,
                    Key2:        42,
                    Key3:        0,
                    Key4:        -17,
                    Underscores: {Key1: 1000,
                                  Key2: 5349221,
                                  Key3: 12345}},
         Float:    {Fractional:  {Key1: 1,
                                  Key2: 3.1415,
                                  Key3: -0.01},
                    Exponent:    {Key1: 5e+22,
                                  Key2: 1e+06,
                                  Key3: -0.02},
                    Both:        {Key: 6.626e-34},
                    Underscores: {Key1: 9.224617445991227e+06,
                                  Key2: 1e+100}},
         Boolean:  {True:  true,
                    False: false},
         Datetime: {Key1: 1979-05-27 07:32:00 +0000 UTC,
                    Key2: 1979-05-27 00:32:00 -0700 -0700,
                    Key3: 1979-05-27 00:32:00.999999 -0700 -0700},
         Array:    {Key1: [1,2,3],
                    Key2: ["red","yellow","green"],
                    Key3: [[1,2],[3,4,5]],
                    Key4: [[1,2],["a","b","c"]],
                    Key5: [1,2,3],
                    Key6: [1,2]},
         Products: [{Name:  "Hammer",
                     Sku:   738594937,
                     Color: ""},
                    {Name:  "",
                     Sku:   0,
                     Color: ""},
                    {Name:  "Nail",
                     Sku:   284758393,
                     Color: "gray"}],
         Fruit:    [{Name:     "apple",
                     Physical: {Color: "red",
                                Shape: "round"},
                     Variety:  [{Name:"red delicious"},{Name:"granny smith"}]},
                    {Name:     "banana",
                     Physical: {Color: "",
                                Shape: ""},
                     Variety:  [{Name:"plantain"}]}]}
        Diff:
        -[table]
        -key = "value"
        +[table]
        +key = "value"
        +
        +[table.subtable]
        +key = "another value"
        +
        +[table.inline.name]
        +first = "Tom"
        +last = "Preston-Werner"
        +
        +[table.inline.point]
        +x = 1
        +y = 2
        +
        +[string.basic]
        +basic = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."
        +
        +[string.multiline]
        +key1 = "One\nTwo"
        +key2 = "One\nTwo"
        +key3 = "One\nTwo"
        +
        +[string.multiline.continued]
        +key1 = "The quick brown fox jumps over the lazy dog."
        +key2 = "The quick brown fox jumps over the lazy dog."
        +key3 = "The quick brown fox jumps over the lazy dog."
        +
        +[string.literal]
        +winpath = "C:\\Users\\nodejs\\templates"
        +winpath2 = "\\\\ServerX\\admin$\\system32\\"
        +quoted = "Tom \"Dubs\" Preston-Werner"
        +regex = "<\\i\\c*\\s*>"
        +
        +[string.literal.multiline]
        +regex2 = "I [dw]on't need \\d{2} apples"
        +lines = "The first newline is\ntrimmed in raw strings.\n   All other whitespace\n   is preserved.\n"
        +
        +[integer]
        +key1 = 99
        +key2 = 42
        +key3 = 0
        +key4 = -17
        +
        +[integer.underscores]
        +key1 = 1000
        +key2 = 5349221
        +key3 = 12345
        +
        +[float.fractional]
        +key1 = 1e+00
        +key2 = 3.1415e+00
        +key3 = -1e-02
        +
        +[float.exponent]
        +key1 = 5e+22
        +key2 = 1e+06
        +key3 = -2e-02
        +
        +[float.both]
        +key = 6.626e-34
        +
        +[float.underscores]
        +key1 = 9.224617445991227e+06
        +key2 = 1e+100
        +
        +[boolean]
        +true = true
        +false = false
        +
        +[datetime]
        +key1 = 1979-05-27T07:32:00Z
        +key2 = 1979-05-27T00:32:00-07:00
        +key3 = 1979-05-27T00:32:00.999999-07:00
        +
        +[array]
        +key1 = [1, 2, 3]
        +key2 = ["red", "yellow", "green"]
        +key3 = [[1, 2], [3, 4, 5]]
        +key4 = [[1, 2], ["a", "b", "c"]]
        +key5 = [1, 2, 3]
        +key6 = [1, 2]
        +
        +[[products]]
        +name = "Hammer"
        +sku = 738594937
        +
        +[[products]]
        +
        +[[products]]
        +name = "Nail"
        +sku = 284758393
        +color = "gray"
        +
        +[[fruit]]
        +name = "apple"
        +
        +[fruit.physical]
        +color = "red"
        +shape = "round"
        +
        +[[fruit.variety]]
        +name = "red delicious"
        +
        +[[fruit.variety]]
        +name = "granny smith"
        +
        +[[fruit]]
        +name = "banana"
        +
        +[fruit.physical]
        +color = ""
        +shape = ""
        +
        +[[fruit.variety]]
        +name = "plantain"
         
        -[table.subtable]
        -key = "another value"
        -
        -[table.inline.name]
        -first = "Tom"
        -last = "Preston-Werner"
        -
        -[table.inline.point]
        -x = 1
        -y = 2
        -
        -[string.basic]
        -basic = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."
        -
        -[string.multiline]
        -key1 = "One\nTwo"
        -key2 = "One\nTwo"
        -key3 = "One\nTwo"
        -
        -[string.multiline.continued]
        -key1 = "The quick brown fox jumps over the lazy dog."
        -key2 = "The quick brown fox jumps over the lazy dog."
        -key3 = "The quick brown fox jumps over the lazy dog."
        -
        -[string.literal]
        -winpath = "C:\\Users\\nodejs\\templates"
        -winpath2 = "\\\\ServerX\\admin$\\system32\\"
        -quoted = "Tom \"Dubs\" Preston-Werner"
        -regex = "<\\i\\c*\\s*>"
        -
        -[string.literal.multiline]
        -regex2 = "I [dw]on't need \\d{2} apples"
        -lines = "The first newline is\ntrimmed in raw strings.\n   All other whitespace\n   is preserved.\n"
        -
        -[integer]
        -key1 = 99
        -key2 = 42
        -key3 = 0
        -key4 = -17
        -
        -[integer.underscores]
        -key1 = 1000
        -key2 = 5349221
        -key3 = 12345
        -
        -[float.fractional]
        -key1 = 1e+00
        -key2 = 3.1415e+00
        -key3 = -1e-02
        -
        -[float.exponent]
        -key1 = 5e+22
        -key2 = 1e+06
        -key3 = -2e-02
        -
        -[float.both]
        -key = 6.626e-34
        -
        -[float.underscores]
        -key1 = 9.224617445991227e+06
        -key2 = 1e+100
        -
        -[boolean]
        -true = true
        -false = false
        -
        -[datetime]
        -key1 = 1979-05-27T07:32:00Z
        -key2 = 1979-05-27T00:32:00-07:00
        -key3 = 1979-05-27T00:32:00.999999-07:00
        -
        -[array]
        -key1 = [1, 2, 3]
        -key2 = ["red", "yellow", "green"]
        -key3 = [[1, 2], [3, 4, 5]]
        -key4 = [[1, 2], ["a", "b", "c"]]
        -key5 = [1, 2, 3]
        -key6 = [1, 2]
        -
        -[[products]]
        -name = "Hammer"
        -sku = 738594937
        -
        -[[products]]
        -
        -[[products]]
        -name = "Nail"
        -sku = 284758393
        -color = "gray"
        -
        -[[fruit]]
        -name = "apple"
        -
        -[fruit.physical]
        -color = "red"
        -shape = "round"
        -
        -[[fruit.variety]]
        -name = "red delicious"
        -
        -[[fruit.variety]]
        -name = "granny smith"
        -
        -[[fruit]]
        -name = "banana"
        -
        -[fruit.physical]
        -color = ""
        -shape = ""
        -
        -[[fruit.variety]]
        -name = "plantain"
        -
    encode_test.go:185: Output mismatch:
        Value:
        {intKeys:       {1: 1,
                         2: 2,
                         3: 3},
         marshalerKeys: {0001-01-01 00:00:00 +0000 UTC: 1}}
        Diff:
        -[intKeys]
        -1 = 1
        -2 = 2
        -3 = 3
        +[intKeys]
        +1 = 1
        +2 = 2
        +3 = 3
        +
        +[marshalerKeys]
        +"0001-01-01T00:00:00Z" = 1
         
        -[marshalerKeys]
        -"0001-01-01T00:00:00Z" = 1
        -
    encode_test.go:185: Output mismatch:
        Value:
        {m1: {S: "1"},
         m2: {S: "2"},
         m3: {S: "3"}}
        Diff:
        -m1 = 1
        -m2 = 2
        -m3 = 3
        +m1 = 1
        +m2 = 2
        +m3 = 3
         
    encode_test.go:185: Output mismatch:
        Value:
        {m1:  {},
         m2:  {},
         m3:  {},
         sub: {}}
        Diff:
        -m1 = 1
        -m2 = 2
        -m3 = 3
        +m1 = 1
        +m2 = 2
        +m3 = 3
        +
        +[sub]
        +key = 1
         
        -[sub]
        -key = 1
        -
    encode_test.go:185: Output mismatch:
        Value:
        {:                    "empty",
          :                   "space",
         -:                   "dash (not quoted)",
         1:                   "number (not quoted)",
         subtable with space: {depth:                  1,
                               subsubtable with space: {depth: 2}},
         ʎǝʞ:              "reverse"}
        Diff:
        -"" = "empty"
        -" " = "space"
        -- = "dash (not quoted)"
        -1 = "number (not quoted)"
        -"ʎǝʞ" = "reverse"
        +"" = "empty"
        +" " = "space"
        +- = "dash (not quoted)"
        +1 = "number (not quoted)"
        +"ʎǝʞ" = "reverse"
        +
        +["subtable with space"]
        +depth = 1
        +
        +["subtable with space"."subsubtable with space"]
        +depth = 2
         
        -["subtable with space"]
        -depth = 1
        -
        -["subtable with space"."subsubtable with space"]
        -depth = 2
        -
=== RUN   TestMarshalRoundTrip
--- PASS: TestMarshalRoundTrip (0.00s)
=== RUN   TestMarshalArrayTableEmptyParent
--- FAIL: TestMarshalArrayTableEmptyParent (0.00s)
    encode_test.go:222: Output mismatch:
        -[[bars]]
        -[bars.baz]
        -key = 1
        +[[bars]]
        +[bars.baz]
        +key = 1
        +
        +[[bars]]
        +[bars.baz]
        +key = 2
         
        -[[bars]]
        -[bars.baz]
        -key = 2
        -
=== RUN   TestMarshalPointerError
--- PASS: TestMarshalPointerError (0.00s)
=== RUN   TestMarshalNonStruct
--- PASS: TestMarshalNonStruct (0.00s)
=== RUN   TestMarshalOmitempty
--- PASS: TestMarshalOmitempty (0.00s)
=== RUN   Example
--- PASS: Example (0.00s)
=== RUN   ExampleUnmarshalerRec
--- PASS: ExampleUnmarshalerRec (0.00s)
=== RUN   ExampleUnmarshaler
--- PASS: ExampleUnmarshaler (0.00s)
=== RUN   Example_textUnmarshaler
--- PASS: Example_textUnmarshaler (0.00s)
=== RUN   Example_textUnmarshalerError
--- PASS: Example_textUnmarshalerError (0.00s)
FAIL
FAIL	github.com/naoina/toml	0.283s
?   	github.com/naoina/toml/ast	[no test files]

This does not parse, while being toml v0.4.0 compliant.

This does not parse, and it should, according to the spec.

[irc_servers]
    freenode = "chat.freenode.net:8001"
    quakenet = "irc.quakenet.org:6667"

[users]
    [users.alice]
        nick = "tester_Alice"
        name = "Alice"
        channels = {freenode = ["#test123456", "#test1234567"], quakenet = ["#xonotic"]}

    [users.bob]
        nick = "tester_Bob"
        name = "Bob"
        channels = {freenode = ["#test123456"], quakenet = ["#xonotic", "#quake"]}

I have also tested this with https://github.com/avakar/pytoml, which can parse this without problems.

I use these structs:

type TomlUser struct {
    Nick string
    Name string
    Channels map[string][]string
}

type TomlConfig struct {
    IrcServers map[string]string
    Users map[string]TomlUser
}

Full example program: http://play.golang.org/p/-qWU5Y0RW-

Gives me this error:

panic: toml:http://play.golang.org/p/aeGl88Vog5

goroutine 1 [running]:
main.main()
        /tmp/test/marshltest.go:40 +0xdf

goroutine 5 [chan send]:
github.com/naoina/toml.(*tokens16).Tokens.func1(0xc82001a300, 0xc8200d8000)
        /Bestanden/Arch/Go/src/github.com/naoina/toml/parse.peg.go:466 +0x134
created by github.com/naoina/toml.(*tokens16).Tokens
        /Bestanden/Arch/Go/src/github.com/naoina/toml/parse.peg.go:469 +0x5d
exit status 2

If I replace map[string]string with []string it does work: http://play.golang.org/p/mtLIcqURv5.

User defined string types don't unmarshal

A common pattern for mapping enums into config files is to define a new string type and consts for the acceptable values. This marshals correctly, but wont unmarshal properly.

Example:

package main

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

type Color string

const (
        Red  Color = "red"
        Blue Color = "blue"
)

type cfg struct {
        MyColor Color
}

func main() { 
        c1 := cfg{Red}

        data, err := toml.Marshal(&c1)
        if err != nil {
                panic(err)
        }
        fmt.Printf("Serialized: %s", string(data))
        
        var c2 cfg

        if err := toml.Unmarshal(data, &c2); err != nil {
                panic(err)
        }
        fmt.Printf("Unmarshaled: %#v\n", c2)
}

Output:

Serialized: my_color="red"
panic: toml: unmarshal: line 1: main.cfg.MyColor: `string' type is not assignable to `main.Color' type

goroutine 1 [running]:
panic(0x4d5260, 0xc42000a790)
	/usr/local/go/src/runtime/panic.go:500 +0x1a1
main.main()
	/home/daniel/code/go/toml/tagorder.go:31 +0x2a4
exit status 2

The same pattern works properly with https://golang.org/pkg/encoding/json/

Cannot decode into map[K]*V

I cannot decode into a map[K]*V, but can to a map[K]V.

type Config struct {
    Backends       map[string]*Backend
}

type Backend struct {
    Hosts  []string
    Addr   string
    Scheme string
    Verify *bool

    hc *http.Client
}

panic: reflect.Value.SetMapIndex: value of type main.Backend is not assignable to type *main.Backend

marshalling a struct pointer does not work

quick reproducer:

package main

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

type appcfg struct {
  Debug  bool     `toml:"debug"`
  Admins []string `toml:"admins"`
  API    struct {
    URL       string `toml:"URL"`
    AuthToken string `toml:"authtoken"`
  }
}

func main() {
  str, _ := toml.Marshal(&appcfg{})
  println(str)
}

fails with error:

panic: reflect: call of reflect.Value.NumField on ptr Value

goroutine 1 [running]:
reflect.flag.mustBe(0x16, 0x19)
        /usr/local/go/src/reflect/value.go:199 +0xa6
reflect.Value.NumField(0x6aa600, 0xc82004ccb0, 0x16, 0x7f99090340a8)
        /usr/local/go/src/reflect/value.go:1146 +0x2a
github.com/naoina/toml.marshal(0x0, 0x0, 0x0, 0x0, 0x0, 0x6aa600, 0xc82004ccb0, 0x16, 0x7f9909030000, 0x0, ...)
        /home/sztanpet/go/gocode/src/github.com/naoina/toml/encode.go:77 +0xca
github.com/naoina/toml.Marshal(0x6aa600, 0xc82004ccb0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /home/sztanpet/go/gocode/src/github.com/naoina/toml/encode.go:44 +0xc4
github.com/naoina/toml.(*Encoder).Encode(0xc820047da8, 0x6aa600, 0xc82004ccb0, 0x0, 0x0)
        /home/sztanpet/go/gocode/src/github.com/naoina/toml/encode.go:62 +0x47

Marshal: map[string]Object is not supported

It does not seem like marshalling of map[string]Object is implemented. The sample from the documentation fails:

type Config struct {
    Server map[string]Server
}

Returns the error toml: marshal: unsupported type map.

strange not defined error

I am getting this strange error. Left is the error, right is the toml file:
screen shot 2015-12-16 at 10 58 12 pm

The struct I have is:
screen shot 2015-12-16 at 11 00 34 pm

Removing the adminservice and vortexservice from toml file makes it work again without problem.

Section about using the toml.UnmarshalTOML interface contains several errors.

The section about using the toml.UnmarshalTOML interface contains 2 errors.

package main

import (
    "time"

    "github.com/naoina/toml"
)

type Config struct {
    Duration Duration
}

type Duration struct {
    time.Duration
}

func (d *Duration) UnmarshalTOML(data []byte) error {
    n, err := time.ParseDuration(string(data))
    d.Duration = n
    return err
}

func main() {
    data := []byte("duration=\"10s\"")

    var config Config
    if err := toml.Unmarshal(data, &config); err != nil {
        panic(err)
    }
}

First the line marked with a > won't compile, it raises non-name d.Duration on left side of :=:

func (d *Duration) UnmarshalTOML(data []byte) error {
>   d.Duration, err := time.ParseDuration(string(data))
    return err
}

It can be fixed by rewriting it to:

func (d *Duration) UnmarshalTOML(data []byte) error {
    n, err := time.ParseDuration(string(data))
    d.Duration = n
    return err
}

But still the code isn't good. It would fail with:

panic: toml: unmarshal: line 1: main.Config.Duration: time: invalid duration "10s"

It took me a while to discover what was wrong. But the time.ParseDuration() is called with "10", a string including the double quotes. These need to be removed like this:

func (d *Duration) UnmarshalTOML(data []byte) error {
    n, err := time.ParseDuration(strings.Trim(string(data), "\""))
    d.Duration = n
    return err
}

Decoding into embedded struct

At some point this library was changed so it can't decode into embedded structs any more

package main

import (
	"fmt"

	"github.com/naoina/toml"
)

type DBConfig struct {
	User string `toml:"user"`
}

type DBClusterConfig struct {
	DBConfig
}

func main() {
	doc := []byte(`
		user = "test_user"
	`)

	config := new(DBClusterConfig)
	if err := toml.Unmarshal(doc, &config); err != nil {
		panic(err)
	}
	fmt.Println("user", config.User)
}

Old revisions (such as 7a3d4a6) work just fine.

support field validation

It would be awesome if the library could support some simple validations, such as marking which keys are required vs optional and perhaps having some regexp validations for strings and some ranges for ints. This could cover 80%-90% of use-cases and avoid having to write validations manually in main.

Behavior change between 0.1.0 and 0.1.1

If this were a major version change, I'd accept that "things change" but since it was a minor revision, I feel compelled to point out my now-broken use-case with this library. Maybe someone can correct my flaws and show me a valid way to do this. Or we can identify this as some sort of regression.

type tomlConfig struct {
	IncludeDir string
	Services []*Config
}

type Config struct {
	Name string
	Paths []string
}

func loadConfigFile(configFile string) (tomlConfig, error) {
	var config tomlConfig
	if buf, err := ioutil.ReadFile(configFile); err != nil {
		return config, err
	} else if err := toml.Unmarshal(buf, &config); err != nil {
		return config, err
	} else if config.IncludeDir == "" {
		return config, nil
	}

	// include_dir is not empty, read in more config files from a conf.d folder.
	includedConfigFiles, err := ioutil.ReadDir(config.IncludeDir)
	if err != nil {
		return config, err
	}

	for _, includeFile := range includedConfigFiles {
		filePath := filepath.Join(config.IncludeDir, includeFile.Name())
		if !strings.HasSuffix(filePath, ".conf") {
			continue // Only read files that end with .conf.
		} else if buf, err := ioutil.ReadFile(filePath); err != nil {
			return config, err
		} else if err := toml.Unmarshal(buf, &config); err != nil {
			return config, err
		}
	}
	return config, nil
}

The above code works great with toml library 0.1.0. The Services slice is appended to for each [[services]] found in the included config files. In version 0.1.1 the slice only contains the data from the last config file Unmarshal()'d. Thoughts?

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.