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, &notFound) {
    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  }