1// Copyright (c) 2020, Peter Ohler, All rights reserved.
2
3package gen
4
5import "fmt"
6
7// Builder is assists in build a more complex Node.
8type Builder struct {
9	stack  []Node
10	starts []int
11}
12
13// Reset clears the the Builder of previous built nodes.
14func (b *Builder) Reset() {
15	if 0 < cap(b.stack) && 0 < len(b.stack) {
16		b.stack = b.stack[:0]
17		b.starts = b.starts[:0]
18	} else {
19		b.stack = make([]Node, 0, 64)
20		b.starts = make([]int, 0, 16)
21	}
22}
23
24// MustObject adds an object to the builder. A key is required if adding to a
25// parent object.
26func (b *Builder) MustObject(key ...string) {
27	if err := b.Object(key...); err != nil {
28		panic(err)
29	}
30}
31
32// Object adds an object to the builder. A key is required if adding to a
33// parent object.
34func (b *Builder) Object(key ...string) error {
35	newObj := Object{}
36	if 0 < len(key) {
37		if len(b.starts) == 0 || 0 <= b.starts[len(b.starts)-1] {
38			return fmt.Errorf("can not use a key when pushing to an array")
39		}
40		if obj, _ := b.stack[len(b.stack)-1].(Object); obj != nil {
41			obj[key[0]] = newObj
42		}
43	} else if 0 < len(b.starts) && b.starts[len(b.starts)-1] < 0 {
44		return fmt.Errorf("must have a key when pushing to an object")
45	}
46	b.starts = append(b.starts, -1)
47	b.stack = append(b.stack, newObj)
48
49	return nil
50}
51
52// MustArray adds an array to the builder. A key is required if adding to a
53// parent object.
54func (b *Builder) MustArray(key ...string) {
55	if err := b.Array(key...); err != nil {
56		panic(err)
57	}
58}
59
60// Array adds an array to the builder. A key is required if adding to a parent
61// object.
62func (b *Builder) Array(key ...string) error {
63	if 0 < len(key) {
64		if len(b.starts) == 0 || 0 <= b.starts[len(b.starts)-1] {
65			return fmt.Errorf("can not use a key when pushing to an array")
66		}
67		b.stack = append(b.stack, Key(key[0]))
68	} else if 0 < len(b.starts) && b.starts[len(b.starts)-1] < 0 {
69		return fmt.Errorf("must have a key when pushing to an object")
70	}
71	b.starts = append(b.starts, len(b.stack))
72	b.stack = append(b.stack, EmptyArray)
73
74	return nil
75}
76
77// MustValue adds a Node to the builder. A key is required if adding to a
78// parent object.
79func (b *Builder) MustValue(value Node, key ...string) {
80	if err := b.Value(value, key...); err != nil {
81		panic(err)
82	}
83}
84
85// Value adds a Node to the builder. A key is required if adding to a parent
86// object.
87func (b *Builder) Value(value Node, key ...string) error {
88	if 0 < len(key) {
89		if len(b.starts) == 0 || 0 <= b.starts[len(b.starts)-1] {
90			return fmt.Errorf("can not use a key when pushing to an array")
91		}
92		if obj, _ := b.stack[len(b.stack)-1].(Object); obj != nil {
93			obj[key[0]] = value
94		}
95	} else if 0 < len(b.starts) && b.starts[len(b.starts)-1] < 0 {
96		return fmt.Errorf("must have a key when pushing to an object")
97	} else {
98		b.stack = append(b.stack, value)
99	}
100	return nil
101}
102
103// Pop close a parent Object or Array Node.
104func (b *Builder) Pop() {
105	if 0 < len(b.starts) {
106		start := b.starts[len(b.starts)-1]
107		if 0 <= start { // array
108			start++
109			size := len(b.stack) - start
110			a := Array(make([]Node, size))
111			copy(a, b.stack[start:len(b.stack)])
112			b.stack = b.stack[:start]
113			b.stack[start-1] = a
114			if 2 < len(b.stack) {
115				if k, ok := b.stack[len(b.stack)-2].(Key); ok {
116					if obj, _ := b.stack[len(b.stack)-3].(Object); obj != nil {
117						obj[string(k)] = a
118						b.stack = b.stack[:len(b.stack)-2]
119					}
120				}
121			}
122		}
123		b.starts = b.starts[:len(b.starts)-1]
124	}
125}
126
127// PopAll close all parent Object or Array Nodes.
128func (b *Builder) PopAll() {
129	for 0 < len(b.starts) {
130		b.Pop()
131	}
132}
133
134// Result returns the current built Node.
135func (b *Builder) Result() (result Node) {
136	if 0 < len(b.stack) {
137		result = b.stack[0]
138	}
139	return
140}
141