zoukankan      html  css  js  c++  java
  • viper 学习笔记

    viper

    Functional options

    在 c# 等语言中,一般多个参数这么实现

    funcion add(){
    
    }
    funcion add(int a){
    
    }
    funcion add(int a,int b){
    
    }
    

    在 golang 中,由于没有重载,在实现多个参数是会比较麻烦,可能的实现方式是

    func add(){
    
    }
    func adda(a int){
    
    }
    func addab(a int,b int){
    
    }
    

    这种方式的扩展性较差,因此提倡一种使用 functional option,使用函数参数的方式来进行参数赋值

    
    type optionFunc func(options *option)
    
    func seta(int a) optionFunc{
        return optionFunc(func(options *option){
            options.a = a;
        })
    }
    
    func add(opts ...optionFunc){
    	for _, opt := range opts {
    		opt(v)
    	}
    }
    
    

    当然,还可以利用接口来承载函数,

    type Option interface {
    	apply(v *Viper)
    }
    
    type optionFunc func(v *Viper)
    
    func (fn optionFunc) apply(v *Viper) {
    	fn(v)
    }
    func KeyDelimiter(d string) Option {
    	return optionFunc(func(v *Viper) {
    		v.keyDelim = d
    	})
    }
    
    func NewWithOptions(opts ...Option) *Viper {
    	v := New()
    
    	for _, opt := range opts {
    		opt.apply(v)
    	}
    
    	return v
    }
    

    基本用法

    viper.SetConfigName("config") // name of config file (without extension)
    viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
    viper.AddConfigPath("/etc/appname/")   // path to look for the config file in
    viper.AddConfigPath("$HOME/.appname")  // call multiple times to add many search paths
    viper.AddConfigPath(".")               // optionally look for config in the working directory
    err := viper.ReadInConfig() // Find and read the config file
    if err != nil { // Handle errors reading the config file
    	panic(fmt.Errorf("Fatal error config file: %s 
    ", err))
    }
    

    核心方法:

    func (v *Viper) ReadInConfig() error {
    	jww.INFO.Println("Attempting to read in config file")
    	filename, err := v.getConfigFile()
    	if err != nil {
    		return err
    	}
    
    	if !stringInSlice(v.getConfigType(), SupportedExts) {
    		return UnsupportedConfigError(v.getConfigType())
    	}
    
    	jww.DEBUG.Println("Reading file: ", filename)
    	file, err := afero.ReadFile(v.fs, filename)
    	if err != nil {
    		return err
    	}
    
    	config := make(map[string]interface{})
    
    	err = v.unmarshalReader(bytes.NewReader(file), config)
    	if err != nil {
    		return err
    	}
    
    	v.config = config
    	return nil
    }
    
    
    func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
    	buf := new(bytes.Buffer)
    	buf.ReadFrom(in)
    
    	switch strings.ToLower(v.getConfigType()) {
    	case "yaml", "yml":
    		if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil {
    			return ConfigParseError{err}
    		}
    
    	case "json":
    		if err := json.Unmarshal(buf.Bytes(), &c); err != nil {
    			return ConfigParseError{err}
    		}
    
    	case "hcl":
    		obj, err := hcl.Parse(buf.String())
    		if err != nil {
    			return ConfigParseError{err}
    		}
    		if err = hcl.DecodeObject(&c, obj); err != nil {
    			return ConfigParseError{err}
    		}
    
    	case "toml":
    		tree, err := toml.LoadReader(buf)
    		if err != nil {
    			return ConfigParseError{err}
    		}
    		tmap := tree.ToMap()
    		for k, v := range tmap {
    			c[k] = v
    		}
    
    	case "dotenv", "env":
    		env, err := gotenv.StrictParse(buf)
    		if err != nil {
    			return ConfigParseError{err}
    		}
    		for k, v := range env {
    			c[k] = v
    		}
    
    	case "properties", "props", "prop":
    		v.properties = properties.NewProperties()
    		var err error
    		if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
    			return ConfigParseError{err}
    		}
    		for _, key := range v.properties.Keys() {
    			value, _ := v.properties.Get(key)
    			// recursively build nested maps
    			path := strings.Split(key, ".")
    			lastKey := strings.ToLower(path[len(path)-1])
    			deepestMap := deepSearch(c, path[0:len(path)-1])
    			// set innermost value
    			deepestMap[lastKey] = value
    		}
    
    	case "ini":
    		cfg := ini.Empty()
    		err := cfg.Append(buf.Bytes())
    		if err != nil {
    			return ConfigParseError{err}
    		}
    		sections := cfg.Sections()
    		for i := 0; i < len(sections); i++ {
    			section := sections[i]
    			keys := section.Keys()
    			for j := 0; j < len(keys); j++ {
    				key := keys[j]
    				value := cfg.Section(section.Name()).Key(key.Name()).String()
    				c[section.Name()+"."+key.Name()] = value
    			}
    		}
    	}
    
    	insensitiviseMap(c)
    	return nil
    }
    
    

    查找核心代码

    
    // Given a key, find the value.
    //
    // Viper will check to see if an alias exists first.
    // Viper will then check in the following order:
    // flag, env, config file, key/value store.
    // Lastly, if no value was found and flagDefault is true, and if the key
    // corresponds to a flag, the flag's default value is returned.
    //
    // Note: this assumes a lower-cased key given.
    func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
    	var (
    		val    interface{}
    		exists bool
    		path   = strings.Split(lcaseKey, v.keyDelim)
    		nested = len(path) > 1
    	)
    
    	// compute the path through the nested maps to the nested value
    	if nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != "" {
    		return nil
    	}
    
    	// if the requested key is an alias, then return the proper key
    	lcaseKey = v.realKey(lcaseKey)
    	path = strings.Split(lcaseKey, v.keyDelim)
    	nested = len(path) > 1
    
    	// Set() override first
    	val = v.searchMap(v.override, path)
    	if val != nil {
    		return val
    	}
    	if nested && v.isPathShadowedInDeepMap(path, v.override) != "" {
    		return nil
    	}
    
    	// PFlag override next
    	flag, exists := v.pflags[lcaseKey]
    	if exists && flag.HasChanged() {
    		switch flag.ValueType() {
    		case "int", "int8", "int16", "int32", "int64":
    			return cast.ToInt(flag.ValueString())
    		case "bool":
    			return cast.ToBool(flag.ValueString())
    		case "stringSlice":
    			s := strings.TrimPrefix(flag.ValueString(), "[")
    			s = strings.TrimSuffix(s, "]")
    			res, _ := readAsCSV(s)
    			return res
    		case "intSlice":
    			s := strings.TrimPrefix(flag.ValueString(), "[")
    			s = strings.TrimSuffix(s, "]")
    			res, _ := readAsCSV(s)
    			return cast.ToIntSlice(res)
    		case "stringToString":
    			return stringToStringConv(flag.ValueString())
    		default:
    			return flag.ValueString()
    		}
    	}
    	if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" {
    		return nil
    	}
    
    	// Env override next
    	if v.automaticEnvApplied {
    		// even if it hasn't been registered, if automaticEnv is used,
    		// check any Get request
    		if val, ok := v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); ok {
    			return val
    		}
    		if nested && v.isPathShadowedInAutoEnv(path) != "" {
    			return nil
    		}
    	}
    	envkeys, exists := v.env[lcaseKey]
    	if exists {
    		for _, envkey := range envkeys {
    			if val, ok := v.getEnv(envkey); ok {
    				return val
    			}
    		}
    	}
    	if nested && v.isPathShadowedInFlatMap(path, v.env) != "" {
    		return nil
    	}
    
    	// Config file next
    	val = v.searchIndexableWithPathPrefixes(v.config, path)
    	if val != nil {
    		return val
    	}
    	if nested && v.isPathShadowedInDeepMap(path, v.config) != "" {
    		return nil
    	}
    
    	// K/V store next
    	val = v.searchMap(v.kvstore, path)
    	if val != nil {
    		return val
    	}
    	if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" {
    		return nil
    	}
    
    	// Default next
    	val = v.searchMap(v.defaults, path)
    	if val != nil {
    		return val
    	}
    	if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" {
    		return nil
    	}
    
    	if flagDefault {
    		// last chance: if no value is found and a flag does exist for the key,
    		// get the flag's default value even if the flag's value has not been set.
    		if flag, exists := v.pflags[lcaseKey]; exists {
    			switch flag.ValueType() {
    			case "int", "int8", "int16", "int32", "int64":
    				return cast.ToInt(flag.ValueString())
    			case "bool":
    				return cast.ToBool(flag.ValueString())
    			case "stringSlice":
    				s := strings.TrimPrefix(flag.ValueString(), "[")
    				s = strings.TrimSuffix(s, "]")
    				res, _ := readAsCSV(s)
    				return res
    			case "intSlice":
    				s := strings.TrimPrefix(flag.ValueString(), "[")
    				s = strings.TrimSuffix(s, "]")
    				res, _ := readAsCSV(s)
    				return cast.ToIntSlice(res)
    			case "stringToString":
    				return stringToStringConv(flag.ValueString())
    			default:
    				return flag.ValueString()
    			}
    		}
    		// last item, no need to check shadowing
    	}
    
    	return nil
    }
    
  • 相关阅读:
    Office加载项安装
    Office加载项
    centos部署vue项目
    centos系统下安装Nginx
    MongoDB 安装笔记
    CDN基本工作过程
    前端常见跨域解决方案(全)
    JS 扁平化(flatten) 数组
    console.log 打印的值不准确
    arr.flat(Infinity)数组扁平化
  • 原文地址:https://www.cnblogs.com/SLchuck/p/13969985.html
Copyright © 2011-2022 走看看