first version
This commit is contained in:
		
						commit
						f8942f3d43
					
				
							
								
								
									
										9
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
module git.trj.tw/golang/config-loader
 | 
			
		||||
 | 
			
		||||
go 1.14
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349
 | 
			
		||||
	github.com/BurntSushi/toml v0.3.1
 | 
			
		||||
	gopkg.in/yaml.v2 v2.3.0
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										8
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349 h1:V6ifeiJ3ExnjaUylTOz37n6z5uLwm6fjKjnztbTCaQI=
 | 
			
		||||
git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349/go.mod h1:yE+qbsUsijCTdwsaQRkPT1CXYk7ftMzXsCaaYx/0QI0=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 | 
			
		||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
							
								
								
									
										278
									
								
								loader.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								loader.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,278 @@
 | 
			
		||||
package confloader
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"git.trj.tw/golang/utils"
 | 
			
		||||
	"github.com/BurntSushi/toml"
 | 
			
		||||
	"gopkg.in/yaml.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ConfigFileType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ConfigFileTypeJSON ConfigFileType = iota
 | 
			
		||||
	ConfigFileTypeYAML
 | 
			
		||||
	ConfigFileTypeTOML
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ConfigFile struct {
 | 
			
		||||
	Type ConfigFileType
 | 
			
		||||
	Path string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LoadOptions struct {
 | 
			
		||||
	ConfigFile *ConfigFile
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Load(i interface{}, opts *LoadOptions) error {
 | 
			
		||||
	t := reflect.TypeOf(i)
 | 
			
		||||
	if t.Kind() != reflect.Ptr {
 | 
			
		||||
		return errors.New("input arg not ptr")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for t.Kind() == reflect.Ptr {
 | 
			
		||||
		t = t.Elem()
 | 
			
		||||
	}
 | 
			
		||||
	if t.Kind() != reflect.Struct {
 | 
			
		||||
		return errors.New("input not a struct")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// load default value
 | 
			
		||||
	LoadDefaultIntoStruct(i)
 | 
			
		||||
 | 
			
		||||
	// not config file opts, return
 | 
			
		||||
	if opts == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// no config file
 | 
			
		||||
	if opts.ConfigFile == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.ConfigFile.Path == "" {
 | 
			
		||||
		return errors.New("config file path empty")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// resolve file path
 | 
			
		||||
	opts.ConfigFile.Path = utils.ParsePath(opts.ConfigFile.Path)
 | 
			
		||||
	// check file exists
 | 
			
		||||
	if !utils.CheckExists(opts.ConfigFile.Path, false) {
 | 
			
		||||
		return errors.New("config file not found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filebyte, err := ioutil.ReadFile(opts.ConfigFile.Path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch opts.ConfigFile.Type {
 | 
			
		||||
	case ConfigFileTypeJSON:
 | 
			
		||||
		err := json.Unmarshal(filebyte, i)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case ConfigFileTypeTOML:
 | 
			
		||||
		err := toml.Unmarshal(filebyte, i)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case ConfigFileTypeYAML:
 | 
			
		||||
		err := yaml.Unmarshal(filebyte, i)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	default:
 | 
			
		||||
		return errors.New("file type not impl")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func LoadDefaultIntoStruct(i interface{}) {
 | 
			
		||||
	t := reflect.ValueOf(i)
 | 
			
		||||
 | 
			
		||||
	for t.Kind() == reflect.Ptr {
 | 
			
		||||
		t = t.Elem()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// not struct skip
 | 
			
		||||
	if t.Kind() != reflect.Struct {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fieldLen := t.NumField()
 | 
			
		||||
	for idx := 0; idx < fieldLen; idx++ {
 | 
			
		||||
		v := t.Field(idx)
 | 
			
		||||
		f := t.Type().Field(idx)
 | 
			
		||||
 | 
			
		||||
		val, tagExists := f.Tag.Lookup("default")
 | 
			
		||||
 | 
			
		||||
		if v.Type().Kind() == reflect.Slice {
 | 
			
		||||
			minLen := 0
 | 
			
		||||
			if defLen := f.Tag.Get("length"); defLen != "" {
 | 
			
		||||
				if convInt, err := strconv.ParseInt(defLen, 10, 64); err == nil {
 | 
			
		||||
					minLen = int(convInt)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if minLen < 1 {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			val, tagExists := f.Tag.Lookup("default")
 | 
			
		||||
 | 
			
		||||
			slice := reflect.MakeSlice(f.Type, minLen, minLen)
 | 
			
		||||
 | 
			
		||||
			item := reflect.Indirect(slice.Index(0))
 | 
			
		||||
 | 
			
		||||
			if item.Type().Kind() == reflect.Slice {
 | 
			
		||||
				//slice in slice  skip proc
 | 
			
		||||
			} else if item.Type().Kind() == reflect.Struct {
 | 
			
		||||
				LoadDefaultIntoStruct(item.Addr().Interface())
 | 
			
		||||
			} else {
 | 
			
		||||
				if tagExists {
 | 
			
		||||
					procValue(item, val)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for i := 0; i < slice.Len(); i++ {
 | 
			
		||||
				slice.Index(i).Set(item)
 | 
			
		||||
			}
 | 
			
		||||
			v.Set(slice)
 | 
			
		||||
		} else if v.Type().Kind() == reflect.Struct {
 | 
			
		||||
			LoadDefaultIntoStruct(v.Addr().Interface())
 | 
			
		||||
		} else {
 | 
			
		||||
			if tagExists {
 | 
			
		||||
				procValue(v, val)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func procValue(v reflect.Value, val string) {
 | 
			
		||||
	if !v.IsValid() || !v.CanSet() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	switch v.Type().Kind() {
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		v.SetString(val)
 | 
			
		||||
		break
 | 
			
		||||
	case reflect.Int,
 | 
			
		||||
		reflect.Int8,
 | 
			
		||||
		reflect.Int16,
 | 
			
		||||
		reflect.Int32,
 | 
			
		||||
		reflect.Int64:
 | 
			
		||||
		if convInt, err := strconv.ParseInt(val, 10, 64); err == nil {
 | 
			
		||||
			if !v.OverflowInt(convInt) {
 | 
			
		||||
				v.SetInt(convInt)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case reflect.Uint,
 | 
			
		||||
		reflect.Uint8,
 | 
			
		||||
		reflect.Uint16,
 | 
			
		||||
		reflect.Uint32,
 | 
			
		||||
		reflect.Uint64:
 | 
			
		||||
		if convUint, err := strconv.ParseUint(val, 10, 64); err == nil {
 | 
			
		||||
			if !v.OverflowUint(convUint) {
 | 
			
		||||
				v.SetUint(convUint)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case reflect.Float32:
 | 
			
		||||
	case reflect.Float64:
 | 
			
		||||
		if convFloat, err := strconv.ParseFloat(val, 64); err == nil {
 | 
			
		||||
			if !v.OverflowFloat(convFloat) {
 | 
			
		||||
				v.SetFloat(convFloat)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		if convBool, err := strconv.ParseBool(val); err == nil {
 | 
			
		||||
			v.SetBool(convBool)
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func procSlice(field *reflect.StructField) {
 | 
			
		||||
	minLen := 0
 | 
			
		||||
	if defLen := field.Tag.Get("length"); defLen != "" {
 | 
			
		||||
		if convInt, err := strconv.ParseInt(defLen, 10, 64); err == nil {
 | 
			
		||||
			minLen = int(convInt)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if minLen < 1 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	val := field.Tag.Get("default")
 | 
			
		||||
 | 
			
		||||
	slice := reflect.MakeSlice(field.Type, minLen, minLen)
 | 
			
		||||
 | 
			
		||||
	item := reflect.Indirect(slice.Index(0))
 | 
			
		||||
 | 
			
		||||
	switch item.Kind() {
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		for i := 0; i < slice.Len(); i++ {
 | 
			
		||||
			slice.Index(i).Set(reflect.ValueOf(val))
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case reflect.Int,
 | 
			
		||||
		reflect.Int8,
 | 
			
		||||
		reflect.Int16,
 | 
			
		||||
		reflect.Int32,
 | 
			
		||||
		reflect.Int64:
 | 
			
		||||
		if convInt, err := strconv.ParseInt(val, 10, 64); err == nil {
 | 
			
		||||
			if !slice.Index(0).OverflowInt(convInt) {
 | 
			
		||||
				for i := 0; i < slice.Len(); i++ {
 | 
			
		||||
					slice.Index(i).Set(reflect.ValueOf(convInt))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case reflect.Uint,
 | 
			
		||||
		reflect.Uint8,
 | 
			
		||||
		reflect.Uint16,
 | 
			
		||||
		reflect.Uint32,
 | 
			
		||||
		reflect.Uint64:
 | 
			
		||||
		if convUint, err := strconv.ParseUint(val, 10, 64); err == nil {
 | 
			
		||||
			if !slice.Index(0).OverflowUint(convUint) {
 | 
			
		||||
				for i := 0; i < slice.Len(); i++ {
 | 
			
		||||
					slice.Index(i).Set(reflect.ValueOf(convUint))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case reflect.Float32,
 | 
			
		||||
		reflect.Float64:
 | 
			
		||||
		if convFloat, err := strconv.ParseFloat(val, 64); err == nil {
 | 
			
		||||
			if !slice.Index(0).OverflowFloat(convFloat) {
 | 
			
		||||
				for i := 0; i < slice.Len(); i++ {
 | 
			
		||||
					slice.Index(i).Set(reflect.ValueOf(convFloat))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		if conv, err := strconv.ParseBool(val); err == nil {
 | 
			
		||||
			for i := 0; i < slice.Len(); i++ {
 | 
			
		||||
				slice.Index(i).Set(reflect.ValueOf(conv))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v := reflect.ValueOf(field)
 | 
			
		||||
	v.Set(slice)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										152
									
								
								loader_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								loader_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,152 @@
 | 
			
		||||
package confloader
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestLoad(t *testing.T) {
 | 
			
		||||
	type ObjKey struct {
 | 
			
		||||
		Name string `json:"name" yaml:"name" toml:"name"`
 | 
			
		||||
	}
 | 
			
		||||
	type ArrObj struct {
 | 
			
		||||
		Key string `json:"key" yaml:"key" toml:"key"`
 | 
			
		||||
	}
 | 
			
		||||
	type Config struct {
 | 
			
		||||
		StrKey   string   `json:"strKey" yaml:"strKey" toml:"strKey" default:"def value"`
 | 
			
		||||
		IntKey   int      `json:"intKey" yaml:"intKey" toml:"intKey"`
 | 
			
		||||
		BoolKey  bool     `json:"boolKey" yaml:"boolKey" toml:"boolKey"`
 | 
			
		||||
		FloatKey float64  `json:"floatKey" yaml:"floatKey" toml:"floatKey"`
 | 
			
		||||
		StrArr   []string `json:"strArr" yaml:"strArr" toml:"strArr" default:"arrval" length:"1"`
 | 
			
		||||
		StrArr2  []string `json:"strArr2" yaml:"strArr2" toml:"strArr2" default:"arrval2" length:"2"`
 | 
			
		||||
		ObjKey   ObjKey   `json:"objKey" yaml:"objKey" toml:"objKey"`
 | 
			
		||||
		ArrObj   []ArrObj `json:"arrObj" yaml:"arrObj" toml:"arrObj"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := Config{
 | 
			
		||||
		StrKey:   "string value",
 | 
			
		||||
		IntKey:   1000,
 | 
			
		||||
		BoolKey:  true,
 | 
			
		||||
		FloatKey: 1.2345,
 | 
			
		||||
		StrArr:   []string{"arr1"},
 | 
			
		||||
		StrArr2:  []string{"arrval2", "arrval2"},
 | 
			
		||||
		ObjKey: ObjKey{
 | 
			
		||||
			Name: "name",
 | 
			
		||||
		},
 | 
			
		||||
		ArrObj: []ArrObj{{Key: "val"}},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	src := Config{}
 | 
			
		||||
 | 
			
		||||
	type args struct {
 | 
			
		||||
		i    interface{}
 | 
			
		||||
		opts *LoadOptions
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		// TODO: Add test cases.
 | 
			
		||||
		{
 | 
			
		||||
			name: "test load json file with default",
 | 
			
		||||
			args: args{
 | 
			
		||||
				i: &src,
 | 
			
		||||
				opts: &LoadOptions{
 | 
			
		||||
					ConfigFile: &ConfigFile{
 | 
			
		||||
						Type: ConfigFileTypeJSON,
 | 
			
		||||
						Path: "./test/config.json",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test load yaml file with default",
 | 
			
		||||
			args: args{
 | 
			
		||||
				i: &src,
 | 
			
		||||
				opts: &LoadOptions{
 | 
			
		||||
					ConfigFile: &ConfigFile{
 | 
			
		||||
						Type: ConfigFileTypeYAML,
 | 
			
		||||
						Path: "./test/config.yaml",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test load toml file with default",
 | 
			
		||||
			args: args{
 | 
			
		||||
				i: &src,
 | 
			
		||||
				opts: &LoadOptions{
 | 
			
		||||
					ConfigFile: &ConfigFile{
 | 
			
		||||
						Type: ConfigFileTypeTOML,
 | 
			
		||||
						Path: "./test/config.toml",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if err := Load(tt.args.i, tt.args.opts); (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(src, expected) {
 | 
			
		||||
				t.Errorf("Load and expected not match")
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_loadDefaultIntoStruct(t *testing.T) {
 | 
			
		||||
	type SubStruct struct {
 | 
			
		||||
		S string `default:"sss"`
 | 
			
		||||
	}
 | 
			
		||||
	type ArrStruct struct {
 | 
			
		||||
		S string `default:"inslice"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type SS struct {
 | 
			
		||||
		S   string `default:"str"`
 | 
			
		||||
		I   int    `default:"123"`
 | 
			
		||||
		B   bool   `default:"true"`
 | 
			
		||||
		SS  SubStruct
 | 
			
		||||
		SL  []string    `default:"z" length:"1"`
 | 
			
		||||
		SL2 []ArrStruct `length:"1"`
 | 
			
		||||
		SL3 []int       `default:"100" length:"3"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s := SS{}
 | 
			
		||||
	expected := SS{
 | 
			
		||||
		S:   "str",
 | 
			
		||||
		I:   123,
 | 
			
		||||
		B:   true,
 | 
			
		||||
		SS:  SubStruct{S: "sss"},
 | 
			
		||||
		SL:  []string{"z"},
 | 
			
		||||
		SL2: []ArrStruct{{S: "inslice"}},
 | 
			
		||||
		SL3: []int{100, 100, 100},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type args struct {
 | 
			
		||||
		i interface{}
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		args args
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "test 1",
 | 
			
		||||
			args: args{
 | 
			
		||||
				i: &s,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			LoadDefaultIntoStruct(tt.args.i)
 | 
			
		||||
			reflect.DeepEqual(s, expected)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								test/config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								test/config.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
{
 | 
			
		||||
  "strKey": "string value",
 | 
			
		||||
  "intKey": 1000,
 | 
			
		||||
  "boolKey": true,
 | 
			
		||||
  "floatKey": 1.2345,
 | 
			
		||||
  "strArr": ["arr1"],
 | 
			
		||||
  "objKey": {
 | 
			
		||||
    "name": "name"
 | 
			
		||||
  },
 | 
			
		||||
  "arrObj": [{"key": "val"}]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								test/config.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								test/config.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
strKey = "string value"
 | 
			
		||||
intKey = 1000
 | 
			
		||||
boolKey = true
 | 
			
		||||
floatKey = 1.2345
 | 
			
		||||
strArr = [
 | 
			
		||||
  "arr1"
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[objKey]
 | 
			
		||||
name = "name"
 | 
			
		||||
 | 
			
		||||
[[arrObj]]
 | 
			
		||||
key = "val"
 | 
			
		||||
							
								
								
									
										11
									
								
								test/config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								test/config.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
strKey: string value
 | 
			
		||||
intKey: 1000
 | 
			
		||||
boolKey: true
 | 
			
		||||
floatKey: 1.2345
 | 
			
		||||
strArr:
 | 
			
		||||
  - arr1
 | 
			
		||||
objKey:
 | 
			
		||||
    name: name
 | 
			
		||||
arrObj:
 | 
			
		||||
  - key: val
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user