1package toml 2 3import ( 4 "fmt" 5 "reflect" 6 "time" 7) 8 9var kindToType = [reflect.String + 1]reflect.Type{ 10 reflect.Bool: reflect.TypeOf(true), 11 reflect.String: reflect.TypeOf(""), 12 reflect.Float32: reflect.TypeOf(float64(1)), 13 reflect.Float64: reflect.TypeOf(float64(1)), 14 reflect.Int: reflect.TypeOf(int64(1)), 15 reflect.Int8: reflect.TypeOf(int64(1)), 16 reflect.Int16: reflect.TypeOf(int64(1)), 17 reflect.Int32: reflect.TypeOf(int64(1)), 18 reflect.Int64: reflect.TypeOf(int64(1)), 19 reflect.Uint: reflect.TypeOf(uint64(1)), 20 reflect.Uint8: reflect.TypeOf(uint64(1)), 21 reflect.Uint16: reflect.TypeOf(uint64(1)), 22 reflect.Uint32: reflect.TypeOf(uint64(1)), 23 reflect.Uint64: reflect.TypeOf(uint64(1)), 24} 25 26// typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found. 27// supported values: 28// string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32 29func typeFor(k reflect.Kind) reflect.Type { 30 if k > 0 && int(k) < len(kindToType) { 31 return kindToType[k] 32 } 33 return nil 34} 35 36func simpleValueCoercion(object interface{}) (interface{}, error) { 37 switch original := object.(type) { 38 case string, bool, int64, uint64, float64, time.Time: 39 return original, nil 40 case int: 41 return int64(original), nil 42 case int8: 43 return int64(original), nil 44 case int16: 45 return int64(original), nil 46 case int32: 47 return int64(original), nil 48 case uint: 49 return uint64(original), nil 50 case uint8: 51 return uint64(original), nil 52 case uint16: 53 return uint64(original), nil 54 case uint32: 55 return uint64(original), nil 56 case float32: 57 return float64(original), nil 58 case fmt.Stringer: 59 return original.String(), nil 60 default: 61 return nil, fmt.Errorf("cannot convert type %T to Tree", object) 62 } 63} 64 65func sliceToTree(object interface{}) (interface{}, error) { 66 // arrays are a bit tricky, since they can represent either a 67 // collection of simple values, which is represented by one 68 // *tomlValue, or an array of tables, which is represented by an 69 // array of *Tree. 70 71 // holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice 72 value := reflect.ValueOf(object) 73 insideType := value.Type().Elem() 74 length := value.Len() 75 if length > 0 { 76 insideType = reflect.ValueOf(value.Index(0).Interface()).Type() 77 } 78 if insideType.Kind() == reflect.Map { 79 // this is considered as an array of tables 80 tablesArray := make([]*Tree, 0, length) 81 for i := 0; i < length; i++ { 82 table := value.Index(i) 83 tree, err := toTree(table.Interface()) 84 if err != nil { 85 return nil, err 86 } 87 tablesArray = append(tablesArray, tree.(*Tree)) 88 } 89 return tablesArray, nil 90 } 91 92 sliceType := typeFor(insideType.Kind()) 93 if sliceType == nil { 94 sliceType = insideType 95 } 96 97 arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length) 98 99 for i := 0; i < length; i++ { 100 val := value.Index(i).Interface() 101 simpleValue, err := simpleValueCoercion(val) 102 if err != nil { 103 return nil, err 104 } 105 arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue)) 106 } 107 return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil 108} 109 110func toTree(object interface{}) (interface{}, error) { 111 value := reflect.ValueOf(object) 112 113 if value.Kind() == reflect.Map { 114 values := map[string]interface{}{} 115 keys := value.MapKeys() 116 for _, key := range keys { 117 if key.Kind() != reflect.String { 118 if _, ok := key.Interface().(string); !ok { 119 return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind()) 120 } 121 } 122 123 v := value.MapIndex(key) 124 newValue, err := toTree(v.Interface()) 125 if err != nil { 126 return nil, err 127 } 128 values[key.String()] = newValue 129 } 130 return &Tree{values: values, position: Position{}}, nil 131 } 132 133 if value.Kind() == reflect.Array || value.Kind() == reflect.Slice { 134 return sliceToTree(object) 135 } 136 137 simpleValue, err := simpleValueCoercion(object) 138 if err != nil { 139 return nil, err 140 } 141 return &tomlValue{value: simpleValue, position: Position{}}, nil 142} 143