Tips: 建议先看下官方文档,如何使用env再看源码

项目信息

  • 地址:https://github.com/caarlos0/env
  • 描述:一个可以parse环境变量到go struct的简单的三方库
  • 版本:tag: v6.8.0

源码阅读

入口函数(Parse)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Parse parses a struct containing `env` tags and loads its values from
// environment variables.
func Parse(v interface{}, opts ...Options) error {
	return ParseWithFuncs(v, map[reflect.Type]ParserFunc{}, opts...)
}
// ParseWithFuncs is the same as `Parse` except it also allows the user to pass
// in custom parsers.
func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc, opts ...Options) error {
	opts = configure(opts)

	ptrRef := reflect.ValueOf(v)
	if ptrRef.Kind() != reflect.Ptr {
		return ErrNotAStructPtr
	}
	ref := ptrRef.Elem()
	if ref.Kind() != reflect.Struct {
		return ErrNotAStructPtr
	}
	parsers := defaultTypeParsers
	for k, v := range funcMap {
		parsers[k] = v
	}

	return doParse(ref, parsers, opts)
}

入口函数Parse相当于对ParseWithFuncs做了一个封装,送入了一个空的funcMap参数,一般这种操作都是送一个默认的配置给下层的函数,估计是在下层的函数中做的处理。 在ParseWithFuncs,先判定了用户传入的需要被parse的go struct是否是一个struct指针,然后会将用funcMapdefaultTypeParsers去合并出一个新的parsers,接下来就是使用处理后的go struct piont, parserts, opts处理数据。

默认的数据转换函数映射(defaultBuiltInParsers, defaultTypeParsers)

defaultBuiltInParsersdefaultTypeParsers 都是 map[reflect.Kind]ParserFunc 类型

defaultBuiltInParsers

defaultBuiltInParsers 主要是对基本数据类型做数据转换,如:bool, string, int, int16, int32, int64, int8, uint, uint16, uint32, uint64, uint8, float64, float32

源码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    defaultBuiltInParsers = map[reflect.Kind]ParserFunc{
		reflect.Bool: func(v string) (interface{}, error) {
			return strconv.ParseBool(v)
		},
		reflect.String: func(v string) (interface{}, error) {
			return v, nil
		},
		reflect.Int: func(v string) (interface{}, error) {
			i, err := strconv.ParseInt(v, 10, 32)
			return int(i), err
		},
		reflect.Int16: func(v string) (interface{}, error) {
			i, err := strconv.ParseInt(v, 10, 16)
			return int16(i), err
		},
		reflect.Int32: func(v string) (interface{}, error) {
			i, err := strconv.ParseInt(v, 10, 32)
			return int32(i), err
		},
		reflect.Int64: func(v string) (interface{}, error) {
			return strconv.ParseInt(v, 10, 64)
		},
		reflect.Int8: func(v string) (interface{}, error) {
			i, err := strconv.ParseInt(v, 10, 8)
			return int8(i), err
		},
		reflect.Uint: func(v string) (interface{}, error) {
			i, err := strconv.ParseUint(v, 10, 32)
			return uint(i), err
		},
		reflect.Uint16: func(v string) (interface{}, error) {
			i, err := strconv.ParseUint(v, 10, 16)
			return uint16(i), err
		},
		reflect.Uint32: func(v string) (interface{}, error) {
			i, err := strconv.ParseUint(v, 10, 32)
			return uint32(i), err
		},
		reflect.Uint64: func(v string) (interface{}, error) {
			i, err := strconv.ParseUint(v, 10, 64)
			return i, err
		},
		reflect.Uint8: func(v string) (interface{}, error) {
			i, err := strconv.ParseUint(v, 10, 8)
			return uint8(i), err
		},
		reflect.Float64: func(v string) (interface{}, error) {
			return strconv.ParseFloat(v, 64)
		},
		reflect.Float32: func(v string) (interface{}, error) {
			f, err := strconv.ParseFloat(v, 32)
			return float32(f), err
		},
	}

defaultTypeParsers

defaultTypeParsers 是对一些golang标准库的一些结构体进行预定义的parser,如:url.URL, time.Nanosecond。 在描述ParseWithFuncs函数时,有说到,ParseWithFuncs 会去合并defaultTypeParsers与用户传入的funcMap参数,所以,如果对某些特殊的类型有自己的parse需求,可以考虑使用ParseWithFuncs函数替代Parse函数

