github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/util/yamlutil/node_tools.go (about)

     1  package yamlutil
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"gopkg.in/yaml.v3"
     7  )
     8  
     9  type yamlError struct {
    10  	*yaml.Node
    11  	err string
    12  }
    13  
    14  func (e yamlError) Error() string {
    15  	return fmt.Sprintf("[%v:%v] %v", e.Node.Line, e.Node.Column, e.err)
    16  }
    17  
    18  func YamlErrorf(node *yaml.Node, format string, a ...interface{}) error {
    19  	return yamlError{Node: node, err: fmt.Sprintf(format, a...)}
    20  }
    21  
    22  // Get attempts to obtain the value at an index in a sequence or in a mapping. Returns the node and true if successful;
    23  // nil and false if out of bounds or not found; and nil, false, and an error if any error occurs.
    24  func Get(l *yaml.Node, key interface{}) (*yaml.Node, bool, error) {
    25  	switch l.Kind {
    26  	case yaml.DocumentNode:
    27  		// Automatically recurse as documents contain a single element
    28  		return Get(l.Content[0], key)
    29  	case yaml.SequenceNode:
    30  		if idx, ok := key.(int); ok {
    31  			if len(l.Content) > idx {
    32  				return l.Content[idx], true, nil
    33  			}
    34  			return nil, false, nil
    35  		}
    36  		return nil, false, YamlErrorf(l, "unsupported index type %T, found sequence node and expected int index", key)
    37  	case yaml.MappingNode:
    38  		for i, v := range l.Content {
    39  			if i%2 == 1 {
    40  				continue
    41  			}
    42  
    43  			switch k := key.(type) {
    44  			case string:
    45  				if v.Tag == "str" && v.Value == k {
    46  					return l.Content[i+1], true, nil
    47  				}
    48  			default:
    49  				return nil, false, YamlErrorf(l, "unsupported key type %T, found mapping node and expected string key", key)
    50  			}
    51  		}
    52  		// failure to get is not an error
    53  		return nil, false, nil
    54  	case yaml.ScalarNode:
    55  		return nil, false, YamlErrorf(l, "failed to get key %v from scalar: %v", key, l.Value)
    56  	case yaml.AliasNode:
    57  		return nil, false, YamlErrorf(l, "aliases not supported in project files: %v", l.Value)
    58  	default:
    59  		return nil, false, YamlErrorf(l, "failed to parse yaml node: %v", l.Value)
    60  	}
    61  }
    62  
    63  func Set(l *yaml.Node, value string) error {
    64  	var newNode yaml.Node
    65  	err := yaml.Unmarshal([]byte(value), &newNode)
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	*l = *newNode.Content[0]
    71  
    72  	return nil
    73  }
    74  
    75  func Insert(l *yaml.Node, key interface{}, value string) error {
    76  	var newNode yaml.Node
    77  	err := yaml.Unmarshal([]byte(value), &newNode)
    78  	if err != nil {
    79  		return err
    80  	}
    81  	newNode = *newNode.Content[0]
    82  
    83  	switch l.Kind {
    84  	case yaml.DocumentNode:
    85  		// Automatically recurse as documents contain a single element
    86  		return Insert(l.Content[0], key, value)
    87  	case yaml.SequenceNode:
    88  		if idx, ok := key.(int); ok {
    89  			if len(l.Content) > idx {
    90  				l.Content[idx] = &newNode
    91  			} else if len(l.Content) == idx {
    92  				l.Content = append(l.Content, &newNode)
    93  			}
    94  			return YamlErrorf(l, "index %v out of bounds of node: %v", idx, l.Value)
    95  		}
    96  		return YamlErrorf(l, "unsupported index type %T, found sequence node and expected int index", key)
    97  	case yaml.MappingNode:
    98  		for i, v := range l.Content {
    99  			if i%2 == 1 {
   100  				continue
   101  			}
   102  
   103  			switch k := key.(type) {
   104  			case string:
   105  				if v.Tag == "!!str" && v.Value == k {
   106  					l.Content[i+1] = &newNode
   107  					return nil
   108  				}
   109  			default:
   110  				return YamlErrorf(l, "unsupported key type %T, found mapping node and expected string key", key)
   111  			}
   112  		}
   113  
   114  		var keyNode yaml.Node
   115  		switch k := key.(type) {
   116  		case string:
   117  			err := Set(&keyNode, k)
   118  			if err != nil {
   119  				return err
   120  			}
   121  			l.Content = append(l.Content, &keyNode, &newNode)
   122  			return nil
   123  		default:
   124  			return YamlErrorf(l, "unsupported key type %T, found mapping node and expected string key", key)
   125  		}
   126  	case yaml.ScalarNode:
   127  		return YamlErrorf(l, "failed to get key %v from scalar: %v", key, l.Content)
   128  	case yaml.AliasNode:
   129  		return YamlErrorf(l, "aliases not supported in project files: %v", l.Content)
   130  	default:
   131  		return YamlErrorf(l, "failed to parse yaml node: %v", l.Content)
   132  	}
   133  }
   134  
   135  func Delete(l *yaml.Node, key interface{}) error {
   136  	switch l.Kind {
   137  	case yaml.DocumentNode:
   138  		// Automatically recurse as documents contain a single element
   139  		return Delete(l.Content[0], key)
   140  	case yaml.SequenceNode:
   141  		if idx, ok := key.(int); ok {
   142  			var content []*yaml.Node
   143  			content = append(content, l.Content[:idx]...)
   144  			content = append(content, l.Content[idx+1:]...)
   145  			l.Content = content
   146  			return nil
   147  		}
   148  		return YamlErrorf(l, "unsupported index type %T, found sequence node and expected int index", key)
   149  
   150  	case yaml.MappingNode:
   151  		for idx, v := range l.Content {
   152  			if idx%2 == 1 {
   153  				continue
   154  			}
   155  
   156  			switch k := key.(type) {
   157  			case string:
   158  				if v.Tag == "!!str" && v.Value == k {
   159  					var content []*yaml.Node
   160  					content = append(content, l.Content[:idx]...)
   161  					content = append(content, l.Content[idx+2:]...)
   162  					l.Content = content
   163  					return nil
   164  				}
   165  			default:
   166  				return YamlErrorf(l, "unsupported key type %T, found mapping node and expected string key", key)
   167  			}
   168  		}
   169  		return nil
   170  	case yaml.ScalarNode:
   171  		return YamlErrorf(l, "failed to get key %v from scalar: %v", key, l.Content)
   172  	case yaml.AliasNode:
   173  		return YamlErrorf(l, "aliases not supported in project files: %v", l.Content)
   174  	default:
   175  		return YamlErrorf(l, "failed to parse yaml node: %v", l.Content)
   176  	}
   177  }