mirror of
https://github.com/sbrow/envr.git
synced 2026-06-27 10:38:33 -04:00
feat: Added table viewer.
This commit is contained in:
3
.envrc
3
.envrc
@@ -1,3 +1,4 @@
|
||||
use flake
|
||||
|
||||
export PATH=".:/home/spencer/github.com/envr-zig/deps/zig:/home/spencer/github.com/envr-zig/deps/zls:$PATH"
|
||||
ROOT="/home/spencer/Desktop/envr"
|
||||
export PATH=".:${ROOT}/deps/zig:${ROOT}/deps/zls:$PATH"
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
# dev env
|
||||
.direnv
|
||||
/.env
|
||||
|
||||
# dependencies
|
||||
deps
|
||||
|
||||
20
src/Db.zig
20
src/Db.zig
@@ -46,11 +46,27 @@ pub fn open(
|
||||
defer private_keys.deinit(gpa);
|
||||
|
||||
for (opts.config.keys) |key| {
|
||||
private_keys.appendAssumeCapacity(key.private);
|
||||
// FIXME: cheating here
|
||||
if (std.mem.startsWith(u8, key.private, "~/")) {
|
||||
const key_path = try std.fs.path.join(gpa, &.{
|
||||
opts.home,
|
||||
key.private[2..],
|
||||
});
|
||||
private_keys.appendAssumeCapacity(key_path);
|
||||
// defer gpa.free(key_path);
|
||||
} else {
|
||||
private_keys.appendAssumeCapacity(key.private);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Pass key(s) from Config
|
||||
try age.decrypt(io, gpa, private_keys.items, db_path, tmp_db_path);
|
||||
|
||||
for (opts.config.keys, 0..) |key, i| {
|
||||
if (std.mem.startsWith(u8, key.private, "~/")) {
|
||||
gpa.free(private_keys.items[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +189,7 @@ const EnvFile = struct {
|
||||
sha256: []const u8,
|
||||
contents: []const u8,
|
||||
|
||||
fn deinit(self: *EnvFile, alloc: std.mem.Allocator) void {
|
||||
pub fn deinit(self: *EnvFile, alloc: std.mem.Allocator) void {
|
||||
alloc.free(self.path);
|
||||
alloc.free(self.remotes);
|
||||
alloc.free(self.sha256);
|
||||
|
||||
99
src/root.zig
99
src/root.zig
@@ -2,11 +2,11 @@
|
||||
const std = @import("std");
|
||||
const Io = std.Io;
|
||||
|
||||
const comma = @import("comma");
|
||||
const Command = comma.Command;
|
||||
const Command = @import("comma").Command;
|
||||
|
||||
const Config = @import("Config.zig");
|
||||
const Db = @import("Db.zig");
|
||||
const tabula = @import("./tabula.zig");
|
||||
|
||||
pub const root: Command = .new(.{
|
||||
.name = "envr",
|
||||
@@ -80,15 +80,23 @@ pub fn list(
|
||||
.tmp = tmp,
|
||||
});
|
||||
|
||||
_ = try out.write("Path\n");
|
||||
const files = try db.list(arena);
|
||||
for (files) |file| {
|
||||
// TODO: Table printer
|
||||
try out.print("{s}\n", .{file.path});
|
||||
}
|
||||
defer arena.free(files);
|
||||
|
||||
try tabula.structs(
|
||||
@TypeOf(files[0]),
|
||||
files,
|
||||
.{ .out = out, .fields = .initOne(.path) },
|
||||
// .{ .out = out },
|
||||
);
|
||||
try out.flush();
|
||||
|
||||
return db.close(io, arena); // TODO: Defer this
|
||||
try db.close(io, arena); // TODO: Defer this
|
||||
|
||||
// TODO: Is this bad?
|
||||
for (files) |file| {
|
||||
@constCast(&file).deinit(arena);
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
@@ -129,3 +137,78 @@ test "parse unknown" {
|
||||
|
||||
try std.testing.expectEqual(.unknown, cmd);
|
||||
}
|
||||
|
||||
test "list returns a table" {
|
||||
const io = std.testing.io;
|
||||
const gpa = std.testing.allocator;
|
||||
|
||||
var tmp_dir = std.testing.tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
|
||||
try tmp_dir.dir.createDir(io, "home", .default_dir);
|
||||
try tmp_dir.dir.createDir(io, "home/.envr", .default_dir);
|
||||
try tmp_dir.dir.createDir(io, "home/.ssh", .default_dir);
|
||||
try tmp_dir.dir.createDir(io, "tmp", .default_dir);
|
||||
|
||||
const tmp_dir_path = try tmp_dir.dir.realPathFileAlloc(io, ".", gpa);
|
||||
defer gpa.free(tmp_dir_path);
|
||||
|
||||
const home = try std.fs.path.join(gpa, &.{ tmp_dir_path, "home" });
|
||||
defer gpa.free(home);
|
||||
const tmp = try std.fs.path.join(gpa, &.{ tmp_dir_path, "tmp" });
|
||||
defer gpa.free(tmp);
|
||||
|
||||
try std.Io.Dir.cwd().copyFile(
|
||||
"fixtures/encrypted-single-file.db.age",
|
||||
tmp_dir.dir,
|
||||
"home/.envr/data.age",
|
||||
io,
|
||||
.{},
|
||||
);
|
||||
|
||||
try std.Io.Dir.cwd().copyFile(
|
||||
"fixtures/default_config.json",
|
||||
tmp_dir.dir,
|
||||
"home/.envr/config.json",
|
||||
io,
|
||||
.{},
|
||||
);
|
||||
|
||||
try std.Io.Dir.cwd().copyFile(
|
||||
"fixtures/insecure-test-key",
|
||||
tmp_dir.dir,
|
||||
"home/.ssh/id_ed25519",
|
||||
io,
|
||||
.{},
|
||||
);
|
||||
|
||||
try std.Io.Dir.cwd().copyFile(
|
||||
"fixtures/insecure-test-key.pub",
|
||||
tmp_dir.dir,
|
||||
"home/.ssh/id_ed25519.pub",
|
||||
io,
|
||||
.{},
|
||||
);
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
// Run Test
|
||||
|
||||
try list(
|
||||
io,
|
||||
std.testing.allocator,
|
||||
&out.writer,
|
||||
home,
|
||||
tmp,
|
||||
);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌────────────────────────┐
|
||||
\\│ path │
|
||||
\\├────────────────────────┤
|
||||
\\│ ~/project/.env.example │
|
||||
\\└────────────────────────┘
|
||||
\\
|
||||
, try out.toOwnedSlice());
|
||||
}
|
||||
|
||||
314
src/tabula.orig.zig
Normal file
314
src/tabula.orig.zig
Normal file
@@ -0,0 +1,314 @@
|
||||
const std = @import("std");
|
||||
|
||||
const hor = "─";
|
||||
const tl = "┌";
|
||||
const tm = "┬";
|
||||
const tr = "┐";
|
||||
const sep = "│";
|
||||
const ml = "├";
|
||||
const mm = "┼";
|
||||
const mr = "┤";
|
||||
const bl = "└";
|
||||
const bm = "┴";
|
||||
const br = "┘";
|
||||
|
||||
// Print a list of structs as a table to opts.out.
|
||||
pub fn structs(
|
||||
comptime T: type,
|
||||
items: []T,
|
||||
opts: struct {
|
||||
out: *std.Io.Writer, // TODO: Default stdout.
|
||||
fields: std.EnumSet(std.meta.FieldEnum(T)) = .full,
|
||||
},
|
||||
) !void {
|
||||
const writer = opts.out;
|
||||
const max_column_widths = determine_col_widths(T, items);
|
||||
|
||||
try header(T, opts.fields, &max_column_widths, opts.out);
|
||||
|
||||
// Print body
|
||||
for (items) |item| {
|
||||
_ = try writer.write(sep);
|
||||
|
||||
// TODO: Not the most efficient
|
||||
const all_fields = @typeInfo(T).@"struct".fields;
|
||||
inline for (all_fields) |field| {
|
||||
var itr = opts.fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |c| : (i += 1) {
|
||||
if (std.mem.eql(u8, @tagName(c), field.name)) {
|
||||
_ = try writer.write(" ");
|
||||
try write_aligned(writer, @field(item, field.name), max_column_widths[i], .left);
|
||||
try writer.print(" {s}", .{sep});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = try writer.write("\n");
|
||||
}
|
||||
|
||||
// Print post-body
|
||||
{
|
||||
_ = try writer.write(bl);
|
||||
|
||||
var itr = opts.fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |_| : (i += 1) {
|
||||
if (i > 0) {
|
||||
_ = try writer.write(bm);
|
||||
}
|
||||
|
||||
const padding = max_column_widths[i] + 2;
|
||||
for (0..padding) |_| {
|
||||
_ = try writer.write(hor);
|
||||
}
|
||||
}
|
||||
|
||||
_ = try writer.write(br);
|
||||
_ = try writer.write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_col_widths(
|
||||
T: type,
|
||||
items: []T,
|
||||
) [@typeInfo(T).@"struct".fields.len]usize {
|
||||
const all_fields = @typeInfo(T).@"struct".fields;
|
||||
|
||||
var max_column_widths: [all_fields.len]usize = @splat(0);
|
||||
for (items) |item| {
|
||||
inline for (all_fields, 0..) |field, i| {
|
||||
// TODO: Get str len of item
|
||||
const value_len = @field(item, field.name).len;
|
||||
max_column_widths[i] = @max(
|
||||
max_column_widths[i],
|
||||
field.name.len,
|
||||
value_len,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return max_column_widths;
|
||||
}
|
||||
|
||||
// Print the header of a table
|
||||
fn header(
|
||||
T: type,
|
||||
fields: std.EnumSet(std.meta.FieldEnum(T)),
|
||||
max_column_widths: []const usize,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
|
||||
// Print Pre-Header
|
||||
{
|
||||
_ = try writer.write(tl);
|
||||
|
||||
var itr = fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |_| : (i += 1) {
|
||||
if (i > 0) {
|
||||
_ = try writer.write(tm);
|
||||
}
|
||||
const padding = max_column_widths[i] + 2;
|
||||
for (0..padding) |_| {
|
||||
_ = try writer.write(hor);
|
||||
}
|
||||
}
|
||||
|
||||
_ = try writer.write(tr ++ "\n");
|
||||
}
|
||||
|
||||
// Main Header
|
||||
{
|
||||
_ = try writer.write(sep);
|
||||
|
||||
var itr = fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |field| : (i += 1) {
|
||||
_ = try writer.write(" ");
|
||||
try write_aligned(
|
||||
writer,
|
||||
@tagName(field),
|
||||
max_column_widths[i],
|
||||
.center,
|
||||
);
|
||||
try writer.print(" {s}", .{sep});
|
||||
}
|
||||
|
||||
try writer.print("\n", .{});
|
||||
}
|
||||
|
||||
// Print post-header
|
||||
{
|
||||
_ = try writer.write(ml);
|
||||
|
||||
var itr = fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |_| : (i += 1) {
|
||||
if (i > 0) {
|
||||
_ = try writer.write(mm);
|
||||
}
|
||||
const padding = max_column_widths[i] + 2;
|
||||
for (0..padding) |_| {
|
||||
_ = try writer.write(hor);
|
||||
}
|
||||
}
|
||||
|
||||
_ = try writer.write(mr ++ "\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn write_aligned(
|
||||
writer: *std.Io.Writer,
|
||||
data: []const u8,
|
||||
max_width: usize,
|
||||
alignment: Alignment,
|
||||
) !void {
|
||||
const padding: [2]usize = switch (alignment) {
|
||||
.left => .{ 0, max_width - data.len },
|
||||
.right => .{ max_width - data.len, 0 },
|
||||
.center => blk: {
|
||||
// Faster to inline the divFloor?
|
||||
const half = @divFloor(max_width - data.len, 2);
|
||||
break :blk .{ half, max_width - data.len - half };
|
||||
},
|
||||
};
|
||||
|
||||
for (0..padding[0]) |_| {
|
||||
_ = try writer.write(" ");
|
||||
}
|
||||
|
||||
_ = try writer.write(data);
|
||||
|
||||
for (0..padding[1]) |_| {
|
||||
_ = try writer.write(" ");
|
||||
}
|
||||
}
|
||||
|
||||
const Alignment = enum { left, center, right };
|
||||
|
||||
test "can print a simple table" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, bar: []const u8 };
|
||||
// const table: Table(F) = .{ .rows = &.{.{ .foo = "bat", .bar = "baz" }} };
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
const rows: [1]F = .{.{ .foo = "bat", .bar = "baz" }};
|
||||
try structs(F, @constCast(&rows), .{ .out = &out.writer });
|
||||
|
||||
// try out.writer.print("{f}", .{table});
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┬─────┐
|
||||
\\│ foo │ bar │
|
||||
\\├─────┼─────┤
|
||||
\\│ bat │ baz │
|
||||
\\└─────┴─────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
test "can print a table with varying header widths" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, abart: []const u8 };
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
const rows: [1]F = .{.{ .foo = "bat", .abart = "baz" }};
|
||||
try structs(F, @constCast(&rows), .{ .out = &out.writer });
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┬───────┐
|
||||
\\│ foo │ abart │
|
||||
\\├─────┼───────┤
|
||||
\\│ bat │ baz │
|
||||
\\└─────┴───────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
test "can print a table with varying column widths" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, bar: []const u8 };
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
const rows: [1]F = .{.{ .foo = "bat", .bar = "bazzar" }};
|
||||
try structs(F, @constCast(&rows), .{ .out = &out.writer });
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┬────────┐
|
||||
\\│ foo │ bar │
|
||||
\\├─────┼────────┤
|
||||
\\│ bat │ bazzar │
|
||||
\\└─────┴────────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
test "can print a multi row table with varying column widths" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, bar: []const u8 };
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
const rows: []const F = &.{
|
||||
.{ .foo = "baz", .bar = "quz" },
|
||||
.{ .foo = "bat", .bar = "bazzar" },
|
||||
};
|
||||
try structs(F, @constCast(rows), .{ .out = &out.writer });
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┬────────┐
|
||||
\\│ foo │ bar │
|
||||
\\├─────┼────────┤
|
||||
\\│ baz │ quz │
|
||||
\\│ bat │ bazzar │
|
||||
\\└─────┴────────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
test "can print a table with limited columns" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, bar: []const u8 };
|
||||
const rows: [1]F = .{.{ .foo = "bat", .bar = "baz" }};
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
try structs(F, @constCast(&rows), .{ .out = &out.writer, .fields = .initOne(.foo) });
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┐
|
||||
\\│ foo │
|
||||
\\├─────┤
|
||||
\\│ bat │
|
||||
\\└─────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
// TODO: test "returns a table" {}
|
||||
331
src/tabula.zig
Normal file
331
src/tabula.zig
Normal file
@@ -0,0 +1,331 @@
|
||||
const std = @import("std");
|
||||
|
||||
const hor = "─";
|
||||
const tl = "┌";
|
||||
const tm = "┬";
|
||||
const tr = "┐";
|
||||
const sep = "│";
|
||||
const ml = "├";
|
||||
const mm = "┼";
|
||||
const mr = "┤";
|
||||
const bl = "└";
|
||||
const bm = "┴";
|
||||
const br = "┘";
|
||||
|
||||
// Print a list of structs as a table to opts.out.
|
||||
pub fn structs(
|
||||
comptime T: type,
|
||||
items: []T,
|
||||
opts: struct {
|
||||
out: *std.Io.Writer, // TODO: Default stdout.
|
||||
fields: std.EnumSet(std.meta.FieldEnum(T)) = .full,
|
||||
},
|
||||
) error{WriteFailed}!void {
|
||||
const writer = opts.out;
|
||||
const max_column_widths = determine_col_widths(T, items);
|
||||
var buf: [255]u8 = undefined;
|
||||
|
||||
try header(T, opts.fields, &max_column_widths, opts.out);
|
||||
|
||||
// Print body
|
||||
for (items) |item| {
|
||||
_ = try writer.write(sep);
|
||||
|
||||
// TODO: Not the most efficient
|
||||
const all_fields = @typeInfo(T).@"struct".fields;
|
||||
inline for (all_fields) |field| {
|
||||
var itr = opts.fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |c| : (i += 1) {
|
||||
if (std.mem.eql(u8, @tagName(c), field.name)) {
|
||||
const col = buf[0..max_column_widths[i]];
|
||||
@memset(col, ' ');
|
||||
|
||||
align_text(@field(item, field.name), col, .left);
|
||||
|
||||
try writer.print(" {s} {s}", .{ col, sep });
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = try writer.write("\n");
|
||||
}
|
||||
|
||||
// Print post-body
|
||||
{
|
||||
_ = try writer.write(bl);
|
||||
|
||||
var itr = opts.fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |_| : (i += 1) {
|
||||
if (i > 0) {
|
||||
_ = try writer.write(bm);
|
||||
}
|
||||
|
||||
const len = hor.len;
|
||||
const padding = buf[0 .. (max_column_widths[i] + 2) * len];
|
||||
|
||||
for (0..max_column_widths[i] + 2) |j| {
|
||||
@memcpy(padding[len * j .. len * j + len], hor);
|
||||
}
|
||||
|
||||
_ = try writer.write(padding);
|
||||
}
|
||||
|
||||
_ = try writer.write(br ++ "\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_col_widths(
|
||||
T: type,
|
||||
items: []T,
|
||||
) [@typeInfo(T).@"struct".fields.len]usize {
|
||||
const all_fields = @typeInfo(T).@"struct".fields;
|
||||
|
||||
var max_column_widths: [all_fields.len]usize = @splat(0);
|
||||
for (items) |item| {
|
||||
inline for (all_fields, 0..) |field, i| {
|
||||
// TODO: Get str len of item
|
||||
const value_len = @field(item, field.name).len;
|
||||
max_column_widths[i] = @max(
|
||||
max_column_widths[i],
|
||||
field.name.len,
|
||||
value_len,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return max_column_widths;
|
||||
}
|
||||
|
||||
// Print the header of a table
|
||||
fn header(
|
||||
T: type,
|
||||
fields: std.EnumSet(std.meta.FieldEnum(T)),
|
||||
max_column_widths: []const usize,
|
||||
writer: *std.Io.Writer,
|
||||
) error{WriteFailed}!void {
|
||||
|
||||
// Print Pre-Header
|
||||
{
|
||||
_ = try writer.write(tl);
|
||||
|
||||
var itr = fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |_| : (i += 1) {
|
||||
if (i > 0) {
|
||||
_ = try writer.write(tm);
|
||||
}
|
||||
const padding = max_column_widths[i] + 2;
|
||||
for (0..padding) |_| {
|
||||
_ = try writer.write(hor);
|
||||
}
|
||||
}
|
||||
|
||||
_ = try writer.write(tr ++ "\n");
|
||||
}
|
||||
|
||||
// Main Header
|
||||
{
|
||||
var buf: [255]u8 = undefined;
|
||||
|
||||
_ = try writer.write(sep);
|
||||
|
||||
var itr = fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |field| : (i += 1) {
|
||||
const col = buf[0..max_column_widths[i]];
|
||||
@memset(col, ' ');
|
||||
align_text(@tagName(field), col, .center);
|
||||
|
||||
try writer.print(" {s} {s}", .{ col, sep });
|
||||
}
|
||||
|
||||
try writer.print("\n", .{});
|
||||
}
|
||||
|
||||
// Print post-header
|
||||
{
|
||||
_ = try writer.write(ml);
|
||||
|
||||
var itr = fields.iterator();
|
||||
var i: usize = 0;
|
||||
while (itr.next()) |_| : (i += 1) {
|
||||
if (i > 0) {
|
||||
_ = try writer.write(mm);
|
||||
}
|
||||
const padding = max_column_widths[i] + 2;
|
||||
for (0..padding) |_| {
|
||||
_ = try writer.write(hor);
|
||||
}
|
||||
}
|
||||
|
||||
_ = try writer.write(mr ++ "\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn align_text(data: []const u8, buf: []u8, alignment: Alignment) void {
|
||||
const dest = switch (alignment) {
|
||||
.left => buf[0..data.len],
|
||||
.right => buf[buf.len - data.len .. buf.len],
|
||||
.center => blk: {
|
||||
const start = (buf.len - data.len) / 2;
|
||||
|
||||
break :blk buf[start .. start + data.len];
|
||||
},
|
||||
};
|
||||
|
||||
@memcpy(dest, data);
|
||||
}
|
||||
|
||||
const Alignment = enum { left, center, right };
|
||||
|
||||
test "align text left" {
|
||||
var buf: [12]u8 = @splat(' ');
|
||||
|
||||
align_text("foo", &buf, .left);
|
||||
|
||||
try std.testing.expectEqualStrings("foo ", &buf);
|
||||
}
|
||||
|
||||
test "align text right" {
|
||||
var buf: [12]u8 = @splat(' ');
|
||||
|
||||
align_text("foo", &buf, .right);
|
||||
|
||||
try std.testing.expectEqualStrings(" foo", &buf);
|
||||
}
|
||||
|
||||
test "align text center" {
|
||||
var buf: [12]u8 = @splat(' ');
|
||||
|
||||
align_text("foo", &buf, .center);
|
||||
|
||||
try std.testing.expectEqualStrings(" foo ", &buf);
|
||||
}
|
||||
|
||||
test "can print a simple table" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, bar: []const u8 };
|
||||
// const table: Table(F) = .{ .rows = &.{.{ .foo = "bat", .bar = "baz" }} };
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
const rows: [1]F = .{.{ .foo = "bat", .bar = "baz" }};
|
||||
try structs(F, @constCast(&rows), .{ .out = &out.writer });
|
||||
|
||||
// try out.writer.print("{f}", .{table});
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┬─────┐
|
||||
\\│ foo │ bar │
|
||||
\\├─────┼─────┤
|
||||
\\│ bat │ baz │
|
||||
\\└─────┴─────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
test "can print a table with varying header widths" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, abart: []const u8 };
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
const rows: [1]F = .{.{ .foo = "bat", .abart = "baz" }};
|
||||
try structs(F, @constCast(&rows), .{ .out = &out.writer });
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┬───────┐
|
||||
\\│ foo │ abart │
|
||||
\\├─────┼───────┤
|
||||
\\│ bat │ baz │
|
||||
\\└─────┴───────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
test "can print a table with varying column widths" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, bar: []const u8 };
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
const rows: [1]F = .{.{ .foo = "bat", .bar = "bazzar" }};
|
||||
try structs(F, @constCast(&rows), .{ .out = &out.writer });
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┬────────┐
|
||||
\\│ foo │ bar │
|
||||
\\├─────┼────────┤
|
||||
\\│ bat │ bazzar │
|
||||
\\└─────┴────────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
test "can print a multi row table with varying column widths" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, bar: []const u8 };
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
const rows: []const F = &.{
|
||||
.{ .foo = "baz", .bar = "quz" },
|
||||
.{ .foo = "bat", .bar = "bazzar" },
|
||||
};
|
||||
try structs(F, @constCast(rows), .{ .out = &out.writer });
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┬────────┐
|
||||
\\│ foo │ bar │
|
||||
\\├─────┼────────┤
|
||||
\\│ baz │ quz │
|
||||
\\│ bat │ bazzar │
|
||||
\\└─────┴────────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
test "can print a table with limited columns" {
|
||||
const gpa = std.testing.allocator;
|
||||
const F = struct { foo: []const u8, bar: []const u8 };
|
||||
const rows: [1]F = .{.{ .foo = "bat", .bar = "baz" }};
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
try structs(F, @constCast(&rows), .{ .out = &out.writer, .fields = .initOne(.foo) });
|
||||
|
||||
const got = try out.toOwnedSlice();
|
||||
defer gpa.free(got);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
\\┌─────┐
|
||||
\\│ foo │
|
||||
\\├─────┤
|
||||
\\│ bat │
|
||||
\\└─────┘
|
||||
\\
|
||||
, got);
|
||||
}
|
||||
|
||||
// TODO: test "returns a table" {}
|
||||
Reference in New Issue
Block a user