Skip to content

Commit

Permalink
add naive jsonencode implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
drlau committed Feb 2, 2021
1 parent bba8bf3 commit 6905cee
Show file tree
Hide file tree
Showing 5 changed files with 518 additions and 2 deletions.
137 changes: 136 additions & 1 deletion jsonencode.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,138 @@
package tfplanparse

// TODO implement
import (
"fmt"
"strings"
)

type JSONEncodeAttributeChange struct {
Name string
AttributeChanges []attributeChange
UpdateType UpdateType
}

var _ attributeChange = &JSONEncodeAttributeChange{}

// IsJSONEncodeAttributeChangeLine returns true if the line is a valid attribute change
// This requires the line to start with "+", "-" or "~", delimited with a space, and the value to start with "jsonencode(".
func IsJSONEncodeAttributeChangeLine(line string) bool {
line = strings.TrimSpace(line)
attribute := strings.SplitN(line, ATTRIBUTE_DEFINITON_DELIMITER, 2)
if len(attribute) != 2 {
return false
}

validPrefix := strings.HasPrefix(line, "+") || strings.HasPrefix(line, "-") || strings.HasPrefix(line, "~")
isJSONEncode := strings.HasPrefix(attribute[1], "jsonencode(")
return validPrefix && isJSONEncode && !IsResourceChangeLine(line)
}

// IsJSONEncodeAttributeTerminator returns true if the line is ")"
// TODO: verify this
func IsJSONEncodeAttributeTerminator(line string) bool {
return strings.TrimSuffix(strings.TrimSpace(line), " -> null") == ")"
}

// NewJSONEncodeAttributeChangeFromLine initializes a JSONEncodeAttributeChange from a line containing a JSONEncode change
// It expects a line that passes the IsJSONEncodeAttributeChangeLine check
func NewJSONEncodeAttributeChangeFromLine(line string) (*JSONEncodeAttributeChange, error) {
line = strings.TrimSpace(line)
if !IsJSONEncodeAttributeChangeLine(line) {
return nil, fmt.Errorf("%s is not a valid line to initialize a JSONEncodeAttributeChange", line)
}
attribute := strings.SplitN(removeChangeTypeCharacters(line), ATTRIBUTE_DEFINITON_DELIMITER, 2)

if strings.HasPrefix(line, "+") {
// add
return &JSONEncodeAttributeChange{
Name: dequote(strings.TrimSpace(attribute[0])),
UpdateType: NewResource,
}, nil
} else if strings.HasPrefix(line, "-") {
// destroy
return &JSONEncodeAttributeChange{
Name: dequote(strings.TrimSpace(attribute[0])),
UpdateType: DestroyResource,
}, nil
} else if strings.HasPrefix(line, "~") {
// replace
updateType := UpdateInPlaceResource
if strings.HasSuffix(attribute[1], " # forces replacement") {
updateType = ForceReplaceResource
}

return &JSONEncodeAttributeChange{
Name: dequote(strings.TrimSpace(attribute[0])),
UpdateType: updateType,
}, nil
} else {
return nil, fmt.Errorf("unrecognized line pattern")
}
}

// GetName returns the name of the attribute
func (j *JSONEncodeAttributeChange) GetName() string {
return j.Name
}

// GetUpdateType returns the UpdateType of the attribute
func (j *JSONEncodeAttributeChange) GetUpdateType() UpdateType {
return j.UpdateType
}

// IsSensitive returns true if the attribute contains a sensitive value
func (j *JSONEncodeAttributeChange) IsSensitive() bool {
for _, ac := range j.AttributeChanges {
if ac.IsSensitive() {
return true
}
}
return false
}

// IsComputed returns true if the attribute contains a computed value
func (j *JSONEncodeAttributeChange) IsComputed() bool {
for _, ac := range j.AttributeChanges {
if ac.IsComputed() {
return true
}
}
return false
}

// IsNoOp returns true if the attribute has not changed
func (j *JSONEncodeAttributeChange) IsNoOp() bool {
return j.UpdateType == NoOpResource
}

func (j *JSONEncodeAttributeChange) GetBefore(opts ...GetBeforeAfterOptions) interface{} {
result := map[string]interface{}{}

attrs:
for _, a := range j.AttributeChanges {
for _, opt := range opts {
if opt(a) {
continue attrs
}
}
result[a.GetName()] = a.GetBefore(opts...)
}

return result
}

