...

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

Documentation: github.com/pelletier/go-toml

     1  package toml
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"runtime"
    10  	"strings"
    11  )
    12  
    13  type tomlValue struct {
    14  	value     interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
    15  	comment   string
    16  	commented bool
    17  	multiline bool
    18  	literal   bool
    19  	position  Position
    20  }
    21  
    22  // Tree is the result of the parsing of a TOML file.
    23  type Tree struct {
    24  	values    map[string]interface{} // string -> *tomlValue, *Tree, []*Tree
    25  	comment   string
    26  	commented bool
    27  	inline    bool
    28  	position  Position
    29  }
    30  
    31  func newTree() *Tree {
    32  	return newTreeWithPosition(Position{})
    33  }
    34  
    35  func newTreeWithPosition(pos Position) *Tree {
    36  	return &Tree{
    37  		values:   make(map[string]interface{}),
    38  		position: pos,
    39  	}
    40  }
    41  
    42  // TreeFromMap initializes a new Tree object using the given map.
    43  func TreeFromMap(m map[string]interface{}) (*Tree, error) {
    44  	result, err := toTree(m)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	return result.(*Tree), nil
    49  }
    50  
    51  // Position returns the position of the tree.
    52  func (t *Tree) Position() Position {
    53  	return t.position
    54  }
    55  
    56  // Has returns a boolean indicating if the given key exists.
    57  func (t *Tree) Has(key string) bool {
    58  	if key == "" {
    59  		return false
    60  	}
    61  	return t.HasPath(strings.Split(key, "."))
    62  }
    63  
    64  // HasPath returns true if the given path of keys exists, false otherwise.
    65  func (t *Tree) HasPath(keys []string) bool {
    66  	return t.GetPath(keys) != nil
    67  }
    68  
    69  // Keys returns the keys of the toplevel tree (does not recurse).
    70  func (t *Tree) Keys() []string {
    71  	keys := make([]string, len(t.values))
    72  	i := 0
    73  	for k := range t.values {
    74  		keys[i] = k
    75  		i++
    76  	}
    77  	return keys
    78  }
    79  
    80  // Get the value at key in the Tree.
    81  // Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
    82  // If you need to retrieve non-bare keys, use GetPath.
    83  // Returns nil if the path does not exist in the tree.
    84  // If keys is of length zero, the current tree is returned.
    85  func (t *Tree) Get(key string) interface{} {
    86  	if key == "" {
    87  		return t
    88  	}
    89  	return t.GetPath(strings.Split(key, "."))
    90  }
    91  
    92  // GetPath returns the element in the tree indicated by 'keys'.
    93  // If keys is of length zero, the current tree is returned.
    94  func (t *Tree) GetPath(keys []string) interface{} {
    95  	if len(keys) == 0 {
    96  		return t
    97  	}
    98  	subtree := t
    99  	for _, intermediateKey := range keys[:len(keys)-1] {
   100  		value, exists := subtree.values[intermediateKey]
   101  		if !exists {
   102  			return nil
   103  		}
   104  		switch node := value.(type) {
   105  		case *Tree:
   106  			subtree = node
   107  		case []*Tree:
   108  			// go to most recent element
   109  			if len(node) == 0 {
   110  				return nil
   111  			}
   112  			subtree = node[len(node)-1]
   113  		default:
   114  			return nil // cannot navigate through other node types
   115  		}
   116  	}
   117  	// branch based on final node type
   118  	switch node := subtree.values[keys[len(keys)-1]].(type) {
   119  	case *tomlValue:
   120  		return node.value
   121  	default:
   122  		return node
   123  	}
   124  }
   125  
   126  // GetArray returns the value at key in the Tree.
   127  // It returns []string, []int64, etc type if key has homogeneous lists
   128  // Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
   129  // Returns nil if the path does not exist in the tree.
   130  // If keys is of length zero, the current tree is returned.
   131  func (t *Tree) GetArray(key string) interface{} {
   132  	if key == "" {
   133  		return t
   134  	}
   135  	return t.GetArrayPath(strings.Split(key, "."))
   136  }
   137  
   138  // GetArrayPath returns the element in the tree indicated by 'keys'.
   139  // If keys is of length zero, the current tree is returned.
   140  func (t *Tree) GetArrayPath(keys []string) interface{} {
   141  	if len(keys) == 0 {
   142  		return t
   143  	}
   144  	subtree := t
   145  	for _, intermediateKey := range keys[:len(keys)-1] {
   146  		value, exists := subtree.values[intermediateKey]
   147  		if !exists {
   148  			return nil
   149  		}
   150  		switch node := value.(type) {
   151  		case *Tree:
   152  			subtree = node
   153  		case []*Tree:
   154  			// go to most recent element
   155  			if len(node) == 0 {
   156  				return nil
   157  			}
   158  			subtree = node[len(node)-1]
   159  		default:
   160  			return nil // cannot navigate through other node types
   161  		}
   162  	}
   163  	// branch based on final node type
   164  	switch node := subtree.values[keys[len(keys)-1]].(type) {
   165  	case *tomlValue:
   166  		switch n := node.value.(type) {
   167  		case []interface{}:
   168  			return getArray(n)
   169  		default:
   170  			return node.value
   171  		}
   172  	default:
   173  		return node
   174  	}
   175  }
   176  
   177  // if homogeneous array, then return slice type object over []interface{}
   178  func getArray(n []interface{}) interface{} {
   179  	var s []string
   180  	var i64 []int64
   181  	var f64 []float64
   182  	var bl []bool
   183  	for _, value := range n {
   184  		switch v := value.(type) {
   185  		case string:
   186  			s = append(s, v)
   187  		case int64:
   188  			i64 = append(i64, v)
   189  		case float64:
   190  			f64 = append(f64, v)
   191  		case bool:
   192  			bl = append(bl, v)
   193  		default:
   194  			return n
   195  		}
   196  	}
   197  	if len(s) == len(n) {
   198  		return s
   199  	} else if len(i64) == len(n) {
   200  		return i64
   201  	} else if len(f64) == len(n) {
   202  		return f64
   203  	} else if len(bl) == len(n) {
   204  		return bl
   205  	}
   206  	return n
   207  }
   208  
   209  // GetPosition returns the position of the given key.
   210  func (t *Tree) GetPosition(key string) Position {
   211  	if key == "" {
   212  		return t.position
   213  	}
   214  	return t.GetPositionPath(strings.Split(key, "."))
   215  }
   216  
   217  // SetPositionPath sets the position of element in the tree indicated by 'keys'.
   218  // If keys is of length zero, the current tree position is set.
   219  func (t *Tree) SetPositionPath(keys []string, pos Position) {
   220  	if len(keys) == 0 {
   221  		t.position = pos
   222  		return
   223  	}
   224  	subtree := t
   225  	for _, intermediateKey := range keys[:len(keys)-1] {
   226  		value, exists := subtree.values[intermediateKey]
   227  		if !exists {
   228  			return
   229  		}
   230  		switch node := value.(type) {
   231  		case *Tree:
   232  			subtree = node
   233  		case []*Tree:
   234  			// go to most recent element
   235  			if len(node) == 0 {
   236  				return
   237  			}
   238  			subtree = node[len(node)-1]
   239  		default:
   240  			return
   241  		}
   242  	}
   243  	// branch based on final node type
   244  	switch node := subtree.values[keys[len(keys)-1]].(type) {
   245  	case *tomlValue:
   246  		node.position = pos
   247  		return
   248  	case *Tree:
   249  		node.position = pos
   250  		return
   251  	case []*Tree:
   252  		// go to most recent element
   253  		if len(node) == 0 {
   254  			return
   255  		}
   256  		node[len(node)-1].position = pos
   257  		return
   258  	}
   259  }
   260  
   261  // GetPositionPath returns the element in the tree indicated by 'keys'.
   262  // If keys is of length zero, the current tree is returned.
   263  func (t *Tree) GetPositionPath(keys []string) Position {
   264  	if len(keys) == 0 {
   265  		return t.position
   266  	}
   267  	subtree := t
   268  	for _, intermediateKey := range keys[:len(keys)-1] {
   269  		value, exists := subtree.values[intermediateKey]
   270  		if !exists {
   271  			return Position{0, 0}
   272  		}
   273  		switch node := value.(type) {
   274  		case *Tree:
   275  			subtree = node
   276  		case []*Tree:
   277  			// go to most recent element
   278  			if len(node) == 0 {
   279  				return Position{0, 0}
   280  			}
   281  			subtree = node[len(node)-1]
   282  		default:
   283  			return Position{0, 0}
   284  		}
   285  	}
   286  	// branch based on final node type
   287  	switch node := subtree.values[keys[len(keys)-1]].(type) {
   288  	case *tomlValue:
   289  		return node.position
   290  	case *Tree:
   291  		return node.position
   292  	case []*Tree:
   293  		// go to most recent element
   294  		if len(node) == 0 {
   295  			return Position{0, 0}
   296  		}
   297  		return node[len(node)-1].position
   298  	default:
   299  		return Position{0, 0}
   300  	}
   301  }
   302  
   303  // GetDefault works like Get but with a default value
   304  func (t *Tree) GetDefault(key string, def interface{}) interface{} {
   305  	val := t.Get(key)
   306  	if val == nil {
   307  		return def
   308  	}
   309  	return val
   310  }
   311  
   312  // SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour.
   313  // The default values within the struct are valid default options.
   314  type SetOptions struct {
   315  	Comment   string
   316  	Commented bool
   317  	Multiline bool
   318  	Literal   bool
   319  }
   320  
   321  // SetWithOptions is the same as Set, but allows you to provide formatting
   322  // instructions to the key, that will be used by Marshal().
   323  func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
   324  	t.SetPathWithOptions(strings.Split(key, "."), opts, value)
   325  }
   326  
   327  // SetPathWithOptions is the same as SetPath, but allows you to provide
   328  // formatting instructions to the key, that will be reused by Marshal().
   329  func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
   330  	subtree := t
   331  	for i, intermediateKey := range keys[:len(keys)-1] {
   332  		nextTree, exists := subtree.values[intermediateKey]
   333  		if !exists {
   334  			nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
   335  			subtree.values[intermediateKey] = nextTree // add new element here
   336  		}
   337  		switch node := nextTree.(type) {
   338  		case *Tree:
   339  			subtree = node
   340  		case []*Tree:
   341  			// go to most recent element
   342  			if len(node) == 0 {
   343  				// create element if it does not exist
   344  				node = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
   345  				subtree.values[intermediateKey] = node
   346  			}
   347  			subtree = node[len(node)-1]
   348  		}
   349  	}
   350  
   351  	var toInsert interface{}
   352  
   353  	switch v := value.(type) {
   354  	case *Tree:
   355  		v.comment = opts.Comment
   356  		v.commented = opts.Commented
   357  		toInsert = value
   358  	case []*Tree:
   359  		for i := range v {
   360  			v[i].commented = opts.Commented
   361  		}
   362  		toInsert = value
   363  	case *tomlValue:
   364  		v.comment = opts.Comment
   365  		v.commented = opts.Commented
   366  		v.multiline = opts.Multiline
   367  		v.literal = opts.Literal
   368  		toInsert = v
   369  	default:
   370  		toInsert = &tomlValue{value: value,
   371  			comment:   opts.Comment,
   372  			commented: opts.Commented,
   373  			multiline: opts.Multiline,
   374  			literal:   opts.Literal,
   375  			position:  Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}}
   376  	}
   377  
   378  	subtree.values[keys[len(keys)-1]] = toInsert
   379  }
   380  
   381  // Set an element in the tree.
   382  // Key is a dot-separated path (e.g. a.b.c).
   383  // Creates all necessary intermediate trees, if needed.
   384  func (t *Tree) Set(key string, value interface{}) {
   385  	t.SetWithComment(key, "", false, value)
   386  }
   387  
   388  // SetWithComment is the same as Set, but allows you to provide comment
   389  // information to the key, that will be reused by Marshal().
   390  func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) {
   391  	t.SetPathWithComment(strings.Split(key, "."), comment, commented, value)
   392  }
   393  
   394  // SetPath sets an element in the tree.
   395  // Keys is an array of path elements (e.g. {"a","b","c"}).
   396  // Creates all necessary intermediate trees, if needed.
   397  func (t *Tree) SetPath(keys []string, value interface{}) {
   398  	t.SetPathWithComment(keys, "", false, value)
   399  }
   400  
   401  // SetPathWithComment is the same as SetPath, but allows you to provide comment
   402  // information to the key, that will be reused by Marshal().
   403  func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) {
   404  	t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value)
   405  }
   406  
   407  // Delete removes a key from the tree.
   408  // Key is a dot-separated path (e.g. a.b.c).
   409  func (t *Tree) Delete(key string) error {
   410  	keys, err := parseKey(key)
   411  	if err != nil {
   412  		return err
   413  	}
   414  	return t.DeletePath(keys)
   415  }
   416  
   417  // DeletePath removes a key from the tree.
   418  // Keys is an array of path elements (e.g. {"a","b","c"}).
   419  func (t *Tree) DeletePath(keys []string) error {
   420  	keyLen := len(keys)
   421  	if keyLen == 1 {
   422  		delete(t.values, keys[0])
   423  		return nil
   424  	}
   425  	tree := t.GetPath(keys[:keyLen-1])
   426  	item := keys[keyLen-1]
   427  	switch node := tree.(type) {
   428  	case *Tree:
   429  		delete(node.values, item)
   430  		return nil
   431  	}
   432  	return errors.New("no such key to delete")
   433  }
   434  
   435  // createSubTree takes a tree and a key and create the necessary intermediate
   436  // subtrees to create a subtree at that point. In-place.
   437  //
   438  // e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
   439  // and tree[a][b][c]
   440  //
   441  // Returns nil on success, error object on failure
   442  func (t *Tree) createSubTree(keys []string, pos Position) error {
   443  	subtree := t
   444  	for i, intermediateKey := range keys {
   445  		nextTree, exists := subtree.values[intermediateKey]
   446  		if !exists {
   447  			tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
   448  			tree.position = pos
   449  			tree.inline = subtree.inline
   450  			subtree.values[intermediateKey] = tree
   451  			nextTree = tree
   452  		}
   453  
   454  		switch node := nextTree.(type) {
   455  		case []*Tree:
   456  			subtree = node[len(node)-1]
   457  		case *Tree:
   458  			subtree = node
   459  		default:
   460  			return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
   461  				strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
   462  		}
   463  	}
   464  	return nil
   465  }
   466  
   467  // LoadBytes creates a Tree from a []byte.
   468  func LoadBytes(b []byte) (tree *Tree, err error) {
   469  	defer func() {
   470  		if r := recover(); r != nil {
   471  			if _, ok := r.(runtime.Error); ok {
   472  				panic(r)
   473  			}
   474  			err = fmt.Errorf("%s", r)
   475  		}
   476  	}()
   477  
   478  	if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) {
   479  		b = b[4:]
   480  	} else if len(b) >= 3 && hasUTF8BOM3(b) {
   481  		b = b[3:]
   482  	} else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) {
   483  		b = b[2:]
   484  	}
   485  
   486  	tree = parseToml(lexToml(b))
   487  	return
   488  }
   489  
   490  func hasUTF16BigEndianBOM2(b []byte) bool {
   491  	return b[0] == 0xFE && b[1] == 0xFF
   492  }
   493  
   494  func hasUTF16LittleEndianBOM2(b []byte) bool {
   495  	return b[0] == 0xFF && b[1] == 0xFE
   496  }
   497  
   498  func hasUTF8BOM3(b []byte) bool {
   499  	return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
   500  }
   501  
   502  func hasUTF32BigEndianBOM4(b []byte) bool {
   503  	return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF
   504  }
   505  
   506  func hasUTF32LittleEndianBOM4(b []byte) bool {
   507  	return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00
   508  }
   509  
   510  // LoadReader creates a Tree from any io.Reader.
   511  func LoadReader(reader io.Reader) (tree *Tree, err error) {
   512  	inputBytes, err := ioutil.ReadAll(reader)
   513  	if err != nil {
   514  		return
   515  	}
   516  	tree, err = LoadBytes(inputBytes)
   517  	return
   518  }
   519  
   520  // Load creates a Tree from a string.
   521  func Load(content string) (tree *Tree, err error) {
   522  	return LoadBytes([]byte(content))
   523  }
   524  
   525  // LoadFile creates a Tree from a file.
   526  func LoadFile(path string) (tree *Tree, err error) {
   527  	file, err := os.Open(path)
   528  	if err != nil {
   529  		return nil, err
   530  	}
   531  	defer file.Close()
   532  	return LoadReader(file)
   533  }
   534  

View as plain text