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 }