func (j *JSONEncodeAttributeChange) GetAfter(opts ...GetBeforeAfterOptions) interface{} {
result := map[string]interface{}{}

attrs:
for _, a := range j.AttributeChanges {
for _, opt := range opts {
if opt(a) {
continue attrs
}
}
result[a.GetName()] = a.GetAfter(opts...)
}

return result
}
65 changes: 65 additions & 0 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ func parseResource(s *bufio.Scanner) (*ResourceChange, error) {
return nil, err
}
rc.AttributeChanges = append(rc.AttributeChanges, aa)
case IsJSONEncodeAttributeChangeLine(text):
ja, err := parseJSONEncodeAttribute(s)
if err != nil {
return nil, err
}
rc.AttributeChanges = append(rc.AttributeChanges, ja)
case IsHeredocAttributeChangeLine(text):
ha, err := parseHeredocAttribute(s)
if err != nil {
Expand Down Expand Up @@ -136,6 +142,12 @@ func parseMapAttribute(s *bufio.Scanner) (*MapAttributeChange, error) {
return nil, err
}
result.AttributeChanges = append(result.AttributeChanges, aa)
case IsJSONEncodeAttributeChangeLine(text):
ja, err := parseJSONEncodeAttribute(s)
if err != nil {
return nil, err
}
result.AttributeChanges = append(result.AttributeChanges, ja)
case IsHeredocAttributeChangeLine(text):
ha, err := parseHeredocAttribute(s)
if err != nil {
Expand Down Expand Up @@ -183,6 +195,12 @@ func parseArrayAttribute(s *bufio.Scanner) (*ArrayAttributeChange, error) {
return nil, err
}
result.AttributeChanges = append(result.AttributeChanges, ma)
case IsJSONEncodeAttributeChangeLine(text):
ja, err := parseJSONEncodeAttribute(s)
if err != nil {
return nil, err
}
result.AttributeChanges = append(result.AttributeChanges, ja)
case IsHeredocAttributeChangeLine(text):
ha, err := parseHeredocAttribute(s)
if err != nil {
Expand All @@ -201,6 +219,53 @@ func parseArrayAttribute(s *bufio.Scanner) (*ArrayAttributeChange, error) {
return nil, fmt.Errorf("unexpected end of input while parsing array attribute")
}

func parseJSONEncodeAttribute(s *bufio.Scanner) (*JSONEncodeAttributeChange, error) {
normalized := formatInput(s.Bytes())
result, err := NewJSONEncodeAttributeChangeFromLine(normalized)
if err != nil {
return nil, err
}
// TODO: check if oneline check needed

for s.Scan() {
text := formatInput(s.Bytes())
switch {
case IsJSONEncodeAttributeTerminator(text):
return result, nil
case IsResourceCommentLine(text), strings.Contains(text, CHANGES_END_STRING):
return nil, fmt.Errorf("unexpected line while parsing jsonencode attribute: %s", text)
case IsMapAttributeChangeLine(text):
ma, err := parseMapAttribute(s)
if err != nil {
return nil, err
}
result.AttributeChanges = append(result.AttributeChanges, ma)
case IsArrayAttributeChangeLine(text):
aa, err := parseArrayAttribute(s)
if err != nil {
return nil, err
}
result.AttributeChanges = append(result.AttributeChanges, aa)
case IsHeredocAttributeChangeLine(text):
// TODO: check if this is even allowed by terraform
ha, err := parseHeredocAttribute(s)
if err != nil {
return nil, err
}
result.AttributeChanges = append(result.AttributeChanges, ha)
case IsAttributeChangeLine(text):
// TODO: check if this is even allowed by terraform
ac, err := NewAttributeChangeFromLine(text)
if err != nil {
return nil, err
}
result.AttributeChanges = append(result.AttributeChanges, ac)
}
}

return nil, fmt.Errorf("unexpected end of input while parsing jsonencode attribute")
}

func parseHeredocAttribute(s *bufio.Scanner) (*HeredocAttributeChange, error) {
normalized := formatInput(s.Bytes())
result, err := NewHeredocAttributeChangeFromLine(normalized)
Expand Down
Loading

0 comments on commit 6905cee

Please sign in to comment.