github.com/sacloud/iaas-api-go@v1.12.0/mapconv/map.go (about)

     1  // Copyright 2022-2023 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  }