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 }