123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- 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:
- }
- }
|