github.com/GoogleCloudPlatform/terraformer@v0.8.18/terraformutils/walk.go (about)

     1  // Copyright 2019 The Terraformer Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package terraformutils
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"strings"
    21  )
    22  
    23  func WalkAndGet(path string, data interface{}) []interface{} {
    24  	_, values := walkAndGet(path, data)
    25  	return values
    26  }
    27  func WalkAndCheckField(path string, data interface{}) bool {
    28  	hasField, _ := walkAndGet(path, data)
    29  	return hasField
    30  }
    31  
    32  func WalkAndOverride(path, oldValue, newValue string, data interface{}) {
    33  	pathSegments := strings.Split(path, ".")
    34  	walkAndOverride(pathSegments, oldValue, newValue, data)
    35  }
    36  
    37  func walkAndGet(path string, data interface{}) (bool, []interface{}) {
    38  	val := reflect.ValueOf(data)
    39  
    40  	if data == nil {
    41  		if path == "" {
    42  			return true, []interface{}{}
    43  		}
    44  		return false, []interface{}{}
    45  	}
    46  
    47  	if isArray(val.Interface()) {
    48  		var arrayValues []interface{}
    49  		for i := 0; i < val.Len(); i++ {
    50  			foundField, fieldValue := walkAndGet(path, val.Index(i).Interface())
    51  			if foundField {
    52  				arrayValues = append(arrayValues, fieldValue...)
    53  			}
    54  		}
    55  		return len(arrayValues) > 0, arrayValues
    56  	}
    57  
    58  	if val.Kind() == reflect.Map {
    59  		for _, e := range val.MapKeys() {
    60  			v := val.MapIndex(e)
    61  			pathFirstElement := strings.SplitN(path, ".", 2)
    62  			if e.String() == pathFirstElement[0] {
    63  				var pathReminder = ""
    64  				if len(pathFirstElement) > 1 {
    65  					pathReminder = pathFirstElement[1]
    66  				}
    67  				hasField, value := walkAndGet(pathReminder, v.Interface())
    68  				if !hasField {
    69  					hasField, value = walkAndGet(path, v.Interface())
    70  				}
    71  				return hasField, value
    72  			} else if e.String() == path {
    73  				return walkAndGet("", v.Interface())
    74  			}
    75  		}
    76  	}
    77  
    78  	if val.Kind() == reflect.String && path == "" {
    79  		return true, []interface{}{val.Interface()}
    80  	}
    81  
    82  	return false, []interface{}{}
    83  }
    84  
    85  func walkAndOverride(pathSegments []string, oldValue, newValue string, data interface{}) {
    86  	val := reflect.ValueOf(data)
    87  	switch {
    88  	case isArray(val.Interface()):
    89  		for i := 0; i < val.Len(); i++ {
    90  			arrayValue := val.Index(i).Interface()
    91  			walkAndOverride(pathSegments, oldValue, newValue, arrayValue)
    92  		}
    93  	case len(pathSegments) == 1:
    94  		if val.Kind() == reflect.Map {
    95  			for _, e := range val.MapKeys() {
    96  				v := val.MapIndex(e)
    97  				if e.String() == pathSegments[0] {
    98  					switch {
    99  					case isArray(v.Interface()):
   100  						valss := v.Interface().([]interface{})
   101  						for idx, currentValue := range valss {
   102  							if oldValue == currentValue.(string) {
   103  								valss[idx] = newValue
   104  							}
   105  						}
   106  					case isStringArray(v.Interface()):
   107  						valss := v.Interface().([]string)
   108  						for idx, currentValue := range valss {
   109  							if oldValue == currentValue {
   110  								valss[idx] = newValue
   111  							}
   112  						}
   113  					case oldValue == fmt.Sprint(v.Interface()):
   114  						val.Interface().(map[string]interface{})[pathSegments[0]] = newValue
   115  					}
   116  				}
   117  			}
   118  		}
   119  	case val.Kind() == reflect.Map:
   120  		for _, e := range val.MapKeys() {
   121  			v := val.MapIndex(e)
   122  			if e.String() == pathSegments[0] {
   123  				walkAndOverride(pathSegments[1:], oldValue, newValue, v.Interface())
   124  			}
   125  		}
   126  	}
   127  }
   128  
   129  func isArray(val interface{}) bool { // Go reflect lib can't sometimes detect given value is array
   130  	switch val.(type) {
   131  	case []interface{}:
   132  		return true
   133  	default:
   134  		return false
   135  	}
   136  }
   137  
   138  func isStringArray(val interface{}) bool { // to support locally established arrays
   139  	switch val.(type) {
   140  	case []string:
   141  		return true
   142  	default:
   143  		return false
   144  	}
   145  }