13 func TestDecodeSimple(t *testing.T) {
18 now = 1987-07-05T05:45:00Z
19 nowEast = 2017-06-22T16:15:21+08:00
20 nowWest = 2017-06-22T02:14:36-06:00
24 ["red", "green", "blue"],
25 ["cyan", "magenta", "yellow", "black"],
51 _, err := Decode(testSimple, &val)
56 now, err := time.Parse("2006-01-02T15:04:05", "1987-07-05T05:45:00")
60 nowEast, err := time.Parse("2006-01-02T15:04:05-07:00", "2017-06-22T16:15:21+08:00")
64 nowWest, err := time.Parse("2006-01-02T15:04:05-07:00", "2017-06-22T02:14:36-06:00")
78 {"red", "green", "blue"},
79 {"cyan", "magenta", "yellow", "black"},
82 "Cats": {Plato: "cat 1", Cauchy: "cat 2"},
85 if !reflect.DeepEqual(val, answer) {
86 t.Fatalf("Expected\n-----\n%#v\n-----\nbut got\n-----\n%#v\n",
91 func TestDecodeEmbedded(t *testing.T) {
92 type Dog struct{ Name string }
94 type cat struct{ Name string }
96 for _, test := range []struct {
99 decodeInto interface{}
100 wantDecoded interface{}
103 label: "embedded struct",
104 input: `Name = "milton"`,
105 decodeInto: &struct{ Dog }{},
106 wantDecoded: &struct{ Dog }{Dog{"milton"}},
109 label: "embedded non-nil pointer to struct",
110 input: `Name = "milton"`,
111 decodeInto: &struct{ *Dog }{},
112 wantDecoded: &struct{ *Dog }{&Dog{"milton"}},
115 label: "embedded nil pointer to struct",
117 decodeInto: &struct{ *Dog }{},
118 wantDecoded: &struct{ *Dog }{nil},
121 label: "unexported embedded struct",
122 input: `Name = "socks"`,
123 decodeInto: &struct{ cat }{},
124 wantDecoded: &struct{ cat }{cat{"socks"}},
127 label: "embedded int",
129 decodeInto: &struct{ Age }{},
130 wantDecoded: &struct{ Age }{-5},
133 _, err := Decode(test.input, test.decodeInto)
137 if !reflect.DeepEqual(test.wantDecoded, test.decodeInto) {
138 t.Errorf("%s: want decoded == %+v, got %+v",
139 test.label, test.wantDecoded, test.decodeInto)
144 func TestDecodeIgnoredFields(t *testing.T) {
146 Number int `toml:"-"`
153 if _, err := Decode(input, &s); err != nil {
157 t.Errorf("got: %d; want 0", s.Number)
161 func TestTableArrays(t *testing.T) {
162 var tomlTableArrays = `
170 name = "Meeting Across the River"
173 name = "Born in the USA"
179 name = "Dancing in the Dark"
195 expected := Music{[]Album{
196 {"Born to Run", []Song{{"Jungleland"}, {"Meeting Across the River"}}},
197 {"Born in the USA", []Song{{"Glory Days"}, {"Dancing in the Dark"}}},
200 if _, err := Decode(tomlTableArrays, &got); err != nil {
203 if !reflect.DeepEqual(expected, got) {
204 t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
208 func TestTableNesting(t *testing.T) {
209 for _, tt := range []struct {
213 {"[a.b.c]", []string{"a", "b", "c"}},
214 {`[a."b.c"]`, []string{"a", "b.c"}},
215 {`[a.'b.c']`, []string{"a", "b.c"}},
216 {`[a.' b ']`, []string{"a", " b "}},
217 {"[ d.e.f ]", []string{"d", "e", "f"}},
218 {"[ g . h . i ]", []string{"g", "h", "i"}},
219 {`[ j . "Êž" . 'l' ]`, []string{"j", "Êž", "l"}},
221 var m map[string]interface{}
222 if _, err := Decode(tt.t, &m); err != nil {
223 t.Errorf("Decode(%q): got error: %s", tt.t, err)
226 if keys := extractNestedKeys(m); !reflect.DeepEqual(keys, tt.want) {
227 t.Errorf("Decode(%q): got nested keys %#v; want %#v",
233 func extractNestedKeys(v map[string]interface{}) []string {
239 for k, m := range v {
240 result = append(result, k)
242 v, ok = m.(map[string]interface{})
251 // Case insensitive matching tests.
252 // A bit more comprehensive than needed given the current implementation,
253 // but implementations change.
254 // Probably still missing demonstrations of some ugly corner cases regarding
255 // case insensitive matching and multiple fields.
256 func TestCase(t *testing.T) {
262 tOpdate = 2006-01-02T15:04:05Z
263 tOparray = [ "array" ]
264 Match = "i should be in Match only"
265 MatcH = "i should be in MatcH only"
268 nEstedString = "another string"
271 type InsensitiveEd struct {
275 type InsensitiveNest struct {
279 type Insensitive struct {
293 tme, err := time.Parse(time.RFC3339, time.RFC3339[:len(time.RFC3339)-5])
297 expected := Insensitive{
303 TopArray: []string{"array"},
304 MatcH: "i should be in MatcH only",
305 Match: "i should be in Match only",
308 Nest: InsensitiveNest{
309 Ed: InsensitiveEd{NestedString: "another string"},
313 if _, err := Decode(caseToml, &got); err != nil {
316 if !reflect.DeepEqual(expected, got) {
317 t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
321 func TestPointers(t *testing.T) {
328 NamedObject map[string]*Object
333 s1, s2, s3 := "blah", "abc", "def"
336 Strptrs: []*string{&s2, &s3},
337 NamedObject: map[string]*Object{
338 "foo": {"FOO", "fooooo!!!"},
339 "bar": {"BAR", "ba-ba-ba-ba-barrrr!!!"},
341 BaseObject: &Object{"BASE", "da base"},
346 Strptrs = ["abc", "def"]
350 Description = "fooooo!!!"
354 Description = "ba-ba-ba-ba-barrrr!!!"
358 Description = "da base"
361 _, err := Decode(ex1, dict)
363 t.Errorf("Decode error: %v", err)
365 if !reflect.DeepEqual(expected, dict) {
366 t.Fatalf("\n%#v\n!=\n%#v\n", expected, dict)
370 func TestDecodeDatetime(t *testing.T) {
371 const noTimestamp = "2006-01-02T15:04:05"
372 for _, tt := range []struct {
377 {"1979-05-27T07:32:00Z", "1979-05-27T07:32:00Z", time.RFC3339},
378 {"1979-05-27T00:32:00-07:00", "1979-05-27T00:32:00-07:00", time.RFC3339},
380 "1979-05-27T00:32:00.999999-07:00",
381 "1979-05-27T00:32:00.999999-07:00",
384 {"1979-05-27T07:32:00", "1979-05-27T07:32:00", noTimestamp},
386 "1979-05-27T00:32:00.999999",
387 "1979-05-27T00:32:00.999999",
390 {"1979-05-27", "1979-05-27T00:00:00", noTimestamp},
392 var x struct{ D time.Time }
393 input := "d = " + tt.s
394 if _, err := Decode(input, &x); err != nil {
395 t.Errorf("Decode(%q): got error: %s", input, err)
398 want, err := time.ParseInLocation(tt.format, tt.t, time.Local)
402 if !x.D.Equal(want) {
403 t.Errorf("Decode(%q): got %s; want %s", input, x.D, want)
408 func TestDecodeBadDatetime(t *testing.T) {
409 var x struct{ T time.Time }
410 for _, s := range []string{
412 "2006-01-50T00:00:00Z",
417 if _, err := Decode(input, &x); err == nil {
418 t.Errorf("Expected invalid DateTime error for %q", s)
423 func TestDecodeMultilineStrings(t *testing.T) {
431 if _, err := Decode(s0, &x); err != nil {
434 if want := "a b \n c\nd e f\n"; x.S != want {
435 t.Errorf("got: %q; want: %q", x.S, want)
437 const s1 = `s = """a b c\
439 if _, err := Decode(s1, &x); err != nil {
442 if want := "a b c"; x.S != want {
443 t.Errorf("got: %q; want: %q", x.S, want)
452 func TestDecodeSimpleArray(t *testing.T) {
454 if _, err := Decode(`center = [0.0, 1.5, 0.0]`, &s1); err != nil {
459 func TestDecodeArrayWrongSize(t *testing.T) {
461 if _, err := Decode(`center = [0.1, 2.3]`, &s1); err == nil {
462 t.Fatal("Expected array type mismatch error")
466 func TestDecodeLargeIntoSmallInt(t *testing.T) {
471 if _, err := Decode(`value = 500`, &tab); err == nil {
472 t.Fatal("Expected integer out-of-bounds error.")
476 func TestDecodeSizedInts(t *testing.T) {
489 answer := table{1, 1, 1, 1, 1, -1, -1, -1, -1, -1}
503 if _, err := Decode(toml, &tab); err != nil {
507 t.Fatalf("Expected %#v but got %#v", answer, tab)
511 func TestDecodeInts(t *testing.T) {
512 for _, tt := range []struct {
519 {"1_234_567", 1234567},
521 {"-9_223_372_036_854_775_808", math.MinInt64},
522 {"9_223_372_036_854_775_807", math.MaxInt64},
524 var x struct{ N int64 }
525 input := "n = " + tt.s
526 if _, err := Decode(input, &x); err != nil {
527 t.Errorf("Decode(%q): got error: %s", input, err)
531 t.Errorf("Decode(%q): got %d; want %d", input, x.N, tt.want)
536 func TestDecodeFloats(t *testing.T) {
537 for _, tt := range []struct {
547 {"6.626e-34", 6.626e-34},
548 {"9_224_617.445_991_228_313", 9224617.445991228313},
549 {"9_876.54_32e1_0", 9876.5432e10},
551 var x struct{ N float64 }
552 input := "n = " + tt.s
553 if _, err := Decode(input, &x); err != nil {
554 t.Errorf("Decode(%q): got error: %s", input, err)
558 t.Errorf("Decode(%q): got %f; want %f", input, x.N, tt.want)
563 func TestDecodeMalformedNumbers(t *testing.T) {
564 for _, tt := range []struct {
568 {"++99", "expected a digit"},
569 {"0..1", "must be followed by one or more digits"},
570 {"0.1.2", "Invalid float value"},
571 {"1e2.3", "Invalid float value"},
572 {"1e2e3", "Invalid float value"},
573 {"_123", "expected value"},
574 {"123_", "surrounded by digits"},
575 {"1._23", "surrounded by digits"},
576 {"1e__23", "surrounded by digits"},
577 {"123.", "must be followed by one or more digits"},
578 {"1.e2", "must be followed by one or more digits"},
580 var x struct{ N interface{} }
581 input := "n = " + tt.s
582 _, err := Decode(input, &x)
584 t.Errorf("Decode(%q): got nil, want error containing %q",
588 if !strings.Contains(err.Error(), tt.want) {
589 t.Errorf("Decode(%q): got %q, want error containing %q",
595 func TestDecodeBadValues(t *testing.T) {
596 for _, tt := range []struct {
600 {3, "non-pointer int"},
601 {(*int)(nil), "nil"},
603 _, err := Decode(`x = 3`, tt.v)
605 t.Errorf("Decode(%v): got nil; want error containing %q",
609 if !strings.Contains(err.Error(), tt.want) {
610 t.Errorf("Decode(%v): got %q; want error containing %q",
616 func TestUnmarshaler(t *testing.T) {
620 name = "Hamboogie with fries"
623 [[dishes.hamboogie.ingredients]]
626 [[dishes.hamboogie.ingredients]]
629 [[dishes.hamboogie.ingredients]]
630 name = "Real Beef Patty"
632 [[dishes.hamboogie.ingredients]]
636 name = "Egg Salad with rice"
639 [[dishes.eggsalad.ingredients]]
642 [[dishes.eggsalad.ingredients]]
645 [[dishes.eggsalad.ingredients]]
649 if _, err := Decode(tomlBlob, m); err != nil {
653 if len(m.Dishes) != 2 {
654 t.Log("two dishes should be loaded with UnmarshalTOML()")
655 t.Errorf("expected %d but got %d", 2, len(m.Dishes))
658 eggSalad := m.Dishes["eggsalad"]
659 if _, ok := interface{}(eggSalad).(dish); !ok {
660 t.Errorf("expected a dish")
663 if eggSalad.Name != "Egg Salad with rice" {
664 t.Errorf("expected the dish to be named 'Egg Salad with rice'")
667 if len(eggSalad.Ingredients) != 3 {
668 t.Log("dish should be loaded with UnmarshalTOML()")
669 t.Errorf("expected %d but got %d", 3, len(eggSalad.Ingredients))
673 for _, i := range eggSalad.Ingredients {
674 if i.Name == "Rice" {
680 t.Error("Rice was not loaded in UnmarshalTOML()")
683 // test on a value - must be passed as *
685 if _, err := Decode(tomlBlob, &o); err != nil {
691 func TestDecodeInlineTable(t *testing.T) {
694 Types = {Chocolate = "yummy", Oatmeal = "best ever"}
697 Locations = {NY = {Temp = "not cold", Rating = 4}, MI = {Temp = "freezing", Rating = 9}}
699 type cookieJar struct {
700 Types map[string]string
702 type properties struct {
706 type seasons struct {
707 Locations map[string]properties
709 type wrapper struct {
715 meta, err := Decode(input, &got)
720 CookieJar: cookieJar{
721 Types: map[string]string{
722 "Chocolate": "yummy",
723 "Oatmeal": "best ever",
727 Locations: map[string]properties{
739 if !reflect.DeepEqual(got, want) {
740 t.Fatalf("after decode, got:\n\n%#v\n\nwant:\n\n%#v", got, want)
742 if len(meta.keys) != 12 {
743 t.Errorf("after decode, got %d meta keys; want 12", len(meta.keys))
745 if len(meta.types) != 12 {
746 t.Errorf("after decode, got %d meta types; want 12", len(meta.types))
750 func TestDecodeInlineTableArray(t *testing.T) {
757 // Example inline table array from the spec.
759 points = [ { x = 1, y = 2, z = 3 },
760 { x = 7, y = 8, z = 9 },
761 { x = 2, y = 4, z = 8 } ]
764 if _, err := Decode(in, &got); err != nil {
772 if !reflect.DeepEqual(got.Points, want) {
773 t.Errorf("got %#v; want %#v", got.Points, want)
777 func TestDecodeMalformedInlineTable(t *testing.T) {
778 for _, tt := range []struct {
782 {"{,}", "unexpected comma"},
783 {"{x = 3 y = 4}", "expected a comma or an inline table terminator"},
784 {"{x=3,,y=4}", "unexpected comma"},
785 {"{x=3,\ny=4}", "newlines not allowed"},
786 {"{x=3\n,y=4}", "newlines not allowed"},
788 var x struct{ A map[string]int }
789 input := "a = " + tt.s
790 _, err := Decode(input, &x)
792 t.Errorf("Decode(%q): got nil, want error containing %q",
796 if !strings.Contains(err.Error(), tt.want) {
797 t.Errorf("Decode(%q): got %q, want error containing %q",
804 Dishes map[string]dish
807 func (m *menu) UnmarshalTOML(p interface{}) error {
808 m.Dishes = make(map[string]dish)
809 data, _ := p.(map[string]interface{})
810 dishes := data["dishes"].(map[string]interface{})
811 for n, v := range dishes {
812 if d, ok := v.(map[string]interface{}); ok {
817 return fmt.Errorf("not a dish")
826 Ingredients []ingredient
829 func (d *dish) UnmarshalTOML(p interface{}) error {
830 data, _ := p.(map[string]interface{})
831 d.Name, _ = data["name"].(string)
832 d.Price, _ = data["price"].(float32)
833 ingredients, _ := data["ingredients"].([]map[string]interface{})
834 for _, e := range ingredients {
835 n, _ := interface{}(e).(map[string]interface{})
836 name, _ := n["name"].(string)
837 i := ingredient{name}
838 d.Ingredients = append(d.Ingredients, i)
843 type ingredient struct {
847 func TestDecodeSlices(t *testing.T) {
851 for i, tt := range []struct {
857 {T{[]string{}}, "", T{[]string{}}},
858 {T{[]string{"a", "b"}}, "", T{[]string{"a", "b"}}},
859 {T{}, "S = []", T{[]string{}}},
860 {T{[]string{}}, "S = []", T{[]string{}}},
861 {T{[]string{"a", "b"}}, "S = []", T{[]string{}}},
862 {T{}, `S = ["x"]`, T{[]string{"x"}}},
863 {T{[]string{}}, `S = ["x"]`, T{[]string{"x"}}},
864 {T{[]string{"a", "b"}}, `S = ["x"]`, T{[]string{"x"}}},
866 if _, err := Decode(tt.input, &tt.v); err != nil {
867 t.Errorf("[%d] %s", i, err)
870 if !reflect.DeepEqual(tt.v, tt.want) {
871 t.Errorf("[%d] got %#v; want %#v", i, tt.v, tt.want)
876 func TestDecodePrimitive(t *testing.T) {
883 slicep := func(s []int) *[]int { return &s }
884 arrayp := func(a [2]int) *[2]int { return &a }
885 mapp := func(m map[string]int) *map[string]int { return &m }
886 for i, tt := range []struct {
892 {slicep(nil), "", slicep(nil)},
893 {slicep([]int{}), "", slicep([]int{})},
894 {slicep([]int{1, 2, 3}), "", slicep([]int{1, 2, 3})},
895 {slicep(nil), "P = [1,2]", slicep([]int{1, 2})},
896 {slicep([]int{}), "P = [1,2]", slicep([]int{1, 2})},
897 {slicep([]int{1, 2, 3}), "P = [1,2]", slicep([]int{1, 2})},
900 {arrayp([2]int{2, 3}), "", arrayp([2]int{2, 3})},
901 {arrayp([2]int{2, 3}), "P = [3,4]", arrayp([2]int{3, 4})},
904 {mapp(nil), "", mapp(nil)},
905 {mapp(map[string]int{}), "", mapp(map[string]int{})},
906 {mapp(map[string]int{"a": 1}), "", mapp(map[string]int{"a": 1})},
907 {mapp(nil), "[P]\na = 2", mapp(map[string]int{"a": 2})},
908 {mapp(map[string]int{}), "[P]\na = 2", mapp(map[string]int{"a": 2})},
909 {mapp(map[string]int{"a": 1, "b": 3}), "[P]\na = 2", mapp(map[string]int{"a": 2, "b": 3})},
912 {&T{nil}, "[P]", &T{nil}},
913 {&T{[]int{}}, "[P]", &T{[]int{}}},
914 {&T{[]int{1, 2, 3}}, "[P]", &T{[]int{1, 2, 3}}},
915 {&T{nil}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
916 {&T{[]int{}}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
917 {&T{[]int{1, 2, 3}}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
920 md, err := Decode(tt.input, &s)
922 t.Errorf("[%d] Decode error: %s", i, err)
925 if err := md.PrimitiveDecode(s.P, tt.v); err != nil {
926 t.Errorf("[%d] PrimitiveDecode error: %s", i, err)
929 if !reflect.DeepEqual(tt.v, tt.want) {
930 t.Errorf("[%d] got %#v; want %#v", i, tt.v, tt.want)
935 func TestDecodeErrors(t *testing.T) {
936 for _, s := range []string{
941 // Cases found by fuzzing in
942 // https://github.com/BurntSushi/toml/issues/155.
943 `""�`, // used to panic with index out of range
944 `e="""`, // used to hang
947 _, err := Decode(s, &x)
949 t.Errorf("Decode(%q): got nil error", s)
954 // Test for https://github.com/BurntSushi/toml/pull/166.
955 func TestDecodeBoolArray(t *testing.T) {
956 for _, tt := range []struct {
963 &struct{ A []bool }{},
964 &struct{ A []bool }{[]bool{true, false}},
967 "a = {a = true, b = false}",
968 &struct{ A map[string]bool }{},
969 &struct{ A map[string]bool }{map[string]bool{"a": true, "b": false}},
972 if _, err := Decode(tt.s, tt.got); err != nil {
973 t.Errorf("Decode(%q): %s", tt.s, err)
976 if !reflect.DeepEqual(tt.got, tt.want) {
977 t.Errorf("Decode(%q): got %#v; want %#v", tt.s, tt.got, tt.want)
982 func ExampleMetaData_PrimitiveDecode() {
987 ranking = ["Springsteen", "J Geils"]
991 albums = ["Greetings", "WIESS", "Born to Run", "Darkness"]
995 albums = ["The J. Geils Band", "Full House", "Blow Your Face Out"]
1002 type classics struct {
1004 Bands map[string]Primitive
1007 // Do the initial decode. Reflection is delayed on Primitive values.
1009 if md, err = Decode(tomlBlob, &music); err != nil {
1013 // MetaData still includes information on Primitive values.
1014 fmt.Printf("Is `bands.Springsteen` defined? %v\n",
1015 md.IsDefined("bands", "Springsteen"))
1017 // Decode primitive data into Go values.
1018 for _, artist := range music.Ranking {
1019 // A band is a primitive value, so we need to decode it to get a
1020 // real `band` value.
1021 primValue := music.Bands[artist]
1024 if err = md.PrimitiveDecode(primValue, &aBand); err != nil {
1027 fmt.Printf("%s started in %d.\n", artist, aBand.Started)
1029 // Check to see if there were any fields left undecoded.
1030 // Note that this won't be empty before decoding the Primitive value!
1031 fmt.Printf("Undecoded: %q\n", md.Undecoded())
1034 // Is `bands.Springsteen` defined? true
1035 // Springsteen started in 1973.
1036 // J Geils started in 1970.
1040 func ExampleDecode() {
1047 Ports = [ 8001, 8002 ]
1048 Location = "Toronto"
1049 Created = 1987-07-05T05:45:00Z
1055 Ports = [ 9001, 9002 ]
1056 Location = "New Jersey"
1057 Created = 1887-01-05T05:55:00Z
1060 type serverConfig struct {
1066 type server struct {
1067 IP string `toml:"ip,omitempty"`
1068 Config serverConfig `toml:"config"`
1071 type servers map[string]server
1074 if _, err := Decode(tomlBlob, &config); err != nil {
1078 for _, name := range []string{"alpha", "beta"} {
1080 fmt.Printf("Server: %s (ip: %s) in %s created on %s\n",
1081 name, s.IP, s.Config.Location,
1082 s.Config.Created.Format("2006-01-02"))
1083 fmt.Printf("Ports: %v\n", s.Config.Ports)
1087 // Server: alpha (ip: 10.0.0.1) in Toronto created on 1987-07-05
1088 // Ports: [8001 8002]
1089 // Server: beta (ip: 10.0.0.2) in New Jersey created on 1887-01-05
1090 // Ports: [9001 9002]
1093 type duration struct {
1097 func (d *duration) UnmarshalText(text []byte) error {
1099 d.Duration, err = time.ParseDuration(string(text))
1103 // Example Unmarshaler shows how to decode TOML strings into your own
1104 // custom data type.
1105 func Example_unmarshaler() {
1108 name = "Thunder Road"
1112 name = "Stairway to Heaven"
1123 if _, err := Decode(blob, &favorites); err != nil {
1127 // Code to implement the TextUnmarshaler interface for `duration`:
1129 // type duration struct {
1133 // func (d *duration) UnmarshalText(text []byte) error {
1135 // d.Duration, err = time.ParseDuration(string(text))
1139 for _, s := range favorites.Song {
1140 fmt.Printf("%s (%s)\n", s.Name, s.Duration)
1143 // Thunder Road (4m49s)
1144 // Stairway to Heaven (8m3s)
1147 // Example StrictDecoding shows how to detect whether there are keys in the
1148 // TOML document that weren't decoded into the value given. This is useful
1149 // for returning an error to the user if they've included extraneous fields
1150 // in their configuration.
1151 func Example_strictDecoding() {
1157 type config struct {
1163 md, err := Decode(blob, &conf)
1167 fmt.Printf("Undecoded keys: %q\n", md.Undecoded())
1169 // Undecoded keys: ["key2"]
1172 // Example UnmarshalTOML shows how to implement a struct type that knows how to
1173 // unmarshal itself. The struct must take full responsibility for mapping the
1174 // values passed into the struct. The method may be used with interfaces in a
1175 // struct in cases where the actual type is not known until the data is
1177 func Example_unmarshalTOML() {
1205 err := Unmarshal([]byte(blob), o)
1210 fmt.Println(len(o.parts))
1212 for _, part := range o.parts {
1213 fmt.Println(part.Name())
1216 // Code to implement UmarshalJSON.
1218 // type order struct {
1219 // // NOTE `order.parts` is a private slice of type `part` which is an
1220 // // interface and may only be loaded from toml using the
1221 // // UnmarshalTOML() method of the Umarshaler interface.
1225 // func (o *order) UnmarshalTOML(data interface{}) error {
1227 // // NOTE the example below contains detailed type casting to show how
1228 // // the 'data' is retrieved. In operational use, a type cast wrapper
1229 // // may be preferred e.g.
1231 // // func AsMap(v interface{}) (map[string]interface{}, error) {
1232 // // return v.(map[string]interface{})
1236 // // d, _ := AsMap(data)
1239 // d, _ := data.(map[string]interface{})
1240 // parts, _ := d["parts"].([]map[string]interface{})
1242 // for _, p := range parts {
1244 // typ, _ := p["type"].(string)
1245 // id, _ := p["id"].(string)
1247 // // detect the type of part and handle each case
1248 // switch p["type"] {
1251 // size := float32(p["size"].(float64))
1252 // rating := int(p["rating"].(int64))
1261 // o.parts = append(o.parts, valve)
1265 // length := float32(p["length"].(float64))
1266 // diameter := int(p["diameter"].(int64))
1272 // Diameter: diameter,
1275 // o.parts = append(o.parts, pipe)
1279 // length := int(p["length"].(int64))
1280 // rating := float32(p["rating"].(float64))
1289 // o.parts = append(o.parts, cable)
1297 // type parts []part
1299 // type part interface {
1303 // type valve struct {
1310 // func (v *valve) Name() string {
1311 // return fmt.Sprintf("VALVE: %s", v.ID)
1314 // type pipe struct {
1321 // func (p *pipe) Name() string {
1322 // return fmt.Sprintf("PIPE: %s", p.ID)
1325 // type cable struct {
1332 // func (c *cable) Name() string {
1333 // return fmt.Sprintf("CABLE: %s", c.ID)
1346 // NOTE `order.parts` is a private slice of type `part` which is an
1347 // interface and may only be loaded from toml using the UnmarshalTOML()
1348 // method of the Umarshaler interface.
1352 func (o *order) UnmarshalTOML(data interface{}) error {
1354 // NOTE the example below contains detailed type casting to show how
1355 // the 'data' is retrieved. In operational use, a type cast wrapper
1356 // may be preferred e.g.
1358 // func AsMap(v interface{}) (map[string]interface{}, error) {
1359 // return v.(map[string]interface{})
1363 // d, _ := AsMap(data)
1366 d, _ := data.(map[string]interface{})
1367 parts, _ := d["parts"].([]map[string]interface{})
1369 for _, p := range parts {
1371 typ, _ := p["type"].(string)
1372 id, _ := p["id"].(string)
1374 // detect the type of part and handle each case
1378 size := float32(p["size"].(float64))
1379 rating := int(p["rating"].(int64))
1388 o.parts = append(o.parts, valve)
1392 length := float32(p["length"].(float64))
1393 diameter := int(p["diameter"].(int64))
1402 o.parts = append(o.parts, pipe)
1406 length := int(p["length"].(int64))
1407 rating := float32(p["rating"].(float64))
1416 o.parts = append(o.parts, cable)
1426 type part interface {
1437 func (v *valve) Name() string {
1438 return fmt.Sprintf("VALVE: %s", v.ID)
1448 func (p *pipe) Name() string {
1449 return fmt.Sprintf("PIPE: %s", p.ID)
1459 func (c *cable) Name() string {
1460 return fmt.Sprintf("CABLE: %s", c.ID)