portingtools/src/csv_reader.zig

80 lines
2.3 KiB
Zig

const std = @import("std");
pub const Record = struct {
cols: [][]u8,
pub fn deinit(self: Record, alloc: std.mem.Allocator) void {
for (self.cols) |col| {
alloc.free(col);
}
alloc.free(self.cols);
}
};
pub fn csvReader(reader: anytype) CsvReader(@TypeOf(reader)) {
return .{ .reader = reader };
}
pub fn CsvReader(comptime Reader: type) type {
return struct {
reader: Reader,
const Self = @This();
pub fn next(self: *Self, alloc: std.mem.Allocator) !?Record {
var prev_quote = false;
var in_quote = false;
var cols = std.ArrayList([]u8).init(alloc);
defer cols.deinit();
var buf = std.ArrayList(u8).init(alloc);
defer buf.deinit();
while (true) {
var ch_buf: [1]u8 = undefined;
if (try self.reader.read(&ch_buf) == 0) {
if (buf.items.len != 0) {
try cols.append(try buf.toOwnedSlice());
return Record{ .cols = try cols.toOwnedSlice() };
}
return null;
}
const ch = ch_buf[0];
switch (ch) {
'"' => {
if (prev_quote) {
try buf.append('"');
} else {
prev_quote = true;
in_quote = !in_quote;
}
},
',' => {
prev_quote = false;
if (in_quote) {
try buf.append(',');
} else {
defer buf.clearRetainingCapacity();
try cols.append(try alloc.dupe(u8, buf.items));
}
},
'\n' => {
try cols.append(try buf.toOwnedSlice());
return Record{ .cols = try cols.toOwnedSlice() };
},
else => {
prev_quote = false;
try buf.append(ch);
},
}
}
}
};
}