| package toml |
| |
| import ( |
| "fmt" |
| "reflect" |
| "time" |
| ) |
| |
| var kindToType = [reflect.String + 1]reflect.Type{ |
| reflect.Bool: reflect.TypeOf(true), |
| reflect.String: reflect.TypeOf(""), |
| reflect.Float32: reflect.TypeOf(float64(1)), |
| reflect.Float64: reflect.TypeOf(float64(1)), |
| reflect.Int: reflect.TypeOf(int64(1)), |
| reflect.Int8: reflect.TypeOf(int64(1)), |
| reflect.Int16: reflect.TypeOf(int64(1)), |
| reflect.Int32: reflect.TypeOf(int64(1)), |
| reflect.Int64: reflect.TypeOf(int64(1)), |
| reflect.Uint: reflect.TypeOf(uint64(1)), |
| reflect.Uint8: reflect.TypeOf(uint64(1)), |
| reflect.Uint16: reflect.TypeOf(uint64(1)), |
| reflect.Uint32: reflect.TypeOf(uint64(1)), |
| reflect.Uint64: reflect.TypeOf(uint64(1)), |
| } |
| |
| // typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found. |
| // supported values: |
| // string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32 |
| func typeFor(k reflect.Kind) reflect.Type { |
| if k > 0 && int(k) < len(kindToType) { |
| return kindToType[k] |
| } |
| return nil |
| } |
| |
| func simpleValueCoercion(object interface{}) (interface{}, error) { |
| switch original := object.(type) { |
| case string, bool, int64, uint64, float64, time.Time: |
| return original, nil |
| case int: |
| return int64(original), nil |
| case int8: |
| return int64(original), nil |
| case int16: |
| return int64(original), nil |
| case int32: |
| return int64(original), nil |
| case uint: |
| return uint64(original), nil |
| case uint8: |
| return uint64(original), nil |
| case uint16: |
| return uint64(original), nil |
| case uint32: |
| return uint64(original), nil |
| case float32: |
| return float64(original), nil |
| case fmt.Stringer: |
| return original.String(), nil |
| default: |
| return nil, fmt.Errorf("cannot convert type %T to Tree", object) |
| } |
| } |
| |
| func sliceToTree(object interface{}) (interface{}, error) { |
| // arrays are a bit tricky, since they can represent either a |
| // collection of simple values, which is represented by one |
| // *tomlValue, or an array of tables, which is represented by an |
| // array of *Tree. |
| |
| // holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice |
| value := reflect.ValueOf(object) |
| insideType := value.Type().Elem() |
| length := value.Len() |
| if length > 0 { |
| insideType = reflect.ValueOf(value.Index(0).Interface()).Type() |
| } |
| if insideType.Kind() == reflect.Map { |
| // this is considered as an array of tables |
| tablesArray := make([]*Tree, 0, length) |
| for i := 0; i < length; i++ { |
| table := value.Index(i) |
| tree, err := toTree(table.Interface()) |
| if err != nil { |
| return nil, err |
| } |
| tablesArray = append(tablesArray, tree.(*Tree)) |
| } |
| return tablesArray, nil |
| } |
| |
| sliceType := typeFor(insideType.Kind()) |
| if sliceType == nil { |
| sliceType = insideType |
| } |
| |
| arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length) |
| |
| for i := 0; i < length; i++ { |
| val := value.Index(i).Interface() |
| simpleValue, err := simpleValueCoercion(val) |
| if err != nil { |
| return nil, err |
| } |
| arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue)) |
| } |
| return &tomlValue{arrayValue.Interface(), Position{}}, nil |
| } |
| |
| func toTree(object interface{}) (interface{}, error) { |
| value := reflect.ValueOf(object) |
| |
| if value.Kind() == reflect.Map { |
| values := map[string]interface{}{} |
| keys := value.MapKeys() |
| for _, key := range keys { |
| if key.Kind() != reflect.String { |
| if _, ok := key.Interface().(string); !ok { |
| return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind()) |
| } |
| } |
| |
| v := value.MapIndex(key) |
| newValue, err := toTree(v.Interface()) |
| if err != nil { |
| return nil, err |
| } |
| values[key.String()] = newValue |
| } |
| return &Tree{values, Position{}}, nil |
| } |
| |
| if value.Kind() == reflect.Array || value.Kind() == reflect.Slice { |
| return sliceToTree(object) |
| } |
| |
| simpleValue, err := simpleValueCoercion(object) |
| if err != nil { |
| return nil, err |
| } |
| return &tomlValue{simpleValue, Position{}}, nil |
| } |