|  | 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.Errorf("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, "") | 
|  | } | 
|  |  | 
|  | // Same as ToToml(), but does not panic and returns an error | 
|  | func (t *TomlTree) toTomlSafe(indent, keyspace string) (result string, err error) { | 
|  | defer func() { | 
|  | if r := recover(); r != nil { | 
|  | result = "" | 
|  | switch x := r.(type) { | 
|  | case error: | 
|  | err = x | 
|  | default: | 
|  | err = fmt.Errorf("unknown panic: %s", r) | 
|  | } | 
|  | } | 
|  | }() | 
|  | result = t.toToml(indent, keyspace) | 
|  | return | 
|  | } | 
|  |  | 
|  | 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 generates a human-readable representation of the current tree. | 
|  | // Output spans multiple lines, and is suitable for ingest by a TOML parser. | 
|  | // If the conversion cannot be performed, ToString returns a non-nil error. | 
|  | func (t *TomlTree) ToString() (string, error) { | 
|  | return t.toTomlSafe("", "") | 
|  | } | 
|  |  | 
|  | // String generates a human-readable representation of the current tree. | 
|  | // Alias of ToString. | 
|  | func (t *TomlTree) String() string { | 
|  | result, _ := t.ToString() | 
|  | return result | 
|  | } | 
|  |  | 
|  | // 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 | 
|  | } |