aboutsummaryrefslogblamecommitdiff
path: root/model/node.go
blob: a4a509fb7cb13d33c2397547329cdcb5750437ca (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                             
                         







































































































                                                                                        





























                                                                                 


                                
package model

import (
	"bytes"
	"fmt"
	"strconv"
)

// Node of JSON tree
type Node struct {
	Type         NodeType
	Meta         Meta
	stringValue  string
	numberValue  float64
	objectValue  NodeObjectValue
	arrayValue   NodeArrayValue
	booleanValue bool
}

// NewNode creates new node from value
func NewNode(value any) *Node {
	n := new(Node)
	n.SetValue(value)
	return n
}

// Value returns value of node
func (n *Node) Value() any {
	switch n.Type {
	case StringNode:
		return n.stringValue
	case NumberNode:
		return n.numberValue
	case ObjectNode:
		return n.objectValue
	case ArrayNode:
		return n.arrayValue
	case BooleanNode:
		return n.booleanValue
	default:
		return nil
	}
}

// SetValue to node
func (n *Node) SetValue(value any) {
	switch value := value.(type) {
	case string:
		n.Type = StringNode
		n.stringValue = value
	case float64:
		n.Type = NumberNode
		n.numberValue = value
	case int:
		n.Type = NumberNode
		n.numberValue = float64(value)
	case NodeObjectValue:
		n.Type = ObjectNode
		n.objectValue = value
	case NodeArrayValue:
		n.Type = ArrayNode
		n.arrayValue = value
	case bool:
		n.Type = BooleanNode
		n.booleanValue = value
	default:
		n.Type = NullNode
	}
}

// MarshalJSON to []byte
func (n *Node) MarshalJSON() ([]byte, error) {
	switch n.Type {
	case StringNode:
		return []byte(`"` + n.stringValue + `"`), nil
	case NumberNode:
		return []byte(strconv.FormatFloat(n.numberValue, 'g', -1, 64)), nil
	case ObjectNode:
		result := make([][]byte, 0, len(n.objectValue))
		for k, v := range n.objectValue {
			b, err := v.MarshalJSON()
			if err != nil {
				return nil, err
			}
			result = append(result, []byte(fmt.Sprintf("\"%s\": %s", k, b)))
		}
		return bytes.Join(
			[][]byte{
				[]byte("{"),
				bytes.Join(result, []byte(", ")),
				[]byte("}"),
			}, []byte("")), nil
	case ArrayNode:
		result := make([][]byte, 0, len(n.arrayValue))
		for _, v := range n.arrayValue {
			b, err := v.MarshalJSON()
			if err != nil {
				return nil, err
			}
			result = append(result, b)
		}
		return bytes.Join(
			[][]byte{
				[]byte("["),
				bytes.Join(result, []byte(", ")),
				[]byte("]"),
			}, []byte("")), nil
	case BooleanNode:
		if n.booleanValue {
			return []byte("true"), nil
		}
		return []byte("false"), nil
	default:
		return []byte("null"), nil
	}
}

// Merge two object or array nodes
func (n *Node) Merge(node *Node) error {
	if n.Type != node.Type {
		return fmt.Errorf("can't merge nodes of different types")
	}
	switch n.Type {
	case ObjectNode:
		for k, v := range node.objectValue {
			n.objectValue[k] = v
		}
	case ArrayNode:
		n.arrayValue = append(n.arrayValue, node.arrayValue...)
	default:
		return fmt.Errorf("merge not implemented for type %s", n.Type)
	}
	return nil
}

// Len returns length of object or array nodes
func (n *Node) Len() (int, error) {
	switch n.Type {
	case ObjectNode:
		return len(n.objectValue), nil
	case ArrayNode:
		return len(n.arrayValue), nil
	default:
		return 0, fmt.Errorf("merge not implemented for type %s", n.Type)
	}
}

// Meta represents node metadata
type Meta map[string]any