You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

393 lines
10 KiB

  1. package toml
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "runtime"
  9. "strings"
  10. )
  11. type tomlValue struct {
  12. value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
  13. comment string
  14. commented bool
  15. multiline bool
  16. position Position
  17. }
  18. // Tree is the result of the parsing of a TOML file.
  19. type Tree struct {
  20. values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree
  21. comment string
  22. commented bool
  23. position Position
  24. }
  25. func newTree() *Tree {
  26. return newTreeWithPosition(Position{})
  27. }
  28. func newTreeWithPosition(pos Position) *Tree {
  29. return &Tree{
  30. values: make(map[string]interface{}),
  31. position: pos,
  32. }
  33. }
  34. // TreeFromMap initializes a new Tree object using the given map.
  35. func TreeFromMap(m map[string]interface{}) (*Tree, error) {
  36. result, err := toTree(m)
  37. if err != nil {
  38. return nil, err
  39. }
  40. return result.(*Tree), nil
  41. }
  42. // Position returns the position of the tree.
  43. func (t *Tree) Position() Position {
  44. return t.position
  45. }
  46. // Has returns a boolean indicating if the given key exists.
  47. func (t *Tree) Has(key string) bool {
  48. if key == "" {
  49. return false
  50. }
  51. return t.HasPath(strings.Split(key, "."))
  52. }
  53. // HasPath returns true if the given path of keys exists, false otherwise.
  54. func (t *Tree) HasPath(keys []string) bool {
  55. return t.GetPath(keys) != nil
  56. }
  57. // Keys returns the keys of the toplevel tree (does not recurse).
  58. func (t *Tree) Keys() []string {
  59. keys := make([]string, len(t.values))
  60. i := 0
  61. for k := range t.values {
  62. keys[i] = k
  63. i++
  64. }
  65. return keys
  66. }
  67. // Get the value at key in the Tree.
  68. // Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
  69. // If you need to retrieve non-bare keys, use GetPath.
  70. // Returns nil if the path does not exist in the tree.
  71. // If keys is of length zero, the current tree is returned.
  72. func (t *Tree) Get(key string) interface{} {
  73. if key == "" {
  74. return t
  75. }
  76. return t.GetPath(strings.Split(key, "."))
  77. }
  78. // GetPath returns the element in the tree indicated by 'keys'.
  79. // If keys is of length zero, the current tree is returned.
  80. func (t *Tree) GetPath(keys []string) interface{} {
  81. if len(keys) == 0 {
  82. return t
  83. }
  84. subtree := t
  85. for _, intermediateKey := range keys[:len(keys)-1] {
  86. value, exists := subtree.values[intermediateKey]
  87. if !exists {
  88. return nil
  89. }
  90. switch node := value.(type) {
  91. case *Tree:
  92. subtree = node
  93. case []*Tree:
  94. // go to most recent element
  95. if len(node) == 0 {
  96. return nil
  97. }
  98. subtree = node[len(node)-1]
  99. default:
  100. return nil // cannot navigate through other node types
  101. }
  102. }
  103. // branch based on final node type
  104. switch node := subtree.values[keys[len(keys)-1]].(type) {
  105. case *tomlValue:
  106. return node.value
  107. default:
  108. return node
  109. }
  110. }
  111. // GetPosition returns the position of the given key.
  112. func (t *Tree) GetPosition(key string) Position {
  113. if key == "" {
  114. return t.position
  115. }
  116. return t.GetPositionPath(strings.Split(key, "."))
  117. }
  118. // GetPositionPath returns the element in the tree indicated by 'keys'.
  119. // If keys is of length zero, the current tree is returned.
  120. func (t *Tree) GetPositionPath(keys []string) Position {
  121. if len(keys) == 0 {
  122. return t.position
  123. }
  124. subtree := t
  125. for _, intermediateKey := range keys[:len(keys)-1] {
  126. value, exists := subtree.values[intermediateKey]
  127. if !exists {
  128. return Position{0, 0}
  129. }
  130. switch node := value.(type) {
  131. case *Tree:
  132. subtree = node
  133. case []*Tree:
  134. // go to most recent element
  135. if len(node) == 0 {
  136. return Position{0, 0}
  137. }
  138. subtree = node[len(node)-1]
  139. default:
  140. return Position{0, 0}
  141. }
  142. }
  143. // branch based on final node type
  144. switch node := subtree.values[keys[len(keys)-1]].(type) {
  145. case *tomlValue:
  146. return node.position
  147. case *Tree:
  148. return node.position
  149. case []*Tree:
  150. // go to most recent element
  151. if len(node) == 0 {
  152. return Position{0, 0}
  153. }
  154. return node[len(node)-1].position
  155. default:
  156. return Position{0, 0}
  157. }
  158. }
  159. // GetDefault works like Get but with a default value
  160. func (t *Tree) GetDefault(key string, def interface{}) interface{} {
  161. val := t.Get(key)
  162. if val == nil {
  163. return def
  164. }
  165. return val
  166. }
  167. // SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour.
  168. // The default values within the struct are valid default options.
  169. type SetOptions struct {
  170. Comment string
  171. Commented bool
  172. Multiline bool
  173. }
  174. // SetWithOptions is the same as Set, but allows you to provide formatting
  175. // instructions to the key, that will be used by Marshal().
  176. func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
  177. t.SetPathWithOptions(strings.Split(key, "."), opts, value)
  178. }
  179. // SetPathWithOptions is the same as SetPath, but allows you to provide
  180. // formatting instructions to the key, that will be reused by Marshal().
  181. func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
  182. subtree := t
  183. for i, intermediateKey := range keys[:len(keys)-1] {
  184. nextTree, exists := subtree.values[intermediateKey]
  185. if !exists {
  186. nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
  187. subtree.values[intermediateKey] = nextTree // add new element here
  188. }
  189. switch node := nextTree.(type) {
  190. case *Tree:
  191. subtree = node
  192. case []*Tree:
  193. // go to most recent element
  194. if len(node) == 0 {
  195. // create element if it does not exist
  196. subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
  197. }
  198. subtree = node[len(node)-1]
  199. }
  200. }
  201. var toInsert interface{}
  202. switch v := value.(type) {
  203. case *Tree:
  204. v.comment = opts.Comment
  205. toInsert = value
  206. case []*Tree:
  207. toInsert = value
  208. case *tomlValue:
  209. v.comment = opts.Comment
  210. toInsert = v
  211. default:
  212. toInsert = &tomlValue{value: value,
  213. comment: opts.Comment,
  214. commented: opts.Commented,
  215. multiline: opts.Multiline,
  216. position: Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}}
  217. }
  218. subtree.values[keys[len(keys)-1]] = toInsert
  219. }
  220. // Set an element in the tree.
  221. // Key is a dot-separated path (e.g. a.b.c).
  222. // Creates all necessary intermediate trees, if needed.
  223. func (t *Tree) Set(key string, value interface{}) {
  224. t.SetWithComment(key, "", false, value)
  225. }
  226. // SetWithComment is the same as Set, but allows you to provide comment
  227. // information to the key, that will be reused by Marshal().
  228. func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) {
  229. t.SetPathWithComment(strings.Split(key, "."), comment, commented, value)
  230. }
  231. // SetPath sets an element in the tree.
  232. // Keys is an array of path elements (e.g. {"a","b","c"}).
  233. // Creates all necessary intermediate trees, if needed.
  234. func (t *Tree) SetPath(keys []string, value interface{}) {
  235. t.SetPathWithComment(keys, "", false, value)
  236. }
  237. // SetPathWithComment is the same as SetPath, but allows you to provide comment
  238. // information to the key, that will be reused by Marshal().
  239. func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) {
  240. t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value)
  241. }
  242. // Delete removes a key from the tree.
  243. // Key is a dot-separated path (e.g. a.b.c).
  244. func (t *Tree) Delete(key string) error {
  245. keys, err := parseKey(key)
  246. if err != nil {
  247. return err
  248. }
  249. return t.DeletePath(keys)
  250. }
  251. // DeletePath removes a key from the tree.
  252. // Keys is an array of path elements (e.g. {"a","b","c"}).
  253. func (t *Tree) DeletePath(keys []string) error {
  254. keyLen := len(keys)
  255. if keyLen == 1 {
  256. delete(t.values, keys[0])
  257. return nil
  258. }
  259. tree := t.GetPath(keys[:keyLen-1])
  260. item := keys[keyLen-1]
  261. switch node := tree.(type) {
  262. case *Tree:
  263. delete(node.values, item)
  264. return nil
  265. }
  266. return errors.New("no such key to delete")
  267. }
  268. // createSubTree takes a tree and a key and create the necessary intermediate
  269. // subtrees to create a subtree at that point. In-place.
  270. //
  271. // e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
  272. // and tree[a][b][c]
  273. //
  274. // Returns nil on success, error object on failure
  275. func (t *Tree) createSubTree(keys []string, pos Position) error {
  276. subtree := t
  277. for i, intermediateKey := range keys {
  278. nextTree, exists := subtree.values[intermediateKey]
  279. if !exists {
  280. tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
  281. tree.position = pos
  282. subtree.values[intermediateKey] = tree
  283. nextTree = tree
  284. }
  285. switch node := nextTree.(type) {
  286. case []*Tree:
  287. subtree = node[len(node)-1]
  288. case *Tree:
  289. subtree = node
  290. default:
  291. return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
  292. strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
  293. }
  294. }
  295. return nil
  296. }
  297. // LoadBytes creates a Tree from a []byte.
  298. func LoadBytes(b []byte) (tree *Tree, err error) {
  299. defer func() {
  300. if r := recover(); r != nil {
  301. if _, ok := r.(runtime.Error); ok {
  302. panic(r)
  303. }
  304. err = errors.New(r.(string))
  305. }
  306. }()
  307. if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) {
  308. b = b[4:]
  309. } else if len(b) >= 3 && hasUTF8BOM3(b) {
  310. b = b[3:]
  311. } else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) {
  312. b = b[2:]
  313. }
  314. tree = parseToml(lexToml(b))
  315. return
  316. }
  317. func hasUTF16BigEndianBOM2(b []byte) bool {
  318. return b[0] == 0xFE && b[1] == 0xFF
  319. }
  320. func hasUTF16LittleEndianBOM2(b []byte) bool {
  321. return b[0] == 0xFF && b[1] == 0xFE
  322. }
  323. func hasUTF8BOM3(b []byte) bool {
  324. return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
  325. }
  326. func hasUTF32BigEndianBOM4(b []byte) bool {
  327. return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF
  328. }
  329. func hasUTF32LittleEndianBOM4(b []byte) bool {
  330. return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00
  331. }
  332. // LoadReader creates a Tree from any io.Reader.
  333. func LoadReader(reader io.Reader) (tree *Tree, err error) {
  334. inputBytes, err := ioutil.ReadAll(reader)
  335. if err != nil {
  336. return
  337. }
  338. tree, err = LoadBytes(inputBytes)
  339. return
  340. }
  341. // Load creates a Tree from a string.
  342. func Load(content string) (tree *Tree, err error) {
  343. return LoadBytes([]byte(content))
  344. }
  345. // LoadFile creates a Tree from a file.
  346. func LoadFile(path string) (tree *Tree, err error) {
  347. file, err := os.Open(path)
  348. if err != nil {
  349. return nil, err
  350. }
  351. defer file.Close()
  352. return LoadReader(file)
  353. }