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