github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/config/viper.go (about)

     1  /*
     2   * Copyright (C) 2019 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  /*
    19  The MIT License (MIT)
    20  
    21  Copyright (c) 2014 Steve Francia
    22  
    23  Permission is hereby granted, free of charge, to any person obtaining a copy
    24  of this software and associated documentation files (the "Software"), to deal
    25  in the Software without restriction, including without limitation the rights
    26  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    27  copies of the Software, and to permit persons to whom the Software is
    28  furnished to do so, subject to the following conditions:
    29  
    30  The above copyright notice and this permission notice shall be included in all
    31  copies or substantial portions of the Software.
    32  
    33  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    34  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    35  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    36  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    37  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    38  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    39  SOFTWARE.
    40  */
    41  
    42  package config
    43  
    44  import (
    45  	"fmt"
    46  	"strings"
    47  
    48  	"github.com/spf13/cast"
    49  )
    50  
    51  // Contains snippets from https://github.com/spf13/viper
    52  
    53  // SearchMap recursively searches for a value for path in source map.
    54  // Returns nil if not found.
    55  // Note: This assumes that the path entries and map keys are lower cased.
    56  func SearchMap(source map[string]interface{}, path []string) interface{} {
    57  	if len(path) == 0 {
    58  		return source
    59  	}
    60  
    61  	next, ok := source[path[0]]
    62  	if ok {
    63  		// Fast path
    64  		if len(path) == 1 {
    65  			return next
    66  		}
    67  
    68  		// Nested case
    69  		switch next := next.(type) {
    70  		case map[interface{}]interface{}:
    71  			return SearchMap(cast.ToStringMap(next), path[1:])
    72  		case map[string]interface{}:
    73  			// Type assertion is safe here since it is only reached
    74  			// if the type of `next` is the same as the type being asserted
    75  			return SearchMap(next, path[1:])
    76  		default:
    77  			// got a value but nested key expected, return "nil" for not found
    78  			return nil
    79  		}
    80  	}
    81  	return nil
    82  }
    83  
    84  // deepSearch scans deep maps, following the key indexes listed in the
    85  // sequence "path".
    86  // The last value is expected to be another map, and is returned.
    87  //
    88  // In case intermediate keys do not exist, or map to a non-map value,
    89  // a new map is created and inserted, and the search continues from there:
    90  // the initial map "m" may be modified!
    91  func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
    92  	for _, k := range path {
    93  		m2, ok := m[k]
    94  		if !ok {
    95  			// intermediate key does not exist
    96  			// => create it and continue from there
    97  			m3 := make(map[string]interface{})
    98  			m[k] = m3
    99  			m = m3
   100  			continue
   101  		}
   102  		m3, ok := m2.(map[string]interface{})
   103  		if !ok {
   104  			// intermediate key is a value
   105  			// => replace with a new map
   106  			m3 = make(map[string]interface{})
   107  			m[k] = m3
   108  		}
   109  		// continue search from here
   110  		m = m3
   111  	}
   112  	return m
   113  }
   114  
   115  func keyExists(k string, m map[string]interface{}) string {
   116  	lk := strings.ToLower(k)
   117  	for mk := range m {
   118  		lmk := strings.ToLower(mk)
   119  		if lmk == lk {
   120  			return mk
   121  		}
   122  	}
   123  	return ""
   124  }
   125  
   126  func castToMapStringInterface(
   127  	src map[interface{}]interface{}) map[string]interface{} {
   128  	tgt := map[string]interface{}{}
   129  	for k, v := range src {
   130  		tgt[fmt.Sprintf("%v", k)] = v
   131  	}
   132  	return tgt
   133  }
   134  
   135  // mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's
   136  // insistence on parsing nested structures as `map[interface{}]interface{}`
   137  // instead of using a `string` as the key for nest structures beyond one level
   138  // deep. Both map types are supported as there is a go-yaml fork that uses
   139  // `map[string]interface{}` instead.
   140  func mergeMaps(
   141  	src, tgt map[string]interface{}, itgt map[interface{}]interface{}) {
   142  	for sk, sv := range src {
   143  		tk := keyExists(sk, tgt)
   144  		if tk == "" {
   145  			tgt[sk] = sv
   146  			if itgt != nil {
   147  				itgt[sk] = sv
   148  			}
   149  			continue
   150  		}
   151  
   152  		tv, ok := tgt[tk]
   153  		if !ok {
   154  			tgt[sk] = sv
   155  			if itgt != nil {
   156  				itgt[sk] = sv
   157  			}
   158  			continue
   159  		}
   160  
   161  		switch ttv := tv.(type) {
   162  		case map[interface{}]interface{}:
   163  			tsv := sv.(map[interface{}]interface{})
   164  			ssv := castToMapStringInterface(tsv)
   165  			stv := castToMapStringInterface(ttv)
   166  			mergeMaps(ssv, stv, ttv)
   167  		case map[string]interface{}:
   168  			mergeMaps(sv.(map[string]interface{}), ttv, nil)
   169  		default:
   170  			tgt[tk] = sv
   171  			if itgt != nil {
   172  				itgt[tk] = sv
   173  			}
   174  		}
   175  	}
   176  }