174 lines
3.9 KiB
Go
174 lines
3.9 KiB
Go
package utini
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"opslag.de/schobers/ut/utio"
|
|
)
|
|
|
|
var stringDelimiters = []byte{'"', '\'', '`'}
|
|
|
|
func getBoolValue(value reflect.Value, tag string) string {
|
|
states := statesTagValue(tag)
|
|
if value.Bool() {
|
|
return states[1]
|
|
}
|
|
return states[0]
|
|
}
|
|
|
|
func getIntValue(value reflect.Value, _ string) string {
|
|
i := value.Int()
|
|
return strconv.FormatInt(i, 10)
|
|
}
|
|
|
|
func getStringValue(value reflect.Value, _ string) string {
|
|
s := value.String()
|
|
for _, delimiter := range stringDelimiters {
|
|
if strings.Contains(s, string([]byte{delimiter})) {
|
|
continue
|
|
}
|
|
return fmt.Sprintf("%c%s%c", delimiter, s, delimiter)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func getUintValue(value reflect.Value, _ string) string {
|
|
i := value.Uint()
|
|
return strconv.FormatUint(i, 10)
|
|
}
|
|
|
|
type getValueFn func(reflect.Value, string) string
|
|
|
|
func mapFieldToProperty(file *File, prefix string, field reflect.StructField, value reflect.Value, get getValueFn) error {
|
|
sectionKey := camelCaseToSnakeCase(prefix)
|
|
section := file.CreateIfNotExists(sectionKey)
|
|
key := camelCaseToSnakeCase(field.Name)
|
|
section.Properties = append(section.Properties, &Property{
|
|
Key: key,
|
|
Value: get(value, iniTag(field)),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func mapStructToFile(file *File, ptr reflect.Value, prefix string) error {
|
|
if ptr.Type().Kind() != reflect.Ptr {
|
|
return errStructPtrExpected
|
|
}
|
|
value := ptr.Elem()
|
|
if value.Type().Kind() != reflect.Struct {
|
|
return errStructPtrExpected
|
|
}
|
|
|
|
valueTyp := value.Type()
|
|
n := valueTyp.NumField()
|
|
for i := 0; i < n; i++ {
|
|
field := valueTyp.Field(i)
|
|
name := appendToPrefix(prefix, field.Name)
|
|
kind := field.Type.Kind()
|
|
fieldValue := value.FieldByName(field.Name)
|
|
switch {
|
|
case kind == reflect.Struct:
|
|
if err := mapStructToFile(file, fieldValue.Addr(), name); err != nil {
|
|
return err
|
|
}
|
|
|
|
case kind == reflect.Bool:
|
|
if err := mapFieldToProperty(file, prefix, field, fieldValue, getBoolValue); err != nil {
|
|
return err
|
|
}
|
|
|
|
case kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64:
|
|
if err := mapFieldToProperty(file, prefix, field, fieldValue, getIntValue); err != nil {
|
|
return err
|
|
}
|
|
|
|
case kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64:
|
|
if err := mapFieldToProperty(file, prefix, field, fieldValue, getUintValue); err != nil {
|
|
return err
|
|
}
|
|
|
|
case kind == reflect.String:
|
|
if err := mapFieldToProperty(file, prefix, field, fieldValue, getStringValue); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func MapToFile(v interface{}) (*File, error) {
|
|
ptr := reflect.ValueOf(v)
|
|
file := &File{}
|
|
err := mapStructToFile(file, ptr, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return file, nil
|
|
}
|
|
|
|
func save(file *File) ([]string, error) {
|
|
var lines []string
|
|
|
|
var keys []string
|
|
for key := range file.Sections {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
for i, key := range keys {
|
|
if i > 0 {
|
|
lines = append(lines, "")
|
|
}
|
|
if key != "" {
|
|
lines = append(lines, fmt.Sprintf("[%s]", key))
|
|
}
|
|
section := file.Sections[key]
|
|
propertyKeys := section.Keys()
|
|
for _, key := range propertyKeys {
|
|
for _, property := range section.Properties {
|
|
if property.Key != key {
|
|
continue
|
|
}
|
|
lines = append(lines, fmt.Sprintf("%s = %s", property.Key, property.Value))
|
|
}
|
|
}
|
|
}
|
|
return lines, nil
|
|
}
|
|
|
|
func Save(w io.Writer, file *File) error {
|
|
lines, err := save(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
liner := &utio.Liner{Lines: lines}
|
|
return liner.Encode(w)
|
|
}
|
|
|
|
func SaveMap(w io.Writer, v interface{}) error {
|
|
file, err := MapToFile(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return Save(w, file)
|
|
}
|
|
|
|
func SaveFile(path string, file *File) error {
|
|
return utio.WriteFile(path, func(w io.Writer) error {
|
|
return Save(w, file)
|
|
})
|
|
}
|
|
|
|
func SaveFileMap(path string, v interface{}) error {
|
|
file, err := MapToFile(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return SaveFile(path, file)
|
|
}
|