github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/common/property_encoder.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "reflect" 6 "sort" 7 "strconv" 8 "strings" 9 10 "github.com/lineaje-labs/syft/internal/log" 11 ) 12 13 // FieldName return a flag to indicate this is a valid field and a name to use 14 type FieldName func(field reflect.StructField) (string, bool) 15 16 // OptionalTag given a tag name, will return the defined tag or fall back to lower camel case field name 17 func OptionalTag(tag string) FieldName { 18 return func(f reflect.StructField) (string, bool) { 19 if n, ok := f.Tag.Lookup(tag); ok { 20 return n, true 21 } 22 return lowerFirst(f.Name), true 23 } 24 } 25 26 // TrimOmitempty trims `,omitempty` from the name 27 func TrimOmitempty(fn FieldName) FieldName { 28 return func(f reflect.StructField) (string, bool) { 29 if v, ok := fn(f); ok { 30 return strings.TrimSuffix(v, ",omitempty"), true 31 } 32 return "", false 33 } 34 } 35 36 // RequiredTag based on the given tag, only use a field if present 37 func RequiredTag(tag string) FieldName { 38 return func(f reflect.StructField) (string, bool) { 39 if n, ok := f.Tag.Lookup(tag); ok { 40 return n, true 41 } 42 return "", false 43 } 44 } 45 46 var ( 47 // OptionalJSONTag uses field names defined in json tags, if available 48 OptionalJSONTag = TrimOmitempty(OptionalTag("json")) 49 ) 50 51 // lowerFirst converts the first character of the string to lower case 52 func lowerFirst(s string) string { 53 return strings.ToLower(s[0:1]) + s[1:] 54 } 55 56 // Encode recursively encodes the object's properties as an ordered set of NameValue pairs 57 func Encode(obj interface{}, prefix string, fn FieldName) map[string]string { 58 if obj == nil { 59 return nil 60 } 61 props := map[string]string{} 62 encode(props, reflect.ValueOf(obj), prefix, fn) 63 return props 64 } 65 66 // NameValue a simple type to store stringified name/value pairs 67 type NameValue struct { 68 Name string 69 Value string 70 } 71 72 // Sorted returns a sorted set of NameValue pairs 73 func Sorted(values map[string]string) (out []NameValue) { 74 var keys []string 75 for k := range values { 76 keys = append(keys, k) 77 } 78 sort.Strings(keys) 79 for _, k := range keys { 80 out = append(out, NameValue{ 81 Name: k, 82 Value: values[k], 83 }) 84 } 85 return 86 } 87 88 func encode(out map[string]string, value reflect.Value, prefix string, fn FieldName) { 89 if !value.IsValid() || value.Type() == nil { 90 return 91 } 92 93 typ := value.Type() 94 95 switch typ.Kind() { 96 case reflect.Ptr: 97 if value.IsNil() { 98 return 99 } 100 value = value.Elem() 101 encode(out, value, prefix, fn) 102 case reflect.String: 103 v := value.String() 104 if v != "" { 105 out[prefix] = v 106 } 107 case reflect.Bool: 108 v := value.Bool() 109 out[prefix] = strconv.FormatBool(v) 110 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 111 v := value.Int() 112 out[prefix] = strconv.FormatInt(v, 10) 113 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 114 v := value.Uint() 115 out[prefix] = strconv.FormatUint(v, 10) 116 case reflect.Float32, reflect.Float64: 117 v := value.Float() 118 out[prefix] = fmt.Sprintf("%f", v) 119 case reflect.Array, reflect.Slice: 120 for idx := 0; idx < value.Len(); idx++ { 121 encode(out, value.Index(idx), fmt.Sprintf("%s:%d", prefix, idx), fn) 122 } 123 case reflect.Struct: 124 for i := 0; i < typ.NumField(); i++ { 125 pv := value.Field(i) 126 f := typ.Field(i) 127 name, ok := fieldName(f, prefix, fn) 128 if !ok { 129 continue 130 } 131 encode(out, pv, name, fn) 132 } 133 case reflect.Map: 134 // currently only map[string]string is really supported 135 for _, key := range value.MapKeys() { 136 encode(out, value.MapIndex(key), fmt.Sprintf("%s:%v", prefix, key.Interface()), fn) 137 } 138 default: 139 log.Warnf("skipping encoding of unsupported property: %s", prefix) 140 } 141 } 142 143 // fieldName gets the name of the field using the provided FieldName function 144 func fieldName(f reflect.StructField, prefix string, fn FieldName) (string, bool) { 145 name, ok := fn(f) 146 if !ok { 147 return "", false 148 } 149 if name == "" { 150 return prefix, true 151 } 152 if prefix != "" { 153 name = fmt.Sprintf("%s:%s", prefix, name) 154 } 155 return name, true 156 } 157 158 // Decode based on the given type, applies all values to hydrate a new instance 159 func Decode(typ reflect.Type, values map[string]string, prefix string, fn FieldName) interface{} { 160 isPtr := false 161 for typ.Kind() == reflect.Ptr { 162 typ = typ.Elem() 163 isPtr = true 164 } 165 166 isSlice := false 167 if typ.Kind() == reflect.Slice { 168 typ = reflect.PtrTo(typ) 169 isSlice = true 170 } 171 172 v := reflect.New(typ) 173 174 decode(values, v, prefix, fn) 175 176 switch { 177 case isSlice && isPtr: 178 return v.Elem().Interface() 179 case isSlice: 180 return PtrToStruct(v.Elem().Interface()) 181 case isPtr: 182 return v.Interface() 183 } 184 return v.Elem().Interface() 185 } 186 187 // DecodeInto decodes all values to hydrate the given object instance 188 func DecodeInto(obj interface{}, values map[string]string, prefix string, fn FieldName) { 189 value := reflect.ValueOf(obj) 190 191 for value.Type().Kind() == reflect.Ptr { 192 value = value.Elem() 193 } 194 195 decode(values, value, prefix, fn) 196 } 197 198 //nolint:funlen,gocognit,gocyclo 199 func decode(vals map[string]string, value reflect.Value, prefix string, fn FieldName) bool { 200 if !value.IsValid() || value.Type() == nil { 201 return false 202 } 203 204 typ := value.Type() 205 206 incoming, valid := vals[prefix] 207 switch typ.Kind() { 208 case reflect.Ptr: 209 t := typ.Elem() 210 v := value 211 if v.IsNil() { 212 v = reflect.New(t) 213 } 214 if decode(vals, v.Elem(), prefix, fn) && value.CanSet() { 215 o := v.Interface() 216 log.Infof("%v", o) 217 value.Set(v) 218 } else { 219 return false 220 } 221 case reflect.String: 222 if valid { 223 value.SetString(incoming) 224 } else { 225 return false 226 } 227 case reflect.Bool: 228 if !valid { 229 return false 230 } 231 if b, err := strconv.ParseBool(incoming); err == nil { 232 value.SetBool(b) 233 } else { 234 return false 235 } 236 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 237 if !valid { 238 return false 239 } 240 if i, err := strconv.ParseInt(incoming, 10, 64); err == nil { 241 value.SetInt(i) 242 } else { 243 return false 244 } 245 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 246 if !valid { 247 return false 248 } 249 if i, err := strconv.ParseUint(incoming, 10, 64); err == nil { 250 value.SetUint(i) 251 } else { 252 return false 253 } 254 case reflect.Float32, reflect.Float64: 255 if !valid { 256 return false 257 } 258 if i, err := strconv.ParseFloat(incoming, 64); err == nil { 259 value.SetFloat(i) 260 } else { 261 return false 262 } 263 case reflect.Array, reflect.Slice: 264 values := false 265 t := typ.Elem() 266 slice := reflect.MakeSlice(typ, 0, 0) 267 for idx := 0; ; idx++ { 268 // test for index 269 str := fmt.Sprintf("%s:%d", prefix, idx) 270 // create new placeholder and decode values 271 newType := t 272 if t.Kind() == reflect.Ptr { 273 newType = t.Elem() 274 } 275 v := reflect.New(newType) 276 if decode(vals, v.Elem(), str, fn) { 277 // append to slice 278 if t.Kind() != reflect.Ptr { 279 v = v.Elem() 280 } 281 slice = reflect.Append(slice, v) 282 values = true 283 } else { 284 break 285 } 286 } 287 if values { 288 value.Set(slice) 289 } else { 290 return false 291 } 292 case reflect.Map: 293 values := false 294 keyType := typ.Key() 295 valueType := typ.Elem() 296 outMap := reflect.MakeMap(typ) 297 str := fmt.Sprintf("%s:", prefix) 298 keyVals := map[string]string{} 299 // iterate through all keys to find those prefixed with a reference to this map 300 // NOTE: this will not work for nested maps 301 for key := range vals { 302 // test for map prefix 303 if strings.HasPrefix(key, str) { 304 keyVals[key] = strings.TrimPrefix(key, str) 305 // create new placeholder and decode key 306 newKeyType := keyType 307 if keyType.Kind() == reflect.Ptr { 308 newKeyType = keyType.Elem() 309 } 310 k := reflect.New(newKeyType) 311 if !decode(keyVals, k.Elem(), key, fn) { 312 log.Debugf("unable to decode key for: %s", key) 313 continue 314 } 315 if keyType.Kind() != reflect.Ptr { 316 k = k.Elem() 317 } 318 319 // create new placeholder and decode value 320 newValueType := valueType 321 if valueType.Kind() == reflect.Ptr { 322 newValueType = valueType.Elem() 323 } 324 v := reflect.New(newValueType) 325 if decode(vals, v.Elem(), key, fn) { 326 if valueType.Kind() != reflect.Ptr { 327 v = v.Elem() 328 } 329 330 // set in map 331 outMap.SetMapIndex(k, v) 332 values = true 333 } 334 } 335 } 336 if values { 337 value.Set(outMap) 338 } else { 339 return false 340 } 341 case reflect.Struct: 342 values := false 343 for i := 0; i < typ.NumField(); i++ { 344 f := typ.Field(i) 345 v := value.Field(i) 346 347 name, ok := fieldName(f, prefix, fn) 348 if !ok { 349 continue 350 } 351 352 if decode(vals, v, name, fn) { 353 values = true 354 } 355 } 356 return values 357 default: 358 log.Warnf("unable to set field: %s", prefix) 359 return false 360 } 361 return true 362 } 363 364 func PtrToStruct(ptr interface{}) interface{} { 365 v := reflect.ValueOf(ptr) 366 if v.IsZero() && v.Type().Kind() != reflect.Struct { 367 return nil 368 } 369 switch v.Type().Kind() { 370 case reflect.Ptr: 371 return PtrToStruct(v.Elem().Interface()) 372 case reflect.Interface: 373 return PtrToStruct(v.Elem().Interface()) 374 } 375 return v.Interface() 376 }