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") }