...

Source file src/github.com/pelletier/go-toml/v2/marshaler_test.go

Documentation: github.com/pelletier/go-toml/v2

     1  package toml_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math"
     8  	"math/big"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/pelletier/go-toml/v2"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  type marshalTextKey struct {
    19  	A string
    20  	B string
    21  }
    22  
    23  func (k marshalTextKey) MarshalText() ([]byte, error) {
    24  	return []byte(k.A + "-" + k.B), nil
    25  }
    26  
    27  type marshalBadTextKey struct{}
    28  
    29  func (k marshalBadTextKey) MarshalText() ([]byte, error) {
    30  	return nil, fmt.Errorf("error")
    31  }
    32  
    33  func TestMarshal(t *testing.T) {
    34  	someInt := 42
    35  
    36  	type structInline struct {
    37  		A interface{} `toml:",inline"`
    38  	}
    39  
    40  	type comments struct {
    41  		One   int
    42  		Two   int   `comment:"Before kv"`
    43  		Three []int `comment:"Before array"`
    44  	}
    45  
    46  	examples := []struct {
    47  		desc     string
    48  		v        interface{}
    49  		expected string
    50  		err      bool
    51  	}{
    52  		{
    53  			desc: "simple map and string",
    54  			v: map[string]string{
    55  				"hello": "world",
    56  			},
    57  			expected: "hello = 'world'\n",
    58  		},
    59  		{
    60  			desc: "map with new line in key",
    61  			v: map[string]string{
    62  				"hel\nlo": "world",
    63  			},
    64  			expected: "\"hel\\nlo\" = 'world'\n",
    65  		},
    66  		{
    67  			desc: `map with " in key`,
    68  			v: map[string]string{
    69  				`hel"lo`: "world",
    70  			},
    71  			expected: "'hel\"lo' = 'world'\n",
    72  		},
    73  		{
    74  			desc: "map in map and string",
    75  			v: map[string]map[string]string{
    76  				"table": {
    77  					"hello": "world",
    78  				},
    79  			},
    80  			expected: `[table]
    81  hello = 'world'
    82  `,
    83  		},
    84  		{
    85  			desc: "map in map in map and string",
    86  			v: map[string]map[string]map[string]string{
    87  				"this": {
    88  					"is": {
    89  						"a": "test",
    90  					},
    91  				},
    92  			},
    93  			expected: `[this]
    94  [this.is]
    95  a = 'test'
    96  `,
    97  		},
    98  		{
    99  			desc: "map in map in map and string with values",
   100  			v: map[string]interface{}{
   101  				"this": map[string]interface{}{
   102  					"is": map[string]string{
   103  						"a": "test",
   104  					},
   105  					"also": "that",
   106  				},
   107  			},
   108  			expected: `[this]
   109  also = 'that'
   110  
   111  [this.is]
   112  a = 'test'
   113  `,
   114  		},
   115  		{
   116  			desc: `map with text key`,
   117  			v: map[marshalTextKey]string{
   118  				{A: "a", B: "1"}: "value 1",
   119  				{A: "a", B: "2"}: "value 2",
   120  				{A: "b", B: "1"}: "value 3",
   121  			},
   122  			expected: `a-1 = 'value 1'
   123  a-2 = 'value 2'
   124  b-1 = 'value 3'
   125  `,
   126  		},
   127  		{
   128  			desc: `table with text key`,
   129  			v: map[marshalTextKey]map[string]string{
   130  				{A: "a", B: "1"}: {"value": "foo"},
   131  			},
   132  			expected: `[a-1]
   133  value = 'foo'
   134  `,
   135  		},
   136  		{
   137  			desc: `map with ptr text key`,
   138  			v: map[*marshalTextKey]string{
   139  				{A: "a", B: "1"}: "value 1",
   140  				{A: "a", B: "2"}: "value 2",
   141  				{A: "b", B: "1"}: "value 3",
   142  			},
   143  			expected: `a-1 = 'value 1'
   144  a-2 = 'value 2'
   145  b-1 = 'value 3'
   146  `,
   147  		},
   148  		{
   149  			desc: `map with bad text key`,
   150  			v: map[marshalBadTextKey]string{
   151  				{}: "value 1",
   152  			},
   153  			err: true,
   154  		},
   155  		{
   156  			desc: `map with bad ptr text key`,
   157  			v: map[*marshalBadTextKey]string{
   158  				{}: "value 1",
   159  			},
   160  			err: true,
   161  		},
   162  		{
   163  			desc: "simple string array",
   164  			v: map[string][]string{
   165  				"array": {"one", "two", "three"},
   166  			},
   167  			expected: `array = ['one', 'two', 'three']
   168  `,
   169  		},
   170  		{
   171  			desc:     "empty string array",
   172  			v:        map[string][]string{},
   173  			expected: ``,
   174  		},
   175  		{
   176  			desc:     "map",
   177  			v:        map[string][]string{},
   178  			expected: ``,
   179  		},
   180  		{
   181  			desc: "nested string arrays",
   182  			v: map[string][][]string{
   183  				"array": {{"one", "two"}, {"three"}},
   184  			},
   185  			expected: `array = [['one', 'two'], ['three']]
   186  `,
   187  		},
   188  		{
   189  			desc: "mixed strings and nested string arrays",
   190  			v: map[string][]interface{}{
   191  				"array": {"a string", []string{"one", "two"}, "last"},
   192  			},
   193  			expected: `array = ['a string', ['one', 'two'], 'last']
   194  `,
   195  		},
   196  		{
   197  			desc: "array of maps",
   198  			v: map[string][]map[string]string{
   199  				"top": {
   200  					{"map1.1": "v1.1"},
   201  					{"map2.1": "v2.1"},
   202  				},
   203  			},
   204  			expected: `[[top]]
   205  'map1.1' = 'v1.1'
   206  
   207  [[top]]
   208  'map2.1' = 'v2.1'
   209  `,
   210  		},
   211  		{
   212  			desc: "fixed size string array",
   213  			v: map[string][3]string{
   214  				"array": {"one", "two", "three"},
   215  			},
   216  			expected: `array = ['one', 'two', 'three']
   217  `,
   218  		},
   219  		{
   220  			desc: "fixed size nested string arrays",
   221  			v: map[string][2][2]string{
   222  				"array": {{"one", "two"}, {"three"}},
   223  			},
   224  			expected: `array = [['one', 'two'], ['three', '']]
   225  `,
   226  		},
   227  		{
   228  			desc: "mixed strings and fixed size nested string arrays",
   229  			v: map[string][]interface{}{
   230  				"array": {"a string", [2]string{"one", "two"}, "last"},
   231  			},
   232  			expected: `array = ['a string', ['one', 'two'], 'last']
   233  `,
   234  		},
   235  		{
   236  			desc: "fixed size array of maps",
   237  			v: map[string][2]map[string]string{
   238  				"ftop": {
   239  					{"map1.1": "v1.1"},
   240  					{"map2.1": "v2.1"},
   241  				},
   242  			},
   243  			expected: `[[ftop]]
   244  'map1.1' = 'v1.1'
   245  
   246  [[ftop]]
   247  'map2.1' = 'v2.1'
   248  `,
   249  		},
   250  		{
   251  			desc: "map with two keys",
   252  			v: map[string]string{
   253  				"key1": "value1",
   254  				"key2": "value2",
   255  			},
   256  			expected: `key1 = 'value1'
   257  key2 = 'value2'
   258  `,
   259  		},
   260  		{
   261  			desc: "simple struct",
   262  			v: struct {
   263  				A string
   264  			}{
   265  				A: "foo",
   266  			},
   267  			expected: `A = 'foo'
   268  `,
   269  		},
   270  		{
   271  			desc: "one level of structs within structs",
   272  			v: struct {
   273  				A interface{}
   274  			}{
   275  				A: struct {
   276  					K1 string
   277  					K2 string
   278  				}{
   279  					K1: "v1",
   280  					K2: "v2",
   281  				},
   282  			},
   283  			expected: `[A]
   284  K1 = 'v1'
   285  K2 = 'v2'
   286  `,
   287  		},
   288  		{
   289  			desc: "structs in array with interfaces",
   290  			v: map[string]interface{}{
   291  				"root": map[string]interface{}{
   292  					"nested": []interface{}{
   293  						map[string]interface{}{"name": "Bob"},
   294  						map[string]interface{}{"name": "Alice"},
   295  					},
   296  				},
   297  			},
   298  			expected: `[root]
   299  [[root.nested]]
   300  name = 'Bob'
   301  
   302  [[root.nested]]
   303  name = 'Alice'
   304  `,
   305  		},
   306  		{
   307  			desc: "string escapes",
   308  			v: map[string]interface{}{
   309  				"a": "'\b\f\r\t\"\\",
   310  			},
   311  			expected: `a = "'\b\f\r\t\"\\"
   312  `,
   313  		},
   314  		{
   315  			desc: "string utf8 low",
   316  			v: map[string]interface{}{
   317  				"a": "'Ę",
   318  			},
   319  			expected: `a = "'Ę"
   320  `,
   321  		},
   322  		{
   323  			desc: "string utf8 low 2",
   324  			v: map[string]interface{}{
   325  				"a": "'\u10A85",
   326  			},
   327  			expected: "a = \"'\u10A85\"\n",
   328  		},
   329  		{
   330  			desc: "string utf8 low 2",
   331  			v: map[string]interface{}{
   332  				"a": "'\u10A85",
   333  			},
   334  			expected: "a = \"'\u10A85\"\n",
   335  		},
   336  		{
   337  			desc: "emoji",
   338  			v: map[string]interface{}{
   339  				"a": "'😀",
   340  			},
   341  			expected: "a = \"'😀\"\n",
   342  		},
   343  		{
   344  			desc: "control char",
   345  			v: map[string]interface{}{
   346  				"a": "'\u001A",
   347  			},
   348  			expected: `a = "'\u001A"
   349  `,
   350  		},
   351  		{
   352  			desc: "multi-line string",
   353  			v: map[string]interface{}{
   354  				"a": "hello\nworld",
   355  			},
   356  			expected: `a = "hello\nworld"
   357  `,
   358  		},
   359  		{
   360  			desc: "multi-line forced",
   361  			v: struct {
   362  				A string `toml:",multiline"`
   363  			}{
   364  				A: "hello\nworld",
   365  			},
   366  			expected: `A = """
   367  hello
   368  world"""
   369  `,
   370  		},
   371  		{
   372  			desc: "inline field",
   373  			v: struct {
   374  				A map[string]string `toml:",inline"`
   375  				B map[string]string
   376  			}{
   377  				A: map[string]string{
   378  					"isinline": "yes",
   379  				},
   380  				B: map[string]string{
   381  					"isinline": "no",
   382  				},
   383  			},
   384  			expected: `A = {isinline = 'yes'}
   385  
   386  [B]
   387  isinline = 'no'
   388  `,
   389  		},
   390  		{
   391  			desc: "mutiline array int",
   392  			v: struct {
   393  				A []int `toml:",multiline"`
   394  				B []int
   395  			}{
   396  				A: []int{1, 2, 3, 4},
   397  				B: []int{1, 2, 3, 4},
   398  			},
   399  			expected: `A = [
   400    1,
   401    2,
   402    3,
   403    4
   404  ]
   405  B = [1, 2, 3, 4]
   406  `,
   407  		},
   408  		{
   409  			desc: "mutiline array in array",
   410  			v: struct {
   411  				A [][]int `toml:",multiline"`
   412  			}{
   413  				A: [][]int{{1, 2}, {3, 4}},
   414  			},
   415  			expected: `A = [
   416    [1, 2],
   417    [3, 4]
   418  ]
   419  `,
   420  		},
   421  		{
   422  			desc: "nil interface not supported at root",
   423  			v:    nil,
   424  			err:  true,
   425  		},
   426  		{
   427  			desc: "nil interface not supported in slice",
   428  			v: map[string]interface{}{
   429  				"a": []interface{}{"a", nil, 2},
   430  			},
   431  			err: true,
   432  		},
   433  		{
   434  			desc: "nil pointer in slice uses zero value",
   435  			v: struct {
   436  				A []*int
   437  			}{
   438  				A: []*int{nil},
   439  			},
   440  			expected: `A = [0]
   441  `,
   442  		},
   443  		{
   444  			desc: "nil pointer in slice uses zero value",
   445  			v: struct {
   446  				A []*int
   447  			}{
   448  				A: []*int{nil},
   449  			},
   450  			expected: `A = [0]
   451  `,
   452  		},
   453  		{
   454  			desc: "pointer in slice",
   455  			v: struct {
   456  				A []*int
   457  			}{
   458  				A: []*int{&someInt},
   459  			},
   460  			expected: `A = [42]
   461  `,
   462  		},
   463  		{
   464  			desc: "inline table in inline table",
   465  			v: structInline{
   466  				A: structInline{
   467  					A: structInline{
   468  						A: "hello",
   469  					},
   470  				},
   471  			},
   472  			expected: `A = {A = {A = 'hello'}}
   473  `,
   474  		},
   475  		{
   476  			desc: "empty slice in map",
   477  			v: map[string][]string{
   478  				"a": {},
   479  			},
   480  			expected: `a = []
   481  `,
   482  		},
   483  		{
   484  			desc: "map in slice",
   485  			v: map[string][]map[string]string{
   486  				"a": {{"hello": "world"}},
   487  			},
   488  			expected: `[[a]]
   489  hello = 'world'
   490  `,
   491  		},
   492  		{
   493  			desc: "newline in map in slice",
   494  			v: map[string][]map[string]string{
   495  				"a\n": {{"hello": "world"}},
   496  			},
   497  			expected: `[["a\n"]]
   498  hello = 'world'
   499  `,
   500  		},
   501  		{
   502  			desc: "newline in map in slice",
   503  			v: map[string][]map[string]*customTextMarshaler{
   504  				"a": {{"hello": &customTextMarshaler{1}}},
   505  			},
   506  			err: true,
   507  		},
   508  		{
   509  			desc: "empty slice of empty struct",
   510  			v: struct {
   511  				A []struct{}
   512  			}{
   513  				A: []struct{}{},
   514  			},
   515  			expected: `A = []
   516  `,
   517  		},
   518  		{
   519  			desc: "nil field is ignored",
   520  			v: struct {
   521  				A interface{}
   522  			}{
   523  				A: nil,
   524  			},
   525  			expected: ``,
   526  		},
   527  		{
   528  			desc: "private fields are ignored",
   529  			v: struct {
   530  				Public  string
   531  				private string
   532  			}{
   533  				Public:  "shown",
   534  				private: "hidden",
   535  			},
   536  			expected: `Public = 'shown'
   537  `,
   538  		},
   539  		{
   540  			desc: "fields tagged - are ignored",
   541  			v: struct {
   542  				Public  string `toml:"-"`
   543  				private string
   544  			}{
   545  				Public: "hidden",
   546  			},
   547  			expected: ``,
   548  		},
   549  		{
   550  			desc: "nil value in map is ignored",
   551  			v: map[string]interface{}{
   552  				"A": nil,
   553  			},
   554  			expected: ``,
   555  		},
   556  		{
   557  			desc: "new line in table key",
   558  			v: map[string]interface{}{
   559  				"hello\nworld": 42,
   560  			},
   561  			expected: `"hello\nworld" = 42
   562  `,
   563  		},
   564  		{
   565  			desc: "new line in parent of nested table key",
   566  			v: map[string]interface{}{
   567  				"hello\nworld": map[string]interface{}{
   568  					"inner": 42,
   569  				},
   570  			},
   571  			expected: `["hello\nworld"]
   572  inner = 42
   573  `,
   574  		},
   575  		{
   576  			desc: "new line in nested table key",
   577  			v: map[string]interface{}{
   578  				"parent": map[string]interface{}{
   579  					"in\ner": map[string]interface{}{
   580  						"foo": 42,
   581  					},
   582  				},
   583  			},
   584  			expected: `[parent]
   585  [parent."in\ner"]
   586  foo = 42
   587  `,
   588  		},
   589  		{
   590  			desc: "invalid map key",
   591  			v:    map[int]interface{}{1: "a"},
   592  			err:  true,
   593  		},
   594  		{
   595  			desc:     "invalid map key but empty",
   596  			v:        map[int]interface{}{},
   597  			expected: "",
   598  		},
   599  		{
   600  			desc: "unhandled type",
   601  			v: struct {
   602  				A chan int
   603  			}{
   604  				A: make(chan int),
   605  			},
   606  			err: true,
   607  		},
   608  		{
   609  			desc: "time",
   610  			v: struct {
   611  				T time.Time
   612  			}{
   613  				T: time.Time{},
   614  			},
   615  			expected: `T = 0001-01-01T00:00:00Z
   616  `,
   617  		},
   618  		{
   619  			desc: "time nano",
   620  			v: struct {
   621  				T time.Time
   622  			}{
   623  				T: time.Date(1979, time.May, 27, 0, 32, 0, 999999000, time.UTC),
   624  			},
   625  			expected: `T = 1979-05-27T00:32:00.999999Z
   626  `,
   627  		},
   628  		{
   629  			desc: "bool",
   630  			v: struct {
   631  				A bool
   632  				B bool
   633  			}{
   634  				A: false,
   635  				B: true,
   636  			},
   637  			expected: `A = false
   638  B = true
   639  `,
   640  		},
   641  		{
   642  			desc: "numbers",
   643  			v: struct {
   644  				A float32
   645  				B uint64
   646  				C uint32
   647  				D uint16
   648  				E uint8
   649  				F uint
   650  				G int64
   651  				H int32
   652  				I int16
   653  				J int8
   654  				K int
   655  				L float64
   656  			}{
   657  				A: 1.1,
   658  				B: 42,
   659  				C: 42,
   660  				D: 42,
   661  				E: 42,
   662  				F: 42,
   663  				G: 42,
   664  				H: 42,
   665  				I: 42,
   666  				J: 42,
   667  				K: 42,
   668  				L: 2.2,
   669  			},
   670  			expected: `A = 1.1
   671  B = 42
   672  C = 42
   673  D = 42
   674  E = 42
   675  F = 42
   676  G = 42
   677  H = 42
   678  I = 42
   679  J = 42
   680  K = 42
   681  L = 2.2
   682  `,
   683  		},
   684  		{
   685  			desc: "comments",
   686  			v: struct {
   687  				Table comments `comment:"Before table"`
   688  			}{
   689  				Table: comments{
   690  					One:   1,
   691  					Two:   2,
   692  					Three: []int{1, 2, 3},
   693  				},
   694  			},
   695  			expected: `# Before table
   696  [Table]
   697  One = 1
   698  # Before kv
   699  Two = 2
   700  # Before array
   701  Three = [1, 2, 3]
   702  `,
   703  		},
   704  	}
   705  
   706  	for _, e := range examples {
   707  		e := e
   708  		t.Run(e.desc, func(t *testing.T) {
   709  			b, err := toml.Marshal(e.v)
   710  			if e.err {
   711  				require.Error(t, err)
   712  
   713  				return
   714  			}
   715  
   716  			require.NoError(t, err)
   717  			assert.Equal(t, e.expected, string(b))
   718  
   719  			// make sure the output is always valid TOML
   720  			defaultMap := map[string]interface{}{}
   721  			err = toml.Unmarshal(b, &defaultMap)
   722  			require.NoError(t, err)
   723  
   724  			testWithAllFlags(t, func(t *testing.T, flags int) {
   725  				t.Helper()
   726  
   727  				var buf bytes.Buffer
   728  				enc := toml.NewEncoder(&buf)
   729  				setFlags(enc, flags)
   730  
   731  				err := enc.Encode(e.v)
   732  				require.NoError(t, err)
   733  
   734  				inlineMap := map[string]interface{}{}
   735  				err = toml.Unmarshal(buf.Bytes(), &inlineMap)
   736  				require.NoError(t, err)
   737  
   738  				require.Equal(t, defaultMap, inlineMap)
   739  			})
   740  		})
   741  	}
   742  }
   743  
   744  type flagsSetters []struct {
   745  	name string
   746  	f    func(enc *toml.Encoder, flag bool) *toml.Encoder
   747  }
   748  
   749  var allFlags = flagsSetters{
   750  	{"arrays-multiline", (*toml.Encoder).SetArraysMultiline},
   751  	{"tables-inline", (*toml.Encoder).SetTablesInline},
   752  	{"indent-tables", (*toml.Encoder).SetIndentTables},
   753  }
   754  
   755  func setFlags(enc *toml.Encoder, flags int) {
   756  	for i := 0; i < len(allFlags); i++ {
   757  		enabled := flags&1 > 0
   758  		allFlags[i].f(enc, enabled)
   759  	}
   760  }
   761  
   762  func testWithAllFlags(t *testing.T, testfn func(t *testing.T, flags int)) {
   763  	t.Helper()
   764  	testWithFlags(t, 0, allFlags, testfn)
   765  }
   766  
   767  func testWithFlags(t *testing.T, flags int, setters flagsSetters, testfn func(t *testing.T, flags int)) {
   768  	t.Helper()
   769  
   770  	if len(setters) == 0 {
   771  		testfn(t, flags)
   772  
   773  		return
   774  	}
   775  
   776  	s := setters[0]
   777  
   778  	for _, enabled := range []bool{false, true} {
   779  		name := fmt.Sprintf("%s=%t", s.name, enabled)
   780  		newFlags := flags << 1
   781  
   782  		if enabled {
   783  			newFlags++
   784  		}
   785  
   786  		t.Run(name, func(t *testing.T) {
   787  			testWithFlags(t, newFlags, setters[1:], testfn)
   788  		})
   789  	}
   790  }
   791  
   792  func TestMarshalFloats(t *testing.T) {
   793  	v := map[string]float32{
   794  		"nan":  float32(math.NaN()),
   795  		"+inf": float32(math.Inf(1)),
   796  		"-inf": float32(math.Inf(-1)),
   797  	}
   798  
   799  	expected := `'+inf' = inf
   800  -inf = -inf
   801  nan = nan
   802  `
   803  
   804  	actual, err := toml.Marshal(v)
   805  	require.NoError(t, err)
   806  	require.Equal(t, expected, string(actual))
   807  
   808  	v64 := map[string]float64{
   809  		"nan":  math.NaN(),
   810  		"+inf": math.Inf(1),
   811  		"-inf": math.Inf(-1),
   812  	}
   813  
   814  	actual, err = toml.Marshal(v64)
   815  	require.NoError(t, err)
   816  	require.Equal(t, expected, string(actual))
   817  }
   818  
   819  //nolint:funlen
   820  func TestMarshalIndentTables(t *testing.T) {
   821  	examples := []struct {
   822  		desc     string
   823  		v        interface{}
   824  		expected string
   825  	}{
   826  		{
   827  			desc: "one kv",
   828  			v: map[string]interface{}{
   829  				"foo": "bar",
   830  			},
   831  			expected: `foo = 'bar'
   832  `,
   833  		},
   834  		{
   835  			desc: "one level table",
   836  			v: map[string]map[string]string{
   837  				"foo": {
   838  					"one": "value1",
   839  					"two": "value2",
   840  				},
   841  			},
   842  			expected: `[foo]
   843    one = 'value1'
   844    two = 'value2'
   845  `,
   846  		},
   847  		{
   848  			desc: "two levels table",
   849  			v: map[string]interface{}{
   850  				"root": "value0",
   851  				"level1": map[string]interface{}{
   852  					"one": "value1",
   853  					"level2": map[string]interface{}{
   854  						"two": "value2",
   855  					},
   856  				},
   857  			},
   858  			expected: `root = 'value0'
   859  
   860  [level1]
   861    one = 'value1'
   862  
   863    [level1.level2]
   864      two = 'value2'
   865  `,
   866  		},
   867  	}
   868  
   869  	for _, e := range examples {
   870  		e := e
   871  		t.Run(e.desc, func(t *testing.T) {
   872  			var buf strings.Builder
   873  			enc := toml.NewEncoder(&buf)
   874  			enc.SetIndentTables(true)
   875  			err := enc.Encode(e.v)
   876  			require.NoError(t, err)
   877  			assert.Equal(t, e.expected, buf.String())
   878  		})
   879  	}
   880  }
   881  
   882  type customTextMarshaler struct {
   883  	value int64
   884  }
   885  
   886  func (c *customTextMarshaler) MarshalText() ([]byte, error) {
   887  	if c.value == 1 {
   888  		return nil, fmt.Errorf("cannot represent 1 because this is a silly test")
   889  	}
   890  	return []byte(fmt.Sprintf("::%d", c.value)), nil
   891  }
   892  
   893  func TestMarshalTextMarshaler_NoRoot(t *testing.T) {
   894  	c := customTextMarshaler{}
   895  	_, err := toml.Marshal(&c)
   896  	require.Error(t, err)
   897  }
   898  
   899  func TestMarshalTextMarshaler_Error(t *testing.T) {
   900  	m := map[string]interface{}{"a": &customTextMarshaler{value: 1}}
   901  	_, err := toml.Marshal(m)
   902  	require.Error(t, err)
   903  }
   904  
   905  func TestMarshalTextMarshaler_ErrorInline(t *testing.T) {
   906  	type s struct {
   907  		A map[string]interface{} `inline:"true"`
   908  	}
   909  
   910  	d := s{
   911  		A: map[string]interface{}{"a": &customTextMarshaler{value: 1}},
   912  	}
   913  
   914  	_, err := toml.Marshal(d)
   915  	require.Error(t, err)
   916  }
   917  
   918  func TestMarshalTextMarshaler(t *testing.T) {
   919  	m := map[string]interface{}{"a": &customTextMarshaler{value: 2}}
   920  	r, err := toml.Marshal(m)
   921  	require.NoError(t, err)
   922  	assert.Equal(t, "a = '::2'\n", string(r))
   923  }
   924  
   925  type brokenWriter struct{}
   926  
   927  func (b *brokenWriter) Write([]byte) (int, error) {
   928  	return 0, fmt.Errorf("dead")
   929  }
   930  
   931  func TestEncodeToBrokenWriter(t *testing.T) {
   932  	w := brokenWriter{}
   933  	enc := toml.NewEncoder(&w)
   934  	err := enc.Encode(map[string]string{"hello": "world"})
   935  	require.Error(t, err)
   936  }
   937  
   938  func TestEncoderSetIndentSymbol(t *testing.T) {
   939  	var w strings.Builder
   940  	enc := toml.NewEncoder(&w)
   941  	enc.SetIndentTables(true)
   942  	enc.SetIndentSymbol(">>>")
   943  	err := enc.Encode(map[string]map[string]string{"parent": {"hello": "world"}})
   944  	require.NoError(t, err)
   945  	expected := `[parent]
   946  >>>hello = 'world'
   947  `
   948  	assert.Equal(t, expected, w.String())
   949  }
   950  
   951  func TestEncoderOmitempty(t *testing.T) {
   952  	type doc struct {
   953  		String  string            `toml:",omitempty,multiline"`
   954  		Bool    bool              `toml:",omitempty,multiline"`
   955  		Int     int               `toml:",omitempty,multiline"`
   956  		Int8    int8              `toml:",omitempty,multiline"`
   957  		Int16   int16             `toml:",omitempty,multiline"`
   958  		Int32   int32             `toml:",omitempty,multiline"`
   959  		Int64   int64             `toml:",omitempty,multiline"`
   960  		Uint    uint              `toml:",omitempty,multiline"`
   961  		Uint8   uint8             `toml:",omitempty,multiline"`
   962  		Uint16  uint16            `toml:",omitempty,multiline"`
   963  		Uint32  uint32            `toml:",omitempty,multiline"`
   964  		Uint64  uint64            `toml:",omitempty,multiline"`
   965  		Float32 float32           `toml:",omitempty,multiline"`
   966  		Float64 float64           `toml:",omitempty,multiline"`
   967  		MapNil  map[string]string `toml:",omitempty,multiline"`
   968  		Slice   []string          `toml:",omitempty,multiline"`
   969  		Ptr     *string           `toml:",omitempty,multiline"`
   970  		Iface   interface{}       `toml:",omitempty,multiline"`
   971  		Struct  struct{}          `toml:",omitempty,multiline"`
   972  	}
   973  
   974  	d := doc{}
   975  
   976  	b, err := toml.Marshal(d)
   977  	require.NoError(t, err)
   978  
   979  	expected := ``
   980  
   981  	assert.Equal(t, expected, string(b))
   982  }
   983  
   984  func TestEncoderTagFieldName(t *testing.T) {
   985  	type doc struct {
   986  		String string `toml:"hello"`
   987  		OkSym  string `toml:"#"`
   988  		Bad    string `toml:"\"`
   989  	}
   990  
   991  	d := doc{String: "world"}
   992  
   993  	b, err := toml.Marshal(d)
   994  	require.NoError(t, err)
   995  
   996  	expected := `hello = 'world'
   997  '#' = ''
   998  Bad = ''
   999  `
  1000  
  1001  	assert.Equal(t, expected, string(b))
  1002  }
  1003  
  1004  func TestIssue436(t *testing.T) {
  1005  	data := []byte(`{"a": [ { "b": { "c": "d" } } ]}`)
  1006  
  1007  	var v interface{}
  1008  	err := json.Unmarshal(data, &v)
  1009  	require.NoError(t, err)
  1010  
  1011  	var buf bytes.Buffer
  1012  	err = toml.NewEncoder(&buf).Encode(v)
  1013  	require.NoError(t, err)
  1014  
  1015  	expected := `[[a]]
  1016  [a.b]
  1017  c = 'd'
  1018  `
  1019  	assert.Equal(t, expected, buf.String())
  1020  }
  1021  
  1022  func TestIssue424(t *testing.T) {
  1023  	type Message1 struct {
  1024  		Text string
  1025  	}
  1026  
  1027  	type Message2 struct {
  1028  		Text string `multiline:"true"`
  1029  	}
  1030  
  1031  	msg1 := Message1{"Hello\\World"}
  1032  	msg2 := Message2{"Hello\\World"}
  1033  
  1034  	toml1, err := toml.Marshal(msg1)
  1035  	require.NoError(t, err)
  1036  
  1037  	toml2, err := toml.Marshal(msg2)
  1038  	require.NoError(t, err)
  1039  
  1040  	msg1parsed := Message1{}
  1041  	err = toml.Unmarshal(toml1, &msg1parsed)
  1042  	require.NoError(t, err)
  1043  	require.Equal(t, msg1, msg1parsed)
  1044  
  1045  	msg2parsed := Message2{}
  1046  	err = toml.Unmarshal(toml2, &msg2parsed)
  1047  	require.NoError(t, err)
  1048  	require.Equal(t, msg2, msg2parsed)
  1049  }
  1050  
  1051  func TestIssue567(t *testing.T) {
  1052  	var m map[string]interface{}
  1053  	err := toml.Unmarshal([]byte("A = 12:08:05"), &m)
  1054  	require.NoError(t, err)
  1055  	require.IsType(t, m["A"], toml.LocalTime{})
  1056  }
  1057  
  1058  func TestIssue590(t *testing.T) {
  1059  	type CustomType int
  1060  	var cfg struct {
  1061  		Option CustomType `toml:"option"`
  1062  	}
  1063  	err := toml.Unmarshal([]byte("option = 42"), &cfg)
  1064  	require.NoError(t, err)
  1065  }
  1066  
  1067  func TestIssue571(t *testing.T) {
  1068  	type Foo struct {
  1069  		Float32 float32
  1070  		Float64 float64
  1071  	}
  1072  
  1073  	const closeEnough = 1e-9
  1074  
  1075  	foo := Foo{
  1076  		Float32: 42,
  1077  		Float64: 43,
  1078  	}
  1079  	b, err := toml.Marshal(foo)
  1080  	require.NoError(t, err)
  1081  
  1082  	var foo2 Foo
  1083  	err = toml.Unmarshal(b, &foo2)
  1084  	require.NoError(t, err)
  1085  
  1086  	assert.InDelta(t, 42, foo2.Float32, closeEnough)
  1087  	assert.InDelta(t, 43, foo2.Float64, closeEnough)
  1088  }
  1089  
  1090  func TestIssue678(t *testing.T) {
  1091  	type Config struct {
  1092  		BigInt big.Int
  1093  	}
  1094  
  1095  	cfg := &Config{
  1096  		BigInt: *big.NewInt(123),
  1097  	}
  1098  
  1099  	out, err := toml.Marshal(cfg)
  1100  	require.NoError(t, err)
  1101  	assert.Equal(t, "BigInt = '123'\n", string(out))
  1102  
  1103  	cfg2 := &Config{}
  1104  	err = toml.Unmarshal(out, cfg2)
  1105  	require.NoError(t, err)
  1106  	require.Equal(t, cfg, cfg2)
  1107  }
  1108  
  1109  func TestIssue752(t *testing.T) {
  1110  	type Fooer interface {
  1111  		Foo() string
  1112  	}
  1113  
  1114  	type Container struct {
  1115  		Fooer
  1116  	}
  1117  
  1118  	c := Container{}
  1119  
  1120  	out, err := toml.Marshal(c)
  1121  	require.NoError(t, err)
  1122  	require.Equal(t, "", string(out))
  1123  }
  1124  
  1125  func TestIssue768(t *testing.T) {
  1126  	type cfg struct {
  1127  		Name string `comment:"This is a multiline comment.\nThis is line 2."`
  1128  	}
  1129  
  1130  	out, err := toml.Marshal(&cfg{})
  1131  	require.NoError(t, err)
  1132  
  1133  	expected := `# This is a multiline comment.
  1134  # This is line 2.
  1135  Name = ''
  1136  `
  1137  
  1138  	require.Equal(t, expected, string(out))
  1139  }
  1140  
  1141  func TestIssue786(t *testing.T) {
  1142  	type Dependencies struct {
  1143  		Dependencies         []string `toml:"dependencies,multiline,omitempty"`
  1144  		BuildDependencies    []string `toml:"buildDependencies,multiline,omitempty"`
  1145  		OptionalDependencies []string `toml:"optionalDependencies,multiline,omitempty"`
  1146  	}
  1147  
  1148  	type Test struct {
  1149  		Dependencies Dependencies `toml:"dependencies,omitempty"`
  1150  	}
  1151  
  1152  	x := Test{}
  1153  	b, err := toml.Marshal(x)
  1154  	require.NoError(t, err)
  1155  
  1156  	require.Equal(t, "", string(b))
  1157  
  1158  	type General struct {
  1159  		From      string `toml:"from,omitempty" json:"from,omitempty" comment:"from in graphite-web format, the local TZ is used"`
  1160  		Randomize bool   `toml:"randomize" json:"randomize" comment:"randomize starting time with [0,step)"`
  1161  	}
  1162  
  1163  	type Custom struct {
  1164  		Name string `toml:"name" json:"name,omitempty" comment:"names for generator, braces are expanded like in shell"`
  1165  		Type string `toml:"type,omitempty" json:"type,omitempty" comment:"type of generator"`
  1166  		General
  1167  	}
  1168  	type Config struct {
  1169  		General
  1170  		Custom []Custom `toml:"custom,omitempty" json:"custom,omitempty" comment:"generators with custom parameters can be specified separately"`
  1171  	}
  1172  
  1173  	buf := new(bytes.Buffer)
  1174  	config := &Config{General: General{From: "-2d", Randomize: true}}
  1175  	config.Custom = []Custom{{Name: "omit", General: General{Randomize: false}}}
  1176  	config.Custom = append(config.Custom, Custom{Name: "present", General: General{From: "-2d", Randomize: true}})
  1177  	encoder := toml.NewEncoder(buf)
  1178  	encoder.Encode(config)
  1179  
  1180  	expected := `# from in graphite-web format, the local TZ is used
  1181  from = '-2d'
  1182  # randomize starting time with [0,step)
  1183  randomize = true
  1184  
  1185  # generators with custom parameters can be specified separately
  1186  [[custom]]
  1187  # names for generator, braces are expanded like in shell
  1188  name = 'omit'
  1189  # randomize starting time with [0,step)
  1190  randomize = false
  1191  
  1192  [[custom]]
  1193  # names for generator, braces are expanded like in shell
  1194  name = 'present'
  1195  # from in graphite-web format, the local TZ is used
  1196  from = '-2d'
  1197  # randomize starting time with [0,step)
  1198  randomize = true
  1199  `
  1200  
  1201  	require.Equal(t, expected, buf.String())
  1202  }
  1203  
  1204  func TestMarhsalIssue888(t *testing.T) {
  1205  	type Thing struct {
  1206  		FieldA string `comment:"my field A"`
  1207  		FieldB string `comment:"my field B"`
  1208  	}
  1209  
  1210  	type Cfg struct {
  1211  		Custom []Thing `comment:"custom config"`
  1212  	}
  1213  
  1214  	buf := new(bytes.Buffer)
  1215  
  1216  	config := Cfg{
  1217  		Custom: []Thing{
  1218  			{FieldA: "field a 1", FieldB: "field b 1"},
  1219  			{FieldA: "field a 2", FieldB: "field b 2"},
  1220  		},
  1221  	}
  1222  
  1223  	encoder := toml.NewEncoder(buf).SetIndentTables(true)
  1224  	encoder.Encode(config)
  1225  
  1226  	expected := `# custom config
  1227  [[Custom]]
  1228    # my field A
  1229    FieldA = 'field a 1'
  1230    # my field B
  1231    FieldB = 'field b 1'
  1232  
  1233  [[Custom]]
  1234    # my field A
  1235    FieldA = 'field a 2'
  1236    # my field B
  1237    FieldB = 'field b 2'
  1238  `
  1239  
  1240  	require.Equal(t, expected, buf.String())
  1241  }
  1242  
  1243  func TestMarshalNestedAnonymousStructs(t *testing.T) {
  1244  	type Embedded struct {
  1245  		Value string `toml:"value" json:"value"`
  1246  		Top   struct {
  1247  			Value string `toml:"value" json:"value"`
  1248  		} `toml:"top" json:"top"`
  1249  	}
  1250  
  1251  	type Named struct {
  1252  		Value string `toml:"value" json:"value"`
  1253  	}
  1254  
  1255  	var doc struct {
  1256  		Embedded
  1257  		Named     `toml:"named" json:"named"`
  1258  		Anonymous struct {
  1259  			Value string `toml:"value" json:"value"`
  1260  		} `toml:"anonymous" json:"anonymous"`
  1261  	}
  1262  
  1263  	expected := `value = ''
  1264  
  1265  [top]
  1266  value = ''
  1267  
  1268  [named]
  1269  value = ''
  1270  
  1271  [anonymous]
  1272  value = ''
  1273  `
  1274  
  1275  	result, err := toml.Marshal(doc)
  1276  	require.NoError(t, err)
  1277  	require.Equal(t, expected, string(result))
  1278  }
  1279  
  1280  func TestMarshalNestedAnonymousStructs_DuplicateField(t *testing.T) {
  1281  	type Embedded struct {
  1282  		Value string `toml:"value" json:"value"`
  1283  		Top   struct {
  1284  			Value string `toml:"value" json:"value"`
  1285  		} `toml:"top" json:"top"`
  1286  	}
  1287  
  1288  	var doc struct {
  1289  		Value string `toml:"value" json:"value"`
  1290  		Embedded
  1291  	}
  1292  	doc.Embedded.Value = "shadowed"
  1293  	doc.Value = "shadows"
  1294  
  1295  	expected := `value = 'shadows'
  1296  
  1297  [top]
  1298  value = ''
  1299  `
  1300  
  1301  	result, err := toml.Marshal(doc)
  1302  	require.NoError(t, err)
  1303  	require.NoError(t, err)
  1304  	require.Equal(t, expected, string(result))
  1305  }
  1306  
  1307  func TestLocalTime(t *testing.T) {
  1308  	v := map[string]toml.LocalTime{
  1309  		"a": {
  1310  			Hour:       1,
  1311  			Minute:     2,
  1312  			Second:     3,
  1313  			Nanosecond: 4,
  1314  		},
  1315  	}
  1316  
  1317  	expected := `a = 01:02:03.000000004
  1318  `
  1319  
  1320  	out, err := toml.Marshal(v)
  1321  	require.NoError(t, err)
  1322  	require.Equal(t, expected, string(out))
  1323  }
  1324  
  1325  func TestMarshalUint64Overflow(t *testing.T) {
  1326  	// The TOML spec only requires implementation to provide support for the
  1327  	// int64 range. To avoid generating TOML documents that would not be
  1328  	// supported by standard-compliant parsers, uint64 > max int64 cannot be
  1329  	// marshaled.
  1330  	x := map[string]interface{}{
  1331  		"foo": uint64(math.MaxInt64) + 1,
  1332  	}
  1333  
  1334  	_, err := toml.Marshal(x)
  1335  	require.Error(t, err)
  1336  }
  1337  
  1338  func TestIndentWithInlineTable(t *testing.T) {
  1339  	x := map[string][]map[string]string{
  1340  		"one": {
  1341  			{"0": "0"},
  1342  			{"1": "1"},
  1343  		},
  1344  	}
  1345  	expected := `one = [
  1346    {0 = '0'},
  1347    {1 = '1'}
  1348  ]
  1349  `
  1350  	var buf bytes.Buffer
  1351  	enc := toml.NewEncoder(&buf)
  1352  	enc.SetIndentTables(true)
  1353  	enc.SetTablesInline(true)
  1354  	enc.SetArraysMultiline(true)
  1355  	require.NoError(t, enc.Encode(x))
  1356  	assert.Equal(t, expected, buf.String())
  1357  }
  1358  
  1359  type C3 struct {
  1360  	Value  int   `toml:",commented"`
  1361  	Values []int `toml:",commented"`
  1362  }
  1363  
  1364  type C2 struct {
  1365  	Int       int64
  1366  	String    string
  1367  	ArrayInts []int
  1368  	Structs   []C3
  1369  }
  1370  
  1371  type C1 struct {
  1372  	Int       int64  `toml:",commented"`
  1373  	String    string `toml:",commented"`
  1374  	ArrayInts []int  `toml:",commented"`
  1375  	Structs   []C3   `toml:",commented"`
  1376  }
  1377  
  1378  type Commented struct {
  1379  	Int    int64  `toml:",commented"`
  1380  	String string `toml:",commented"`
  1381  
  1382  	C1 C1
  1383  	C2 C2 `toml:",commented"` // same as C1, but commented at top level
  1384  }
  1385  
  1386  func TestMarshalCommented(t *testing.T) {
  1387  	c := Commented{
  1388  		Int:    42,
  1389  		String: "root",
  1390  
  1391  		C1: C1{
  1392  			Int:       11,
  1393  			String:    "C1",
  1394  			ArrayInts: []int{1, 2, 3},
  1395  			Structs: []C3{
  1396  				{Value: 100},
  1397  				{Values: []int{4, 5, 6}},
  1398  			},
  1399  		},
  1400  		C2: C2{
  1401  			Int:       22,
  1402  			String:    "C2",
  1403  			ArrayInts: []int{1, 2, 3},
  1404  			Structs: []C3{
  1405  				{Value: 100},
  1406  				{Values: []int{4, 5, 6}},
  1407  			},
  1408  		},
  1409  	}
  1410  
  1411  	out, err := toml.Marshal(c)
  1412  	require.NoError(t, err)
  1413  
  1414  	expected := `# Int = 42
  1415  # String = 'root'
  1416  
  1417  [C1]
  1418  # Int = 11
  1419  # String = 'C1'
  1420  # ArrayInts = [1, 2, 3]
  1421  
  1422  # [[C1.Structs]]
  1423  # Value = 100
  1424  # Values = []
  1425  
  1426  # [[C1.Structs]]
  1427  # Value = 0
  1428  # Values = [4, 5, 6]
  1429  
  1430  # [C2]
  1431  # Int = 22
  1432  # String = 'C2'
  1433  # ArrayInts = [1, 2, 3]
  1434  
  1435  # [[C2.Structs]]
  1436  # Value = 100
  1437  # Values = []
  1438  
  1439  # [[C2.Structs]]
  1440  # Value = 0
  1441  # Values = [4, 5, 6]
  1442  `
  1443  
  1444  	require.Equal(t, expected, string(out))
  1445  }
  1446  
  1447  func ExampleMarshal() {
  1448  	type MyConfig struct {
  1449  		Version int
  1450  		Name    string
  1451  		Tags    []string
  1452  	}
  1453  
  1454  	cfg := MyConfig{
  1455  		Version: 2,
  1456  		Name:    "go-toml",
  1457  		Tags:    []string{"go", "toml"},
  1458  	}
  1459  
  1460  	b, err := toml.Marshal(cfg)
  1461  	if err != nil {
  1462  		panic(err)
  1463  	}
  1464  	fmt.Println(string(b))
  1465  
  1466  	// Output:
  1467  	// Version = 2
  1468  	// Name = 'go-toml'
  1469  	// Tags = ['go', 'toml']
  1470  }
  1471  
  1472  // Example that uses the 'commented' field tag option to generate an example
  1473  // configuration file that has commented out sections (example from
  1474  // go-graphite/graphite-clickhouse).
  1475  func ExampleMarshal_commented() {
  1476  
  1477  	type Common struct {
  1478  		Listen               string        `toml:"listen"                     comment:"general listener"`
  1479  		PprofListen          string        `toml:"pprof-listen"               comment:"listener to serve /debug/pprof requests. '-pprof' argument overrides it"`
  1480  		MaxMetricsPerTarget  int           `toml:"max-metrics-per-target"     comment:"limit numbers of queried metrics per target in /render requests, 0 or negative = unlimited"`
  1481  		MemoryReturnInterval time.Duration `toml:"memory-return-interval"     comment:"daemon will return the freed memory to the OS when it>0"`
  1482  	}
  1483  
  1484  	type Costs struct {
  1485  		Cost       *int           `toml:"cost"        comment:"default cost (for wildcarded equalence or matched with regex, or if no value cost set)"`
  1486  		ValuesCost map[string]int `toml:"values-cost" comment:"cost with some value (for equalence without wildcards) (additional tuning, usually not needed)"`
  1487  	}
  1488  
  1489  	type ClickHouse struct {
  1490  		URL string `toml:"url" comment:"default url, see https://clickhouse.tech/docs/en/interfaces/http. Can be overwritten with query-params"`
  1491  
  1492  		RenderMaxQueries        int               `toml:"render-max-queries" comment:"Max queries to render queiries"`
  1493  		RenderConcurrentQueries int               `toml:"render-concurrent-queries" comment:"Concurrent queries to render queiries"`
  1494  		TaggedCosts             map[string]*Costs `toml:"tagged-costs,commented"`
  1495  		TreeTable               string            `toml:"tree-table,commented"`
  1496  		ReverseTreeTable        string            `toml:"reverse-tree-table,commented"`
  1497  		DateTreeTable           string            `toml:"date-tree-table,commented"`
  1498  		DateTreeTableVersion    int               `toml:"date-tree-table-version,commented"`
  1499  		TreeTimeout             time.Duration     `toml:"tree-timeout,commented"`
  1500  		TagTable                string            `toml:"tag-table,commented"`
  1501  		ExtraPrefix             string            `toml:"extra-prefix"             comment:"add extra prefix (directory in graphite) for all metrics, w/o trailing dot"`
  1502  		ConnectTimeout          time.Duration     `toml:"connect-timeout"          comment:"TCP connection timeout"`
  1503  		DataTableLegacy         string            `toml:"data-table,commented"`
  1504  		RollupConfLegacy        string            `toml:"rollup-conf,commented"`
  1505  		MaxDataPoints           int               `toml:"max-data-points"          comment:"max points per metric when internal-aggregation=true"`
  1506  		InternalAggregation     bool              `toml:"internal-aggregation"     comment:"ClickHouse-side aggregation, see doc/aggregation.md"`
  1507  	}
  1508  
  1509  	type Tags struct {
  1510  		Rules      string `toml:"rules"`
  1511  		Date       string `toml:"date"`
  1512  		ExtraWhere string `toml:"extra-where"`
  1513  		InputFile  string `toml:"input-file"`
  1514  		OutputFile string `toml:"output-file"`
  1515  	}
  1516  
  1517  	type Config struct {
  1518  		Common     Common     `toml:"common"`
  1519  		ClickHouse ClickHouse `toml:"clickhouse"`
  1520  		Tags       Tags       `toml:"tags,commented"`
  1521  	}
  1522  
  1523  	cfg := &Config{
  1524  		Common: Common{
  1525  			Listen:               ":9090",
  1526  			PprofListen:          "",
  1527  			MaxMetricsPerTarget:  15000, // This is arbitrary value to protect CH from overload
  1528  			MemoryReturnInterval: 0,
  1529  		},
  1530  		ClickHouse: ClickHouse{
  1531  			URL:                 "http://localhost:8123?cancel_http_readonly_queries_on_client_close=1",
  1532  			ExtraPrefix:         "",
  1533  			ConnectTimeout:      time.Second,
  1534  			DataTableLegacy:     "",
  1535  			RollupConfLegacy:    "auto",
  1536  			MaxDataPoints:       1048576,
  1537  			InternalAggregation: true,
  1538  		},
  1539  		Tags: Tags{},
  1540  	}
  1541  
  1542  	out, err := toml.Marshal(cfg)
  1543  	if err != nil {
  1544  		panic(err)
  1545  	}
  1546  	err = toml.Unmarshal(out, &cfg)
  1547  	if err != nil {
  1548  		panic(err)
  1549  	}
  1550  
  1551  	fmt.Println(string(out))
  1552  
  1553  	// Output:
  1554  	// [common]
  1555  	// # general listener
  1556  	// listen = ':9090'
  1557  	// # listener to serve /debug/pprof requests. '-pprof' argument overrides it
  1558  	// pprof-listen = ''
  1559  	// # limit numbers of queried metrics per target in /render requests, 0 or negative = unlimited
  1560  	// max-metrics-per-target = 15000
  1561  	// # daemon will return the freed memory to the OS when it>0
  1562  	// memory-return-interval = 0
  1563  	//
  1564  	// [clickhouse]
  1565  	// # default url, see https://clickhouse.tech/docs/en/interfaces/http. Can be overwritten with query-params
  1566  	// url = 'http://localhost:8123?cancel_http_readonly_queries_on_client_close=1'
  1567  	// # Max queries to render queiries
  1568  	// render-max-queries = 0
  1569  	// # Concurrent queries to render queiries
  1570  	// render-concurrent-queries = 0
  1571  	// # tree-table = ''
  1572  	// # reverse-tree-table = ''
  1573  	// # date-tree-table = ''
  1574  	// # date-tree-table-version = 0
  1575  	// # tree-timeout = 0
  1576  	// # tag-table = ''
  1577  	// # add extra prefix (directory in graphite) for all metrics, w/o trailing dot
  1578  	// extra-prefix = ''
  1579  	// # TCP connection timeout
  1580  	// connect-timeout = 1000000000
  1581  	// # data-table = ''
  1582  	// # rollup-conf = 'auto'
  1583  	// # max points per metric when internal-aggregation=true
  1584  	// max-data-points = 1048576
  1585  	// # ClickHouse-side aggregation, see doc/aggregation.md
  1586  	// internal-aggregation = true
  1587  	//
  1588  	// # [tags]
  1589  	// # rules = ''
  1590  	// # date = ''
  1591  	// # extra-where = ''
  1592  	// # input-file = ''
  1593  	// # output-file = ''
  1594  }
  1595  
  1596  func TestReadmeComments(t *testing.T) {
  1597  	type TLS struct {
  1598  		Cipher  string `toml:"cipher"`
  1599  		Version string `toml:"version"`
  1600  	}
  1601  	type Config struct {
  1602  		Host string `toml:"host" comment:"Host IP to connect to."`
  1603  		Port int    `toml:"port" comment:"Port of the remote server."`
  1604  		Tls  TLS    `toml:"TLS,commented" comment:"Encryption parameters (optional)"`
  1605  	}
  1606  	example := Config{
  1607  		Host: "127.0.0.1",
  1608  		Port: 4242,
  1609  		Tls: TLS{
  1610  			Cipher:  "AEAD-AES128-GCM-SHA256",
  1611  			Version: "TLS 1.3",
  1612  		},
  1613  	}
  1614  	out, err := toml.Marshal(example)
  1615  	require.NoError(t, err)
  1616  
  1617  	expected := `# Host IP to connect to.
  1618  host = '127.0.0.1'
  1619  # Port of the remote server.
  1620  port = 4242
  1621  
  1622  # Encryption parameters (optional)
  1623  # [TLS]
  1624  # cipher = 'AEAD-AES128-GCM-SHA256'
  1625  # version = 'TLS 1.3'
  1626  `
  1627  	require.Equal(t, expected, string(out))
  1628  }
  1629  

View as plain text