summaryrefslogtreecommitdiffstats
path: root/lib/src/csv.dart
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-26 12:38:22 -0300
committerMurilo Ijanc <[email protected]>2026-07-26 12:38:22 -0300
commit8e719fc715eb7ae9aa1c842e633f15b5c6cd3688 (patch)
tree4afda3d13e1b772f2c9a5cf9ceea5e84c647ebdd /lib/src/csv.dart
downloaddart-8e719fc715eb7ae9aa1c842e633f15b5c6cd3688.tar.gz
Initial importv0.1.0
Diffstat (limited to 'lib/src/csv.dart')
-rw-r--r--lib/src/csv.dart86
1 files changed, 86 insertions, 0 deletions
diff --git a/lib/src/csv.dart b/lib/src/csv.dart
new file mode 100644
index 0000000..ab2f0be
--- /dev/null
+++ b/lib/src/csv.dart
@@ -0,0 +1,86 @@
+/// A minimal RFC 4180 reader, for the `/csv` batch endpoint.
+///
+/// Values may contain commas and are then double-quoted with `""` escaping, so
+/// splitting on `,` corrupts every row that carries a street name like
+/// `Rua Um, Dois`. This is here because the Dart SDK ships no CSV parser and
+/// this library keeps its dependency list to one entry.
+library;
+
+const String _bom = '\u{FEFF}';
+
+/// Parses CSV text into records keyed by its header row.
+///
+/// A leading byte-order mark is dropped, `CRLF` and `LF` are both accepted, and
+/// a row shorter than the header has its remaining columns filled with the
+/// empty string. An empty input yields no records.
+List<Map<String, String>> parseCsv(String text) {
+ final rows = _parseRows(text.startsWith(_bom) ? text.substring(1) : text);
+ if (rows.isEmpty) {
+ return <Map<String, String>>[];
+ }
+
+ final header = rows.removeAt(0);
+ final records = <Map<String, String>>[];
+ for (final row in rows) {
+ if (row.length == 1 && row.first.isEmpty) {
+ continue;
+ }
+ final record = <String, String>{};
+ for (var i = 0; i < header.length; i++) {
+ record[header[i]] = i < row.length ? row[i] : '';
+ }
+ records.add(record);
+ }
+ return records;
+}
+
+List<List<String>> _parseRows(String text) {
+ final rows = <List<String>>[];
+ final field = StringBuffer();
+ var row = <String>[];
+ var quoted = false;
+ var started = false;
+
+ for (var i = 0; i < text.length; i++) {
+ final char = text[i];
+
+ if (quoted) {
+ if (char == '"') {
+ if (i + 1 < text.length && text[i + 1] == '"') {
+ field.write('"');
+ i++;
+ } else {
+ quoted = false;
+ }
+ } else {
+ field.write(char);
+ }
+ continue;
+ }
+
+ if (char == '"') {
+ quoted = true;
+ started = true;
+ } else if (char == ',') {
+ row.add(field.toString());
+ field.clear();
+ started = true;
+ } else if (char == '\n') {
+ row.add(field.toString());
+ field.clear();
+ rows.add(row);
+ row = <String>[];
+ started = false;
+ } else if (char != '\r') {
+ field.write(char);
+ started = true;
+ }
+ }
+
+ if (started || field.isNotEmpty || row.isNotEmpty) {
+ row.add(field.toString());
+ rows.add(row);
+ }
+
+ return rows;
+}