(rec super.Value)
| 62 | } |
| 63 | |
| 64 | func (w *Writer) Write(rec super.Value) error { |
| 65 | if rec.Type().Kind() != super.RecordKind { |
| 66 | return fmt.Errorf("CSV output encountered non-record value: %s", sup.FormatValue(rec)) |
| 67 | } |
| 68 | rec, err := w.flattener.Flatten(rec) |
| 69 | if err != nil { |
| 70 | return err |
| 71 | } |
| 72 | recType := super.TypeRecordOf(rec.Type()) |
| 73 | if w.first == nil { |
| 74 | w.first = recType |
| 75 | var hdr []string |
| 76 | if w.header { |
| 77 | for _, f := range rec.Fields() { |
| 78 | hdr = append(hdr, f.Name) |
| 79 | } |
| 80 | if err := w.encoder.Write(hdr); err != nil { |
| 81 | return err |
| 82 | } |
| 83 | } |
| 84 | } else if _, ok := w.types[rec.Type().ID()]; !ok { |
| 85 | if !fieldNamesEqual(w.first.Fields, rec.Fields()) { |
| 86 | return ErrNotDataFrame |
| 87 | } |
| 88 | w.types[rec.Type().ID()] = struct{}{} |
| 89 | } |
| 90 | w.strings = w.strings[:0] |
| 91 | fields := recType.Fields |
| 92 | for i, it := 0, scode.NewRecordIter(rec.Bytes(), recType.Opts); i < len(fields) && !it.Done(); i++ { |
| 93 | var s string |
| 94 | elem, none := it.Next(fields[i].Opt) |
| 95 | if !none { |
| 96 | val := super.NewValue(fields[i].Type, elem).Under() |
| 97 | switch id := val.Type().ID(); { |
| 98 | case id == super.IDBytes && len(val.Bytes()) == 0: |
| 99 | // We want "" instead of "0x" for a zero-length value. |
| 100 | case id == super.IDString: |
| 101 | s = string(val.Bytes()) |
| 102 | case id == super.IDNull: |
| 103 | default: |
| 104 | s = formatValue(val.Type(), val.Bytes()) |
| 105 | if super.IsFloat(id) && strings.HasSuffix(s, ".") { |
| 106 | s = strings.TrimSuffix(s, ".") |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | w.strings = append(w.strings, s) |
| 111 | } |
| 112 | return w.encoder.Write(w.strings) |
| 113 | } |
| 114 | |
| 115 | func formatValue(typ super.Type, bytes scode.Bytes) string { |
| 116 | // Avoid SUP decoration. |
nothing calls this directly
no test coverage detected