Files
doll/vendor/github.com/hjson/hjson-go/v4/node.go
2025-10-26 23:36:16 +03:00

416 lines
13 KiB
Go

package hjson
import (
"encoding/json"
"fmt"
"reflect"
)
type Comments struct {
// Comment/whitespace on line(s) before the value, and before the value on
// the same line. If not empty, is expected to end with a line feed +
// indentation for the value.
Before string
// Comment/whitespace between the key and this value, if this value is an
// element in a map/object.
Key string
// Comment/whitespace after (but still on the same line as) the leading
// bracket ({ or [) for this value, if this value is a slice/array or
// map/object. Is not expected to contain any line feed.
InsideFirst string
// Comment/whitespace from the beginning of the first line after all child
// values belonging to this value, until the closing bracket (} or ]), if
// this value is a slice/array or map/object. If not empty, is expected to
// end with a line feed + indentation for the closing bracket.
InsideLast string
// Comment/whitespace after (but still on the same line as) the value. Is not
// expected to contain any line feed. hjson.Unmarshal() will try to assign
// comments/whitespace from lines between values to `Before` on the value
// after those lines, or to `InsideLast` on the slice/map if the lines
// containing comments appear after the last element inside a slice/map.
After string
}
// Node must be used as destination for Unmarshal() or UnmarshalWithOptions()
// whenever comments should be read from the input. The struct is simply a
// wrapper for the actual values and a helper struct containing any comments.
// The Value in the destination Node will be overwritten in the call to
// Unmarshal() or UnmarshalWithOptions(), i.e. node trees are not merged.
// After the unmarshal, Node.Value will contain any of these types:
//
// nil (no type)
// float64 (if UseJSONNumber == false)
// json.Number (if UseJSONNumber == true)
// string
// bool
// []interface{}
// *hjson.OrderedMap
//
// All elements in an []interface{} or *hjson.OrderedMap will be of the type
// *hjson.Node, so that they can contain comments.
//
// This example shows unmarshalling input with comments, changing the value on
// a single key (the input is assumed to have an object/map as root) and then
// marshalling the node tree again, including comments and with preserved key
// order in the object/map.
//
// var node hjson.Node
// err := hjson.Unmarshal(input, &node)
// if err != nil {
// return err
// }
// _, err = node.SetKey("setting1", 3)
// if err != nil {
// return err
// }
// output, err := hjson.Marshal(node)
// if err != nil {
// return err
// }
type Node struct {
Value interface{}
Cm Comments
}
// Len returns the length of the value wrapped by this Node, if the value is of
// type *hjson.OrderedMap, []interface{} or string. Otherwise 0 is returned.
func (c *Node) Len() int {
if c == nil {
return 0
}
switch cont := c.Value.(type) {
case *OrderedMap:
return cont.Len()
case []interface{}:
return len(cont)
case string:
return len(cont)
}
return 0
}
// AtIndex returns the key (if any) and value (unwrapped from its Node) found
// at the specified index, if this Node contains a value of type
// *hjson.OrderedMap or []interface{}. Returns an error for unexpected types.
// Panics if index < 0 or index >= Len().
func (c *Node) AtIndex(index int) (string, interface{}, error) {
if c == nil {
return "", nil, fmt.Errorf("Node is nil")
}
var key string
var elem interface{}
switch cont := c.Value.(type) {
case *OrderedMap:
key = cont.Keys[index]
elem = cont.Map[key]
case []interface{}:
elem = cont[index]
default:
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
node, ok := elem.(*Node)
if !ok {
return "", nil, fmt.Errorf("Unexpected element type: %v", reflect.TypeOf(elem))
}
return key, node.Value, nil
}
// AtKey returns the value (unwrapped from its Node) found for the specified
// key, if this Node contains a value of type *hjson.OrderedMap. An error is
// returned for unexpected types. The second returned value is true if the key
// was found, false otherwise.
func (c *Node) AtKey(key string) (interface{}, bool, error) {
if c == nil {
return nil, false, nil
}
om, ok := c.Value.(*OrderedMap)
if !ok {
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
elem, ok := om.Map[key]
if !ok {
return nil, false, nil
}
node, ok := elem.(*Node)
if !ok {
return nil, false, fmt.Errorf("Unexpected element type: %v", reflect.TypeOf(elem))
}
return node.Value, true, nil
}
// Append adds the input value to the end of the []interface{} wrapped by this
// Node. If this Node contains nil without a type, an empty []interface{} is
// first created. If this Node contains a value of any other type, an error is
// returned.
func (c *Node) Append(value interface{}) error {
if c == nil {
return fmt.Errorf("Node is nil")
}
var arr []interface{}
if c.Value == nil {
arr = []interface{}{}
} else {
var ok bool
arr, ok = c.Value.([]interface{})
if !ok {
return fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
}
c.Value = append(arr, &Node{Value: value})
return nil
}
// Insert inserts a new key/value pair at the specified index, if this Node
// contains a value of type *hjson.OrderedMap or []interface{}. Returns an error
// for unexpected types. Panics if index < 0 or index > c.Len(). If the key
// already exists in the OrderedMap, the new value is set but the position of
// the key is not changed. Otherwise the value to insert is wrapped in a new
// Node. If this Node contains []interface{}, the key is ignored. Returns the
// old value and true if the key already exists in the/ OrderedMap, nil and
// false otherwise.
func (c *Node) Insert(index int, key string, value interface{}) (interface{}, bool, error) {
if c == nil {
return nil, false, fmt.Errorf("Node is nil")
}
var oldVal interface{}
var found bool
switch cont := c.Value.(type) {
case *OrderedMap:
oldVal, found := cont.Map[key]
if found {
if node, ok := oldVal.(*Node); ok {
oldVal = node.Value
node.Value = value
} else {
cont.Map[key] = &Node{Value: value}
}
} else {
oldVal, found = cont.Insert(index, key, &Node{Value: value})
}
case []interface{}:
value = &Node{Value: value}
if index == len(cont) {
c.Value = append(cont, value)
} else {
cont = append(cont[:index+1], cont[index:]...)
cont[index] = value
c.Value = cont
}
default:
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
return oldVal, found, nil
}
// SetIndex assigns the specified value to the child Node found at the specified
// index, if this Node contains a value of type *hjson.OrderedMap or
// []interface{}. Returns an error for unexpected types. Returns the key (if
// any) and value previously found at the specified index. Panics if index < 0
// or index >= Len().
func (c *Node) SetIndex(index int, value interface{}) (string, interface{}, error) {
if c == nil {
return "", nil, fmt.Errorf("Node is nil")
}
var key string
var elem interface{}
switch cont := c.Value.(type) {
case *OrderedMap:
key = cont.Keys[index]
elem = cont.Map[key]
case []interface{}:
elem = cont[index]
default:
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
var oldVal interface{}
node, ok := elem.(*Node)
if ok {
oldVal = node.Value
node.Value = value
} else {
oldVal = elem
switch cont := c.Value.(type) {
case *OrderedMap:
cont.Map[key] = &Node{Value: value}
case []interface{}:
cont[index] = &Node{Value: value}
}
}
return key, oldVal, nil
}
// SetKey assigns the specified value to the child Node identified by the
// specified key, if this Node contains a value of the type *hjson.OrderedMap.
// If this Node contains nil without a type, an empty *hjson.OrderedMap is
// first created. If this Node contains a value of any other type an error is
// returned. If the key cannot be found in the OrderedMap, a new Node is
// created, wrapping the specified value, and appended to the end of the
// OrderedMap. Returns the old value and true if the key already existed in
// the OrderedMap, nil and false otherwise.
func (c *Node) SetKey(key string, value interface{}) (interface{}, bool, error) {
if c == nil {
return nil, false, fmt.Errorf("Node is nil")
}
var om *OrderedMap
if c.Value == nil {
om = NewOrderedMap()
c.Value = om
} else {
var ok bool
om, ok = c.Value.(*OrderedMap)
if !ok {
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
}
var oldVal interface{}
elem, ok := om.Map[key]
if ok {
var node *Node
node, ok = elem.(*Node)
if ok {
oldVal = node.Value
node.Value = value
}
}
foundKey := true
if !ok {
oldVal, foundKey = om.Set(key, &Node{Value: value})
}
return oldVal, foundKey, nil
}
// DeleteIndex deletes the value or key/value pair found at the specified index,
// if this Node contains a value of type *hjson.OrderedMap or []interface{}.
// Returns an error for unexpected types. Panics if index < 0 or
// index >= c.Len(). Returns the deleted key (if any) and value.
func (c *Node) DeleteIndex(index int) (string, interface{}, error) {
if c == nil {
return "", nil, fmt.Errorf("Node is nil")
}
var key string
var value interface{}
switch cont := c.Value.(type) {
case *OrderedMap:
key, value = cont.DeleteIndex(index)
case []interface{}:
value = cont[index]
cont = append(cont[:index], cont[index+1:]...)
c.Value = cont
default:
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
if node, ok := value.(*Node); ok {
value = node.Value
}
return key, value, nil
}
// DeleteKey deletes the key/value pair with the specified key, if found and if
// this Node contains a value of type *hjson.OrderedMap. Returns an error for
// unexpected types. Returns the deleted value and true if the key was found,
// nil and false otherwise.
func (c *Node) DeleteKey(key string) (interface{}, bool, error) {
if c == nil {
return nil, false, fmt.Errorf("Node is nil")
}
if om, ok := c.Value.(*OrderedMap); ok {
oldValue, found := om.DeleteKey(key)
if node, ok := oldValue.(*Node); ok {
oldValue = node.Value
}
return oldValue, found, nil
}
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
// NI is an acronym formed from "get Node pointer by Index". Returns the *Node
// element found at the specified index, if this Node contains a value of type
// *hjson.OrderedMap or []interface{}. Returns nil otherwise. Panics if
// index < 0 or index >= Len(). Does not create or alter any value.
func (c *Node) NI(index int) *Node {
if c == nil {
return nil
}
var elem interface{}
switch cont := c.Value.(type) {
case *OrderedMap:
elem = cont.AtIndex(index)
case []interface{}:
elem = cont[index]
default:
return nil
}
if node, ok := elem.(*Node); ok {
return node
}
return nil
}
// NK is an acronym formed from "get Node pointer by Key". Returns the *Node
// element found for the specified key, if this Node contains a value of type
// *hjson.OrderedMap. Returns nil otherwise. Does not create or alter anything.
func (c *Node) NK(key string) *Node {
if c == nil {
return nil
}
om, ok := c.Value.(*OrderedMap)
if !ok {
return nil
}
if elem, ok := om.Map[key]; ok {
if node, ok := elem.(*Node); ok {
return node
}
}
return nil
}
// NKC is an acronym formed from "get Node pointer by Key, Create if not found".
// Returns the *Node element found for the specified key, if this Node contains
// a value of type *hjson.OrderedMap. If this Node contains nil without a type,
// an empty *hjson.OrderedMap is first created. If this Node contains a value of
// any other type or if the element idendified by the specified key is not of
// type *Node, an error is returned. If the key cannot be found in the
// OrderedMap, a new Node is created for the specified key. Example usage:
//
// var node hjson.Node
// node.NKC("rootKey1").NKC("subKey1").SetKey("valKey1", "my value")
func (c *Node) NKC(key string) *Node {
if c == nil {
return nil
}
var om *OrderedMap
if c.Value == nil {
om = NewOrderedMap()
c.Value = om
} else {
var ok bool
om, ok = c.Value.(*OrderedMap)
if !ok {
return nil
}
}
if elem, ok := om.Map[key]; ok {
if node, ok := elem.(*Node); ok {
return node
}
} else {
node := &Node{}
om.Set(key, node)
return node
}
return nil
}
// MarshalJSON is an implementation of the json.Marshaler interface, enabling
// hjson.Node trees to be used as input for json.Marshal().
func (c Node) MarshalJSON() ([]byte, error) {
return json.Marshal(c.Value)
}
// UnmarshalJSON is an implementation of the json.Unmarshaler interface,
// enabling hjson.Node to be used as destination for json.Unmarshal().
func (c *Node) UnmarshalJSON(b []byte) error {
return Unmarshal(b, c)
}