3 Commits

Author SHA1 Message Date
b3ebf7cadb fix: Fixed issue with buffer size. 2026-06-09 10:54:34 -04:00
6acd1f9d53 refactor: Moved deps into root.zig. 2026-06-09 09:50:38 -04:00
681931fb3b feat: Added table viewer. 2026-06-09 09:41:13 -04:00
5 changed files with 97 additions and 90 deletions

2
.envrc
View File

@@ -1,4 +1,4 @@
use flake use flake
ROOT="/home/spencer/Desktop/envr" ROOT="."
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, .{}, .{});
} }
const EnvFile = struct { pub 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 deps( return envr.deps(
io, io,
stdout_writer, stdout_writer,
environ_map.get("PATH").?, environ_map.get("PATH").?,
@@ -75,63 +75,6 @@ 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,6 +63,63 @@ 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,
@@ -72,10 +129,13 @@ 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" });
const cfg: Config = (try Config.load(io, arena, cfgPath)).value; defer arena.free(cfgPath);
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, .config = cfg.value,
.home = home, .home = home,
.tmp = tmp, .tmp = tmp,
}); });
@@ -83,15 +143,14 @@ 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(@TypeOf(files[0])); const table: tabula.Table(Db.EnvFile, .initOne(.path)) = .{ .items = files };
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
// TODO: Is this bad? for (files) |*file| {
for (files) |file| { file.deinit(arena);
@constCast(&file).deinit(arena);
} }
} }
@@ -199,12 +258,15 @@ 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 │
\\└────────────────────────┘ \\└────────────────────────┘
\\ \\
, try out.toOwnedSlice()); , got);
} }

View File

@@ -27,38 +27,37 @@ pub fn Table(
// Print body // Print body
for (self.items) |item| { for (self.items) |item| {
_ = try writer.write(sep); try writer.writeAll(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.write(" "); try writer.writeByte(' ');
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.write("\n"); try writer.writeAll("\n");
} }
// Print post-body // Print post-body
{ {
_ = try writer.write(bl); try writer.writeAll(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.write(bm); try writer.writeAll(bm);
} }
const padding = max_column_widths[i] + 2; const padding = max_column_widths[i] + 2;
for (0..padding) |_| { for (0..padding) |_| {
_ = try writer.write(hor); try writer.writeAll(hor);
} }
} }
_ = try writer.write(br); try writer.writeAll(br ++ "\n");
_ = try writer.write("\n");
} }
} }
}; };
@@ -96,29 +95,29 @@ fn header(
// Print Pre-Header // Print Pre-Header
{ {
_ = try writer.write(tl); try writer.writeAll(tl);
inline for (0..comptime fields.count()) |i| { inline for (0..comptime fields.count()) |i| {
if (i > 0) { if (i > 0) {
_ = try writer.write(tm); try writer.writeAll(tm);
} }
const padding = max_column_widths[i] + 2; const padding = max_column_widths[i] + 2;
for (0..padding) |_| { for (0..padding) |_| {
_ = try writer.write(hor); try writer.writeAll(hor);
} }
} }
_ = try writer.write(tr ++ "\n"); try writer.writeAll(tr ++ "\n");
} }
// Main Header // Main Header
{ {
_ = try writer.write(sep); try writer.writeAll(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.write(" "); try writer.writeByte(' ');
try write_aligned( try write_aligned(
writer, writer,
@tagName(field), @tagName(field),
@@ -128,24 +127,24 @@ fn header(
try writer.print(" {s}", .{sep}); try writer.print(" {s}", .{sep});
} }
try writer.print("\n", .{}); try writer.writeByte('\n');
} }
// Print post-header // Print post-header
{ {
_ = try writer.write(ml); try writer.writeAll(ml);
inline for (0..comptime fields.count()) |i| { inline for (0..comptime fields.count()) |i| {
if (i > 0) { if (i > 0) {
_ = try writer.write(mm); try writer.writeAll(mm);
} }
const padding = max_column_widths[i] + 2; const padding = max_column_widths[i] + 2;
for (0..padding) |_| { for (0..padding) |_| {
_ = try writer.write(hor); try writer.writeAll(hor);
} }
} }
_ = try writer.write(mr ++ "\n"); try writer.writeAll(mr ++ "\n");
} }
} }
@@ -155,6 +154,9 @@ 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 },
@@ -166,13 +168,13 @@ fn write_aligned(
}; };
for (0..padding[0]) |_| { for (0..padding[0]) |_| {
_ = try writer.write(" "); try writer.writeByte(' ');
} }
_ = try writer.write(data); try writer.writeAll(data);
for (0..padding[1]) |_| { for (0..padding[1]) |_| {
_ = try writer.write(" "); try writer.writeByte(' ');
} }
} }