Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[syntax] Refactor YAML editing functionality #411

Merged
merged 1 commit into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cmd/esc/cli/env_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/pulumi/esc"
"github.com/pulumi/esc/cmd/esc/cli/client"
"github.com/pulumi/esc/cmd/esc/cli/style"
"github.com/pulumi/esc/syntax/encoding"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
)
Expand Down Expand Up @@ -281,7 +282,7 @@ func (get *envGetCommand) getEnvironmentMember(
}

if len(path) != 0 && path[0] == "imports" {
node, _ := yamlNode{&docNode}.get(path)
node, _ := encoding.YAMLSyntax{Node: &docNode}.Get(path)
if node == nil {
return nil, nil
}
Expand Down Expand Up @@ -313,8 +314,8 @@ func (get *envGetCommand) getEnvironmentMember(
}

definitionYAML := ""
if valuesNode, ok := (yamlNode{&docNode}.get(resource.PropertyPath{"values"})); ok {
if node, _ := (yamlNode{valuesNode}.get(path)); node != nil {
if valuesNode, ok := (encoding.YAMLSyntax{Node: &docNode}.Get(resource.PropertyPath{"values"})); ok {
if node, _ := (encoding.YAMLSyntax{Node: valuesNode}.Get(path)); node != nil {
expr, ok := getEnvExpr(esc.Expr{Object: env.Exprs}, path)
if !ok {
return nil, fmt.Errorf("internal error: no expr for path %v", path)
Expand Down
5 changes: 3 additions & 2 deletions cmd/esc/cli/env_rm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"

"github.com/pulumi/esc/syntax/encoding"
"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
pulumienv "github.com/pulumi/pulumi/sdk/v3/go/common/env"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
Expand Down Expand Up @@ -84,11 +85,11 @@ func newEnvRmCmd(env *envCommand) *cobra.Command {
if docNode.Kind != yaml.DocumentNode {
return nil
}
valuesNode, ok := yamlNode{&docNode}.get(resource.PropertyPath{"values"})
valuesNode, ok := encoding.YAMLSyntax{Node: &docNode}.Get(resource.PropertyPath{"values"})
if !ok {
return nil
}
err = yamlNode{valuesNode}.delete(nil, path)
err = encoding.YAMLSyntax{Node: valuesNode}.Delete(nil, path)
if err != nil {
return err
}
Expand Down
160 changes: 5 additions & 155 deletions cmd/esc/cli/env_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"

"github.com/pulumi/esc/syntax/encoding"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
)
Expand Down Expand Up @@ -107,18 +108,18 @@ func newEnvSetCmd(env *envCommand) *cobra.Command {
}

if path[0] == "imports" {
_, err = yamlNode{&docNode}.set(nil, path, yamlValue)
_, err = encoding.YAMLSyntax{Node: &docNode}.Set(nil, path, yamlValue)
} else {
valuesNode, ok := yamlNode{&docNode}.get(resource.PropertyPath{"values"})
valuesNode, ok := encoding.YAMLSyntax{Node: &docNode}.Get(resource.PropertyPath{"values"})
if !ok {
valuesNode, err = yamlNode{&docNode}.set(nil, resource.PropertyPath{"values"}, yaml.Node{
valuesNode, err = encoding.YAMLSyntax{Node: &docNode}.Set(nil, resource.PropertyPath{"values"}, yaml.Node{
Kind: yaml.MappingNode,
})
if err != nil {
return fmt.Errorf("internal error: %w", err)
}
}
_, err = yamlNode{valuesNode}.set(nil, path, yamlValue)
_, err = encoding.YAMLSyntax{Node: valuesNode}.Set(nil, path, yamlValue)
}
if err != nil {
return err
Expand Down Expand Up @@ -187,154 +188,3 @@ func looksLikeSecret(path resource.PropertyPath, n yaml.Node) bool {
return info.Entropy >= entropyThreshold ||
(info.Entropy >= (entropyThreshold/2) && entropyPerChar >= entropyPerCharThreshold)
}

type yamlNode struct {
*yaml.Node
}

func (n yamlNode) get(path resource.PropertyPath) (_ *yaml.Node, ok bool) {
if n.Kind == yaml.DocumentNode {
return yamlNode{n.Content[0]}.get(path)
}

if len(path) == 0 {
return n.Node, true
}

switch n.Kind {
case yaml.SequenceNode:
index, ok := path[0].(int)
if !ok || index < 0 || index >= len(n.Content) {
return nil, false
}
return yamlNode{n.Content[index]}.get(path[1:])
case yaml.MappingNode:
key, ok := path[0].(string)
if !ok {
return nil, false
}
for i := 0; i < len(n.Content); i += 2 {
keyNode, valueNode := n.Content[i], n.Content[i+1]
if keyNode.Value == key {
return yamlNode{valueNode}.get(path[1:])
}
}
return nil, false
default:
return nil, false
}
}

func (n yamlNode) set(prefix, path resource.PropertyPath, new yaml.Node) (*yaml.Node, error) {
if n.Kind == yaml.DocumentNode {
return yamlNode{n.Content[0]}.set(prefix, path, new)
}

if len(path) == 0 {
n.Content = new.Content
n.Kind = new.Kind
n.Tag = new.Tag
n.Value = new.Value
return n.Node, nil
}

prefix = append(prefix, path[0])
switch n.Kind {
case 0:
switch accessor := path[0].(type) {
case int:
n.Kind, n.Tag = yaml.SequenceNode, "!!seq"
case string:
n.Kind, n.Tag = yaml.MappingNode, "!!map"
default:
contract.Failf("unexpected accessor kind %T", accessor)
return nil, nil
}
return n.set(prefix[:len(prefix)-1], path, new)
case yaml.SequenceNode:
index, ok := path[0].(int)
if !ok {
return nil, fmt.Errorf("%v: key for an array must be an int", prefix)
}
if index < 0 || index > len(n.Content) {
return nil, fmt.Errorf("%v: array index out of range", prefix)
}
if index == len(n.Content) {
n.Content = append(n.Content, &yaml.Node{})
}
elem := n.Content[index]
return yamlNode{elem}.set(prefix, path[1:], new)
case yaml.MappingNode:
key, ok := path[0].(string)
if !ok {
return nil, fmt.Errorf("%v: key for a map must be a string", prefix)
}

var valueNode *yaml.Node
for i := 0; i < len(n.Content); i += 2 {
keyNode, value := n.Content[i], n.Content[i+1]
if keyNode.Value == key {
valueNode = value
break
}
}
if valueNode == nil {
n.Content = append(n.Content, &yaml.Node{
Kind: yaml.ScalarNode,
Value: key,
Tag: "!!str",
})
n.Content = append(n.Content, &yaml.Node{})
valueNode = n.Content[len(n.Content)-1]
}
return yamlNode{valueNode}.set(prefix, path[1:], new)
default:
return nil, fmt.Errorf("%v: expected an array or an object", prefix)
}
}

func (n yamlNode) delete(prefix, path resource.PropertyPath) error {
if n.Kind == yaml.DocumentNode {
return yamlNode{n.Content[0]}.delete(prefix, path)
}

prefix = append(prefix, path[0])
switch n.Kind {
case yaml.SequenceNode:
index, ok := path[0].(int)
if !ok {
return fmt.Errorf("%v: key for an array must be an int", prefix)
}
if index < 0 || index >= len(n.Content) {
return fmt.Errorf("%v: array index out of range", prefix)
}
if len(path) == 1 {
n.Content = append(n.Content[:index], n.Content[index+1:]...)
return nil
}
elem := n.Content[index]
return yamlNode{elem}.delete(prefix, path[1:])
case yaml.MappingNode:
key, ok := path[0].(string)
if !ok {
return fmt.Errorf("%v: key for a map must be a string", prefix)
}

i := 0
for ; i < len(n.Content); i += 2 {
if n.Content[i].Value == key {
break
}
}
if len(path) == 1 {
if i != len(n.Content) {
n.Content = append(n.Content[:i], n.Content[i+2:]...)
}
return nil
}
valueNode := n.Content[i+1]
return yamlNode{valueNode}.delete(prefix, path[1:])
default:
return fmt.Errorf("%v: expected an array or an object", prefix)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
values:
foo: bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
edits:
- values.bar: baz
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
values:
foo: bar
bar: baz
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
values:
array:
- hello
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
edits:
- 'values.array[1]': world
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
values:
array:
- hello
- world
Empty file.
8 changes: 8 additions & 0 deletions syntax/encoding/testdata/yaml-edit/many/edits.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
edits:
- 'object["foo"]': baz
- 'object["bar"]': qux
- 'array[0]': 1
- 'array[1].nested': 2
- '["dotted.property"]': [list, of, strings]
- 'environmentVariables': {HELLO: world}
- '["deeply.nested"].path.with.an["\"array[]\""][0]': neat
18 changes: 18 additions & 0 deletions syntax/encoding/testdata/yaml-edit/many/expected.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
object:
foo: baz
bar: qux
array:
- 1
- nested: 2
dotted.property:
- list
- of
- strings
environmentVariables:
HELLO: world
deeply.nested:
path:
with:
an:
'"array[]"':
- neat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
values:
array:
- hello
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
edits:
- 'values.array[0]': null
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
values:
array: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
values:
foo: bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
edits:
- values.foo: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
values: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
values:
array:
- hello
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
edits:
- 'values.array[0]': bonjour
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
values:
array:
- bonjour
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
values:
foo: bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
edits:
- values.foo: baz
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
values:
foo: baz
Loading
Loading