github.com/abdfnx/gh-api@v0.0.0-20210414084727-f5432eec23b8/internal/config/config_map.go (about) 1 package config 2 3 import ( 4 "errors" 5 6 "gopkg.in/yaml.v3" 7 ) 8 9 // This type implements a low-level get/set config that is backed by an in-memory tree of Yaml 10 // nodes. It allows us to interact with a yaml-based config programmatically, preserving any 11 // comments that were present when the yaml was parsed. 12 type ConfigMap struct { 13 Root *yaml.Node 14 } 15 16 type ConfigEntry struct { 17 KeyNode *yaml.Node 18 ValueNode *yaml.Node 19 Index int 20 } 21 22 type NotFoundError struct { 23 error 24 } 25 26 func (cm *ConfigMap) Empty() bool { 27 return cm.Root == nil || len(cm.Root.Content) == 0 28 } 29 30 func (cm *ConfigMap) GetStringValue(key string) (string, error) { 31 entry, err := cm.FindEntry(key) 32 if err != nil { 33 return "", err 34 } 35 return entry.ValueNode.Value, nil 36 } 37 38 func (cm *ConfigMap) SetStringValue(key, value string) error { 39 entry, err := cm.FindEntry(key) 40 41 var notFound *NotFoundError 42 43 valueNode := entry.ValueNode 44 45 if err != nil && errors.As(err, ¬Found) { 46 keyNode := &yaml.Node{ 47 Kind: yaml.ScalarNode, 48 Value: key, 49 } 50 valueNode = &yaml.Node{ 51 Kind: yaml.ScalarNode, 52 Tag: "!!str", 53 Value: "", 54 } 55 56 cm.Root.Content = append(cm.Root.Content, keyNode, valueNode) 57 } else if err != nil { 58 return err 59 } 60 61 valueNode.Value = value 62 63 return nil 64 } 65 66 func (cm *ConfigMap) FindEntry(key string) (ce *ConfigEntry, err error) { 67 err = nil 68 69 ce = &ConfigEntry{} 70 71 topLevelKeys := cm.Root.Content 72 for i, v := range topLevelKeys { 73 if v.Value == key { 74 ce.KeyNode = v 75 ce.Index = i 76 if i+1 < len(topLevelKeys) { 77 ce.ValueNode = topLevelKeys[i+1] 78 } 79 return 80 } 81 } 82 83 return ce, &NotFoundError{errors.New("not found")} 84 } 85 86 func (cm *ConfigMap) RemoveEntry(key string) { 87 newContent := []*yaml.Node{} 88 89 content := cm.Root.Content 90 for i := 0; i < len(content); i++ { 91 if content[i].Value == key { 92 i++ // skip the next node which is this key's value 93 } else { 94 newContent = append(newContent, content[i]) 95 } 96 } 97 98 cm.Root.Content = newContent 99 }