|
@@ -0,0 +1,414 @@
|
|
|
+package ini
|
|
|
+
|
|
|
+import (
|
|
|
+ "encoding"
|
|
|
+ "fmt"
|
|
|
+ "reflect"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+// Types to look for ("Marshaler"/"Unmarshaler" interface)
|
|
|
+var (
|
|
|
+ marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
|
|
|
+ unmarshalerType = reflect.TypeOf(new(Unmarshaler)).Elem()
|
|
|
+ textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
|
|
|
+ textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
|
|
|
+)
|
|
|
+
|
|
|
+// INIDecoderError is returned on decoding error
|
|
|
+type INIDecoderError struct {
|
|
|
+ msg string
|
|
|
+ err error
|
|
|
+ f *field
|
|
|
+}
|
|
|
+
|
|
|
+// Error returns an formatted string of the INIDecoderError
|
|
|
+func (i *INIDecoderError) Error() string {
|
|
|
+ return fmt.Sprintf(
|
|
|
+ "INI Decoder Error: %s. %s. Field %s, namespace: %s",
|
|
|
+ i.msg, i.err.Error(), i.f.t.Name(), i.f.ns,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+// INIEncoderError is returned on encoding error
|
|
|
+type INIEncoderError struct {
|
|
|
+ msg string
|
|
|
+ err error
|
|
|
+ f *field
|
|
|
+}
|
|
|
+
|
|
|
+// Error returns an formatted string of the INIEncoderError
|
|
|
+func (i *INIEncoderError) Error() string {
|
|
|
+ return fmt.Sprintf(
|
|
|
+ "INI Encoder Error: %s. Field: %s, namespace: %s",
|
|
|
+ i.msg, i.err.Error(), i.f.t.Name(), i.f.ns,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+// decoder and encoder methods
|
|
|
+type decodeFn func([]byte) error
|
|
|
+type encodeFn func() ([]byte, error)
|
|
|
+
|
|
|
+type field struct {
|
|
|
+ ns string
|
|
|
+
|
|
|
+ t reflect.Type
|
|
|
+ v reflect.Value
|
|
|
+
|
|
|
+ p interface{}
|
|
|
+
|
|
|
+ // decoder or encoder method (will be changed when a cache for "elements" is implemented)
|
|
|
+ fn interface{} // Use setFn to set this!
|
|
|
+}
|
|
|
+
|
|
|
+func newField(v reflect.Value, p interface{}, i int) *field {
|
|
|
+ f := &field{
|
|
|
+ t: v.Type(),
|
|
|
+ v: v,
|
|
|
+ p: nil,
|
|
|
+ }
|
|
|
+
|
|
|
+ if p != nil {
|
|
|
+ switch p.(type) {
|
|
|
+ case reflect.Type:
|
|
|
+ f.p = p.(reflect.Type).Field(i)
|
|
|
+ case reflect.Value:
|
|
|
+ f.p = p.(reflect.Value).Type().Field(i)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return f
|
|
|
+}
|
|
|
+
|
|
|
+// implements checks if the field implements the type t, or a pointer method to type t
|
|
|
+func (f *field) implements(t reflect.Type) bool {
|
|
|
+ return (f.t.Implements(t) || (f.t.Kind() != reflect.Ptr && reflect.PtrTo(f.t).Implements(t)))
|
|
|
+}
|
|
|
+
|
|
|
+// setNS determines the namespace for the field
|
|
|
+func (f *field) setNS(ns string) string {
|
|
|
+ if f.p == nil {
|
|
|
+ f.ns = ns + "." + f.t.Name()
|
|
|
+ return f.ns
|
|
|
+ }
|
|
|
+
|
|
|
+ if tag := f.p.(reflect.StructField).Tag.Get("ini"); len(tag) != 0 {
|
|
|
+ f.ns = ns + "." + tag
|
|
|
+ return f.ns
|
|
|
+ }
|
|
|
+
|
|
|
+ f.ns = ns + "." + f.p.(reflect.StructField).Name
|
|
|
+ return f.ns
|
|
|
+}
|
|
|
+
|
|
|
+// setFN sets the function
|
|
|
+func (f *field) setFn(fn interface{}) {
|
|
|
+ f.fn = fn
|
|
|
+}
|
|
|
+
|
|
|
+// decode returns the decoder-method (do not call f.fn, for future changes!)
|
|
|
+func (f *field) decode(b []byte) error {
|
|
|
+ return f.fn.(decodeFn)(b)
|
|
|
+}
|
|
|
+
|
|
|
+// encode returns the decoder-method (do not call f.fn directly, for future changes!)
|
|
|
+func (f *field) encode() ([]byte, error) {
|
|
|
+ return f.fn.(encodeFn)()
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ DECODER METHODS
|
|
|
+**/
|
|
|
+
|
|
|
+func (f *field) valid() error {
|
|
|
+
|
|
|
+ if f.v.CanAddr() {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return fmt.Errorf("Field '%s' not addressable", f.ns)
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) uintDecoder(b []byte) error {
|
|
|
+
|
|
|
+ if err := f.valid(); err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Unsigned integer decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ i, err := strconv.ParseUint(string(b), 10, 64)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Unsigned integer decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ f.v.SetUint(i)
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) intDecoder(b []byte) error {
|
|
|
+
|
|
|
+ if err := f.valid(); err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Integer decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ i, err := strconv.ParseInt(string(b), 10, 64)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Integer decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ f.v.SetInt(i)
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) float32Decoder(b []byte) error {
|
|
|
+
|
|
|
+ if err := f.valid(); err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Float (32) decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ i, err := strconv.ParseFloat(string(b), 32)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Float (32) decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ f.v.SetFloat(i)
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) float64Decoder(b []byte) error {
|
|
|
+
|
|
|
+ if err := f.valid(); err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Float (64) decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ i, err := strconv.ParseFloat(string(b), 64)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Float (64) decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ f.v.SetFloat(i)
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) strDecoder(b []byte) error {
|
|
|
+
|
|
|
+ if err := f.valid(); err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "String decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ f.v.SetString(string(b))
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) boolDecoder(b []byte) error {
|
|
|
+
|
|
|
+ if err := f.valid(); err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Boolean decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ f.v.SetBool(true)
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) unmarshalerDecoder(b []byte) error {
|
|
|
+
|
|
|
+ if err := f.valid(); err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "Unmarshaler decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ v := f.v
|
|
|
+
|
|
|
+ if v.Kind() != reflect.Ptr && v.Type().Name() != "" {
|
|
|
+ v = v.Addr()
|
|
|
+ }
|
|
|
+
|
|
|
+ if v.IsNil() {
|
|
|
+ v.Set(reflect.New(v.Type().Elem()))
|
|
|
+ }
|
|
|
+
|
|
|
+ if i, ok := v.Interface().(Unmarshaler); ok {
|
|
|
+ if err := i.UnmarshalINI(b); err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "UnmarshalINI decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ if i, ok := v.Interface().(encoding.TextUnmarshaler); ok {
|
|
|
+ if err := i.UnmarshalText(b); err != nil {
|
|
|
+ return &INIDecoderError{
|
|
|
+ msg: "UnmarshalText decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return fmt.Errorf("Cannot load Unmarshaler")
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ ENCODER METHODS
|
|
|
+**/
|
|
|
+
|
|
|
+func (f *field) nskey() string {
|
|
|
+ return f.ns[strings.LastIndexByte(f.ns, '.')+1:]
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) uintEncoder() ([]byte, error) {
|
|
|
+ i := strconv.FormatUint(f.v.Uint(), 10)
|
|
|
+ return []byte(f.nskey() + " = " + i), nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) intEncoder() ([]byte, error) {
|
|
|
+ i := strconv.FormatInt(f.v.Int(), 10)
|
|
|
+ return []byte(f.nskey() + " = " + i), nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) float32Encoder() ([]byte, error) {
|
|
|
+ i := strconv.FormatFloat(f.v.Float(), 'G', -1, 32)
|
|
|
+ return []byte(f.nskey() + " = " + i), nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) float64Encoder() ([]byte, error) {
|
|
|
+ i := strconv.FormatFloat(f.v.Float(), 'G', -1, 64)
|
|
|
+ return []byte(f.nskey() + " = " + i), nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) strEncoder() ([]byte, error) {
|
|
|
+ str := strings.Replace(f.v.String(), "\n", "\\\n", -1)
|
|
|
+ return []byte(f.nskey() + " = " + str), nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) boolEncoder() ([]byte, error) {
|
|
|
+ if f.v.Bool() {
|
|
|
+ return []byte(f.nskey()), nil
|
|
|
+ }
|
|
|
+ return nil, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) structEncoder() ([]byte, error) {
|
|
|
+
|
|
|
+ split := strings.SplitN(f.ns, ".", 3)
|
|
|
+
|
|
|
+ if len(split) == 2 {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(split) != 3 {
|
|
|
+ return nil, &INIEncoderError{
|
|
|
+ msg: "Struct encoder",
|
|
|
+ err: fmt.Errorf("Invalid namespace"),
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return []byte("\n[" + split[2] + "]"), nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *field) marshalerDecoder() ([]byte, error) {
|
|
|
+
|
|
|
+ if err := f.valid(); err != nil {
|
|
|
+ return nil, &INIEncoderError{
|
|
|
+ msg: "Unmarshaler decoder",
|
|
|
+ err: err,
|
|
|
+ f: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ v := f.v
|
|
|
+
|
|
|
+ if v.Kind() != reflect.Ptr && v.Type().Name() != "" {
|
|
|
+ v = v.Addr()
|
|
|
+ }
|
|
|
+
|
|
|
+ if v.IsNil() {
|
|
|
+ v.Set(reflect.New(v.Type().Elem()))
|
|
|
+ }
|
|
|
+
|
|
|
+ if i, ok := v.Interface().(Marshaler); ok {
|
|
|
+ b, err := i.MarshalINI()
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if b == nil {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return append([]byte(f.nskey()+" = "), b...), nil
|
|
|
+ }
|
|
|
+
|
|
|
+ if i, ok := v.Interface().(encoding.TextMarshaler); ok {
|
|
|
+ b, err := i.MarshalText()
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if b == nil {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return append([]byte(f.nskey()+" = "), b...), nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil, fmt.Errorf("Cannot load Marshaler")
|
|
|
+}
|