github.com/vmware/govmomi@v0.43.0/object/option_value_list.go (about) 1 /* 2 Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package object 18 19 import ( 20 "fmt" 21 "reflect" 22 "slices" 23 "strings" 24 25 "github.com/vmware/govmomi/vim25/types" 26 ) 27 28 // OptionValueList simplifies manipulation of properties that are arrays of 29 // types.BaseOptionValue, such as ExtraConfig. 30 type OptionValueList []types.BaseOptionValue 31 32 // OptionValueListFromMap returns a new OptionValueList object from the provided 33 // map. 34 func OptionValueListFromMap[T any](in map[string]T) OptionValueList { 35 if len(in) == 0 { 36 return nil 37 } 38 var ( 39 i int 40 out = make(OptionValueList, len(in)) 41 ) 42 for k, v := range in { 43 out[i] = &types.OptionValue{Key: k, Value: v} 44 i++ 45 } 46 return out 47 } 48 49 // IsTrue returns true if the specified key exists with an empty value or value 50 // equal to 1, "1", "on", "t", true, "true", "y", or "yes". 51 // All string comparisons are case-insensitive. 52 func (ov OptionValueList) IsTrue(key string) bool { 53 return ov.isTrueOrFalse(key, true, 1, "", "1", "on", "t", "true", "y", "yes") 54 } 55 56 // IsFalse returns true if the specified key exists and has a value equal to 57 // 0, "0", "f", false, "false", "n", "no", or "off". 58 // All string comparisons are case-insensitive. 59 func (ov OptionValueList) IsFalse(key string) bool { 60 return ov.isTrueOrFalse(key, false, 0, "0", "f", "false", "n", "no", "off") 61 } 62 63 func (ov OptionValueList) isTrueOrFalse( 64 key string, 65 boolVal bool, 66 numVal int, 67 strVals ...string) bool { 68 69 val, ok := ov.Get(key) 70 if !ok { 71 return false 72 } 73 74 switch tval := val.(type) { 75 case string: 76 return slices.Contains(strVals, strings.ToLower(tval)) 77 case bool: 78 return tval == boolVal 79 case uint: 80 return tval == uint(numVal) 81 case uint8: 82 return tval == uint8(numVal) 83 case uint16: 84 return tval == uint16(numVal) 85 case uint32: 86 return tval == uint32(numVal) 87 case uint64: 88 return tval == uint64(numVal) 89 case int: 90 return tval == int(numVal) 91 case int8: 92 return tval == int8(numVal) 93 case int16: 94 return tval == int16(numVal) 95 case int32: 96 return tval == int32(numVal) 97 case int64: 98 return tval == int64(numVal) 99 case float32: 100 return tval == float32(numVal) 101 case float64: 102 return tval == float64(numVal) 103 } 104 105 return false 106 } 107 108 // Get returns the value if exists, otherwise nil is returned. The second return 109 // value is a flag indicating whether the value exists or nil was the actual 110 // value. 111 func (ov OptionValueList) Get(key string) (any, bool) { 112 if ov == nil { 113 return nil, false 114 } 115 for i := range ov { 116 if optVal := ov[i].GetOptionValue(); optVal != nil { 117 if optVal.Key == key { 118 return optVal.Value, true 119 } 120 } 121 } 122 return nil, false 123 } 124 125 // GetString returns the value as a string if the value exists. 126 func (ov OptionValueList) GetString(key string) (string, bool) { 127 if ov == nil { 128 return "", false 129 } 130 for i := range ov { 131 if optVal := ov[i].GetOptionValue(); optVal != nil { 132 if optVal.Key == key { 133 return getOptionValueAsString(optVal.Value), true 134 } 135 } 136 } 137 return "", false 138 } 139 140 // Additions returns a diff that includes only the elements from the provided 141 // list that do not already exist. 142 func (ov OptionValueList) Additions(in ...types.BaseOptionValue) OptionValueList { 143 return ov.diff(in, true) 144 } 145 146 // Diff returns a diff that includes the elements from the provided list that do 147 // not already exist or have different values. 148 func (ov OptionValueList) Diff(in ...types.BaseOptionValue) OptionValueList { 149 return ov.diff(in, false) 150 } 151 152 func (ov OptionValueList) diff(in OptionValueList, addOnly bool) OptionValueList { 153 if ov == nil && in == nil { 154 return nil 155 } 156 var ( 157 out OptionValueList 158 leftOptVals = ov.Map() 159 ) 160 for i := range in { 161 if rightOptVal := in[i].GetOptionValue(); rightOptVal != nil { 162 k, v := rightOptVal.Key, rightOptVal.Value 163 if ov == nil { 164 out = append(out, &types.OptionValue{Key: k, Value: v}) 165 } else if leftOptVal, ok := leftOptVals[k]; !ok { 166 out = append(out, &types.OptionValue{Key: k, Value: v}) 167 } else if !addOnly && v != leftOptVal { 168 out = append(out, &types.OptionValue{Key: k, Value: v}) 169 } 170 } 171 } 172 if len(out) == 0 { 173 return nil 174 } 175 return out 176 } 177 178 // Join combines this list with the provided one and returns the result, joining 179 // the two lists on their shared keys. 180 // Please note, Join(left, right) means the values from right will be appended 181 // to left, without overwriting any values that have shared keys. To overwrite 182 // the shared keys in left from right, use Join(right, left) instead. 183 func (ov OptionValueList) Join(in ...types.BaseOptionValue) OptionValueList { 184 var ( 185 out OptionValueList 186 outKeys map[string]struct{} 187 ) 188 189 // Init the out slice from the left side. 190 if len(ov) > 0 { 191 outKeys = map[string]struct{}{} 192 for i := range ov { 193 if optVal := ov[i].GetOptionValue(); optVal != nil { 194 kv := &types.OptionValue{Key: optVal.Key, Value: optVal.Value} 195 out = append(out, kv) 196 outKeys[optVal.Key] = struct{}{} 197 } 198 } 199 } 200 201 // Join the values from the right side. 202 for i := range in { 203 if rightOptVal := in[i].GetOptionValue(); rightOptVal != nil { 204 k, v := rightOptVal.Key, rightOptVal.Value 205 if _, ok := outKeys[k]; !ok { 206 out = append(out, &types.OptionValue{Key: k, Value: v}) 207 } 208 } 209 } 210 211 if len(out) == 0 { 212 return nil 213 } 214 215 return out 216 } 217 218 // Map returns the list of option values as a map. A nil value is returned if 219 // the list is empty. 220 func (ov OptionValueList) Map() map[string]any { 221 if len(ov) == 0 { 222 return nil 223 } 224 out := map[string]any{} 225 for i := range ov { 226 if optVal := ov[i].GetOptionValue(); optVal != nil { 227 out[optVal.Key] = optVal.Value 228 } 229 } 230 if len(out) == 0 { 231 return nil 232 } 233 return out 234 } 235 236 // StringMap returns the list of option values as a map where the values are 237 // strings. A nil value is returned if the list is empty. 238 func (ov OptionValueList) StringMap() map[string]string { 239 if len(ov) == 0 { 240 return nil 241 } 242 out := map[string]string{} 243 for i := range ov { 244 if optVal := ov[i].GetOptionValue(); optVal != nil { 245 out[optVal.Key] = getOptionValueAsString(optVal.Value) 246 } 247 } 248 if len(out) == 0 { 249 return nil 250 } 251 return out 252 } 253 254 func getOptionValueAsString(val any) string { 255 switch tval := val.(type) { 256 case string: 257 return tval 258 default: 259 if rv := reflect.ValueOf(val); rv.Kind() == reflect.Pointer { 260 if rv.IsNil() { 261 return "" 262 } 263 return fmt.Sprintf("%v", rv.Elem().Interface()) 264 } 265 return fmt.Sprintf("%v", tval) 266 } 267 }