package ini // Decode INI-fomatted data into an interface (typically a struct) // Known types: // - Any kind of ints // - Any kind of unsigned ints // - Structs + pointers // - Boolean // - Strings // - Types with the method `UnmarshalText` // // Unknown types (also known types!) can impement the method UnmarshalINI // to be to override standard unmarshaling method, or make marshalable if it's not. // // e.g: see types.go and the Time example. Time does implement MarshalText, but it doesnt // store the time-value in a very readable/editable manner, so the ini.Time is just a // wrapper-struct with the Marshaler-interface to read/write the time-value differently. import ( "reflect" ) // Unmarshaler is an interface to implement for unknown types type Unmarshaler interface { UnmarshalINI([]byte) error } type decoder struct { scan *scanner ns map[string]*field nss string } // Unmarshal decodes byte-array/slice containing INI into interface v func Unmarshal(d []byte, v interface{}) error { dec := &decoder{ scan: newScanner(d), ns: make(map[string]*field), } // Ensure data is valid before reflecting if err := dec.scan.valid(); err != nil { return err } // Map struct -> INI return dec.unmarshal(v) } // umarshal does the actual mapping by first collecting struct data // then scanning ini-file and mapping data to the struct. func (d *decoder) unmarshal(v interface{}) error { rv := reflect.ValueOf(v) d.imap("", rv, nil, 0) ns := d.nss for elem, err := d.scan.next(); err == EOL || err == EOF; elem, err = d.scan.next() { switch elem.t { case section: ns = d.nss + "." + string(elem.key) case variable: key := ns + "." + string(elem.key) if f, ok := d.ns[key]; ok { if err := f.decode(elem.val); err != nil { return err } } } if err == EOF { break } } return nil } // nsmap is a mapper function, storing each entry on a given namespace func (d *decoder) nsmap(ns string, f *field, fn decodeFn) { f.setFn(fn) d.ns[f.setNS(ns)] = f } // imap maps interface data into namespaces func (d *decoder) imap(ns string, v reflect.Value, p interface{}, i int) { f := newField(v, p, i) if f.implements(unmarshalerType) || f.implements(textUnmarshalerType) { d.nsmap(ns, f, f.unmarshalerDecoder) return } switch f.v.Kind() { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: d.nsmap(ns, f, f.uintDecoder) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: d.nsmap(ns, f, f.intDecoder) case reflect.String: d.nsmap(ns, f, f.strDecoder) case reflect.Bool: d.nsmap(ns, f, f.boolDecoder) case reflect.Float32: d.nsmap(ns, f, f.float32Decoder) case reflect.Float64: d.nsmap(ns, f, f.float64Decoder) case reflect.Struct: if len(d.nss) == 0 { d.nss = f.setNS(ns) } for i := 0; i < f.v.NumField(); i++ { d.imap(f.setNS(ns), f.v.Field(i), f.t, i) } case reflect.Ptr: if f.v.IsNil() { return } d.imap(ns, f.v.Elem(), nil, i) default: } }