hanayo/vendor/github.com/thehowl/conf/conf.go
2019-02-23 13:29:15 +00:00

166 lines
4.6 KiB
Go

// Package conf lets you manage configuration files in the easiest way possible, without the unnecessary pain.
package conf
import (
"errors"
"fmt"
"io/ioutil"
"os"
"reflect"
"strconv"
"strings"
)
// The only custom errors this package will return.
var (
ErrNoFile = errors.New("conf: the configuration file doesn't exist")
ErrNotAStruct = errors.New("conf: the passed into/from variable is not a pointer to a struct")
)
// Load unmarshals a file into the struct passed as the argument "into".
func Load(into interface{}, filename string) error {
intoValue := reflect.ValueOf(into)
if intoValue.Kind() != reflect.Ptr || intoValue.Elem().Kind() != reflect.Struct {
return ErrNotAStruct
}
intoValue = intoValue.Elem()
f, err := ioutil.ReadFile(filename)
if os.IsNotExist(err) {
return ErrNoFile
}
if err != nil {
return err
}
return loadRaw(intoValue, f)
}
// LoadRaw allows you to load into a struct some raw data bytes.
func LoadRaw(into interface{}, data []byte) error {
intoValue := reflect.ValueOf(into)
if intoValue.Kind() != reflect.Ptr || intoValue.Elem().Kind() != reflect.Struct {
return ErrNotAStruct
}
intoValue = intoValue.Elem()
return loadRaw(intoValue, data)
}
func loadRaw(intoValue reflect.Value, data []byte) error {
fvs := Parse(data)
for _, v := range fvs {
for i := 0; i < intoValue.Type().NumField(); i++ {
field := intoValue.Type().Field(i)
if !intoValue.Field(i).CanSet() {
continue
}
if field.Name == v.Field {
switch field.Type.Kind() {
case reflect.String:
intoValue.Field(i).SetString(v.Value)
case reflect.Bool:
boolVal, err := strconv.ParseBool(v.Value)
if err != nil {
return err
}
intoValue.Field(i).SetBool(boolVal)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
intVal, err := strconv.ParseInt(v.Value, 10, 64)
if err != nil {
return err
}
intoValue.Field(i).SetInt(intVal)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
uintVal, err := strconv.ParseUint(v.Value, 10, 64)
if err != nil {
return err
}
intoValue.Field(i).SetUint(uintVal)
case reflect.Float32, reflect.Float64:
floatVal, err := strconv.ParseFloat(v.Value, 64)
if err != nil {
return err
}
intoValue.Field(i).SetFloat(floatVal)
}
}
}
}
return nil
}
// MustLoad has the same behaviour as Load, but panics if it returns an error.
func MustLoad(into interface{}, filename string) {
if err := Load(into, filename); err != nil {
panic(err)
}
}
// MustLoadRaw has the same behaviour as LoadRaw, but panics if it returns an error.
func MustLoadRaw(into interface{}, data []byte) {
if err := LoadRaw(into, data); err != nil {
panic(err)
}
}
// Export uses ExportRaw to put the data into a file, specified with its name.
func Export(from interface{}, filename string) error {
data, err := ExportRaw(from)
if err != nil {
return err
}
return ioutil.WriteFile(filename, data, 0644)
}
// ExportRaw can create a []byte that can then be loaded back by LoadRaw to get a struct's original form back.
// I suck at explaining stuff.
func ExportRaw(from interface{}) ([]byte, error) {
fromValue := reflect.ValueOf(from)
if fromValue.Kind() == reflect.Ptr {
return ExportRaw(fromValue.Elem().Interface())
}
if fromValue.Kind() != reflect.Struct {
return []byte{}, ErrNotAStruct
}
return exportRaw(fromValue), nil
}
func exportRaw(fromValue reflect.Value) []byte {
var ret []byte
for i := 0; i < fromValue.Type().NumField(); i++ {
curfield := fromValue.Field(i)
curfieldType := fromValue.Type().Field(i)
// Dirty hack to ignore that field if we don't support that type.
if !((curfield.Kind() >= reflect.Bool && curfield.Kind() <= reflect.Uint64) ||
curfield.Kind() == reflect.String || curfield.Kind() == reflect.Float32 ||
curfield.Kind() == reflect.Float64) {
continue
}
/* guten */ tag := curfieldType.Tag.Get("description")
if tag != "" {
tag = strings.Replace(tag, "\n", "\n; ", -1)
ret = append(ret, []byte("; "+tag+"\n")...)
}
ret = append(ret, []byte(Escape(curfieldType.Name)+"="+Escape(fmt.Sprint(curfield.Interface()))+"\n")...)
}
return ret
}
// MustExport panics if Export returns an error, removing error checking from your code. For the lazy.
func MustExport(from interface{}, filename string) {
if err := Export(from, filename); err != nil {
panic(err)
}
}
// MustExportRaw panics if ExportRaw returns an error, removing error checking from your code. For the lazy.
func MustExportRaw(from interface{}) []byte {
data, err := ExportRaw(from)
if err != nil {
panic(err)
}
return data
}