1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
package api
import (
"encoding/json"
"fmt"
"strconv"
)
// DevicesMap type is used to hold incus devices configurations. In contrast to
// plain map[string]map[string]string it provides unmarshal methods for JSON and
// YAML, which gracefully handle numbers and bools.
//
// swagger:model
// swagger:type object
//
// Example: {"eth0":{"network":"incusbr0","type":"nic"}
type DevicesMap map[string]map[string]string
// Backwards compatibility tests.
var (
_ map[string]map[string]string = DevicesMap{}
_ DevicesMap = map[string]map[string]string{}
)
// UnmarshalJSON implements json.Unmarshaler interface.
func (m *DevicesMap) UnmarshalJSON(data []byte) error {
var raw map[string]any
err := json.Unmarshal(data, &raw)
if err != nil {
return fmt.Errorf("JSON data not valid for DevicesMap: %w", err)
}
return m.fromMapStringAny(raw)
}
// UnmarshalYAML implements yaml.Unmarshaler interface.
func (m *DevicesMap) UnmarshalYAML(unmarshal func(any) error) error {
var raw map[string]any
err := unmarshal(&raw)
if err != nil {
return fmt.Errorf("YAML data not valid for DevicesMap: %w", err)
}
return m.fromMapStringAny(raw)
}
func (m *DevicesMap) fromMapStringAny(raw map[string]any) error {
if raw == nil {
return nil
}
result := *m
if result == nil {
result = make(DevicesMap, len(raw))
}
for k, v := range raw {
switch val := v.(type) {
case map[string]any:
res, err := fromMapStringAnyInner(val, result[k])
if err != nil {
return fmt.Errorf("inner type for %q: %w", k, err)
}
result[k] = res
case map[any]any:
mapStr := make(map[string]any, len(val))
for key, value := range val {
strKey, ok := key.(string)
if !ok {
return fmt.Errorf(`inner key "%v" is not a string`, key)
}
mapStr[strKey] = value
}
res, err := fromMapStringAnyInner(mapStr, result[k])
if err != nil {
return fmt.Errorf("inner type for %q: %w", k, err)
}
result[k] = res
default:
return fmt.Errorf("type %T is not supported in %T", v, m)
}
}
*m = result
return nil
}
func fromMapStringAnyInner(raw map[string]any, dest map[string]string) (map[string]string, error) {
if dest == nil {
dest = make(map[string]string, len(raw))
}
for k, v := range raw {
switch val := v.(type) {
case string:
dest[k] = val
case int:
dest[k] = strconv.FormatInt(int64(val), 10)
case uint64:
dest[k] = strconv.FormatUint(val, 10)
case float64:
if val == float64(int64(val)) {
dest[k] = strconv.FormatInt(int64(val), 10)
} else {
dest[k] = strconv.FormatFloat(val, 'g', -1, 64)
}
case bool:
dest[k] = strconv.FormatBool(val)
case nil:
dest[k] = ""
default:
return nil, fmt.Errorf("type %T is not supported as inner type for %T", v, DevicesMap{})
}
}
return dest, nil
}
|