1 Commits

Author SHA1 Message Date
2043e20b88 feat: Added table viewer. 2026-06-08 17:39:45 -04:00
5 changed files with 90 additions and 97 deletions

2
.envrc
View File

@@ -1,4 +1,4 @@
use flake use flake
ROOT="." ROOT="/home/spencer/Desktop/envr"
export PATH=".:${ROOT}/deps/zig:${ROOT}/deps/zls:$PATH" export PATH=".:${ROOT}/deps/zig:${ROOT}/deps/zls:$PATH"

View File

@@ -177,7 +177,7 @@ pub fn list(self: *@This(), gpa: std.mem.Allocator) ![]EnvFile {
return stmt.all(EnvFile, gpa, .{}, .{}); return stmt.all(EnvFile, gpa, .{}, .{});
} }
pub const EnvFile = struct { const EnvFile = struct {
// TODO: Should use file_name in the struct and derive from the path. // TODO: Should use file_name in the struct and derive from the path.
path: []const u8, path: []const u8,

View File

@@ -44,7 +44,7 @@ fn run(
var stdout_file_writer: Io.File.Writer = .init(.stdout(), io, &stdout_buffer); var stdout_file_writer: Io.File.Writer = .init(.stdout(), io, &stdout_buffer);
const stdout_writer = &stdout_file_writer.interface; const stdout_writer = &stdout_file_writer.interface;
return envr.deps( return deps(
io, io,
stdout_writer, stdout_writer,
environ_map.get("PATH").?, environ_map.get("PATH").?,
@@ -75,6 +75,63 @@ fn version(writer: *Io.Writer) !void {
try writer.flush(); try writer.flush();
} }
// Display dependency statuses
fn deps(
io: Io,
writer: *Io.Writer,
path: []const u8,
) !void {
const feats: Features = try .scan(io, path);
// FIXME: Draw as a table
try writer.print("features: {}", .{feats});
try writer.flush();
}
const Features = packed struct {
git: bool = false,
fd: bool = false,
const all_features: Features = .{
.git = true,
.fd = true,
};
/// Scans your PATH variable for programs.
pub fn scan(io: Io, path: []const u8) !@This() {
var feats: Features = .{};
var dirs = std.mem.splitScalar(u8, path, std.fs.path.delimiter);
loop: while (dirs.next()) |dir| {
const dirt = Io.Dir.openDir(Io.Dir.cwd(), io, dir, .{ .follow_symlinks = true, .iterate = true }) catch continue;
defer dirt.close(io);
var dir_paths = dirt.iterate();
while (try dir_paths.next(io)) |file| {
// FIXME: Check if executable
if (std.mem.eql(u8, std.fs.path.basename(file.name), "git")) {
feats.git = true;
if (feats == Features.all_features) {
break :loop;
}
}
if (std.mem.eql(u8, std.fs.path.basename(file.name), "fd")) {
feats.fd = true;
if (feats == Features.all_features) {
break :loop;
}
}
}
}
return feats;
}
};
fn fallback_to_go( fn fallback_to_go(
io: Io, io: Io,
arena: std.mem.Allocator, arena: std.mem.Allocator,

View File

@@ -63,63 +63,6 @@ pub const root: Command = .new(.{
}, },
}); });
// Display dependency statuses
pub fn deps(
io: Io,
writer: *Io.Writer,
path: []const u8,
) !void {
const feats: Features = try .scan(io, path);
// FIXME: Draw as a table
try writer.print("features: {}", .{feats});
try writer.flush();
}
const Features = packed struct {
git: bool = false,
fd: bool = false,
const all_features: Features = .{
.git = true,
.fd = true,
};
/// Scans your PATH variable for programs.
pub fn scan(io: Io, path: []const u8) !@This() {
var feats: Features = .{};
var dirs = std.mem.splitScalar(u8, path, std.fs.path.delimiter);
loop: while (dirs.next()) |dir| {
const dirt = Io.Dir.openDir(Io.Dir.cwd(), io, dir, .{ .follow_symlinks = true, .iterate = true }) catch continue;
defer dirt.close(io);
var dir_paths = dirt.iterate();
while (try dir_paths.next(io)) |file| {
// FIXME: Check if executable
if (std.mem.eql(u8, std.fs.path.basename(file.name), "git")) {
feats.git = true;
if (feats == Features.all_features) {
break :loop;
}
}
if (std.mem.eql(u8, std.fs.path.basename(file.name), "fd")) {
feats.fd = true;
if (feats == Features.all_features) {
break :loop;
}
}
}
}
return feats;
}
};
pub fn list( pub fn list(
io: Io, io: Io,
arena: std.mem.Allocator, arena: std.mem.Allocator,
@@ -129,13 +72,10 @@ pub fn list(
) !void { ) !void {
// TODO: Don't hardcode // TODO: Don't hardcode
const cfgPath = try std.fs.path.join(arena, &.{ home, ".envr", "config.json" }); const cfgPath = try std.fs.path.join(arena, &.{ home, ".envr", "config.json" });
defer arena.free(cfgPath); const cfg: Config = (try Config.load(io, arena, cfgPath)).value;
var cfg = (try Config.load(io, arena, cfgPath));
defer cfg.deinit();
var db: Db = try .open(io, arena, .{ var db: Db = try .open(io, arena, .{
.config = cfg.value, .config = cfg,
.home = home, .home = home,
.tmp = tmp, .tmp = tmp,
}); });
@@ -143,14 +83,15 @@ pub fn list(
const files = try db.list(arena); const files = try db.list(arena);
defer arena.free(files); defer arena.free(files);
const table: tabula.Table(Db.EnvFile, .initOne(.path)) = .{ .items = files }; const table = tabula.Table(@TypeOf(files[0]));
try out.print("{f}", .{table}); try out.print("{f}", .{table});
try out.flush(); try out.flush();
try db.close(io, arena); // TODO: Defer this try db.close(io, arena); // TODO: Defer this
for (files) |*file| { // TODO: Is this bad?
file.deinit(arena); for (files) |file| {
@constCast(&file).deinit(arena);
} }
} }
@@ -258,15 +199,12 @@ test "list returns a table" {
tmp, tmp,
); );
const got = try out.toOwnedSlice();
defer gpa.free(got);
try std.testing.expectEqualStrings( try std.testing.expectEqualStrings(
\\┌────────────────────────┐ \\┌────────────────────────┐
\\│ path \\│ path
\\├────────────────────────┤ \\├────────────────────────┤
\\│ ~/project/.env.example │ \\│ ~/project/.env.example │
\\└────────────────────────┘ \\└────────────────────────┘
\\ \\
, got); , try out.toOwnedSlice());
} }

View File

@@ -27,37 +27,38 @@ pub fn Table(
// Print body // Print body
for (self.items) |item| { for (self.items) |item| {
try writer.writeAll(sep); _ = try writer.write(sep);
comptime var itr = fields.iterator(); comptime var itr = fields.iterator();
comptime var i: usize = 0; comptime var i: usize = 0;
inline while (comptime itr.next()) |c| : (i += 1) { inline while (comptime itr.next()) |c| : (i += 1) {
try writer.writeByte(' '); _ = try writer.write(" ");
try write_aligned(writer, @field(item, @tagName(c)), max_column_widths[i], .left); try write_aligned(writer, @field(item, @tagName(c)), max_column_widths[i], .left);
try writer.print(" {s}", .{sep}); try writer.print(" {s}", .{sep});
} }
try writer.writeAll("\n"); _ = try writer.write("\n");
} }
// Print post-body // Print post-body
{ {
try writer.writeAll(bl); _ = try writer.write(bl);
var itr = fields.iterator(); var itr = fields.iterator();
var i: usize = 0; var i: usize = 0;
while (itr.next()) |_| : (i += 1) { while (itr.next()) |_| : (i += 1) {
if (i > 0) { if (i > 0) {
try writer.writeAll(bm); _ = try writer.write(bm);
} }
const padding = max_column_widths[i] + 2; const padding = max_column_widths[i] + 2;
for (0..padding) |_| { for (0..padding) |_| {
try writer.writeAll(hor); _ = try writer.write(hor);
} }
} }
try writer.writeAll(br ++ "\n"); _ = try writer.write(br);
_ = try writer.write("\n");
} }
} }
}; };
@@ -95,29 +96,29 @@ fn header(
// Print Pre-Header // Print Pre-Header
{ {
try writer.writeAll(tl); _ = try writer.write(tl);
inline for (0..comptime fields.count()) |i| { inline for (0..comptime fields.count()) |i| {
if (i > 0) { if (i > 0) {
try writer.writeAll(tm); _ = try writer.write(tm);
} }
const padding = max_column_widths[i] + 2; const padding = max_column_widths[i] + 2;
for (0..padding) |_| { for (0..padding) |_| {
try writer.writeAll(hor); _ = try writer.write(hor);
} }
} }
try writer.writeAll(tr ++ "\n"); _ = try writer.write(tr ++ "\n");
} }
// Main Header // Main Header
{ {
try writer.writeAll(sep); _ = try writer.write(sep);
comptime var itr = fields.iterator(); comptime var itr = fields.iterator();
comptime var i: usize = 0; comptime var i: usize = 0;
inline while (comptime itr.next()) |field| : (i += 1) { inline while (comptime itr.next()) |field| : (i += 1) {
try writer.writeByte(' '); _ = try writer.write(" ");
try write_aligned( try write_aligned(
writer, writer,
@tagName(field), @tagName(field),
@@ -127,24 +128,24 @@ fn header(
try writer.print(" {s}", .{sep}); try writer.print(" {s}", .{sep});
} }
try writer.writeByte('\n'); try writer.print("\n", .{});
} }
// Print post-header // Print post-header
{ {
try writer.writeAll(ml); _ = try writer.write(ml);
inline for (0..comptime fields.count()) |i| { inline for (0..comptime fields.count()) |i| {
if (i > 0) { if (i > 0) {
try writer.writeAll(mm); _ = try writer.write(mm);
} }
const padding = max_column_widths[i] + 2; const padding = max_column_widths[i] + 2;
for (0..padding) |_| { for (0..padding) |_| {
try writer.writeAll(hor); _ = try writer.write(hor);
} }
} }
try writer.writeAll(mr ++ "\n"); _ = try writer.write(mr ++ "\n");
} }
} }
@@ -154,9 +155,6 @@ fn write_aligned(
max_width: usize, max_width: usize,
alignment: Alignment, alignment: Alignment,
) !void { ) !void {
std.debug.assert(data.len > 0);
std.debug.assert(max_width >= data.len);
const padding: [2]usize = switch (alignment) { const padding: [2]usize = switch (alignment) {
.left => .{ 0, max_width - data.len }, .left => .{ 0, max_width - data.len },
.right => .{ max_width - data.len, 0 }, .right => .{ max_width - data.len, 0 },
@@ -168,13 +166,13 @@ fn write_aligned(
}; };
for (0..padding[0]) |_| { for (0..padding[0]) |_| {
try writer.writeByte(' '); _ = try writer.write(" ");
} }
try writer.writeAll(data); _ = try writer.write(data);
for (0..padding[1]) |_| { for (0..padding[1]) |_| {
try writer.writeByte(' '); _ = try writer.write(" ");
} }
} }