node.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "strconv"
  6. "strings"
  7. "golang.org/x/exp/slices"
  8. )
  9. // A interconnected structure which connects to multiple entities
  10. //
  11. // Handles depth (in tabs) for the Endless sky save format (which is ugly)
  12. type Node struct {
  13. Tags []string // A tag, useful for identifying this node from other nodes in Children
  14. Line string // The line of text for the node (the Parser removes the depth number of tabs)
  15. Parent *Node `json:"-"` // The parent of this node (used for traversing up the node tree), must be json ignored or recursive looping
  16. Children []*Node // Any other nodes that can be tied to this node
  17. }
  18. func (n *Node) HasTag(tag string) bool {
  19. return slices.Contains(n.Tags, tag)
  20. }
  21. func (n *Node) AddTag(tag string) {
  22. if !n.HasTag(tag) {
  23. n.Tags = append(n.Tags, strings.ToLower(tag))
  24. }
  25. }
  26. func (n *Node) RmTag(tag string) {
  27. if n.HasTag(tag) {
  28. tags := []string{}
  29. for _, t := range n.Tags {
  30. if t != tag {
  31. tags = append(tags, t)
  32. }
  33. }
  34. n.Tags = tags
  35. }
  36. }
  37. // How deep in tabs we are
  38. func (n *Node) Depth() int {
  39. if n.Parent == nil {
  40. return 0
  41. }
  42. depth := 0
  43. at := n
  44. for at.Parent != nil {
  45. at = at.Parent
  46. depth += 1
  47. }
  48. if at.HasTag("root") {
  49. depth -= 1
  50. }
  51. return depth
  52. }
  53. // Returns the Endless sky representation (node depth is number of tabs)
  54. //
  55. // Useful for representing the node's line at the correct depth
  56. func (n *Node) String() string {
  57. return fmt.Sprintf("%s%s", strings.Repeat("\t", n.Depth()), n.Line)
  58. }
  59. // Strings everything (the current line plus any children)
  60. func (n *Node) StringAll() string {
  61. out := strings.Builder{}
  62. out.WriteString(n.String() + "\n")
  63. if n.Len() != 0 {
  64. // Recurse over children calling this function for the line and other children
  65. for _, kid := range n.Children {
  66. out.WriteString(kid.StringAll())
  67. }
  68. }
  69. return out.String()
  70. }
  71. // Adds the given node as a child
  72. //
  73. // Used to build complex structures
  74. func (n *Node) AddChild(child *Node) {
  75. child.Parent = n
  76. n.Children = append(n.Children, child)
  77. }
  78. // Creates a new child node and returns it
  79. //
  80. // Used to build complex structures
  81. func (n *Node) NewChild() *Node {
  82. kid := Node{
  83. Parent: n,
  84. }
  85. n.AddChild(&kid)
  86. return &kid
  87. }
  88. // Removes the child given index
  89. func (n *Node) RmChild(index int) error {
  90. if index > n.Len() || index < 0 {
  91. return fmt.Errorf("invalid index %d (min=0, max=%d)", index, n.Len())
  92. }
  93. var kids []*Node
  94. for idx, k := range n.Children {
  95. if idx != index {
  96. kids = append(kids, k)
  97. } else {
  98. k.Parent = nil
  99. }
  100. }
  101. n.Children = kids
  102. return nil
  103. }
  104. // Removes all children
  105. //
  106. // Useful for when you will be reassigning a collection of node children underneath
  107. func (n *Node) RmAllChildren() {
  108. for _, k := range n.Children {
  109. k.Parent = nil
  110. }
  111. n.Children = []*Node{}
  112. }
  113. // Removes the current node from the parent
  114. //
  115. // Similar to RmChild or RmAllChildren except called from the child's perspective
  116. func (n *Node) DetachFromParent() (*Node, error) {
  117. if n.Parent != nil {
  118. p := n.Parent
  119. index := -1
  120. for idx, k := range p.Children {
  121. if k.Line == n.Line && k.Depth() == n.Depth() && slices.Equal(k.Tags, n.Tags) && k.Len() == n.Len() {
  122. index = idx
  123. break
  124. }
  125. }
  126. if index != -1 {
  127. return p.SplitChild(index)
  128. } else {
  129. return nil, fmt.Errorf("failed detaching, didn't find ourselves in parent")
  130. }
  131. }
  132. return nil, fmt.Errorf("failed detaching, invalid parent")
  133. }
  134. // Obtain a pointer to the child
  135. func (n *Node) Child(index int) (*Node, error) {
  136. if index > n.Len() || index < 0 {
  137. return nil, fmt.Errorf("invalid index %d (min=0, max=%d)", index, n.Len())
  138. }
  139. return n.Children[index], nil
  140. }
  141. // Replaces the child by index
  142. func (n *Node) ReplaceChild(index int, node *Node) error {
  143. if index > n.Len() || index < 0 {
  144. return fmt.Errorf("invalid index %d (min=0, max=%d)", index, n.Len())
  145. }
  146. n.Children[index].Parent = nil
  147. node.Parent = n
  148. n.Children[index] = node
  149. return nil
  150. }
  151. // Removes the child but returns it (useful for if you plan on replacing it)
  152. func (n *Node) SplitChild(index int) (*Node, error) {
  153. if index > n.Len() || index < 0 {
  154. return nil, fmt.Errorf("invalid index %d (min=0, max=%d)", index, n.Len())
  155. }
  156. k := n.Children[index]
  157. k.Parent = nil
  158. err := n.RmChild(index)
  159. if err != nil {
  160. k.Parent = n // Some error recovery
  161. return nil, err
  162. }
  163. return k, nil
  164. }
  165. // Returns the number of children underneath
  166. func (n *Node) Len() int {
  167. return len(n.Children)
  168. }
  169. // Endless sky stores most data as 'key value'
  170. //
  171. // With special cases:
  172. //
  173. // As String '"some key" value' (Or '"some key" "some value"')
  174. //
  175. // As Int: '"some key" 13'
  176. //
  177. // As Float: '"some key" 9.81'
  178. //
  179. // Return's key part
  180. func (n *Node) Key() string {
  181. parts := strings.Split(n.Line, " ")
  182. if strings.Contains(parts[0], "\"") {
  183. pref := ""
  184. for _, part := range parts {
  185. if strings.HasSuffix(part, "\"") {
  186. if pref != "" {
  187. pref += " "
  188. }
  189. pref += part
  190. break
  191. }
  192. if pref != "" {
  193. pref += " "
  194. }
  195. pref += part
  196. }
  197. pref = strings.ReplaceAll(pref, "\"", "")
  198. return pref
  199. }
  200. return parts[0]
  201. }
  202. // Endless sky stores mose data as 'key value'
  203. //
  204. // With special cases:
  205. //
  206. // As String '"some key" value' (Or '"some key" "some value"')
  207. //
  208. // As Int: '"some key" 13'
  209. //
  210. // As Float: '"some key" 9.81'
  211. //
  212. // Return's value part (as string)
  213. func (n *Node) Value() string {
  214. key := n.Key()
  215. val := strings.Replace(n.Line, n.Format(key)+" ", "", 1)
  216. val = strings.ReplaceAll(val, "\"", "")
  217. return val
  218. }
  219. // Returns the value as integer (errors return 0)
  220. func (n *Node) ValueInt() int {
  221. v, err := strconv.Atoi(n.Value())
  222. if err != nil {
  223. log.Printf("ValueInt('%s') => %v", n.Value(), err)
  224. return 0
  225. }
  226. return v
  227. }
  228. // Returns the value as float64 (errors return 0.0, but are logged)
  229. func (n *Node) ValueFloat64() float64 {
  230. v, err := strconv.ParseFloat(n.Value(), 64)
  231. if err != nil {
  232. log.Printf("ValueFloat64('%s') => %v", n.Value(), err)
  233. return 0.0
  234. }
  235. return v
  236. }
  237. // Returns the value as float32 (errors return 0.0, but are logged)
  238. func (n *Node) ValueFloat32() float32 {
  239. v, err := strconv.ParseFloat(n.Value(), 32)
  240. if err != nil {
  241. log.Printf("ValueFloat32('%s') => %v", n.Value(), err)
  242. return 0.0
  243. }
  244. return float32(v)
  245. }
  246. // Format function
  247. //
  248. // Endless sky demands any multi word key or value must be wrapped in "s
  249. //
  250. // Both Node.Key() and Node.Value() remove the "s so this must be called to form the new Node.Line (for setters)
  251. func (n *Node) Format(value string) string {
  252. if strings.Contains(value, " ") {
  253. return "\"" + value + "\""
  254. }
  255. return value
  256. }
  257. // Assigns a new value for the Node
  258. //
  259. // This updates/changes the Node.Line (as that's the only way to do it)
  260. func (n *Node) SetValue(val interface{}) {
  261. if val == nil { // Make it a flag/boolean
  262. n.Line = n.Format(n.Key())
  263. return
  264. }
  265. str_val := fmt.Sprintf("%v", val)
  266. n.Line = fmt.Sprintf("%s %s", n.Format(n.Key()), n.Format(str_val))
  267. }
  268. // Assigns a new key for the Node
  269. //
  270. // This must be a string (key's can't be anything else)
  271. //
  272. // This updates/changes the Node.Line (as that's the only way to do it)
  273. func (n *Node) SetKey(key string) {
  274. val := n.Value()
  275. if val != "" {
  276. n.Line = fmt.Sprintf("%s %s", n.Format(key), n.Format(val))
  277. } else {
  278. n.Line = n.Format(key)
  279. }
  280. }
  281. // Assigns the whole line from the key to the value
  282. func (n *Node) Set(key string, val interface{}) {
  283. if val == nil {
  284. n.Line = n.Format(key)
  285. return
  286. }
  287. str_val := fmt.Sprintf("%v", val)
  288. n.Line = fmt.Sprintf("%s %s", n.Format(key), n.Format(str_val))
  289. }