@@ -17,13 +17,11 @@ import (
17
17
type tomlEncodeError struct { error }
18
18
19
19
var (
20
- errArrayMixedElementTypes = errors .New ("toml: cannot encode array with mixed element types" )
21
- errArrayNilElement = errors .New ("toml: cannot encode array with nil element" )
22
- errNonString = errors .New ("toml: cannot encode a map with non-string key type" )
23
- errAnonNonStruct = errors .New ("toml: cannot encode an anonymous field that is not a struct" )
24
- errArrayNoTable = errors .New ("toml: TOML array element cannot contain a table" )
25
- errNoKey = errors .New ("toml: top-level values must be Go maps or structs" )
26
- errAnything = errors .New ("" ) // used in testing
20
+ errArrayNilElement = errors .New ("toml: cannot encode array with nil element" )
21
+ errNonString = errors .New ("toml: cannot encode a map with non-string key type" )
22
+ errAnonNonStruct = errors .New ("toml: cannot encode an anonymous field that is not a struct" )
23
+ errNoKey = errors .New ("toml: top-level values must be Go maps or structs" )
24
+ errAnything = errors .New ("" ) // used in testing
27
25
)
28
26
29
27
var quotedReplacer = strings .NewReplacer (
@@ -141,7 +139,7 @@ func (enc *Encoder) encode(key Key, rv reflect.Value) {
141
139
// generic structs (or whatever the underlying type of a TextMarshaler is).
142
140
switch t := rv .Interface ().(type ) {
143
141
case time.Time , encoding.TextMarshaler :
144
- enc .keyEqElement (key , rv )
142
+ enc .writeKeyValue (key , rv , false )
145
143
return
146
144
// TODO: #76 would make this superfluous after implemented.
147
145
case Primitive :
@@ -156,12 +154,12 @@ func (enc *Encoder) encode(key Key, rv reflect.Value) {
156
154
reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 ,
157
155
reflect .Uint64 ,
158
156
reflect .Float32 , reflect .Float64 , reflect .String , reflect .Bool :
159
- enc .keyEqElement (key , rv )
157
+ enc .writeKeyValue (key , rv , false )
160
158
case reflect .Array , reflect .Slice :
161
159
if typeEqual (tomlArrayHash , tomlTypeOfGo (rv )) {
162
160
enc .eArrayOfTables (key , rv )
163
161
} else {
164
- enc .keyEqElement (key , rv )
162
+ enc .writeKeyValue (key , rv , false )
165
163
}
166
164
case reflect .Interface :
167
165
if rv .IsNil () {
@@ -185,31 +183,31 @@ func (enc *Encoder) encode(key Key, rv reflect.Value) {
185
183
}
186
184
}
187
185
188
- // eElement encodes any value that can be an array element (primitives and
189
- // arrays).
186
+ // eElement encodes any value that can be an array element.
190
187
func (enc * Encoder ) eElement (rv reflect.Value ) {
191
188
switch v := rv .Interface ().(type ) {
192
189
case time.Time :
193
190
// Using TextMarshaler adds extra quotes, which we don't want.
194
191
enc .wf (v .Format (time .RFC3339Nano ))
195
192
return
196
193
case encoding.TextMarshaler :
197
- // Special case. Use text marshaler if it's available for this value.
194
+ // Use text marshaler if it's available for this value.
198
195
if s , err := v .MarshalText (); err != nil {
199
196
encPanic (err )
200
197
} else {
201
198
enc .writeQuoted (string (s ))
202
199
}
203
200
return
204
201
}
202
+
205
203
switch rv .Kind () {
204
+ case reflect .String :
205
+ enc .writeQuoted (rv .String ())
206
206
case reflect .Bool :
207
207
enc .wf (strconv .FormatBool (rv .Bool ()))
208
- case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 ,
209
- reflect .Int64 :
208
+ case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
210
209
enc .wf (strconv .FormatInt (rv .Int (), 10 ))
211
- case reflect .Uint , reflect .Uint8 , reflect .Uint16 ,
212
- reflect .Uint32 , reflect .Uint64 :
210
+ case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
213
211
enc .wf (strconv .FormatUint (rv .Uint (), 10 ))
214
212
case reflect .Float32 :
215
213
f := rv .Float ()
@@ -231,17 +229,19 @@ func (enc *Encoder) eElement(rv reflect.Value) {
231
229
}
232
230
case reflect .Array , reflect .Slice :
233
231
enc .eArrayOrSliceElement (rv )
232
+ case reflect .Struct :
233
+ enc .eStruct (nil , rv , true )
234
+ case reflect .Map :
235
+ enc .eMap (nil , rv , true )
234
236
case reflect .Interface :
235
237
enc .eElement (rv .Elem ())
236
- case reflect .String :
237
- enc .writeQuoted (rv .String ())
238
238
default :
239
- encPanic (fmt .Errorf ("unexpected primitive type: %s " , rv .Kind ()))
239
+ encPanic (fmt .Errorf ("unexpected primitive type: %T " , rv .Interface ()))
240
240
}
241
241
}
242
242
243
- // By the TOML spec, all floats must have a decimal with at least one
244
- // number on either side.
243
+ // By the TOML spec, all floats must have a decimal with at least one number on
244
+ // either side.
245
245
func floatAddDecimal (fstr string ) string {
246
246
if ! strings .Contains (fstr , "." ) {
247
247
return fstr + ".0"
@@ -278,7 +278,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
278
278
enc .newline ()
279
279
enc .wf ("%s[[%s]]" , enc .indentStr (key ), key .maybeQuotedAll ())
280
280
enc .newline ()
281
- enc .eMapOrStruct (key , trv )
281
+ enc .eMapOrStruct (key , trv , false )
282
282
}
283
283
}
284
284
@@ -292,22 +292,22 @@ func (enc *Encoder) eTable(key Key, rv reflect.Value) {
292
292
enc .wf ("%s[%s]" , enc .indentStr (key ), key .maybeQuotedAll ())
293
293
enc .newline ()
294
294
}
295
- enc .eMapOrStruct (key , rv )
295
+ enc .eMapOrStruct (key , rv , false )
296
296
}
297
297
298
- func (enc * Encoder ) eMapOrStruct (key Key , rv reflect.Value ) {
298
+ func (enc * Encoder ) eMapOrStruct (key Key , rv reflect.Value , inline bool ) {
299
299
switch rv := eindirect (rv ); rv .Kind () {
300
300
case reflect .Map :
301
- enc .eMap (key , rv )
301
+ enc .eMap (key , rv , inline )
302
302
case reflect .Struct :
303
- enc .eStruct (key , rv )
303
+ enc .eStruct (key , rv , inline )
304
304
default :
305
305
// Should never happen?
306
306
panic ("eTable: unhandled reflect.Value Kind: " + rv .Kind ().String ())
307
307
}
308
308
}
309
309
310
- func (enc * Encoder ) eMap (key Key , rv reflect.Value ) {
310
+ func (enc * Encoder ) eMap (key Key , rv reflect.Value , inline bool ) {
311
311
rt := rv .Type ()
312
312
if rt .Key ().Kind () != reflect .String {
313
313
encPanic (errNonString )
@@ -325,57 +325,76 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value) {
325
325
}
326
326
}
327
327
328
- var writeMapKeys = func (mapKeys []string ) {
328
+ var writeMapKeys = func (mapKeys []string , trailC bool ) {
329
329
sort .Strings (mapKeys )
330
- for _ , mapKey := range mapKeys {
331
- mrv := rv .MapIndex (reflect .ValueOf (mapKey ))
332
- if isNil (mrv ) {
333
- // Don't write anything for nil fields.
330
+ for i , mapKey := range mapKeys {
331
+ val := rv .MapIndex (reflect .ValueOf (mapKey ))
332
+ if isNil (val ) {
334
333
continue
335
334
}
336
- enc .encode (key .add (mapKey ), mrv )
335
+
336
+ if inline {
337
+ enc .writeKeyValue (Key {mapKey }, val , true )
338
+ if trailC || i != len (mapKeys )- 1 {
339
+ enc .wf (", " )
340
+ }
341
+ } else {
342
+ enc .encode (key .add (mapKey ), val )
343
+ }
337
344
}
338
345
}
339
- writeMapKeys (mapKeysDirect )
340
- writeMapKeys (mapKeysSub )
346
+
347
+ if inline {
348
+ enc .wf ("{" )
349
+ }
350
+ writeMapKeys (mapKeysDirect , len (mapKeysSub ) > 0 )
351
+ writeMapKeys (mapKeysSub , false )
352
+ if inline {
353
+ enc .wf ("}" )
354
+ }
341
355
}
342
356
343
- func (enc * Encoder ) eStruct (key Key , rv reflect.Value ) {
357
+ func (enc * Encoder ) eStruct (key Key , rv reflect.Value , inline bool ) {
344
358
// Write keys for fields directly under this key first, because if we write
345
- // a field that creates a new table, then all keys under it will be in that
359
+ // a field that creates a new table then all keys under it will be in that
346
360
// table (not the one we're writing here).
347
- rt := rv .Type ()
348
- var fieldsDirect , fieldsSub [][]int
349
- var addFields func (rt reflect.Type , rv reflect.Value , start []int )
361
+ //
362
+ // Fields is a [][]int: for fieldsDirect this always has one entry (the
363
+ // struct index). For fieldsSub it contains two entries: the parent field
364
+ // index from tv, and the field indexes for the fields of the sub.
365
+ var (
366
+ rt = rv .Type ()
367
+ fieldsDirect , fieldsSub [][]int
368
+ addFields func (rt reflect.Type , rv reflect.Value , start []int )
369
+ )
350
370
addFields = func (rt reflect.Type , rv reflect.Value , start []int ) {
351
371
for i := 0 ; i < rt .NumField (); i ++ {
352
372
f := rt .Field (i )
353
- // skip unexported fields
354
- if f .PkgPath != "" && ! f .Anonymous {
373
+ if f .PkgPath != "" && ! f .Anonymous { /// Skip unexported fields.
355
374
continue
356
375
}
376
+
357
377
frv := rv .Field (i )
378
+
379
+ // Treat anonymous struct fields with tag names as though they are
380
+ // not anonymous, like encoding/json does.
381
+ //
382
+ // Non-struct anonymous fields use the normal encoding logic.
358
383
if f .Anonymous {
359
384
t := f .Type
360
385
switch t .Kind () {
361
386
case reflect .Struct :
362
- // Treat anonymous struct fields with
363
- // tag names as though they are not
364
- // anonymous, like encoding/json does.
365
387
if getOptions (f .Tag ).name == "" {
366
388
addFields (t , frv , append (start , f .Index ... ))
367
389
continue
368
390
}
369
391
case reflect .Ptr :
370
- if t .Elem ().Kind () == reflect .Struct &&
371
- getOptions (f .Tag ).name == "" {
392
+ if t .Elem ().Kind () == reflect .Struct && getOptions (f .Tag ).name == "" {
372
393
if ! frv .IsNil () {
373
394
addFields (t .Elem (), frv .Elem (), append (start , f .Index ... ))
374
395
}
375
396
continue
376
397
}
377
- // Fall through to the normal field encoding logic below
378
- // for non-struct anonymous fields.
379
398
}
380
399
}
381
400
@@ -388,35 +407,49 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
388
407
}
389
408
addFields (rt , rv , nil )
390
409
391
- var writeFields = func (fields [][]int ) {
410
+ writeFields : = func (fields [][]int ) {
392
411
for _ , fieldIndex := range fields {
393
- sft := rt .FieldByIndex (fieldIndex )
394
- sf := rv .FieldByIndex (fieldIndex )
395
- if isNil ( sf ) {
396
- // Don't write anything for nil fields.
412
+ fieldType := rt .FieldByIndex (fieldIndex )
413
+ fieldVal := rv .FieldByIndex (fieldIndex )
414
+
415
+ if isNil ( fieldVal ) { / // Don't write anything for nil fields.
397
416
continue
398
417
}
399
418
400
- opts := getOptions (sft .Tag )
419
+ opts := getOptions (fieldType .Tag )
401
420
if opts .skip {
402
421
continue
403
422
}
404
- keyName := sft .Name
423
+ keyName := fieldType .Name
405
424
if opts .name != "" {
406
425
keyName = opts .name
407
426
}
408
- if opts .omitempty && isEmpty (sf ) {
427
+ if opts .omitempty && isEmpty (fieldVal ) {
409
428
continue
410
429
}
411
- if opts .omitzero && isZero (sf ) {
430
+ if opts .omitzero && isZero (fieldVal ) {
412
431
continue
413
432
}
414
433
415
- enc .encode (key .add (keyName ), sf )
434
+ if inline {
435
+ enc .writeKeyValue (Key {keyName }, fieldVal , true )
436
+ if fieldIndex [0 ] != len (fields )- 1 {
437
+ enc .wf (", " )
438
+ }
439
+ } else {
440
+ enc .encode (key .add (keyName ), fieldVal )
441
+ }
416
442
}
417
443
}
444
+
445
+ if inline {
446
+ enc .wf ("{" )
447
+ }
418
448
writeFields (fieldsDirect )
419
449
writeFields (fieldsSub )
450
+ if inline {
451
+ enc .wf ("}" )
452
+ }
420
453
}
421
454
422
455
// tomlTypeName returns the TOML type name of the Go value's type. It is
@@ -487,31 +520,18 @@ func tomlArrayType(rv reflect.Value) tomlType {
487
520
if isNil (rv ) || ! rv .IsValid () || rv .Len () == 0 {
488
521
return nil
489
522
}
490
- firstType := tomlTypeOfGo (rv .Index (0 ))
491
- if firstType == nil {
492
- encPanic (errArrayNilElement )
493
- }
494
523
524
+ /// Don't allow nil.
495
525
rvlen := rv .Len ()
496
526
for i := 1 ; i < rvlen ; i ++ {
497
- elem := rv .Index (i )
498
- switch elemType := tomlTypeOfGo (elem ); {
499
- case elemType == nil :
527
+ if tomlTypeOfGo (rv .Index (i )) == nil {
500
528
encPanic (errArrayNilElement )
501
- case ! typeEqual (firstType , elemType ):
502
- encPanic (errArrayMixedElementTypes )
503
529
}
504
530
}
505
531
506
- // If we have a nested array, then we must make sure that the nested array
507
- // contains ONLY primitives.
508
- //
509
- // This checks arbitrarily nested arrays.
510
- if typeEqual (firstType , tomlArray ) || typeEqual (firstType , tomlArrayHash ) {
511
- nest := tomlArrayType (eindirect (rv .Index (0 )))
512
- if typeEqual (nest , tomlHash ) || typeEqual (nest , tomlArrayHash ) {
513
- encPanic (errArrayNoTable )
514
- }
532
+ firstType := tomlTypeOfGo (rv .Index (0 ))
533
+ if firstType == nil {
534
+ encPanic (errArrayNilElement )
515
535
}
516
536
return firstType
517
537
}
@@ -570,13 +590,20 @@ func (enc *Encoder) newline() {
570
590
}
571
591
}
572
592
573
- func (enc * Encoder ) keyEqElement (key Key , val reflect.Value ) {
593
+ // Write a key/value pair:
594
+ //
595
+ // key = <any value>
596
+ //
597
+ // If inline is true it won't add a newline at the end.
598
+ func (enc * Encoder ) writeKeyValue (key Key , val reflect.Value , inline bool ) {
574
599
if len (key ) == 0 {
575
600
encPanic (errNoKey )
576
601
}
577
602
enc .wf ("%s%s = " , enc .indentStr (key ), key .maybeQuoted (len (key )- 1 ))
578
603
enc .eElement (val )
579
- enc .newline ()
604
+ if ! inline {
605
+ enc .newline ()
606
+ }
580
607
}
581
608
582
609
func (enc * Encoder ) wf (format string , v ... interface {}) {
0 commit comments