1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
package toml
// Tools to convert a TomlTree to different representations
import (
"fmt"
"strconv"
"strings"
"time"
)
// encodes a string to a TOML-compliant string value
func encodeTomlString(value string) string {
result := ""
for _, rr := range value {
intRr := uint16(rr)
switch rr {
case '\b':
result += "\\b"
case '\t':
result += "\\t"
case '\n':
result += "\\n"
case '\f':
result += "\\f"
case '\r':
result += "\\r"
case '"':
result += "\\\""
case '\\':
result += "\\\\"
default:
if intRr < 0x001F {
result += fmt.Sprintf("\\u%0.4X", intRr)
} else {
result += string(rr)
}
}
}
return result
}
// Value print support function for ToString()
// Outputs the TOML compliant string representation of a value
func toTomlValue(item interface{}, indent int) string {
tab := strings.Repeat(" ", indent)
switch value := item.(type) {
case int:
return tab + strconv.FormatInt(int64(value), 10)
case int8:
return tab + strconv.FormatInt(int64(value), 10)
case int16:
return tab + strconv.FormatInt(int64(value), 10)
case int32:
return tab + strconv.FormatInt(int64(value), 10)
case int64:
return tab + strconv.FormatInt(value, 10)
case uint:
return tab + strconv.FormatUint(uint64(value), 10)
case uint8:
return tab + strconv.FormatUint(uint64(value), 10)
case uint16:
return tab + strconv.FormatUint(uint64(value), 10)
case uint32:
return tab + strconv.FormatUint(uint64(value), 10)
case uint64:
return tab + strconv.FormatUint(value, 10)
case float32:
return tab + strconv.FormatFloat(float64(value), 'f', -1, 32)
case float64:
return tab + strconv.FormatFloat(value, 'f', -1, 64)
case string:
return tab + "\"" + encodeTomlString(value) + "\""
case bool:
if value {
return "true"
}
return "false"
case time.Time:
return tab + value.Format(time.RFC3339)
case []interface{}:
values := []string{}
for _, item := range value {
values = append(values, toTomlValue(item, 0))
}
return "[" + strings.Join(values, ",") + "]"
case nil:
return ""
default:
panic(fmt.Sprintf("unsupported value type %T: %v", value, value))
}
}
// Recursive support function for ToString()
// Outputs a tree, using the provided keyspace to prefix table names
func (t *TomlTree) toToml(indent, keyspace string) string {
resultChunks := []string{}
for k, v := range t.values {
// figure out the keyspace
combinedKey := k
if keyspace != "" {
combinedKey = keyspace + "." + combinedKey
}
resultChunk := ""
// output based on type
switch node := v.(type) {
case []*TomlTree:
for _, item := range node {
if len(item.Keys()) > 0 {
resultChunk += fmt.Sprintf("\n%s[[%s]]\n", indent, combinedKey)
}
resultChunk += item.toToml(indent+" ", combinedKey)
}
resultChunks = append(resultChunks, resultChunk)
case *TomlTree:
if len(node.Keys()) > 0 {
resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
}
resultChunk += node.toToml(indent+" ", combinedKey)
resultChunks = append(resultChunks, resultChunk)
case map[string]interface{}:
sub := TreeFromMap(node)
if len(sub.Keys()) > 0 {
resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
}
resultChunk += sub.toToml(indent+" ", combinedKey)
resultChunks = append(resultChunks, resultChunk)
case map[string]string:
sub := TreeFromMap(convertMapStringString(node))
if len(sub.Keys()) > 0 {
resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
}
resultChunk += sub.toToml(indent+" ", combinedKey)
resultChunks = append(resultChunks, resultChunk)
case map[interface{}]interface{}:
sub := TreeFromMap(convertMapInterfaceInterface(node))
if len(sub.Keys()) > 0 {
resultChunk += fmt.Sprintf("\n%s[%s]\n", indent, combinedKey)
}
resultChunk += sub.toToml(indent+" ", combinedKey)
resultChunks = append(resultChunks, resultChunk)
case *tomlValue:
resultChunk = fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(node.value, 0))
resultChunks = append([]string{resultChunk}, resultChunks...)
default:
resultChunk = fmt.Sprintf("%s%s = %s\n", indent, k, toTomlValue(v, 0))
resultChunks = append([]string{resultChunk}, resultChunks...)
}
}
return strings.Join(resultChunks, "")
}
func convertMapStringString(in map[string]string) map[string]interface{} {
result := make(map[string]interface{}, len(in))
for k, v := range in {
result[k] = v
}
return result
}
func convertMapInterfaceInterface(in map[interface{}]interface{}) map[string]interface{} {
result := make(map[string]interface{}, len(in))
for k, v := range in {
result[k.(string)] = v
}
return result
}
// ToString is an alias for String
func (t *TomlTree) ToString() string {
return t.String()
}
// String generates a human-readable representation of the current tree.
// Output spans multiple lines, and is suitable for ingest by a TOML parser
func (t *TomlTree) String() string {
return t.toToml("", "")
}
// ToMap recursively generates a representation of the current tree using map[string]interface{}.
func (t *TomlTree) ToMap() map[string]interface{} {
result := map[string]interface{}{}
for k, v := range t.values {
switch node := v.(type) {
case []*TomlTree:
var array []interface{}
for _, item := range node {
array = append(array, item.ToMap())
}
result[k] = array
case *TomlTree:
result[k] = node.ToMap()
case map[string]interface{}:
sub := TreeFromMap(node)
result[k] = sub.ToMap()
case *tomlValue:
result[k] = node.value
}
}
return result
}
|