mirror

Mirror free and open-source projects you like with minimal effort
git clone git://git.server.ky/slackcoder/mirror
Log | Files | Refs | README

meta.go (3976B)


      1 package toml
      2 
      3 import (
      4 	"strings"
      5 )
      6 
      7 // MetaData allows access to meta information about TOML data that's not
      8 // accessible otherwise.
      9 //
     10 // It allows checking if a key is defined in the TOML data, whether any keys
     11 // were undecoded, and the TOML type of a key.
     12 type MetaData struct {
     13 	context Key // Used only during decoding.
     14 
     15 	keyInfo map[string]keyInfo
     16 	mapping map[string]any
     17 	keys    []Key
     18 	decoded map[string]struct{}
     19 	data    []byte // Input file; for errors.
     20 }
     21 
     22 // IsDefined reports if the key exists in the TOML data.
     23 //
     24 // The key should be specified hierarchically, for example to access the TOML
     25 // key "a.b.c" you would use IsDefined("a", "b", "c"). Keys are case sensitive.
     26 //
     27 // Returns false for an empty key.
     28 func (md *MetaData) IsDefined(key ...string) bool {
     29 	if len(key) == 0 {
     30 		return false
     31 	}
     32 
     33 	var (
     34 		hash      map[string]any
     35 		ok        bool
     36 		hashOrVal any = md.mapping
     37 	)
     38 	for _, k := range key {
     39 		if hash, ok = hashOrVal.(map[string]any); !ok {
     40 			return false
     41 		}
     42 		if hashOrVal, ok = hash[k]; !ok {
     43 			return false
     44 		}
     45 	}
     46 	return true
     47 }
     48 
     49 // Type returns a string representation of the type of the key specified.
     50 //
     51 // Type will return the empty string if given an empty key or a key that does
     52 // not exist. Keys are case sensitive.
     53 func (md *MetaData) Type(key ...string) string {
     54 	if ki, ok := md.keyInfo[Key(key).String()]; ok {
     55 		return ki.tomlType.typeString()
     56 	}
     57 	return ""
     58 }
     59 
     60 // Keys returns a slice of every key in the TOML data, including key groups.
     61 //
     62 // Each key is itself a slice, where the first element is the top of the
     63 // hierarchy and the last is the most specific. The list will have the same
     64 // order as the keys appeared in the TOML data.
     65 //
     66 // All keys returned are non-empty.
     67 func (md *MetaData) Keys() []Key {
     68 	return md.keys
     69 }
     70 
     71 // Undecoded returns all keys that have not been decoded in the order in which
     72 // they appear in the original TOML document.
     73 //
     74 // This includes keys that haven't been decoded because of a [Primitive] value.
     75 // Once the Primitive value is decoded, the keys will be considered decoded.
     76 //
     77 // Also note that decoding into an empty interface will result in no decoding,
     78 // and so no keys will be considered decoded.
     79 //
     80 // In this sense, the Undecoded keys correspond to keys in the TOML document
     81 // that do not have a concrete type in your representation.
     82 func (md *MetaData) Undecoded() []Key {
     83 	undecoded := make([]Key, 0, len(md.keys))
     84 	for _, key := range md.keys {
     85 		if _, ok := md.decoded[key.String()]; !ok {
     86 			undecoded = append(undecoded, key)
     87 		}
     88 	}
     89 	return undecoded
     90 }
     91 
     92 // Key represents any TOML key, including key groups. Use [MetaData.Keys] to get
     93 // values of this type.
     94 type Key []string
     95 
     96 func (k Key) String() string {
     97 	// This is called quite often, so it's a bit funky to make it faster.
     98 	var b strings.Builder
     99 	b.Grow(len(k) * 25)
    100 outer:
    101 	for i, kk := range k {
    102 		if i > 0 {
    103 			b.WriteByte('.')
    104 		}
    105 		if kk == "" {
    106 			b.WriteString(`""`)
    107 		} else {
    108 			for _, r := range kk {
    109 				// "Inline" isBareKeyChar
    110 				if !((r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r == '-') {
    111 					b.WriteByte('"')
    112 					b.WriteString(dblQuotedReplacer.Replace(kk))
    113 					b.WriteByte('"')
    114 					continue outer
    115 				}
    116 			}
    117 			b.WriteString(kk)
    118 		}
    119 	}
    120 	return b.String()
    121 }
    122 
    123 func (k Key) maybeQuoted(i int) string {
    124 	if k[i] == "" {
    125 		return `""`
    126 	}
    127 	for _, r := range k[i] {
    128 		if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r == '-' {
    129 			continue
    130 		}
    131 		return `"` + dblQuotedReplacer.Replace(k[i]) + `"`
    132 	}
    133 	return k[i]
    134 }
    135 
    136 // Like append(), but only increase the cap by 1.
    137 func (k Key) add(piece string) Key {
    138 	if cap(k) > len(k) {
    139 		return append(k, piece)
    140 	}
    141 	newKey := make(Key, len(k)+1)
    142 	copy(newKey, k)
    143 	newKey[len(k)] = piece
    144 	return newKey
    145 }
    146 
    147 func (k Key) parent() Key  { return k[:len(k)-1] } // all except the last piece.
    148 func (k Key) last() string { return k[len(k)-1] }  // last piece of this key.