vendor/github.com/pelletier/go-toml/tomltree_write.go
changeset 251 1c52a0eeb952
parent 242 2a9ec03fe5a1
child 256 6d9efbef00a9
equal deleted inserted replaced
250:c040f992052f 251:1c52a0eeb952
     3 import (
     3 import (
     4 	"bytes"
     4 	"bytes"
     5 	"fmt"
     5 	"fmt"
     6 	"io"
     6 	"io"
     7 	"math"
     7 	"math"
       
     8 	"math/big"
     8 	"reflect"
     9 	"reflect"
     9 	"sort"
    10 	"sort"
    10 	"strconv"
    11 	"strconv"
    11 	"strings"
    12 	"strings"
    12 	"time"
    13 	"time"
    13 )
    14 )
       
    15 
       
    16 type valueComplexity int
       
    17 
       
    18 const (
       
    19 	valueSimple valueComplexity = iota + 1
       
    20 	valueComplex
       
    21 )
       
    22 
       
    23 type sortNode struct {
       
    24 	key        string
       
    25 	complexity valueComplexity
       
    26 }
    14 
    27 
    15 // Encodes a string to a TOML-compliant multi-line string value
    28 // Encodes a string to a TOML-compliant multi-line string value
    16 // This function is a clone of the existing encodeTomlString function, except that whitespace characters
    29 // This function is a clone of the existing encodeTomlString function, except that whitespace characters
    17 // are preserved. Quotation marks and backslashes are also not escaped.
    30 // are preserved. Quotation marks and backslashes are also not escaped.
    18 func encodeMultilineTomlString(value string) string {
    31 func encodeMultilineTomlString(value string) string {
    92 	case uint64:
   105 	case uint64:
    93 		return strconv.FormatUint(value, 10), nil
   106 		return strconv.FormatUint(value, 10), nil
    94 	case int64:
   107 	case int64:
    95 		return strconv.FormatInt(value, 10), nil
   108 		return strconv.FormatInt(value, 10), nil
    96 	case float64:
   109 	case float64:
    97 		// Ensure a round float does contain a decimal point. Otherwise feeding
   110 		// Default bit length is full 64
    98 		// the output back to the parser would convert to an integer.
   111 		bits := 64
       
   112 		// Float panics if nan is used
       
   113 		if !math.IsNaN(value) {
       
   114 			// if 32 bit accuracy is enough to exactly show, use 32
       
   115 			_, acc := big.NewFloat(value).Float32()
       
   116 			if acc == big.Exact {
       
   117 				bits = 32
       
   118 			}
       
   119 		}
    99 		if math.Trunc(value) == value {
   120 		if math.Trunc(value) == value {
   100 			return strings.ToLower(strconv.FormatFloat(value, 'f', 1, 32)), nil
   121 			return strings.ToLower(strconv.FormatFloat(value, 'f', 1, bits)), nil
   101 		}
   122 		}
   102 		return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil
   123 		return strings.ToLower(strconv.FormatFloat(value, 'f', -1, bits)), nil
   103 	case string:
   124 	case string:
   104 		if tv.multiline {
   125 		if tv.multiline {
   105 			return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil
   126 			return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil
   106 		}
   127 		}
   107 		return "\"" + encodeTomlString(value) + "\"", nil
   128 		return "\"" + encodeTomlString(value) + "\"", nil
   113 			return "true", nil
   134 			return "true", nil
   114 		}
   135 		}
   115 		return "false", nil
   136 		return "false", nil
   116 	case time.Time:
   137 	case time.Time:
   117 		return value.Format(time.RFC3339), nil
   138 		return value.Format(time.RFC3339), nil
       
   139 	case LocalDate:
       
   140 		return value.String(), nil
       
   141 	case LocalDateTime:
       
   142 		return value.String(), nil
       
   143 	case LocalTime:
       
   144 		return value.String(), nil
   118 	case nil:
   145 	case nil:
   119 		return "", nil
   146 		return "", nil
   120 	}
   147 	}
   121 
   148 
   122 	rv := reflect.ValueOf(v)
   149 	rv := reflect.ValueOf(v)
   151 		return "[" + strings.Join(values, ",") + "]", nil
   178 		return "[" + strings.Join(values, ",") + "]", nil
   152 	}
   179 	}
   153 	return "", fmt.Errorf("unsupported value type %T: %v", v, v)
   180 	return "", fmt.Errorf("unsupported value type %T: %v", v, v)
   154 }
   181 }
   155 
   182 
   156 func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) {
   183 func getTreeArrayLine(trees []*Tree) (line int) {
   157 	simpleValuesKeys := make([]string, 0)
   184 	// get lowest line number that is not 0
   158 	complexValuesKeys := make([]string, 0)
   185 	for _, tv := range trees {
       
   186 		if tv.position.Line < line || line == 0 {
       
   187 			line = tv.position.Line
       
   188 		}
       
   189 	}
       
   190 	return
       
   191 }
       
   192 
       
   193 func sortByLines(t *Tree) (vals []sortNode) {
       
   194 	var (
       
   195 		line  int
       
   196 		lines []int
       
   197 		tv    *Tree
       
   198 		tom   *tomlValue
       
   199 		node  sortNode
       
   200 	)
       
   201 	vals = make([]sortNode, 0)
       
   202 	m := make(map[int]sortNode)
       
   203 
       
   204 	for k := range t.values {
       
   205 		v := t.values[k]
       
   206 		switch v.(type) {
       
   207 		case *Tree:
       
   208 			tv = v.(*Tree)
       
   209 			line = tv.position.Line
       
   210 			node = sortNode{key: k, complexity: valueComplex}
       
   211 		case []*Tree:
       
   212 			line = getTreeArrayLine(v.([]*Tree))
       
   213 			node = sortNode{key: k, complexity: valueComplex}
       
   214 		default:
       
   215 			tom = v.(*tomlValue)
       
   216 			line = tom.position.Line
       
   217 			node = sortNode{key: k, complexity: valueSimple}
       
   218 		}
       
   219 		lines = append(lines, line)
       
   220 		vals = append(vals, node)
       
   221 		m[line] = node
       
   222 	}
       
   223 	sort.Ints(lines)
       
   224 
       
   225 	for i, line := range lines {
       
   226 		vals[i] = m[line]
       
   227 	}
       
   228 
       
   229 	return vals
       
   230 }
       
   231 
       
   232 func sortAlphabetical(t *Tree) (vals []sortNode) {
       
   233 	var (
       
   234 		node     sortNode
       
   235 		simpVals []string
       
   236 		compVals []string
       
   237 	)
       
   238 	vals = make([]sortNode, 0)
       
   239 	m := make(map[string]sortNode)
   159 
   240 
   160 	for k := range t.values {
   241 	for k := range t.values {
   161 		v := t.values[k]
   242 		v := t.values[k]
   162 		switch v.(type) {
   243 		switch v.(type) {
   163 		case *Tree, []*Tree:
   244 		case *Tree, []*Tree:
   164 			complexValuesKeys = append(complexValuesKeys, k)
   245 			node = sortNode{key: k, complexity: valueComplex}
       
   246 			compVals = append(compVals, node.key)
   165 		default:
   247 		default:
   166 			simpleValuesKeys = append(simpleValuesKeys, k)
   248 			node = sortNode{key: k, complexity: valueSimple}
   167 		}
   249 			simpVals = append(simpVals, node.key)
   168 	}
   250 		}
   169 
   251 		vals = append(vals, node)
   170 	sort.Strings(simpleValuesKeys)
   252 		m[node.key] = node
   171 	sort.Strings(complexValuesKeys)
   253 	}
   172 
   254 
   173 	for _, k := range simpleValuesKeys {
   255 	// Simples first to match previous implementation
   174 		v, ok := t.values[k].(*tomlValue)
   256 	sort.Strings(simpVals)
   175 		if !ok {
   257 	i := 0
   176 			return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
   258 	for _, key := range simpVals {
   177 		}
   259 		vals[i] = m[key]
   178 
   260 		i++
   179 		repr, err := tomlValueStringRepresentation(v, indent, arraysOneElementPerLine)
   261 	}
   180 		if err != nil {
   262 
   181 			return bytesCount, err
   263 	sort.Strings(compVals)
   182 		}
   264 	for _, key := range compVals {
   183 
   265 		vals[i] = m[key]
   184 		if v.comment != "" {
   266 		i++
   185 			comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1)
   267 	}
   186 			start := "# "
   268 
   187 			if strings.HasPrefix(comment, "#") {
   269 	return vals
   188 				start = ""
   270 }
   189 			}
   271 
   190 			writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n")
   272 func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) {
   191 			bytesCount += int64(writtenBytesCountComment)
   273 	return t.writeToOrdered(w, indent, keyspace, bytesCount, arraysOneElementPerLine, OrderAlphabetical)
   192 			if errc != nil {
   274 }
   193 				return bytesCount, errc
   275 
   194 			}
   276 func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder) (int64, error) {
   195 		}
   277 	var orderedVals []sortNode
   196 
   278 
   197 		var commented string
   279 	switch ord {
   198 		if v.commented {
   280 	case OrderPreserve:
   199 			commented = "# "
   281 		orderedVals = sortByLines(t)
   200 		}
   282 	default:
   201 		writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n")
   283 		orderedVals = sortAlphabetical(t)
   202 		bytesCount += int64(writtenBytesCount)
   284 	}
   203 		if err != nil {
   285 
   204 			return bytesCount, err
   286 	for _, node := range orderedVals {
   205 		}
   287 		switch node.complexity {
   206 	}
   288 		case valueComplex:
   207 
   289 			k := node.key
   208 	for _, k := range complexValuesKeys {
   290 			v := t.values[k]
   209 		v := t.values[k]
   291 
   210 
   292 			combinedKey := k
   211 		combinedKey := k
   293 			if keyspace != "" {
   212 		if keyspace != "" {
   294 				combinedKey = keyspace + "." + combinedKey
   213 			combinedKey = keyspace + "." + combinedKey
   295 			}
   214 		}
   296 			var commented string
   215 		var commented string
   297 			if t.commented {
   216 		if t.commented {
   298 				commented = "# "
   217 			commented = "# "
   299 			}
   218 		}
   300 
   219 
   301 			switch node := v.(type) {
   220 		switch node := v.(type) {
   302 			// node has to be of those two types given how keys are sorted above
   221 		// node has to be of those two types given how keys are sorted above
   303 			case *Tree:
   222 		case *Tree:
   304 				tv, ok := t.values[k].(*Tree)
   223 			tv, ok := t.values[k].(*Tree)
   305 				if !ok {
       
   306 					return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
       
   307 				}
       
   308 				if tv.comment != "" {
       
   309 					comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1)
       
   310 					start := "# "
       
   311 					if strings.HasPrefix(comment, "#") {
       
   312 						start = ""
       
   313 					}
       
   314 					writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment)
       
   315 					bytesCount += int64(writtenBytesCountComment)
       
   316 					if errc != nil {
       
   317 						return bytesCount, errc
       
   318 					}
       
   319 				}
       
   320 				writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n")
       
   321 				bytesCount += int64(writtenBytesCount)
       
   322 				if err != nil {
       
   323 					return bytesCount, err
       
   324 				}
       
   325 				bytesCount, err = node.writeToOrdered(w, indent+"  ", combinedKey, bytesCount, arraysOneElementPerLine, ord)
       
   326 				if err != nil {
       
   327 					return bytesCount, err
       
   328 				}
       
   329 			case []*Tree:
       
   330 				for _, subTree := range node {
       
   331 					writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n")
       
   332 					bytesCount += int64(writtenBytesCount)
       
   333 					if err != nil {
       
   334 						return bytesCount, err
       
   335 					}
       
   336 
       
   337 					bytesCount, err = subTree.writeToOrdered(w, indent+"  ", combinedKey, bytesCount, arraysOneElementPerLine, ord)
       
   338 					if err != nil {
       
   339 						return bytesCount, err
       
   340 					}
       
   341 				}
       
   342 			}
       
   343 		default: // Simple
       
   344 			k := node.key
       
   345 			v, ok := t.values[k].(*tomlValue)
   224 			if !ok {
   346 			if !ok {
   225 				return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
   347 				return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
   226 			}
   348 			}
   227 			if tv.comment != "" {
   349 
   228 				comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1)
   350 			repr, err := tomlValueStringRepresentation(v, indent, arraysOneElementPerLine)
       
   351 			if err != nil {
       
   352 				return bytesCount, err
       
   353 			}
       
   354 
       
   355 			if v.comment != "" {
       
   356 				comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1)
   229 				start := "# "
   357 				start := "# "
   230 				if strings.HasPrefix(comment, "#") {
   358 				if strings.HasPrefix(comment, "#") {
   231 					start = ""
   359 					start = ""
   232 				}
   360 				}
   233 				writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment)
   361 				writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n")
   234 				bytesCount += int64(writtenBytesCountComment)
   362 				bytesCount += int64(writtenBytesCountComment)
   235 				if errc != nil {
   363 				if errc != nil {
   236 					return bytesCount, errc
   364 					return bytesCount, errc
   237 				}
   365 				}
   238 			}
   366 			}
   239 			writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n")
   367 
       
   368 			var commented string
       
   369 			if v.commented {
       
   370 				commented = "# "
       
   371 			}
       
   372 			quotedKey := quoteKeyIfNeeded(k)
       
   373 			writtenBytesCount, err := writeStrings(w, indent, commented, quotedKey, " = ", repr, "\n")
   240 			bytesCount += int64(writtenBytesCount)
   374 			bytesCount += int64(writtenBytesCount)
   241 			if err != nil {
   375 			if err != nil {
   242 				return bytesCount, err
   376 				return bytesCount, err
   243 			}
   377 			}
   244 			bytesCount, err = node.writeTo(w, indent+"  ", combinedKey, bytesCount, arraysOneElementPerLine)
       
   245 			if err != nil {
       
   246 				return bytesCount, err
       
   247 			}
       
   248 		case []*Tree:
       
   249 			for _, subTree := range node {
       
   250 				writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n")
       
   251 				bytesCount += int64(writtenBytesCount)
       
   252 				if err != nil {
       
   253 					return bytesCount, err
       
   254 				}
       
   255 
       
   256 				bytesCount, err = subTree.writeTo(w, indent+"  ", combinedKey, bytesCount, arraysOneElementPerLine)
       
   257 				if err != nil {
       
   258 					return bytesCount, err
       
   259 				}
       
   260 			}
       
   261 		}
   378 		}
   262 	}
   379 	}
   263 
   380 
   264 	return bytesCount, nil
   381 	return bytesCount, nil
       
   382 }
       
   383 
       
   384 // quote a key if it does not fit the bare key format (A-Za-z0-9_-)
       
   385 // quoted keys use the same rules as strings
       
   386 func quoteKeyIfNeeded(k string) string {
       
   387 	// when encoding a map with the 'quoteMapKeys' option enabled, the tree will contain
       
   388 	// keys that have already been quoted.
       
   389 	// not an ideal situation, but good enough of a stop gap.
       
   390 	if len(k) >= 2 && k[0] == '"' && k[len(k)-1] == '"' {
       
   391 		return k
       
   392 	}
       
   393 	isBare := true
       
   394 	for _, r := range k {
       
   395 		if !isValidBareChar(r) {
       
   396 			isBare = false
       
   397 			break
       
   398 		}
       
   399 	}
       
   400 	if isBare {
       
   401 		return k
       
   402 	}
       
   403 	return quoteKey(k)
       
   404 }
       
   405 
       
   406 func quoteKey(k string) string {
       
   407 	return "\"" + encodeTomlString(k) + "\""
   265 }
   408 }
   266 
   409 
   267 func writeStrings(w io.Writer, s ...string) (int, error) {
   410 func writeStrings(w io.Writer, s ...string) (int, error) {
   268 	var n int
   411 	var n int
   269 	for i := range s {
   412 	for i := range s {
   284 
   427 
   285 // ToTomlString generates a human-readable representation of the current tree.
   428 // ToTomlString generates a human-readable representation of the current tree.
   286 // Output spans multiple lines, and is suitable for ingest by a TOML parser.
   429 // Output spans multiple lines, and is suitable for ingest by a TOML parser.
   287 // If the conversion cannot be performed, ToString returns a non-nil error.
   430 // If the conversion cannot be performed, ToString returns a non-nil error.
   288 func (t *Tree) ToTomlString() (string, error) {
   431 func (t *Tree) ToTomlString() (string, error) {
   289 	var buf bytes.Buffer
   432 	b, err := t.Marshal()
   290 	_, err := t.WriteTo(&buf)
       
   291 	if err != nil {
   433 	if err != nil {
   292 		return "", err
   434 		return "", err
   293 	}
   435 	}
   294 	return buf.String(), nil
   436 	return string(b), nil
   295 }
   437 }
   296 
   438 
   297 // String generates a human-readable representation of the current tree.
   439 // String generates a human-readable representation of the current tree.
   298 // Alias of ToString. Present to implement the fmt.Stringer interface.
   440 // Alias of ToString. Present to implement the fmt.Stringer interface.
   299 func (t *Tree) String() string {
   441 func (t *Tree) String() string {