15 // encodes a string to a TOML-compliant string value
16 func encodeTomlString(value string) string {
19 for _, rr := range value {
38 b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
47 func tomlValueStringRepresentation(v interface{}) (string, error) {
48 switch value := v.(type) {
50 return strconv.FormatUint(value, 10), nil
52 return strconv.FormatInt(value, 10), nil
54 // Ensure a round float does contain a decimal point. Otherwise feeding
55 // the output back to the parser would convert to an integer.
56 if math.Trunc(value) == value {
57 return strconv.FormatFloat(value, 'f', 1, 32), nil
59 return strconv.FormatFloat(value, 'f', -1, 32), nil
61 return "\"" + encodeTomlString(value) + "\"", nil
64 return tomlValueStringRepresentation(string(b))
71 return value.Format(time.RFC3339), nil
76 rv := reflect.ValueOf(v)
78 if rv.Kind() == reflect.Slice {
80 for i := 0; i < rv.Len(); i++ {
81 item := rv.Index(i).Interface()
82 itemRepr, err := tomlValueStringRepresentation(item)
86 values = append(values, itemRepr)
88 return "[" + strings.Join(values, ",") + "]", nil
90 return "", fmt.Errorf("unsupported value type %T: %v", v, v)
93 func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (int64, error) {
94 simpleValuesKeys := make([]string, 0)
95 complexValuesKeys := make([]string, 0)
97 for k := range t.values {
101 complexValuesKeys = append(complexValuesKeys, k)
103 simpleValuesKeys = append(simpleValuesKeys, k)
107 sort.Strings(simpleValuesKeys)
108 sort.Strings(complexValuesKeys)
110 for _, k := range simpleValuesKeys {
111 v, ok := t.values[k].(*tomlValue)
113 return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
116 repr, err := tomlValueStringRepresentation(v.value)
118 return bytesCount, err
122 comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1)
124 if strings.HasPrefix(comment, "#") {
127 writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n")
128 bytesCount += int64(writtenBytesCountComment)
130 return bytesCount, errc
138 writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n")
139 bytesCount += int64(writtenBytesCount)
141 return bytesCount, err
145 for _, k := range complexValuesKeys {
150 combinedKey = keyspace + "." + combinedKey
157 switch node := v.(type) {
158 // node has to be of those two types given how keys are sorted above
160 tv, ok := t.values[k].(*Tree)
162 return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
164 if tv.comment != "" {
165 comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1)
167 if strings.HasPrefix(comment, "#") {
170 writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment)
171 bytesCount += int64(writtenBytesCountComment)
173 return bytesCount, errc
176 writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n")
177 bytesCount += int64(writtenBytesCount)
179 return bytesCount, err
181 bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount)
183 return bytesCount, err
186 for _, subTree := range node {
187 writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n")
188 bytesCount += int64(writtenBytesCount)
190 return bytesCount, err
193 bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount)
195 return bytesCount, err
201 return bytesCount, nil
204 func writeStrings(w io.Writer, s ...string) (int, error) {
207 b, err := io.WriteString(w, s[i])
216 // WriteTo encode the Tree as Toml and writes it to the writer w.
217 // Returns the number of bytes written in case of success, or an error if anything happened.
218 func (t *Tree) WriteTo(w io.Writer) (int64, error) {
219 return t.writeTo(w, "", "", 0)
222 // ToTomlString generates a human-readable representation of the current tree.
223 // Output spans multiple lines, and is suitable for ingest by a TOML parser.
224 // If the conversion cannot be performed, ToString returns a non-nil error.
225 func (t *Tree) ToTomlString() (string, error) {
227 _, err := t.WriteTo(&buf)
231 return buf.String(), nil
234 // String generates a human-readable representation of the current tree.
235 // Alias of ToString. Present to implement the fmt.Stringer interface.
236 func (t *Tree) String() string {
237 result, _ := t.ToTomlString()
241 // ToMap recursively generates a representation of the tree using Go built-in structures.
242 // The following types are used:
250 // * map[string]interface{} (where interface{} is any of this list)
251 // * []interface{} (where interface{} is any of this list)
252 func (t *Tree) ToMap() map[string]interface{} {
253 result := map[string]interface{}{}
255 for k, v := range t.values {
256 switch node := v.(type) {
258 var array []interface{}
259 for _, item := range node {
260 array = append(array, item.ToMap())
264 result[k] = node.ToMap()
266 result[k] = node.value