map.go raw
1 // Copyright 2022-2025 The sacloud/iaas-api-go 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 mapconv
16
17 import (
18 "fmt"
19 "reflect"
20 "strings"
21 )
22
23 // Map is wrapper of map[string]interface{}
24 type Map map[string]interface{}
25
26 // Map returns output map
27 func (m *Map) Map() map[string]interface{} {
28 return *m
29 }
30
31 // Set sets map value with dot-separated key
32 func (m *Map) Set(key string, value interface{}) {
33 keys := strings.Split(key, ".")
34 var dest map[string]interface{} = *m
35 for i, k := range keys {
36 last := i == len(keys)-1
37 isSlice := strings.HasPrefix(k, "[]")
38 k = strings.ReplaceAll(k, "[]", "")
39
40 var v interface{}
41 if last {
42 v = value
43 }
44
45 if !last && isSlice {
46 values := valueToSlice(value)
47 var nestMap []map[string]interface{}
48 for _, value := range values {
49 nested := Map(map[string]interface{}{})
50 key := strings.Join(keys[i+1:], ".")
51 nested.Set(key, value)
52 nestMap = append(nestMap, nested)
53 }
54 if _, ok := dest[k]; !ok {
55 dest[k] = nestMap
56 } else {
57 existed, ok := dest[k].([]map[string]interface{})
58 if !ok {
59 dest[k] = nestMap
60 }
61 dest[k] = append(existed, nestMap...)
62 }
63 return
64 }
65
66 setValueWithDefault(dest, k, v)
67 if !last {
68 dest = dest[k].(map[string]interface{})
69 }
70 }
71 }
72
73 func valueToSlice(value interface{}) []interface{} {
74 v := reflect.ValueOf(value)
75 if v.Kind() == reflect.Slice {
76 ret := make([]interface{}, v.Len())
77 for i := 0; i < v.Len(); i++ {
78 ret[i] = v.Index(i).Interface()
79 }
80 return ret
81 }
82 return []interface{}{value}
83 }
84
85 func setValueWithDefault(values map[string]interface{}, key string, value interface{}) {
86 if value == nil {
87 value = map[string]interface{}{}
88 }
89 if _, ok := values[key]; !ok {
90 values[key] = value
91 }
92 }
93
94 // Get returns map value with dot-separated key
95 func (m *Map) Get(key string) (interface{}, error) {
96 keys := strings.Split(key, ".")
97 targetMap := *m
98 for i, k := range keys {
99 last := i == len(keys)-1
100 k = strings.ReplaceAll(k, "[]", "")
101
102 value := targetMap[k]
103 if value == nil || reflect.ValueOf(value).IsZero() {
104 return nil, nil
105 }
106 if last {
107 return value, nil
108 }
109
110 switch value := value.(type) {
111 case map[string]interface{}:
112 targetMap = value
113 case []interface{}:
114 var values []interface{}
115 for _, v := range value {
116 if _, ok := v.(map[string]interface{}); !ok {
117 return nil, fmt.Errorf("elements of key %q(part of %q) are not map[string]interface{}", k, key)
118 }
119 nested := Map(v.(map[string]interface{}))
120 key := strings.Join(keys[i+1:], ".")
121 nv, err := nested.Get(key)
122 if err != nil {
123 return nil, err
124 }
125 if nv != nil {
126 nvs, ok := nv.([]interface{})
127 if ok {
128 values = append(values, nvs...)
129 } else {
130 values = append(values, nv)
131 }
132 }
133 }
134 return values, nil
135 case []map[string]interface{}:
136 var values []interface{}
137 for _, v := range value {
138 nested := Map(v)
139 key := strings.Join(keys[i+1:], ".")
140 nv, err := nested.Get(key)
141 if err != nil {
142 return nil, err
143 }
144 if nv != nil {
145 nvs, ok := nv.([]interface{})
146 if ok {
147 values = append(values, nvs...)
148 } else {
149 values = append(values, nv)
150 }
151 }
152 }
153 return values, nil
154 default:
155 // 対象がオブジェクト(value)、かつフィールドが全て空(nil)の場合にここに到達する
156 return nil, nil
157 }
158 }
159
160 return nil, fmt.Errorf("failed output get input map: invalid state - key:%s values:%v", key, *m)
161 }
162