| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module csv |
| 5 | |
| 6 | import strings |
| 7 | |
| 8 | struct Writer { |
| 9 | use_crlf bool |
| 10 | delimiter u8 |
| 11 | mut: |
| 12 | sb strings.Builder |
| 13 | } |
| 14 | |
| 15 | @[params] |
| 16 | pub struct WriterConfig { |
| 17 | pub: |
| 18 | use_crlf bool |
| 19 | delimiter u8 = `,` |
| 20 | } |
| 21 | |
| 22 | // new_writer returns a reference to a Writer |
| 23 | pub fn new_writer(config WriterConfig) &Writer { |
| 24 | return &Writer{ |
| 25 | sb: strings.new_builder(200) |
| 26 | use_crlf: config.use_crlf |
| 27 | delimiter: config.delimiter |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | // write writes a single record |
| 32 | pub fn (mut w Writer) write(record []string) !bool { |
| 33 | if !valid_delim(w.delimiter) { |
| 34 | return &InvalidDelimiterError{} |
| 35 | } |
| 36 | le := if w.use_crlf { '\r\n' } else { '\n' } |
| 37 | for n, field_ in record { |
| 38 | mut field := field_ |
| 39 | if n > 0 { |
| 40 | w.sb.write_string(w.delimiter.ascii_str()) |
| 41 | } |
| 42 | if !w.field_needs_quotes(field) { |
| 43 | w.sb.write_string(field) |
| 44 | continue |
| 45 | } |
| 46 | w.sb.write_string('"') |
| 47 | for field.len > 0 { |
| 48 | mut i := field.index_any('"\r\n') |
| 49 | if i < 0 { |
| 50 | i = field.len |
| 51 | } |
| 52 | w.sb.write_string(field[..i]) |
| 53 | field = field[i..] |
| 54 | if field.len > 0 { |
| 55 | z := field[0] |
| 56 | match z { |
| 57 | `"` { w.sb.write_string('""') } |
| 58 | `\r`, `\n` { w.sb.write_string(le) } |
| 59 | else {} |
| 60 | } |
| 61 | |
| 62 | field = field[1..] |
| 63 | } |
| 64 | } |
| 65 | w.sb.write_string('"') |
| 66 | } |
| 67 | w.sb.write_string(le) |
| 68 | return true |
| 69 | } |
| 70 | |
| 71 | // Once we have multi dimensional array |
| 72 | // pub fn (w &Writer) write_all(records [][]string) { |
| 73 | // for _, record in records { |
| 74 | // w.write(record) |
| 75 | // } |
| 76 | // } |
| 77 | fn (w &Writer) field_needs_quotes(field string) bool { |
| 78 | if field == '' { |
| 79 | return false |
| 80 | } |
| 81 | if field.contains(w.delimiter.ascii_str()) || field.index_any('"\r\n') != -1 { |
| 82 | return true |
| 83 | } |
| 84 | return false |
| 85 | } |
| 86 | |
| 87 | // str returns the writer contents |
| 88 | pub fn (mut w Writer) str() string { |
| 89 | return w.sb.str() |
| 90 | } |
| 91 | |