mirror of
https://github.com/Tilo-K/csvu.git
synced 2026-01-09 16:11:02 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
65d157852a
|
|||
|
|
d17540cb33 | ||
|
0b60592e61
|
|||
| b14907b3cc | |||
| 12fe0287da |
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
- name: Install Zig
|
||||
uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: 0.14.0
|
||||
version: 0.15.0
|
||||
|
||||
- name: Build for ${{ matrix.triple }}
|
||||
run: |
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
.zig-cache
|
||||
zig-out
|
||||
zig-out
|
||||
.DS_Store
|
||||
|
||||
25
build.zig
25
build.zig
@@ -15,8 +15,7 @@ pub fn build(b: *std.Build) void {
|
||||
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const lib = b.addStaticLibrary(.{
|
||||
.name = "csvu",
|
||||
const lib = b.addModule("csvu", .{
|
||||
// In this case the main source file is merely a path, however, in more
|
||||
// complicated build scripts, this could be a generated file.
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
@@ -27,13 +26,11 @@ pub fn build(b: *std.Build) void {
|
||||
// This declares intent for the library to be installed into the standard
|
||||
// location when the user invokes the "install" step (the default step when
|
||||
// running `zig build`).
|
||||
b.installArtifact(lib);
|
||||
//b.installArtifact(lib);
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "csvu",
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, .imports = &.{.{ .name = "csvu", .module = lib }} }),
|
||||
});
|
||||
|
||||
// This declares intent for the executable to be installed into the
|
||||
@@ -67,17 +64,21 @@ pub fn build(b: *std.Build) void {
|
||||
// Creates a step for unit testing. This only builds the test executable
|
||||
// but does not run it.
|
||||
const lib_unit_tests = b.addTest(.{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
|
||||
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
||||
|
||||
const exe_unit_tests = b.addTest(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
|
||||
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
||||
|
||||
95
src/csv.zig
95
src/csv.zig
@@ -5,16 +5,6 @@ const CsvError = error{
|
||||
NoDelimiterFound,
|
||||
};
|
||||
|
||||
fn concat(one: []const u8, two: []const u8) ![]const u8 {
|
||||
const allocator = std.heap.page_allocator;
|
||||
var result = try allocator.alloc(u8, one.len + two.len);
|
||||
|
||||
std.mem.copyForwards(u8, result[0..], one);
|
||||
std.mem.copyForwards(u8, result[one.len..], two);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn contains(arr: []const u8, target: u8) bool {
|
||||
for (arr) |element| {
|
||||
if (element == target) {
|
||||
@@ -26,10 +16,13 @@ fn contains(arr: []const u8, target: u8) bool {
|
||||
}
|
||||
|
||||
pub fn determineDelimiter(str: []const u8) !u8 {
|
||||
const alloc = std.heap.page_allocator;
|
||||
var allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = allocator.deinit();
|
||||
const alloc = allocator.allocator();
|
||||
|
||||
const possibleDelimiter = [_]u8{ ',', ';', '\t', '|' };
|
||||
var countMap = std.AutoHashMap(u8, u32).init(alloc);
|
||||
defer countMap.deinit();
|
||||
|
||||
for (possibleDelimiter) |del| {
|
||||
try countMap.put(del, 0);
|
||||
@@ -66,6 +59,8 @@ pub fn determineDelimiter(str: []const u8) !u8 {
|
||||
const CsvFile = struct {
|
||||
header: std.ArrayList([]const u8),
|
||||
entries: std.ArrayList(std.ArrayList([]const u8)),
|
||||
alloc: std.mem.Allocator,
|
||||
p_lines: std.ArrayList([]const u8),
|
||||
|
||||
pub fn isValid(self: CsvFile) bool {
|
||||
const colNum = self.header.items.len;
|
||||
@@ -77,15 +72,29 @@ const CsvFile = struct {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *CsvFile) void {
|
||||
for (self.p_lines.items) |line| {
|
||||
self.alloc.free(line);
|
||||
}
|
||||
self.p_lines.deinit(self.alloc);
|
||||
self.header.deinit(self.alloc);
|
||||
|
||||
for (self.entries.items) |entry_c| {
|
||||
var entry = entry_c;
|
||||
entry.deinit(self.alloc);
|
||||
}
|
||||
self.entries.deinit(self.alloc);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn printTable(file: CsvFile) !void {
|
||||
const stdout_file = std.io.getStdOut().writer();
|
||||
var bw = std.io.bufferedWriter(stdout_file);
|
||||
const stdout = bw.writer();
|
||||
var stdout_buf: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&stdout_buf);
|
||||
var stdout = &stdout_writer.interface;
|
||||
|
||||
defer {
|
||||
_ = bw.flush() catch null;
|
||||
_ = stdout.flush() catch null;
|
||||
}
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
@@ -136,7 +145,6 @@ pub fn printTable(file: CsvFile) !void {
|
||||
}
|
||||
|
||||
_ = try stdout.writeAll("\n");
|
||||
_ = try bw.flush();
|
||||
|
||||
for (0..complete_length) |_| {
|
||||
try stdout.print("-", .{});
|
||||
@@ -144,22 +152,25 @@ pub fn printTable(file: CsvFile) !void {
|
||||
try stdout.print("\n", .{});
|
||||
|
||||
for (file.entries.items) |entry| {
|
||||
var out_line: []const u8 = "|";
|
||||
var out_line = std.ArrayList(u8){};
|
||||
defer out_line.deinit(alloc);
|
||||
|
||||
try out_line.appendSlice(alloc, "|");
|
||||
|
||||
for (0..col_nums) |i| {
|
||||
const out = entry.items[i];
|
||||
const missing = col_sizes[i] - out.len;
|
||||
|
||||
out_line = try concat(out_line, out);
|
||||
try out_line.appendSlice(alloc, out);
|
||||
|
||||
for (0..missing) |_| {
|
||||
out_line = try concat(out_line, " ");
|
||||
try out_line.appendSlice(alloc, " ");
|
||||
}
|
||||
out_line = try concat(out_line, "|");
|
||||
try out_line.appendSlice(alloc, "|");
|
||||
}
|
||||
out_line = try concat(out_line, "\n");
|
||||
_ = try stdout.writeAll(out_line);
|
||||
_ = try bw.flush();
|
||||
try out_line.appendSlice(alloc, "\n");
|
||||
|
||||
_ = try stdout.writeAll(out_line.items);
|
||||
}
|
||||
for (0..complete_length) |_| {
|
||||
try stdout.print("-", .{});
|
||||
@@ -167,35 +178,45 @@ pub fn printTable(file: CsvFile) !void {
|
||||
try stdout.print("\n", .{});
|
||||
}
|
||||
|
||||
pub fn loadFile(filepath: []const u8) !CsvFile {
|
||||
const alloc = std.heap.page_allocator;
|
||||
var file = try std.fs.cwd().openFile(filepath, .{});
|
||||
defer file.close();
|
||||
pub fn loadFile(filepath: []const u8, alloc: std.mem.Allocator) !CsvFile {
|
||||
var file_buf: [4096]u8 = undefined;
|
||||
|
||||
var buf_reader = std.io.bufferedReader(file.reader());
|
||||
var in_stream = buf_reader.reader();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var file = try std.fs.cwd().openFile(filepath, .{ .mode = .read_write });
|
||||
defer file.close();
|
||||
var file_reader = file.reader(&file_buf);
|
||||
const in_stream = &file_reader.interface;
|
||||
|
||||
var readHeader = false;
|
||||
var headerList: std.ArrayList([]const u8) = undefined;
|
||||
var entries = std.ArrayList(std.ArrayList([]const u8)).init(alloc);
|
||||
var entries = std.ArrayList(std.ArrayList([]const u8)){};
|
||||
var lines = std.ArrayList([]const u8){};
|
||||
|
||||
var delimiter: u8 = ' ';
|
||||
|
||||
while (try in_stream.readUntilDelimiterOrEof(&buffer, '\n')) |line| {
|
||||
while (true) {
|
||||
const line = in_stream.takeDelimiterExclusive('\n') catch |err| {
|
||||
if (err == error.EndOfStream) {
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
};
|
||||
|
||||
if (delimiter == ' ') {
|
||||
delimiter = try determineDelimiter(line);
|
||||
}
|
||||
|
||||
const del = delimiter;
|
||||
var entr = std.ArrayList([]const u8).init(alloc);
|
||||
var entr = std.ArrayList([]const u8){};
|
||||
var splitIt = std.mem.splitSequence(u8, line, &[_]u8{del});
|
||||
|
||||
while (splitIt.next()) |part| {
|
||||
const dest = try alloc.alloc(u8, part.len);
|
||||
|
||||
lines.append(alloc, dest) catch unreachable;
|
||||
std.mem.copyForwards(u8, dest, part);
|
||||
|
||||
const res = std.mem.trim(u8, dest, &[_]u8{ '\n', '\t', '\r', ' ' });
|
||||
_ = try entr.append(res);
|
||||
|
||||
_ = try entr.append(alloc, res);
|
||||
}
|
||||
|
||||
if (!readHeader) {
|
||||
@@ -203,10 +224,10 @@ pub fn loadFile(filepath: []const u8) !CsvFile {
|
||||
readHeader = true;
|
||||
continue;
|
||||
}
|
||||
_ = try entries.append(entr);
|
||||
_ = try entries.append(alloc, entr);
|
||||
}
|
||||
|
||||
return CsvFile{ .entries = entries, .header = headerList };
|
||||
return CsvFile{ .entries = entries, .header = headerList, .alloc = alloc, .p_lines = lines };
|
||||
}
|
||||
|
||||
test "Determine delimiter" {
|
||||
|
||||
23
src/main.zig
23
src/main.zig
@@ -2,12 +2,17 @@ const std = @import("std");
|
||||
const csv = @import("csv.zig");
|
||||
|
||||
pub fn main() !void {
|
||||
const stdout_file = std.io.getStdOut().writer();
|
||||
var bw = std.io.bufferedWriter(stdout_file);
|
||||
const stdout = bw.writer();
|
||||
const alloc = std.heap.page_allocator;
|
||||
var stdout_buf: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&stdout_buf);
|
||||
var stdout = &stdout_writer.interface;
|
||||
|
||||
var allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = allocator.deinit();
|
||||
const alloc = allocator.allocator();
|
||||
|
||||
var args = try std.process.ArgIterator.initWithAllocator(alloc);
|
||||
defer args.deinit();
|
||||
|
||||
_ = args.next();
|
||||
|
||||
var filepath: [:0]const u8 = "";
|
||||
@@ -18,16 +23,18 @@ pub fn main() !void {
|
||||
|
||||
if (std.mem.eql(u8, filepath, "")) {
|
||||
_ = try stdout.write("No file specified");
|
||||
_ = try bw.flush();
|
||||
_ = try stdout.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
var file = try csv.loadFile(filepath);
|
||||
var file = try csv.loadFile(filepath, alloc);
|
||||
defer file.deinit();
|
||||
|
||||
const valid = file.isValid();
|
||||
if (!valid) return;
|
||||
|
||||
_ = try bw.flush();
|
||||
_ = try stdout.flush();
|
||||
|
||||
try csv.printTable(file);
|
||||
_ = try bw.flush();
|
||||
_ = try stdout.flush();
|
||||
}
|
||||
|
||||
19
src/term.zig
19
src/term.zig
@@ -9,15 +9,18 @@ pub fn getTerminalDimensions() !Dimensions {
|
||||
const os_tag = builtin.os.tag;
|
||||
|
||||
switch (os_tag) {
|
||||
.linux, .macos, .freebsd, .openbsd, .netbsd => {
|
||||
.linux, .freebsd, .openbsd, .netbsd => {
|
||||
return try getUnixTerminalDimensions();
|
||||
},
|
||||
.windows => {
|
||||
return try getWindowsTerminalDimensions();
|
||||
},
|
||||
.macos => {
|
||||
return try getMacOsTerminalDimensions();
|
||||
},
|
||||
else => {
|
||||
return Error.UnsupportedOs;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +35,18 @@ fn getUnixTerminalDimensions() !Dimensions {
|
||||
return Dimensions{ .width = size.ws_col, .height = size.ws_row };
|
||||
}
|
||||
|
||||
pub fn getMacOsTerminalDimensions() !Dimensions {
|
||||
var ws: std.posix.winsize = undefined;
|
||||
const fd = std.io.getStdOut().handle;
|
||||
|
||||
_ = std.c.ioctl(fd, std.posix.T.IOCGWINSZ, @intFromPtr(&ws));
|
||||
|
||||
return Dimensions{
|
||||
.width = ws.col,
|
||||
.height = ws.row,
|
||||
};
|
||||
}
|
||||
|
||||
fn getWindowsTerminalDimensions() !Dimensions {
|
||||
const win = std.os.windows;
|
||||
const handle = win.GetStdHandle(win.STD_OUTPUT_HANDLE) catch return Error.ErrorFetchingDimensions;
|
||||
|
||||
Reference in New Issue
Block a user