OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / pelletier / go-toml / tomltree_write.go
1 package toml
2
3 import (
4         "bytes"
5         "fmt"
6         "io"
7         "math"
8         "reflect"
9         "sort"
10         "strconv"
11         "strings"
12         "time"
13 )
14
15 // encodes a string to a TOML-compliant string value
16 func encodeTomlString(value string) string {
17         var b bytes.Buffer
18
19         for _, rr := range value {
20                 switch rr {
21                 case '\b':
22                         b.WriteString(`\b`)
23                 case '\t':
24                         b.WriteString(`\t`)
25                 case '\n':
26                         b.WriteString(`\n`)
27                 case '\f':
28                         b.WriteString(`\f`)
29                 case '\r':
30                         b.WriteString(`\r`)
31                 case '"':
32                         b.WriteString(`\"`)
33                 case '\\':
34                         b.WriteString(`\\`)
35                 default:
36                         intRr := uint16(rr)
37                         if intRr < 0x001F {
38                                 b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
39                         } else {
40                                 b.WriteRune(rr)
41                         }
42                 }
43         }
44         return b.String()
45 }
46
47 func tomlValueStringRepresentation(v interface{}) (string, error) {
48         switch value := v.(type) {
49         case uint64:
50                 return strconv.FormatUint(value, 10), nil
51         case int64:
52                 return strconv.FormatInt(value, 10), nil
53         case float64:
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
58                 }
59                 return strconv.FormatFloat(value, 'f', -1, 32), nil
60         case string:
61                 return "\"" + encodeTomlString(value) + "\"", nil
62         case []byte:
63                 b, _ := v.([]byte)
64                 return tomlValueStringRepresentation(string(b))
65         case bool:
66                 if value {
67                         return "true", nil
68                 }
69                 return "false", nil
70         case time.Time:
71                 return value.Format(time.RFC3339), nil
72         case nil:
73                 return "", nil
74         }
75
76         rv := reflect.ValueOf(v)
77
78         if rv.Kind() == reflect.Slice {
79                 values := []string{}
80                 for i := 0; i < rv.Len(); i++ {
81                         item := rv.Index(i).Interface()
82                         itemRepr, err := tomlValueStringRepresentation(item)
83                         if err != nil {
84                                 return "", err
85                         }
86                         values = append(values, itemRepr)
87                 }
88                 return "[" + strings.Join(values, ",") + "]", nil
89         }
90         return "", fmt.Errorf("unsupported value type %T: %v", v, v)
91 }
92
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)
96
97         for k := range t.values {
98                 v := t.values[k]
99                 switch v.(type) {
100                 case *Tree, []*Tree:
101                         complexValuesKeys = append(complexValuesKeys, k)
102                 default:
103                         simpleValuesKeys = append(simpleValuesKeys, k)
104                 }
105         }
106
107         sort.Strings(simpleValuesKeys)
108         sort.Strings(complexValuesKeys)
109
110         for _, k := range simpleValuesKeys {
111                 v, ok := t.values[k].(*tomlValue)
112                 if !ok {
113                         return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
114                 }
115
116                 repr, err := tomlValueStringRepresentation(v.value)
117                 if err != nil {
118                         return bytesCount, err
119                 }
120
121                 if v.comment != "" {
122                         comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1)
123                         start := "# "
124                         if strings.HasPrefix(comment, "#") {
125                                 start = ""
126                         }
127                         writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n")
128                         bytesCount += int64(writtenBytesCountComment)
129                         if errc != nil {
130                                 return bytesCount, errc
131                         }
132                 }
133
134                 var commented string
135                 if v.commented {
136                         commented = "# "
137                 }
138                 writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n")
139                 bytesCount += int64(writtenBytesCount)
140                 if err != nil {
141                         return bytesCount, err
142                 }
143         }
144
145         for _, k := range complexValuesKeys {
146                 v := t.values[k]
147
148                 combinedKey := k
149                 if keyspace != "" {
150                         combinedKey = keyspace + "." + combinedKey
151                 }
152                 var commented string
153                 if t.commented {
154                         commented = "# "
155                 }
156
157                 switch node := v.(type) {
158                 // node has to be of those two types given how keys are sorted above
159                 case *Tree:
160                         tv, ok := t.values[k].(*Tree)
161                         if !ok {
162                                 return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
163                         }
164                         if tv.comment != "" {
165                                 comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1)
166                                 start := "# "
167                                 if strings.HasPrefix(comment, "#") {
168                                         start = ""
169                                 }
170                                 writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment)
171                                 bytesCount += int64(writtenBytesCountComment)
172                                 if errc != nil {
173                                         return bytesCount, errc
174                                 }
175                         }
176                         writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n")
177                         bytesCount += int64(writtenBytesCount)
178                         if err != nil {
179                                 return bytesCount, err
180                         }
181                         bytesCount, err = node.writeTo(w, indent+"  ", combinedKey, bytesCount)
182                         if err != nil {
183                                 return bytesCount, err
184                         }
185                 case []*Tree:
186                         for _, subTree := range node {
187                                 writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n")
188                                 bytesCount += int64(writtenBytesCount)
189                                 if err != nil {
190                                         return bytesCount, err
191                                 }
192
193                                 bytesCount, err = subTree.writeTo(w, indent+"  ", combinedKey, bytesCount)
194                                 if err != nil {
195                                         return bytesCount, err
196                                 }
197                         }
198                 }
199         }
200
201         return bytesCount, nil
202 }
203
204 func writeStrings(w io.Writer, s ...string) (int, error) {
205         var n int
206         for i := range s {
207                 b, err := io.WriteString(w, s[i])
208                 n += b
209                 if err != nil {
210                         return n, err
211                 }
212         }
213         return n, nil
214 }
215
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)
220 }
221
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) {
226         var buf bytes.Buffer
227         _, err := t.WriteTo(&buf)
228         if err != nil {
229                 return "", err
230         }
231         return buf.String(), nil
232 }
233
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()
238         return result
239 }
240
241 // ToMap recursively generates a representation of the tree using Go built-in structures.
242 // The following types are used:
243 //
244 //      * bool
245 //      * float64
246 //      * int64
247 //      * string
248 //      * uint64
249 //      * time.Time
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{}{}
254
255         for k, v := range t.values {
256                 switch node := v.(type) {
257                 case []*Tree:
258                         var array []interface{}
259                         for _, item := range node {
260                                 array = append(array, item.ToMap())
261                         }
262                         result[k] = array
263                 case *Tree:
264                         result[k] = node.ToMap()
265                 case *tomlValue:
266                         result[k] = node.value
267                 }
268         }
269         return result
270 }