github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/encode_test.go (about)

     1  package yaml_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"math"
     8  	"reflect"
     9  	"strconv"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/bingoohuang/gg/pkg/yaml"
    14  	"github.com/bingoohuang/gg/pkg/yaml/ast"
    15  )
    16  
    17  var (
    18  	zero     = 0
    19  	emptyStr = ""
    20  )
    21  
    22  func TestEncoder(t *testing.T) {
    23  	tests := []struct {
    24  		source  string
    25  		value   interface{}
    26  		options []yaml.EncodeOption
    27  	}{
    28  		{
    29  			"null\n",
    30  			(*struct{})(nil),
    31  			nil,
    32  		},
    33  		{
    34  			"v: hi\n",
    35  			map[string]string{"v": "hi"},
    36  			nil,
    37  		},
    38  		{
    39  			"v: \"true\"\n",
    40  			map[string]string{"v": "true"},
    41  			nil,
    42  		},
    43  		{
    44  			"v: \"false\"\n",
    45  			map[string]string{"v": "false"},
    46  			nil,
    47  		},
    48  		{
    49  			"v: true\n",
    50  			map[string]interface{}{"v": true},
    51  			nil,
    52  		},
    53  		{
    54  			"v: false\n",
    55  			map[string]bool{"v": false},
    56  			nil,
    57  		},
    58  		{
    59  			"v: 10\n",
    60  			map[string]int{"v": 10},
    61  			nil,
    62  		},
    63  		{
    64  			"v: -10\n",
    65  			map[string]int{"v": -10},
    66  			nil,
    67  		},
    68  		{
    69  			"v: 4294967296\n",
    70  			map[string]int{"v": 4294967296},
    71  			nil,
    72  		},
    73  		{
    74  			"v: 0.1\n",
    75  			map[string]interface{}{"v": 0.1},
    76  			nil,
    77  		},
    78  		{
    79  			"v: 0.99\n",
    80  			map[string]float32{"v": 0.99},
    81  			nil,
    82  		},
    83  		{
    84  			"v: 0.123456789\n",
    85  			map[string]float64{"v": 0.123456789},
    86  			nil,
    87  		},
    88  		{
    89  			"v: -0.1\n",
    90  			map[string]float64{"v": -0.1},
    91  			nil,
    92  		},
    93  		{
    94  			"v: 1.0\n",
    95  			map[string]float64{"v": 1.0},
    96  			nil,
    97  		},
    98  		{
    99  			"v: 1e+06\n",
   100  			map[string]float64{"v": 1000000},
   101  			nil,
   102  		},
   103  		{
   104  			"v: .inf\n",
   105  			map[string]interface{}{"v": math.Inf(0)},
   106  			nil,
   107  		},
   108  		{
   109  			"v: -.inf\n",
   110  			map[string]interface{}{"v": math.Inf(-1)},
   111  			nil,
   112  		},
   113  		{
   114  			"v: .nan\n",
   115  			map[string]interface{}{"v": math.NaN()},
   116  			nil,
   117  		},
   118  		{
   119  			"v: null\n",
   120  			map[string]interface{}{"v": nil},
   121  			nil,
   122  		},
   123  		{
   124  			"v: \"\"\n",
   125  			map[string]string{"v": ""},
   126  			nil,
   127  		},
   128  		{
   129  			"v:\n- A\n- B\n",
   130  			map[string][]string{"v": {"A", "B"}},
   131  			nil,
   132  		},
   133  		{
   134  			"v:\n  - A\n  - B\n",
   135  			map[string][]string{"v": {"A", "B"}},
   136  			[]yaml.EncodeOption{
   137  				yaml.IndentSequence(true),
   138  			},
   139  		},
   140  		{
   141  			"v:\n- A\n- B\n",
   142  			map[string][2]string{"v": {"A", "B"}},
   143  			nil,
   144  		},
   145  		{
   146  			"v:\n  - A\n  - B\n",
   147  			map[string][2]string{"v": {"A", "B"}},
   148  			[]yaml.EncodeOption{
   149  				yaml.IndentSequence(true),
   150  			},
   151  		},
   152  		{
   153  			"a: -\n",
   154  			map[string]string{"a": "-"},
   155  			nil,
   156  		},
   157  		{
   158  			"123\n",
   159  			123,
   160  			nil,
   161  		},
   162  		{
   163  			"hello: world\n",
   164  			map[string]string{"hello": "world"},
   165  			nil,
   166  		},
   167  		{
   168  			"hello: |\n  hello\n  world\n",
   169  			map[string]string{"hello": "hello\nworld\n"},
   170  			nil,
   171  		},
   172  		{
   173  			"hello: |-\n  hello\n  world\n",
   174  			map[string]string{"hello": "hello\nworld"},
   175  			nil,
   176  		},
   177  		{
   178  			"hello: |+\n  hello\n  world\n\n",
   179  			map[string]string{"hello": "hello\nworld\n\n"},
   180  			nil,
   181  		},
   182  		{
   183  			"hello:\n  hello: |\n    hello\n    world\n",
   184  			map[string]map[string]string{"hello": {"hello": "hello\nworld\n"}},
   185  			nil,
   186  		},
   187  		{
   188  			"hello: |\r  hello\r  world\n",
   189  			map[string]string{"hello": "hello\rworld\r"},
   190  			nil,
   191  		},
   192  		{
   193  			"hello: |\r\n  hello\r\n  world\n",
   194  			map[string]string{"hello": "hello\r\nworld\r\n"},
   195  			nil,
   196  		},
   197  		{
   198  			"v: |-\n  username: hello\n  password: hello123\n",
   199  			map[string]interface{}{"v": "username: hello\npassword: hello123"},
   200  			[]yaml.EncodeOption{
   201  				yaml.UseLiteralStyleIfMultiline(true),
   202  			},
   203  		},
   204  		{
   205  			"v: |-\n  # comment\n  username: hello\n  password: hello123\n",
   206  			map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"},
   207  			[]yaml.EncodeOption{
   208  				yaml.UseLiteralStyleIfMultiline(true),
   209  			},
   210  		},
   211  		{
   212  			"v: \"# comment\\nusername: hello\\npassword: hello123\"\n",
   213  			map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"},
   214  			[]yaml.EncodeOption{
   215  				yaml.UseLiteralStyleIfMultiline(false),
   216  			},
   217  		},
   218  		{
   219  			"v:\n- A\n- 1\n- B:\n  - 2\n  - 3\n",
   220  			map[string]interface{}{
   221  				"v": []interface{}{
   222  					"A",
   223  					1,
   224  					map[string][]int{
   225  						"B": {2, 3},
   226  					},
   227  				},
   228  			},
   229  			nil,
   230  		},
   231  		{
   232  			"v:\n  - A\n  - 1\n  - B:\n      - 2\n      - 3\n  - 2\n",
   233  			map[string]interface{}{
   234  				"v": []interface{}{
   235  					"A",
   236  					1,
   237  					map[string][]int{
   238  						"B": {2, 3},
   239  					},
   240  					2,
   241  				},
   242  			},
   243  			[]yaml.EncodeOption{
   244  				yaml.IndentSequence(true),
   245  			},
   246  		},
   247  		{
   248  			"a:\n  b: c\n",
   249  			map[string]interface{}{
   250  				"a": map[string]string{
   251  					"b": "c",
   252  				},
   253  			},
   254  			nil,
   255  		},
   256  		{
   257  			"t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n",
   258  			map[string]string{
   259  				"t2": "2018-01-09T10:40:47Z",
   260  				"t4": "2098-01-09T10:40:47Z",
   261  			},
   262  			nil,
   263  		},
   264  		{
   265  			"a:\n  b: c\n  d: e\n",
   266  			map[string]interface{}{
   267  				"a": map[string]string{
   268  					"b": "c",
   269  					"d": "e",
   270  				},
   271  			},
   272  			nil,
   273  		},
   274  		{
   275  			"a: 3s\n",
   276  			map[string]string{
   277  				"a": "3s",
   278  			},
   279  			nil,
   280  		},
   281  		{
   282  			"a: <foo>\n",
   283  			map[string]string{"a": "<foo>"},
   284  			nil,
   285  		},
   286  		{
   287  			"a: \"1:1\"\n",
   288  			map[string]string{"a": "1:1"},
   289  			nil,
   290  		},
   291  		{
   292  			"a: 1.2.3.4\n",
   293  			map[string]string{"a": "1.2.3.4"},
   294  			nil,
   295  		},
   296  		{
   297  			"a: \"b: c\"\n",
   298  			map[string]string{"a": "b: c"},
   299  			nil,
   300  		},
   301  		{
   302  			"a: \"Hello #comment\"\n",
   303  			map[string]string{"a": "Hello #comment"},
   304  			nil,
   305  		},
   306  		{
   307  			"a: 100.5\n",
   308  			map[string]interface{}{
   309  				"a": 100.5,
   310  			},
   311  			nil,
   312  		},
   313  		{
   314  			"a: \"\\\\0\"\n",
   315  			map[string]string{"a": "\\0"},
   316  			nil,
   317  		},
   318  		{
   319  			"a: 1\nb: 2\nc: 3\nd: 4\nsub:\n  e: 5\n",
   320  			map[string]interface{}{
   321  				"a": 1,
   322  				"b": 2,
   323  				"c": 3,
   324  				"d": 4,
   325  				"sub": map[string]int{
   326  					"e": 5,
   327  				},
   328  			},
   329  			nil,
   330  		},
   331  		{
   332  			"a: 1\nb: []\n",
   333  			struct {
   334  				A int
   335  				B []string
   336  			}{
   337  				1, ([]string)(nil),
   338  			},
   339  			nil,
   340  		},
   341  		{
   342  			"a: 1\nb: []\n",
   343  			struct {
   344  				A int
   345  				B []string
   346  			}{
   347  				1, []string{},
   348  			},
   349  			nil,
   350  		},
   351  		{
   352  			"a: {}\n",
   353  			struct {
   354  				A map[string]interface{}
   355  			}{
   356  				map[string]interface{}{},
   357  			},
   358  			nil,
   359  		},
   360  		{
   361  			"a: b\nc: d\n",
   362  			struct {
   363  				A string
   364  				C string `yaml:"c"`
   365  			}{
   366  				"b", "d",
   367  			},
   368  			nil,
   369  		},
   370  		{
   371  			"a: 1\n",
   372  			struct {
   373  				A int
   374  				B int `yaml:"-"`
   375  			}{
   376  				1, 0,
   377  			},
   378  			nil,
   379  		},
   380  		{
   381  			"a: \"\"\n",
   382  			struct {
   383  				A string
   384  			}{
   385  				"",
   386  			},
   387  			nil,
   388  		},
   389  		{
   390  			"a: null\n",
   391  			struct {
   392  				A *string
   393  			}{
   394  				nil,
   395  			},
   396  			nil,
   397  		},
   398  		{
   399  			"a: \"\"\n",
   400  			struct {
   401  				A *string
   402  			}{
   403  				&emptyStr,
   404  			},
   405  			nil,
   406  		},
   407  		{
   408  			"a: null\n",
   409  			struct {
   410  				A *int
   411  			}{
   412  				nil,
   413  			},
   414  			nil,
   415  		},
   416  		{
   417  			"a: 0\n",
   418  			struct {
   419  				A *int
   420  			}{
   421  				&zero,
   422  			},
   423  			nil,
   424  		},
   425  
   426  		// Conditional flag
   427  		{
   428  			"a: 1\n",
   429  			struct {
   430  				A int `yaml:"a,omitempty"`
   431  				B int `yaml:"b,omitempty"`
   432  			}{1, 0},
   433  			nil,
   434  		},
   435  		{
   436  			"{}\n",
   437  			struct {
   438  				A int `yaml:"a,omitempty"`
   439  				B int `yaml:"b,omitempty"`
   440  			}{0, 0},
   441  			nil,
   442  		},
   443  
   444  		{
   445  			"a:\n  y: \"\"\n",
   446  			struct {
   447  				A *struct {
   448  					X string `yaml:"x,omitempty"`
   449  					Y string
   450  				}
   451  			}{&struct {
   452  				X string `yaml:"x,omitempty"`
   453  				Y string
   454  			}{}},
   455  			nil,
   456  		},
   457  
   458  		{
   459  			"a: {}\n",
   460  			struct {
   461  				A *struct {
   462  					X string `yaml:"x,omitempty"`
   463  					Y string `yaml:"y,omitempty"`
   464  				}
   465  			}{&struct {
   466  				X string `yaml:"x,omitempty"`
   467  				Y string `yaml:"y,omitempty"`
   468  			}{}},
   469  			nil,
   470  		},
   471  
   472  		{
   473  			"a: {x: 1}\n",
   474  			struct {
   475  				A *struct{ X, y int } `yaml:"a,omitempty,flow"`
   476  			}{&struct{ X, y int }{1, 2}},
   477  			nil,
   478  		},
   479  
   480  		{
   481  			"{}\n",
   482  			struct {
   483  				A *struct{ X, y int } `yaml:"a,omitempty,flow"`
   484  			}{nil},
   485  			nil,
   486  		},
   487  
   488  		{
   489  			"a: {x: 0}\n",
   490  			struct {
   491  				A *struct{ X, y int } `yaml:"a,omitempty,flow"`
   492  			}{&struct{ X, y int }{}},
   493  			nil,
   494  		},
   495  
   496  		{
   497  			"a: {x: 1}\n",
   498  			struct {
   499  				A struct{ X, y int } `yaml:"a,omitempty,flow"`
   500  			}{struct{ X, y int }{1, 2}},
   501  			nil,
   502  		},
   503  		{
   504  			"{}\n",
   505  			struct {
   506  				A struct{ X, y int } `yaml:"a,omitempty,flow"`
   507  			}{struct{ X, y int }{0, 1}},
   508  			nil,
   509  		},
   510  		{
   511  			"a: 1.0\n",
   512  			struct {
   513  				A float64 `yaml:"a,omitempty"`
   514  				B float64 `yaml:"b,omitempty"`
   515  			}{1, 0},
   516  			nil,
   517  		},
   518  		{
   519  			"a: 1\n",
   520  			struct {
   521  				A int
   522  				B []string `yaml:"b,omitempty"`
   523  			}{
   524  				1, []string{},
   525  			},
   526  			nil,
   527  		},
   528  
   529  		// Flow flag
   530  		{
   531  			"a: [1, 2]\n",
   532  			struct {
   533  				A []int `yaml:"a,flow"`
   534  			}{[]int{1, 2}},
   535  			nil,
   536  		},
   537  		{
   538  			"a: {b: c, d: e}\n",
   539  			&struct {
   540  				A map[string]string `yaml:"a,flow"`
   541  			}{map[string]string{"b": "c", "d": "e"}},
   542  			nil,
   543  		},
   544  		{
   545  			"a: {b: c, d: e}\n",
   546  			struct {
   547  				A struct {
   548  					B, D string
   549  				} `yaml:"a,flow"`
   550  			}{struct{ B, D string }{"c", "e"}},
   551  			nil,
   552  		},
   553  
   554  		// Multi bytes
   555  		{
   556  			"v: あいうえお\nv2: かきくけこ\n",
   557  			map[string]string{"v": "あいうえお", "v2": "かきくけこ"},
   558  			nil,
   559  		},
   560  
   561  		// time value
   562  		{
   563  			"v: 0001-01-01T00:00:00Z\n",
   564  			map[string]time.Time{"v": {}},
   565  			nil,
   566  		},
   567  		{
   568  			"v: 0001-01-01T00:00:00Z\n",
   569  			map[string]*time.Time{"v": {}},
   570  			nil,
   571  		},
   572  		{
   573  			"v: null\n",
   574  			map[string]*time.Time{"v": nil},
   575  			nil,
   576  		},
   577  		{
   578  			"v: 30s\n",
   579  			map[string]time.Duration{"v": 30 * time.Second},
   580  			nil,
   581  		},
   582  	}
   583  	for _, test := range tests {
   584  		var buf bytes.Buffer
   585  		enc := yaml.NewEncoder(&buf, test.options...)
   586  		if err := enc.Encode(test.value); err != nil {
   587  			t.Fatalf("%+v", err)
   588  		}
   589  		if test.source != buf.String() {
   590  			t.Fatalf("expect = [%s], actual = [%s]", test.source, buf.String())
   591  		}
   592  	}
   593  }
   594  
   595  func TestEncodeStructIncludeMap(t *testing.T) {
   596  	type U struct {
   597  		M map[string]string
   598  	}
   599  	type T struct {
   600  		A U
   601  	}
   602  	bytes, err := yaml.Marshal(T{
   603  		A: U{
   604  			M: map[string]string{"x": "y"},
   605  		},
   606  	})
   607  	if err != nil {
   608  		t.Fatalf("%+v", err)
   609  	}
   610  	expect := "a:\n  m:\n    x: y\n"
   611  	actual := string(bytes)
   612  	if actual != expect {
   613  		t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual)
   614  	}
   615  }
   616  
   617  func TestEncodeDefinedTypeKeyMap(t *testing.T) {
   618  	type K string
   619  	type U struct {
   620  		M map[K]string
   621  	}
   622  	bytes, err := yaml.Marshal(U{
   623  		M: map[K]string{K("x"): "y"},
   624  	})
   625  	if err != nil {
   626  		t.Fatalf("%+v", err)
   627  	}
   628  	expect := "m:\n  x: y\n"
   629  	actual := string(bytes)
   630  	if actual != expect {
   631  		t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual)
   632  	}
   633  }
   634  
   635  func TestEncodeWithAnchorAndAlias(t *testing.T) {
   636  	var buf bytes.Buffer
   637  	enc := yaml.NewEncoder(&buf)
   638  	type T struct {
   639  		A int
   640  		B string
   641  	}
   642  	var v struct {
   643  		A *T `yaml:"a,anchor=c"`
   644  		B *T `yaml:"b,alias=c"`
   645  	}
   646  	v.A = &T{A: 1, B: "hello"}
   647  	v.B = v.A
   648  	if err := enc.Encode(v); err != nil {
   649  		t.Fatalf("%+v", err)
   650  	}
   651  	expect := "a: &c\n  a: 1\n  b: hello\nb: *c\n"
   652  	if expect != buf.String() {
   653  		t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String())
   654  	}
   655  }
   656  
   657  func TestEncodeWithAutoAlias(t *testing.T) {
   658  	var buf bytes.Buffer
   659  	enc := yaml.NewEncoder(&buf)
   660  	type T struct {
   661  		I int
   662  		S string
   663  	}
   664  	var v struct {
   665  		A *T `yaml:"a,anchor=a"`
   666  		B *T `yaml:"b,anchor=b"`
   667  		C *T `yaml:"c,alias"`
   668  		D *T `yaml:"d,alias"`
   669  	}
   670  	v.A = &T{I: 1, S: "hello"}
   671  	v.B = &T{I: 2, S: "world"}
   672  	v.C = v.A
   673  	v.D = v.B
   674  	if err := enc.Encode(v); err != nil {
   675  		t.Fatalf("%+v", err)
   676  	}
   677  	expect := `a: &a
   678    i: 1
   679    s: hello
   680  b: &b
   681    i: 2
   682    s: world
   683  c: *a
   684  d: *b
   685  `
   686  	if expect != buf.String() {
   687  		t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String())
   688  	}
   689  }
   690  
   691  func TestEncodeWithImplicitAnchorAndAlias(t *testing.T) {
   692  	var buf bytes.Buffer
   693  	enc := yaml.NewEncoder(&buf)
   694  	type T struct {
   695  		I int
   696  		S string
   697  	}
   698  	var v struct {
   699  		A *T `yaml:"a,anchor"`
   700  		B *T `yaml:"b,anchor"`
   701  		C *T `yaml:"c,alias"`
   702  		D *T `yaml:"d,alias"`
   703  	}
   704  	v.A = &T{I: 1, S: "hello"}
   705  	v.B = &T{I: 2, S: "world"}
   706  	v.C = v.A
   707  	v.D = v.B
   708  	if err := enc.Encode(v); err != nil {
   709  		t.Fatalf("%+v", err)
   710  	}
   711  	expect := `a: &a
   712    i: 1
   713    s: hello
   714  b: &b
   715    i: 2
   716    s: world
   717  c: *a
   718  d: *b
   719  `
   720  	if expect != buf.String() {
   721  		t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String())
   722  	}
   723  }
   724  
   725  func TestEncodeWithMerge(t *testing.T) {
   726  	type Person struct {
   727  		*Person `yaml:",omitempty,inline,alias"`
   728  		Name    string `yaml:",omitempty"`
   729  		Age     int    `yaml:",omitempty"`
   730  	}
   731  	defaultPerson := &Person{
   732  		Name: "John Smith",
   733  		Age:  20,
   734  	}
   735  	people := []*Person{
   736  		{
   737  			Person: defaultPerson,
   738  			Name:   "Ken",
   739  			Age:    10,
   740  		},
   741  		{
   742  			Person: defaultPerson,
   743  		},
   744  	}
   745  	var doc struct {
   746  		Default *Person   `yaml:"default,anchor"`
   747  		People  []*Person `yaml:"people"`
   748  	}
   749  	doc.Default = defaultPerson
   750  	doc.People = people
   751  	var buf bytes.Buffer
   752  	enc := yaml.NewEncoder(&buf)
   753  	if err := enc.Encode(doc); err != nil {
   754  		t.Fatalf("%+v", err)
   755  	}
   756  	expect := `default: &default
   757    name: John Smith
   758    age: 20
   759  people:
   760  - <<: *default
   761    name: Ken
   762    age: 10
   763  - <<: *default
   764  `
   765  	if expect != buf.String() {
   766  		t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String())
   767  	}
   768  }
   769  
   770  func TestEncodeWithNestedYAML(t *testing.T) {
   771  	// Represents objects containing stringified YAML, and special chars
   772  	tests := []struct {
   773  		value interface{}
   774  		// If true, expects a different result between when using forced literal style or not
   775  		expectDifferent bool
   776  	}{
   777  		{
   778  			value:           map[string]interface{}{"v": "# comment\nname: hello\npassword: hello123\nspecial: \":ghost:\"\ntext: |\n  nested multiline!"},
   779  			expectDifferent: true,
   780  		},
   781  		{
   782  			value:           map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"},
   783  			expectDifferent: true,
   784  		},
   785  		{
   786  			value:           map[string]interface{}{"v": "# comment\n"},
   787  			expectDifferent: true,
   788  		},
   789  		{
   790  			value: map[string]interface{}{"v": "\n"},
   791  		},
   792  	}
   793  
   794  	for _, test := range tests {
   795  		yamlBytesForced, err := yaml.MarshalWithOptions(test.value, yaml.UseLiteralStyleIfMultiline(true))
   796  		if err != nil {
   797  			t.Fatalf("%+v", err)
   798  		}
   799  
   800  		// Convert it back for proper equality testing
   801  		var unmarshaled interface{}
   802  
   803  		if err := yaml.Unmarshal(yamlBytesForced, &unmarshaled); err != nil {
   804  			t.Fatalf("%+v", err)
   805  		}
   806  
   807  		if !reflect.DeepEqual(test.value, unmarshaled) {
   808  			t.Fatalf("expected %v(%T). but actual %v(%T)", test.value, test.value, unmarshaled, unmarshaled)
   809  		}
   810  
   811  		if test.expectDifferent {
   812  			yamlBytesNotForced, err := yaml.MarshalWithOptions(test.value)
   813  			if err != nil {
   814  				t.Fatalf("%+v", err)
   815  			}
   816  
   817  			if string(yamlBytesForced) == string(yamlBytesNotForced) {
   818  				t.Fatalf("expected different strings when force literal style is not enabled. forced: %s, not forced: %s", string(yamlBytesForced), string(yamlBytesNotForced))
   819  			}
   820  		}
   821  	}
   822  }
   823  
   824  func TestEncoder_Inline(t *testing.T) {
   825  	type base struct {
   826  		A int
   827  		B string
   828  	}
   829  	var buf bytes.Buffer
   830  	enc := yaml.NewEncoder(&buf)
   831  	if err := enc.Encode(struct {
   832  		*base `yaml:",inline"`
   833  		C     bool
   834  	}{
   835  		base: &base{
   836  			A: 1,
   837  			B: "hello",
   838  		},
   839  		C: true,
   840  	}); err != nil {
   841  		t.Fatalf("%+v", err)
   842  	}
   843  	expect := `
   844  a: 1
   845  b: hello
   846  c: true
   847  `
   848  	actual := "\n" + buf.String()
   849  	if expect != actual {
   850  		t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual)
   851  	}
   852  }
   853  
   854  func TestEncoder_InlineAndConflictKey(t *testing.T) {
   855  	type base struct {
   856  		A int
   857  		B string
   858  	}
   859  	var buf bytes.Buffer
   860  	enc := yaml.NewEncoder(&buf)
   861  	if err := enc.Encode(struct {
   862  		*base `yaml:",inline"`
   863  		A     int // conflict
   864  		C     bool
   865  	}{
   866  		base: &base{
   867  			A: 1,
   868  			B: "hello",
   869  		},
   870  		A: 0, // default value
   871  		C: true,
   872  	}); err != nil {
   873  		t.Fatalf("%+v", err)
   874  	}
   875  	expect := `
   876  b: hello
   877  a: 0
   878  c: true
   879  `
   880  	actual := "\n" + buf.String()
   881  	if expect != actual {
   882  		t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual)
   883  	}
   884  }
   885  
   886  func TestEncoder_Flow(t *testing.T) {
   887  	var buf bytes.Buffer
   888  	enc := yaml.NewEncoder(&buf, yaml.Flow(true))
   889  	var v struct {
   890  		A int
   891  		B string
   892  		C struct {
   893  			D int
   894  			E string
   895  		}
   896  		F []int
   897  	}
   898  	v.A = 1
   899  	v.B = "hello"
   900  	v.C.D = 3
   901  	v.C.E = "world"
   902  	v.F = []int{1, 2}
   903  	if err := enc.Encode(v); err != nil {
   904  		t.Fatalf("%+v", err)
   905  	}
   906  	expect := `
   907  {a: 1, b: hello, c: {d: 3, e: world}, f: [1, 2]}
   908  `
   909  	actual := "\n" + buf.String()
   910  	if expect != actual {
   911  		t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual)
   912  	}
   913  }
   914  
   915  func TestEncoder_FlowRecursive(t *testing.T) {
   916  	var v struct {
   917  		M map[string][]int `yaml:",flow"`
   918  	}
   919  	v.M = map[string][]int{
   920  		"test": {1, 2, 3},
   921  	}
   922  	var buf bytes.Buffer
   923  	if err := yaml.NewEncoder(&buf).Encode(v); err != nil {
   924  		t.Fatalf("%+v", err)
   925  	}
   926  	expect := `
   927  m: {test: [1, 2, 3]}
   928  `
   929  	actual := "\n" + buf.String()
   930  	if expect != actual {
   931  		t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual)
   932  	}
   933  }
   934  
   935  func TestEncoder_JSON(t *testing.T) {
   936  	var buf bytes.Buffer
   937  	enc := yaml.NewEncoder(&buf, yaml.JSON())
   938  	type st struct {
   939  		I int8
   940  		S string
   941  		F float32
   942  	}
   943  	if err := enc.Encode(struct {
   944  		I        int
   945  		U        uint
   946  		S        string
   947  		F        float64
   948  		Struct   *st
   949  		Slice    []int
   950  		Map      map[string]interface{}
   951  		Time     time.Time
   952  		Duration time.Duration
   953  	}{
   954  		I: -10,
   955  		U: 10,
   956  		S: "hello",
   957  		F: 3.14,
   958  		Struct: &st{
   959  			I: 2,
   960  			S: "world",
   961  			F: 1.23,
   962  		},
   963  		Slice: []int{1, 2, 3, 4, 5},
   964  		Map: map[string]interface{}{
   965  			"a": 1,
   966  			"b": 1.23,
   967  			"c": "json",
   968  		},
   969  		Time:     time.Time{},
   970  		Duration: 5 * time.Minute,
   971  	}); err != nil {
   972  		t.Fatalf("%+v", err)
   973  	}
   974  	expect := `
   975  {"i": -10, "u": 10, "s": "hello", "f": 3.14, "struct": {"i": 2, "s": "world", "f": 1.23}, "slice": [1, 2, 3, 4, 5], "map": {"a": 1, "b": 1.23, "c": "json"}, "time": "0001-01-01T00:00:00Z", "duration": "5m0s"}
   976  `
   977  	actual := "\n" + buf.String()
   978  	if expect != actual {
   979  		t.Fatalf("JSON style marshal error: expect=[%s] actual=[%s]", expect, actual)
   980  	}
   981  }
   982  
   983  func TestEncoder_MarshalAnchor(t *testing.T) {
   984  	type Host struct {
   985  		Hostname string
   986  		Username string
   987  		Password string
   988  	}
   989  	type HostDecl struct {
   990  		Host *Host `yaml:",anchor"`
   991  	}
   992  	type Queue struct {
   993  		Name  string `yaml:","`
   994  		*Host `yaml:",alias"`
   995  	}
   996  	var doc struct {
   997  		Hosts  []*HostDecl `yaml:"hosts"`
   998  		Queues []*Queue    `yaml:"queues"`
   999  	}
  1000  	host1 := &Host{
  1001  		Hostname: "host1.example.com",
  1002  		Username: "userA",
  1003  		Password: "pass1",
  1004  	}
  1005  	host2 := &Host{
  1006  		Hostname: "host2.example.com",
  1007  		Username: "userB",
  1008  		Password: "pass2",
  1009  	}
  1010  	doc.Hosts = []*HostDecl{
  1011  		{
  1012  			Host: host1,
  1013  		},
  1014  		{
  1015  			Host: host2,
  1016  		},
  1017  	}
  1018  	doc.Queues = []*Queue{
  1019  		{
  1020  			Name: "queue",
  1021  			Host: host1,
  1022  		}, {
  1023  			Name: "queue2",
  1024  			Host: host2,
  1025  		},
  1026  	}
  1027  	hostIdx := 1
  1028  	opt := yaml.MarshalAnchor(func(anchor *ast.AnchorNode, value interface{}) error {
  1029  		if _, ok := value.(*Host); ok {
  1030  			nameNode := anchor.Name.(*ast.StringNode)
  1031  			nameNode.Value = fmt.Sprintf("host%d", hostIdx)
  1032  			hostIdx++
  1033  		}
  1034  		return nil
  1035  	})
  1036  
  1037  	var buf bytes.Buffer
  1038  	if err := yaml.NewEncoder(&buf, opt).Encode(doc); err != nil {
  1039  		t.Fatalf("%+v", err)
  1040  	}
  1041  	expect := `
  1042  hosts:
  1043  - host: &host1
  1044      hostname: host1.example.com
  1045      username: userA
  1046      password: pass1
  1047  - host: &host2
  1048      hostname: host2.example.com
  1049      username: userB
  1050      password: pass2
  1051  queues:
  1052  - name: queue
  1053    host: *host1
  1054  - name: queue2
  1055    host: *host2
  1056  `
  1057  	if "\n"+buf.String() != expect {
  1058  		t.Fatalf("unexpected output. %s", buf.String())
  1059  	}
  1060  }
  1061  
  1062  type useJSONMarshalerTest struct{}
  1063  
  1064  func (t useJSONMarshalerTest) MarshalJSON() ([]byte, error) {
  1065  	return []byte(`{"a":[1, 2, 3]}`), nil
  1066  }
  1067  
  1068  func TestEncoder_UseJSONMarshaler(t *testing.T) {
  1069  	got, err := yaml.MarshalWithOptions(useJSONMarshalerTest{}, yaml.UseJSONMarshaler())
  1070  	if err != nil {
  1071  		t.Fatal(err)
  1072  	}
  1073  	expected := `
  1074  a:
  1075  - 1
  1076  - 2
  1077  - 3
  1078  `
  1079  	if expected != "\n"+string(got) {
  1080  		t.Fatalf("failed to use json marshaler. expected [%q] but got [%q]", expected, string(got))
  1081  	}
  1082  }
  1083  
  1084  func Example_Marshal_Node() {
  1085  	type T struct {
  1086  		Text ast.Node `yaml:"text"`
  1087  	}
  1088  	stringNode, err := yaml.ValueToNode("node example")
  1089  	if err != nil {
  1090  		panic(err)
  1091  	}
  1092  	bytes, err := yaml.Marshal(T{Text: stringNode})
  1093  	if err != nil {
  1094  		panic(err)
  1095  	}
  1096  	fmt.Println(string(bytes))
  1097  	// OUTPUT:
  1098  	// text: node example
  1099  }
  1100  
  1101  func Example_Marshal_ExplicitAnchorAlias() {
  1102  	type T struct {
  1103  		A int
  1104  		B string
  1105  	}
  1106  	var v struct {
  1107  		C *T `yaml:"c,anchor=x"`
  1108  		D *T `yaml:"d,alias=x"`
  1109  	}
  1110  	v.C = &T{A: 1, B: "hello"}
  1111  	v.D = v.C
  1112  	bytes, err := yaml.Marshal(v)
  1113  	if err != nil {
  1114  		panic(err)
  1115  	}
  1116  	fmt.Println(string(bytes))
  1117  	// OUTPUT:
  1118  	// c: &x
  1119  	//   a: 1
  1120  	//   b: hello
  1121  	// d: *x
  1122  }
  1123  
  1124  func Example_Marshal_ImplicitAnchorAlias() {
  1125  	type T struct {
  1126  		I int
  1127  		S string
  1128  	}
  1129  	var v struct {
  1130  		A *T `yaml:"a,anchor"`
  1131  		B *T `yaml:"b,anchor"`
  1132  		C *T `yaml:"c,alias"`
  1133  		D *T `yaml:"d,alias"`
  1134  	}
  1135  	v.A = &T{I: 1, S: "hello"}
  1136  	v.B = &T{I: 2, S: "world"}
  1137  	v.C = v.A // C has same pointer address to A
  1138  	v.D = v.B // D has same pointer address to B
  1139  	bytes, err := yaml.Marshal(v)
  1140  	if err != nil {
  1141  		panic(err)
  1142  	}
  1143  	fmt.Println(string(bytes))
  1144  	// OUTPUT:
  1145  	// a: &a
  1146  	//   i: 1
  1147  	//   s: hello
  1148  	// b: &b
  1149  	//   i: 2
  1150  	//   s: world
  1151  	// c: *a
  1152  	// d: *b
  1153  }
  1154  
  1155  type tMarshal []string
  1156  
  1157  func (t *tMarshal) MarshalYAML() ([]byte, error) {
  1158  	var buf bytes.Buffer
  1159  	buf.WriteString("tags:\n")
  1160  	for i, v := range *t {
  1161  		if i == 0 {
  1162  			fmt.Fprintf(&buf, "- %s\n", v)
  1163  		} else {
  1164  			fmt.Fprintf(&buf, "  %s\n", v)
  1165  		}
  1166  	}
  1167  	return buf.Bytes(), nil
  1168  }
  1169  
  1170  func Test_Marshaler(t *testing.T) {
  1171  	const expected = `- hello-world
  1172  `
  1173  
  1174  	// sanity check
  1175  	var l []string
  1176  	if err := yaml.Unmarshal([]byte(expected), &l); err != nil {
  1177  		t.Fatalf("failed to parse string: %s", err)
  1178  	}
  1179  
  1180  	buf, err := yaml.Marshal(tMarshal{"hello-world"})
  1181  	if err != nil {
  1182  		t.Fatalf("failed to marshal: %s", err)
  1183  	}
  1184  
  1185  	if string(buf) != expected {
  1186  		t.Fatalf("expected '%s', got '%s'", expected, buf)
  1187  	}
  1188  
  1189  	t.Logf("%s", buf)
  1190  }
  1191  
  1192  type marshalContext struct{}
  1193  
  1194  func (c *marshalContext) MarshalYAML(ctx context.Context) ([]byte, error) {
  1195  	v, ok := ctx.Value("k").(int)
  1196  	if !ok {
  1197  		return nil, fmt.Errorf("cannot get valid context")
  1198  	}
  1199  	if v != 1 {
  1200  		return nil, fmt.Errorf("cannot get valid context")
  1201  	}
  1202  	return []byte("1"), nil
  1203  }
  1204  
  1205  func Test_MarshalerWithContext(t *testing.T) {
  1206  	ctx := context.WithValue(context.Background(), "k", 1)
  1207  	bytes, err := yaml.MarshalContext(ctx, &marshalContext{})
  1208  	if err != nil {
  1209  		t.Fatalf("%+v", err)
  1210  	}
  1211  	if string(bytes) != "1\n" {
  1212  		t.Fatalf("failed marshal: %q", string(bytes))
  1213  	}
  1214  }
  1215  
  1216  type SlowMarshaler struct {
  1217  	A string
  1218  	B int
  1219  }
  1220  type FastMarshaler struct {
  1221  	A string
  1222  	B int
  1223  }
  1224  type (
  1225  	TextMarshaler          int64
  1226  	TextMarshalerContainer struct {
  1227  		Field TextMarshaler `yaml:"field"`
  1228  	}
  1229  )
  1230  
  1231  func (v SlowMarshaler) MarshalYAML() ([]byte, error) {
  1232  	var buf bytes.Buffer
  1233  	buf.WriteString("tags:\n")
  1234  	buf.WriteString("- slow-marshaler\n")
  1235  	buf.WriteString("a: " + v.A + "\n")
  1236  	buf.WriteString("b: " + strconv.FormatInt(int64(v.B), 10) + "\n")
  1237  	return buf.Bytes(), nil
  1238  }
  1239  
  1240  func (v FastMarshaler) MarshalYAML() (interface{}, error) {
  1241  	return yaml.MapSlice{
  1242  		{"tags", []string{"fast-marshaler"}},
  1243  		{"a", v.A},
  1244  		{"b", v.B},
  1245  	}, nil
  1246  }
  1247  
  1248  func (t TextMarshaler) MarshalText() ([]byte, error) {
  1249  	return []byte(strconv.FormatInt(int64(t), 8)), nil
  1250  }
  1251  
  1252  func Example_MarshalYAML() {
  1253  	var slow SlowMarshaler
  1254  	slow.A = "Hello slow poke"
  1255  	slow.B = 100
  1256  	buf, err := yaml.Marshal(slow)
  1257  	if err != nil {
  1258  		panic(err.Error())
  1259  	}
  1260  
  1261  	fmt.Println(string(buf))
  1262  
  1263  	var fast FastMarshaler
  1264  	fast.A = "Hello speed demon"
  1265  	fast.B = 100
  1266  	buf, err = yaml.Marshal(fast)
  1267  	if err != nil {
  1268  		panic(err.Error())
  1269  	}
  1270  
  1271  	fmt.Println(string(buf))
  1272  
  1273  	text := TextMarshalerContainer{
  1274  		Field: 11,
  1275  	}
  1276  	buf, err = yaml.Marshal(text)
  1277  	if err != nil {
  1278  		panic(err.Error())
  1279  	}
  1280  
  1281  	fmt.Println(string(buf))
  1282  	// OUTPUT:
  1283  	// tags:
  1284  	// - slow-marshaler
  1285  	// a: Hello slow poke
  1286  	// b: 100
  1287  	//
  1288  	// tags:
  1289  	// - fast-marshaler
  1290  	// a: Hello speed demon
  1291  	// b: 100
  1292  	//
  1293  	// field: 13
  1294  }