aboutsummaryrefslogtreecommitdiff
path: root/model/node.go
blob: 0aabfccfb7ee1f84b26e41c7e46c1de5dad6a66d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package model

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

// Node of JSON tree
type Node struct {
	Type         NodeType
	Meta         NodeObjectValue
	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
		meta, hasMeta := value["@"]
		if hasMeta {
			n.Meta = meta.ObjectValue
			delete(value, "@")
		}
		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