源码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
	defaultTypeParsers = map[reflect.Type]ParserFunc{
		reflect.TypeOf(url.URL{}): func(v string) (interface{}, error) {
			u, err := url.Parse(v)
			if err != nil {
				return nil, fmt.Errorf("unable to parse URL: %v", err)
			}
			return *u, nil
		},
		reflect.TypeOf(time.Nanosecond): func(v string) (interface{}, error) {
			s, err := time.ParseDuration(v)
			if err != nil {
				return nil, fmt.Errorf("unable to parse duration: %v", err)
			}
			return s, err
		},
	}

环境变量解析逻辑(doParse)

源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Options) error {
    // 反射获取到用户传入的结构体类型
	refType := ref.Type()

    // 遍历结构体中的所有字段
	for i := 0; i < refType.NumField(); i++ {
        // 获取到某个字段的反射信息
		refField := ref.Field(i)
        // 如果对应字段是不可以被设置的,跳过本次循环
		if !refField.CanSet() {
			continue
		}
        // 如果字段是一个非空的结构体指针,会再一次调用`ParseWithFuncs`函数进行递归处理数据,并且根据当前字段设置的env前缀构建出一个新的`option`列表,本字段的env前置会放在原前缀的最后,例如,原前缀是ENV_,当前字段的前缀是Field_,那么构造出来的新前缀就是ENV_Field_
        // 如果不是一个结构体指针,那么一样会调用`ParseWithFuncs`函数,但会报错
		if reflect.Ptr == refField.Kind() && !refField.IsNil() {
			if refField.Elem().Kind() == reflect.Struct {
				if err := ParseWithFuncs(refField.Interface(), funcMap, optsWithPrefix(refType.Field(i), opts)...); err != nil {
					return err
				}
				continue
			}
			if err := ParseWithFuncs(refField.Interface(), funcMap, opts...); err != nil {
				return err
			}
			continue
		}
        // 如果字段是一个结构体,那么会去取这个字段对应的字段,递归调用`Parse`函数去解析本指针
		if reflect.Struct == refField.Kind() && refField.CanAddr() && refField.Type().Name() == "" {
			if err := Parse(refField.Addr().Interface(), opts...); err != nil {
				return err
			}
			continue
		}
		refTypeField := refType.Field(i)
        // 获取field对应的数据
		value, err := get(refTypeField, opts)
		if err != nil {
			return err
		}
		if value == "" {
			if reflect.Struct == refField.Kind() {
				if err := doParse(refField, funcMap, optsWithPrefix(refType.Field(i), opts)); err != nil {
					return err
				}
			}
			continue
		}
        // 设置field对应的数据
		if err := set(refField, refTypeField, value, funcMap); err != nil {
			return err
		}
	}
	return nil
}

使用反射来设置结构体中对应字段的值(set)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// field => field的反射值
// sf => 结构体中field的反射信息
// value => 根据field的tag设置,取出来的field的值(string)
// funcMap => 数据转换的parser
func set(field reflect.Value, sf reflect.StructField, value string, funcMap map[reflect.Type]ParserFunc) error {
    // 判断字段是否是一个TextUnmarshaler,如果是,就使用UnmarshalText解析数据
	if tm := asTextUnmarshaler(field); tm != nil {
		if err := tm.UnmarshalText([]byte(value)); err != nil {
			return newParseError(sf, err)
		}
		return nil
	}

	typee := sf.Type
	fieldee := field
	if typee.Kind() == reflect.Ptr {
		typee = typee.Elem()
		fieldee = field.Elem()
	}

    // 取出对应的parser,构建数据,设置数据
	parserFunc, ok := funcMap[typee]
	if ok {
		val, err := parserFunc(value)
		if err != nil {
			return newParseError(sf, err)
		}

		fieldee.Set(reflect.ValueOf(val))
		return nil
	}

    // 如果不是特殊类型,是golang内置基本数据结构,一样的取出对应的parser,构建数据,设置数据
	parserFunc, ok = defaultBuiltInParsers[typee.Kind()]
	if ok {
		val, err := parserFunc(value)
		if err != nil {
			return newParseError(sf, err)
		}

		fieldee.Set(reflect.ValueOf(val).Convert(typee))
		return nil
	}

	if field.Kind() == reflect.Slice {
		return handleSlice(field, value, sf, funcMap)
	}

	return newNoParserError(sf)
}