git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/toml/example_test.go (about)

     1  package toml_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"net/mail"
     8  	"time"
     9  
    10  	"git.sr.ht/~pingoo/stdx/toml"
    11  )
    12  
    13  func ExampleEncoder_Encode() {
    14  	var (
    15  		date, _ = time.Parse(time.RFC822, "14 Mar 10 18:00 UTC")
    16  		buf     = new(bytes.Buffer)
    17  	)
    18  	err := toml.NewEncoder(buf).Encode(map[string]interface{}{
    19  		"date":   date,
    20  		"counts": []int{1, 1, 2, 3, 5, 8},
    21  		"hash": map[string]string{
    22  			"key1": "val1",
    23  			"key2": "val2",
    24  		},
    25  	})
    26  	if err != nil {
    27  		log.Fatal(err)
    28  	}
    29  	fmt.Println(buf.String())
    30  
    31  	// Output:
    32  	// counts = [1, 1, 2, 3, 5, 8]
    33  	// date = 2010-03-14T18:00:00Z
    34  	//
    35  	// [hash]
    36  	//   key1 = "val1"
    37  	//   key2 = "val2"
    38  }
    39  
    40  func ExampleMetaData_PrimitiveDecode() {
    41  	tomlBlob := `
    42  		ranking = ["Springsteen", "J Geils"]
    43  
    44  		[bands.Springsteen]
    45  		started = 1973
    46  		albums = ["Greetings", "WIESS", "Born to Run", "Darkness"]
    47  
    48  		[bands."J Geils"]
    49  		started = 1970
    50  		albums = ["The J. Geils Band", "Full House", "Blow Your Face Out"]
    51  		`
    52  
    53  	type (
    54  		band struct {
    55  			Started int
    56  			Albums  []string
    57  		}
    58  		classics struct {
    59  			Ranking []string
    60  			Bands   map[string]toml.Primitive
    61  		}
    62  	)
    63  
    64  	// Do the initial decode; reflection is delayed on Primitive values.
    65  	var music classics
    66  	md, err := toml.Decode(tomlBlob, &music)
    67  	if err != nil {
    68  		log.Fatal(err)
    69  	}
    70  
    71  	// MetaData still includes information on Primitive values.
    72  	fmt.Printf("Is `bands.Springsteen` defined? %v\n",
    73  		md.IsDefined("bands", "Springsteen"))
    74  
    75  	// Decode primitive data into Go values.
    76  	for _, artist := range music.Ranking {
    77  		// A band is a primitive value, so we need to decode it to get a real
    78  		// `band` value.
    79  		primValue := music.Bands[artist]
    80  
    81  		var aBand band
    82  		err = md.PrimitiveDecode(primValue, &aBand)
    83  		if err != nil {
    84  			log.Fatal(err)
    85  		}
    86  		fmt.Printf("%s started in %d.\n", artist, aBand.Started)
    87  	}
    88  
    89  	// Check to see if there were any fields left undecoded. Note that this
    90  	// won't be empty before decoding the Primitive value!
    91  	fmt.Printf("Undecoded: %q\n", md.Undecoded())
    92  
    93  	// Output:
    94  	// Is `bands.Springsteen` defined? true
    95  	// Springsteen started in 1973.
    96  	// J Geils started in 1970.
    97  	// Undecoded: []
    98  }
    99  
   100  func ExampleDecode() {
   101  	tomlBlob := `
   102  		# Some comments.
   103  		[alpha]
   104  		ip = "10.0.0.1"
   105  
   106  			[alpha.config]
   107  			Ports = [ 8001, 8002 ]
   108  			Location = "Toronto"
   109  			Created = 1987-07-05T05:45:00Z
   110  
   111  		[beta]
   112  		ip = "10.0.0.2"
   113  
   114  			[beta.config]
   115  			Ports = [ 9001, 9002 ]
   116  			Location = "New Jersey"
   117  			Created = 1887-01-05T05:55:00Z
   118  	`
   119  
   120  	type (
   121  		serverConfig struct {
   122  			Ports    []int
   123  			Location string
   124  			Created  time.Time
   125  		}
   126  		server struct {
   127  			IP     string       `toml:"ip,omitempty"`
   128  			Config serverConfig `toml:"config"`
   129  		}
   130  		servers map[string]server
   131  	)
   132  
   133  	var config servers
   134  	_, err := toml.Decode(tomlBlob, &config)
   135  	if err != nil {
   136  		log.Fatal(err)
   137  	}
   138  
   139  	for _, name := range []string{"alpha", "beta"} {
   140  		s := config[name]
   141  		fmt.Printf("Server: %s (ip: %s) in %s created on %s\n",
   142  			name, s.IP, s.Config.Location,
   143  			s.Config.Created.Format("2006-01-02"))
   144  		fmt.Printf("Ports: %v\n", s.Config.Ports)
   145  	}
   146  
   147  	// Output:
   148  	// Server: alpha (ip: 10.0.0.1) in Toronto created on 1987-07-05
   149  	// Ports: [8001 8002]
   150  	// Server: beta (ip: 10.0.0.2) in New Jersey created on 1887-01-05
   151  	// Ports: [9001 9002]
   152  }
   153  
   154  type address struct{ *mail.Address }
   155  
   156  func (a *address) UnmarshalText(text []byte) error {
   157  	var err error
   158  	a.Address, err = mail.ParseAddress(string(text))
   159  	return err
   160  }
   161  
   162  // Example Unmarshaler shows how to decode TOML strings into your own
   163  // custom data type.
   164  func Example_unmarshaler() {
   165  	blob := `
   166  		contacts = [
   167  			"Donald Duck <donald@duckburg.com>",
   168  			"Scrooge McDuck <scrooge@duckburg.com>",
   169  		]
   170  	`
   171  
   172  	var contacts struct {
   173  		// Implementation of the address type:
   174  		//
   175  		//     type address struct{ *mail.Address }
   176  		//
   177  		//     func (a *address) UnmarshalText(text []byte) error {
   178  		//         var err error
   179  		//         a.Address, err = mail.ParseAddress(string(text))
   180  		//         return err
   181  		//     }
   182  
   183  		Contacts []address
   184  	}
   185  
   186  	_, err := toml.Decode(blob, &contacts)
   187  	if err != nil {
   188  		log.Fatal(err)
   189  	}
   190  
   191  	for _, c := range contacts.Contacts {
   192  		fmt.Printf("%#v\n", c.Address)
   193  	}
   194  
   195  	// Output:
   196  	// &mail.Address{Name:"Donald Duck", Address:"donald@duckburg.com"}
   197  	// &mail.Address{Name:"Scrooge McDuck", Address:"scrooge@duckburg.com"}
   198  }
   199  
   200  // Example StrictDecoding shows how to detect if there are keys in the TOML
   201  // document that weren't decoded into the value given. This is useful for
   202  // returning an error to the user if they've included extraneous fields in their
   203  // configuration.
   204  func Example_strictDecoding() {
   205  	var blob = `
   206  		key1 = "value1"
   207  		key2 = "value2"
   208  		key3 = "value3"
   209  	`
   210  
   211  	var conf struct {
   212  		Key1 string
   213  		Key3 string
   214  	}
   215  	md, err := toml.Decode(blob, &conf)
   216  	if err != nil {
   217  		log.Fatal(err)
   218  	}
   219  
   220  	fmt.Printf("Undecoded keys: %q\n", md.Undecoded())
   221  	// Output:
   222  	// Undecoded keys: ["key2"]
   223  }
   224  
   225  type order struct {
   226  	// NOTE `order.parts` is a private slice of type `part` which is an
   227  	// interface and may only be loaded from toml using the UnmarshalTOML()
   228  	// method of the Umarshaler interface.
   229  	parts parts
   230  }
   231  
   232  type parts []part
   233  
   234  type part interface {
   235  	Name() string
   236  }
   237  
   238  type valve struct {
   239  	Type   string
   240  	ID     string
   241  	Size   float32
   242  	Rating int
   243  }
   244  
   245  func (v *valve) Name() string {
   246  	return fmt.Sprintf("VALVE: %s", v.ID)
   247  }
   248  
   249  type pipe struct {
   250  	Type     string
   251  	ID       string
   252  	Length   float32
   253  	Diameter int
   254  }
   255  
   256  func (p *pipe) Name() string {
   257  	return fmt.Sprintf("PIPE: %s", p.ID)
   258  }
   259  
   260  type cable struct {
   261  	Type   string
   262  	ID     string
   263  	Length int
   264  	Rating float32
   265  }
   266  
   267  func (c *cable) Name() string {
   268  	return fmt.Sprintf("CABLE: %s", c.ID)
   269  }
   270  
   271  func (o *order) UnmarshalTOML(data interface{}) error {
   272  	// NOTE the example below contains detailed type casting to show how the
   273  	// 'data' is retrieved. In operational use, a type cast wrapper may be
   274  	// preferred e.g.
   275  	//
   276  	// func AsMap(v interface{}) (map[string]interface{}, error) {
   277  	// 		return v.(map[string]interface{})
   278  	// }
   279  	//
   280  	// resulting in:
   281  	// d, _ := AsMap(data)
   282  	//
   283  
   284  	d, _ := data.(map[string]interface{})
   285  	parts, _ := d["parts"].([]map[string]interface{})
   286  
   287  	for _, p := range parts {
   288  
   289  		typ, _ := p["type"].(string)
   290  		id, _ := p["id"].(string)
   291  
   292  		// detect the type of part and handle each case
   293  		switch p["type"] {
   294  		case "valve":
   295  
   296  			size := float32(p["size"].(float64))
   297  			rating := int(p["rating"].(int64))
   298  
   299  			valve := &valve{
   300  				Type:   typ,
   301  				ID:     id,
   302  				Size:   size,
   303  				Rating: rating,
   304  			}
   305  
   306  			o.parts = append(o.parts, valve)
   307  
   308  		case "pipe":
   309  
   310  			length := float32(p["length"].(float64))
   311  			diameter := int(p["diameter"].(int64))
   312  
   313  			pipe := &pipe{
   314  				Type:     typ,
   315  				ID:       id,
   316  				Length:   length,
   317  				Diameter: diameter,
   318  			}
   319  
   320  			o.parts = append(o.parts, pipe)
   321  
   322  		case "cable":
   323  
   324  			length := int(p["length"].(int64))
   325  			rating := float32(p["rating"].(float64))
   326  
   327  			cable := &cable{
   328  				Type:   typ,
   329  				ID:     id,
   330  				Length: length,
   331  				Rating: rating,
   332  			}
   333  
   334  			o.parts = append(o.parts, cable)
   335  
   336  		}
   337  	}
   338  
   339  	return nil
   340  }
   341  
   342  // Example UnmarshalTOML shows how to implement a struct type that knows how to
   343  // unmarshal itself. The struct must take full responsibility for mapping the
   344  // values passed into the struct. The method may be used with interfaces in a
   345  // struct in cases where the actual type is not known until the data is
   346  // examined.
   347  func Example_unmarshalTOML() {
   348  	blob := `
   349  		[[parts]]
   350  		type = "valve"
   351  		id = "valve-1"
   352  		size = 1.2
   353  		rating = 4
   354  
   355  		[[parts]]
   356  		type = "valve"
   357  		id = "valve-2"
   358  		size = 2.1
   359  		rating = 5
   360  
   361  		[[parts]]
   362  		type = "pipe"
   363  		id = "pipe-1"
   364  		length = 2.1
   365  		diameter = 12
   366  
   367  		[[parts]]
   368  		type = "cable"
   369  		id = "cable-1"
   370  		length = 12
   371  		rating = 3.1
   372  	`
   373  
   374  	// See example_test.go in the source for the implementation of the order
   375  	// type.
   376  	o := &order{}
   377  
   378  	err := toml.Unmarshal([]byte(blob), o)
   379  	if err != nil {
   380  		log.Fatal(err)
   381  	}
   382  
   383  	fmt.Println(len(o.parts))
   384  	for _, part := range o.parts {
   385  		fmt.Println(part.Name())
   386  	}
   387  }