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

Walk, Transform, Diff, Equal, and String. #60

Merged
merged 14 commits into from
Mar 22, 2021
35 changes: 35 additions & 0 deletions .changelog/60.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
```release-note:feature
Added tftypes.Walk and tftypes.Transform functions for the tftypes.Value type, allowing providers to traverse and mutate a tftypes.Value, respectively.
```

```release-note:feature
Added tftypes.Diff function to return the elements and attributes that are different between two tftypes.Values.
```

```release-note:enhancement
Added an Equal method to tftypes.Value to compare two tftypes.Values.
```

```release-note:enhancement
Added an Equal method to tftypes.AttributePath to compares two tftypes.AttributePaths.
```

```release-note:enhancement
Added a String method to tftypes.AttributePath to return a string representation of the tftypes.AttributePath.
```

```release-note:breaking-change
tftypes.AttributePath.WithAttributeName, WithElementKeyString, WithElementKeyInt, and WithElementKeyValue no longer accept pointers and mutate the AttributePath. They now copy the AttributePath, and return a version of it with the new AttributePathStep appended.
```

```release-note:enhancement
Added a String method to tftypes.Value, returning a string representation of the tftypes.Value.
```

```release-note:enhancement
Added a Copy method to tftypes.Value, returning a clone of the tftypes.Value such that modifying the clone is guaranteed to not modify the original.
```

```release-note:enhancement
Updated the String method of all tftypes.Type implementations to include any element or attribute types in the string as well.
```
120 changes: 110 additions & 10 deletions tfprotov5/tftypes/attribute_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package tftypes
import (
"errors"
"fmt"
"strconv"
"strings"
)

var (
Expand All @@ -26,6 +28,79 @@ type AttributePath struct {
Steps []AttributePathStep
}

func (a AttributePath) String() string {
var res strings.Builder
for pos, step := range a.Steps {
if pos != 0 {
res.WriteString(".")
}
switch v := step.(type) {
case AttributeName:
res.WriteString(`AttributeName("` + string(v) + `")`)
case ElementKeyString:
res.WriteString(`ElementKeyString("` + string(v) + `")`)
case ElementKeyInt:
res.WriteString(`ElementKeyInt(` + strconv.FormatInt(int64(v), 10) + `)`)
case ElementKeyValue:
res.WriteString(`ElementKeyValue(` + Value(v).String() + `)`)
}
}
return res.String()
}

// Equal returns true if two AttributePaths should be considered equal.
// AttributePaths are considered equal if they have the same number of steps,
// the steps are all the same types, and the steps have all the same values.
func (a AttributePath) Equal(o AttributePath) bool {
if len(a.Steps) != len(o.Steps) {
return false
}
for pos, aStep := range a.Steps {
oStep := o.Steps[pos]
switch aVal := aStep.(type) {
case AttributeName:
oVal, ok := oStep.(AttributeName)
if !ok {
return false
}
if oVal != aVal {
return false
}
case ElementKeyString:
oVal, ok := oStep.(ElementKeyString)
if !ok {
return false
}
if oVal != aVal {
return false
}
case ElementKeyInt:
oVal, ok := oStep.(ElementKeyInt)
if !ok {
return false
}
if oVal != aVal {
return false
}
paddycarver marked this conversation as resolved.
Show resolved Hide resolved
case ElementKeyValue:
oVal, ok := oStep.(ElementKeyValue)
if !ok {
return false
}
diffs, err := Value(aVal).Diff(Value(oVal))
if err != nil {
panic(err)
}
if len(diffs) > 0 {
return false
}
default:
panic(fmt.Sprintf("unknown step %T in ValueDiff.Equal", aStep))
}
}
return true
}

// NewErrorf returns an error associated with the value indicated by `a`. This
// is equivalent to calling a.NewError(fmt.Errorf(f, args...)).
func (a AttributePath) NewErrorf(f string, args ...interface{}) error {
Expand All @@ -46,32 +121,57 @@ func (a AttributePath) NewError(err error) error {

// WithAttributeName adds an AttributeName step to `a`, using `name` as the
// attribute's name.
func (a *AttributePath) WithAttributeName(name string) {
a.Steps = append(a.Steps, AttributeName(name))
func (a AttributePath) WithAttributeName(name string) AttributePath {
steps := make([]AttributePathStep, len(a.Steps))
copy(steps, a.Steps)
return AttributePath{
Steps: append(steps, AttributeName(name)),
}
}

// WithElementKeyString adds an ElementKeyString step to `a`, using `key` as
// the element's key.
func (a *AttributePath) WithElementKeyString(key string) {
a.Steps = append(a.Steps, ElementKeyString(key))
func (a AttributePath) WithElementKeyString(key string) AttributePath {
steps := make([]AttributePathStep, len(a.Steps))
copy(steps, a.Steps)
return AttributePath{
Steps: append(steps, ElementKeyString(key)),
}
}

// WithElementKeyInt adds an ElementKeyInt step to `a`, using `key` as the
// element's key.
func (a *AttributePath) WithElementKeyInt(key int64) {
a.Steps = append(a.Steps, ElementKeyInt(key))
func (a AttributePath) WithElementKeyInt(key int64) AttributePath {
steps := make([]AttributePathStep, len(a.Steps))
copy(steps, a.Steps)
return AttributePath{
Steps: append(steps, ElementKeyInt(key)),
}
}

// WithElementKeyValue adds an ElementKeyValue to `a`, using `key` as the
// element's key.
func (a *AttributePath) WithElementKeyValue(key Value) {
a.Steps = append(a.Steps, ElementKeyValue(key))
func (a AttributePath) WithElementKeyValue(key Value) AttributePath {
steps := make([]AttributePathStep, len(a.Steps))
copy(steps, a.Steps)
return AttributePath{
Steps: append(steps, ElementKeyValue(key.Copy())),
}
}

// WithoutLastStep removes the last step, whatever kind of step it was, from
// `a`.
func (a *AttributePath) WithoutLastStep() {
a.Steps = a.Steps[:len(a.Steps)-1]
func (a AttributePath) WithoutLastStep() AttributePath {
steps := make([]AttributePathStep, len(a.Steps))
copy(steps, a.Steps)
if len(a.Steps) < 1 {
return AttributePath{
Steps: steps,
}
}
return AttributePath{
Steps: steps[:len(steps)-1],
}
}

// AttributePathStep is an intentionally unimplementable interface that
Expand Down
4 changes: 4 additions & 0 deletions tfprotov5/tftypes/attribute_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,7 @@ func TestWalkAttributePath(t *testing.T) {
})
}
}

func TestAttributePathEquals(t *testing.T) {
t.Error("not implemented")
}
Loading