ut/utini/save.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)
}