128 lines
3.1 KiB
Go
128 lines
3.1 KiB
Go
package stack
|
|
|
|
import "gopkg.in/yaml.v3"
|
|
|
|
// svcBuilder — mapping YAML berurut (urutan key = urutan flag, seperti
|
|
// referensi). Semua mutasi lewat helper agar urutan deterministik.
|
|
type svcBuilder struct {
|
|
root *yaml.Node // MappingNode
|
|
}
|
|
|
|
func newSvc() *svcBuilder {
|
|
return &svcBuilder{root: &yaml.Node{Kind: yaml.MappingNode}}
|
|
}
|
|
|
|
func (s *svcBuilder) get(key string) *yaml.Node {
|
|
for i := 0; i+1 < len(s.root.Content); i += 2 {
|
|
if s.root.Content[i].Value == key {
|
|
return s.root.Content[i+1]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *svcBuilder) has(key string) bool { return s.get(key) != nil }
|
|
|
|
func (s *svcBuilder) set(key string, v *yaml.Node) {
|
|
for i := 0; i+1 < len(s.root.Content); i += 2 {
|
|
if s.root.Content[i].Value == key {
|
|
s.root.Content[i+1] = v
|
|
return
|
|
}
|
|
}
|
|
s.root.Content = append(s.root.Content, strNode(key), v)
|
|
}
|
|
|
|
func (s *svcBuilder) del(key string) {
|
|
for i := 0; i+1 < len(s.root.Content); i += 2 {
|
|
if s.root.Content[i].Value == key {
|
|
s.root.Content = append(s.root.Content[:i], s.root.Content[i+2:]...)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// ensureMap — mapping node pada path, dibuat bila belum ada.
|
|
func (s *svcBuilder) ensureMap(path ...string) *yaml.Node {
|
|
cur := s.root
|
|
for _, k := range path {
|
|
next := nodeKey(cur, k)
|
|
if next == nil {
|
|
next = &yaml.Node{Kind: yaml.MappingNode}
|
|
appendKV(cur, strNode(k), next)
|
|
}
|
|
if next.Kind != yaml.MappingNode {
|
|
next.Kind = yaml.MappingNode
|
|
next.Content = nil
|
|
}
|
|
cur = next
|
|
}
|
|
return cur
|
|
}
|
|
|
|
func nodeKey(m *yaml.Node, k string) *yaml.Node {
|
|
for i := 0; i+1 < len(m.Content); i += 2 {
|
|
if m.Content[i].Value == k {
|
|
return m.Content[i+1]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func appendKV(m *yaml.Node, k, v *yaml.Node) {
|
|
m.Content = append(m.Content, k, v)
|
|
}
|
|
|
|
// setPath — set scalar/value pada path (mengganti bila sudah ada).
|
|
func (s *svcBuilder) setPath(path []string, v *yaml.Node) {
|
|
if len(path) == 0 {
|
|
return
|
|
}
|
|
parent := s.ensureMap(path[:len(path)-1]...)
|
|
last := path[len(path)-1]
|
|
if old := nodeKey(parent, last); old != nil {
|
|
*old = *v
|
|
return
|
|
}
|
|
appendKV(parent, strNode(last), v)
|
|
}
|
|
|
|
// appendAt — tambah item ke sequence pada path (dibuat bila belum ada).
|
|
func (s *svcBuilder) appendAt(path []string, item *yaml.Node) {
|
|
if len(path) == 0 {
|
|
return
|
|
}
|
|
parent := s.ensureMap(path[:len(path)-1]...)
|
|
last := path[len(path)-1]
|
|
seq := nodeKey(parent, last)
|
|
if seq == nil {
|
|
seq = &yaml.Node{Kind: yaml.SequenceNode}
|
|
appendKV(parent, strNode(last), seq)
|
|
}
|
|
if seq.Kind != yaml.SequenceNode {
|
|
*seq = yaml.Node{Kind: yaml.SequenceNode}
|
|
}
|
|
seq.Content = append(seq.Content, item)
|
|
}
|
|
|
|
// mapPairs — set key:value berurutan ke dalam mapping pada path.
|
|
func (s *svcBuilder) mapPairs(path []string, pairs [][2]string) {
|
|
m := s.ensureMap(path...)
|
|
for _, kv := range pairs {
|
|
if old := nodeKey(m, kv[0]); old != nil {
|
|
*old = *strNode(kv[1])
|
|
continue
|
|
}
|
|
// nilai numerik → int (parity MapArray/Map referensi)
|
|
appendKV(m, strNode(kv[0]), scalarAuto(kv[1]))
|
|
}
|
|
}
|
|
|
|
// scalarAuto — "2048" → int, selain itu string.
|
|
func scalarAuto(v string) *yaml.Node {
|
|
if isIntStr(v) {
|
|
return intNode(parseInt(v))
|
|
}
|
|
return strNode(v)
|
|
}
|