mirror of
https://github.com/sbrow/envr.git
synced 2026-06-27 18:48:33 -04:00
Compare commits
1 Commits
a547409efd
...
d4653f3566
| Author | SHA1 | Date | |
|---|---|---|---|
| d4653f3566 |
14
README.md
14
README.md
@@ -3,10 +3,6 @@
|
|||||||
Have you ever wanted to back up all your .env files in case your hard drive gets
|
Have you ever wanted to back up all your .env files in case your hard drive gets
|
||||||
nuked? `envr` makes it easier.
|
nuked? `envr` makes it easier.
|
||||||
|
|
||||||
> [!CAUTION]
|
|
||||||
> The Zig community is quite anti-AI. Please read the [AI Disclaimer](#ai-disclaimer)
|
|
||||||
> before wasting your time.
|
|
||||||
|
|
||||||
`envr` is a binary application that tracks your `.env` files
|
`envr` is a binary application that tracks your `.env` files
|
||||||
in an encyrpted sqlite database. Changes can be effortlessly synced with
|
in an encyrpted sqlite database. Changes can be effortlessly synced with
|
||||||
`envr sync`, and restored with `envr restore`.
|
`envr sync`, and restored with `envr restore`.
|
||||||
@@ -136,13 +132,3 @@ This project is licensed under the [MIT License](./LICENSE).
|
|||||||
|
|
||||||
For issues, feature requests, or questions, please
|
For issues, feature requests, or questions, please
|
||||||
[open an issue](https://github.com/sbrow/envr/issues).
|
[open an issue](https://github.com/sbrow/envr/issues).
|
||||||
|
|
||||||
## AI Disclaimer
|
|
||||||
|
|
||||||
Unless noted here, you can be assured that I have personally written and reviewed
|
|
||||||
every line of code in this software.
|
|
||||||
|
|
||||||
- Many compiler errors that couldn't be solved with a quick google search were
|
|
||||||
solved by passing errors to AI and transcribing the suggestions.
|
|
||||||
- The "Pre-Zig" version of this readme was written by AI and then edited by me.
|
|
||||||
- The Go code was mostly written using opencode, and manually tested by me.
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ pub const Command = struct {
|
|||||||
name: []const u8,
|
name: []const u8,
|
||||||
short: ?[]const u8 = null,
|
short: ?[]const u8 = null,
|
||||||
long: ?[]const u8 = null,
|
long: ?[]const u8 = null,
|
||||||
subcommands: []const Command = &.{},
|
subcommands: []const Command = &[0]Command{},
|
||||||
examples: [][]const u8 = &.{},
|
|
||||||
Type: type,
|
Type: type,
|
||||||
|
|
||||||
pub fn new(cmd: CommandOptions) Command {
|
pub fn new(cmd: CommandOptions) Command {
|
||||||
@@ -28,11 +27,10 @@ pub const Command = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(comptime self: @This(), args: []const []const u8) self.Type {
|
pub fn parse(comptime self: @This(), args: []const []const u8) ParseError!self.Type {
|
||||||
if (args.len == 0) {
|
if (args.len == 0) {
|
||||||
return @enumFromInt(0);
|
return ParseError.InvalidType;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = args[0];
|
const target = args[0];
|
||||||
|
|
||||||
inline for (self.subcommands, 1..) |cmd, idx| {
|
inline for (self.subcommands, 1..) |cmd, idx| {
|
||||||
@@ -41,7 +39,7 @@ pub const Command = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return @enumFromInt(self.subcommands.len + 1);
|
return @enumFromInt(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -56,8 +54,8 @@ const CommandOptions = struct {
|
|||||||
subcommands: []const CommandOptions = &[0]CommandOptions{},
|
subcommands: []const CommandOptions = &[0]CommandOptions{},
|
||||||
|
|
||||||
fn as_enum(self: @This()) type {
|
fn as_enum(self: @This()) type {
|
||||||
var field_names: [self.subcommands.len + 2][]const u8 = undefined;
|
var field_names: [self.subcommands.len + 1][]const u8 = undefined;
|
||||||
var field_values: [self.subcommands.len + 2]u32 = undefined;
|
var field_values: [self.subcommands.len + 1]u32 = undefined;
|
||||||
|
|
||||||
field_names[0] = self.name;
|
field_names[0] = self.name;
|
||||||
field_values[0] = 0;
|
field_values[0] = 0;
|
||||||
@@ -67,9 +65,6 @@ const CommandOptions = struct {
|
|||||||
field_values[idx] = idx;
|
field_values[idx] = idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
field_names[self.subcommands.len + 1] = "unknown";
|
|
||||||
field_values[self.subcommands.len + 1] = self.subcommands.len + 1;
|
|
||||||
|
|
||||||
return @Enum(
|
return @Enum(
|
||||||
u32,
|
u32,
|
||||||
.exhaustive,
|
.exhaustive,
|
||||||
|
|||||||
106
src/main.zig
106
src/main.zig
@@ -14,20 +14,20 @@ pub fn main(init: std.process.Init) !void {
|
|||||||
|
|
||||||
const args = try init.minimal.args.toSlice(arena);
|
const args = try init.minimal.args.toSlice(arena);
|
||||||
|
|
||||||
try run(init.environ_map, init.io, arena, args);
|
try run(init.io, init.environ_map, args); //catch return fallback_to_go(init.io, arena, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to run the requested command.
|
/// Attempt to run the requested command.
|
||||||
fn run(
|
fn run(
|
||||||
environ_map: *std.process.Environ.Map,
|
|
||||||
io: Io,
|
io: Io,
|
||||||
arena: std.mem.Allocator,
|
environ_map: *std.process.Environ.Map,
|
||||||
args: []const [:0]const u8,
|
args: []const [:0]const u8,
|
||||||
) !void {
|
) !void {
|
||||||
const cmd = envr.root.parse(args[1..]);
|
const cmd = try envr.root.parse(args[1..]);
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
.envr, .unknown => {
|
.envr => {
|
||||||
return fallback_to_go(io, arena, args);
|
// TODO: Print help
|
||||||
|
return comma.ParseError.InvalidType;
|
||||||
},
|
},
|
||||||
.version => {
|
.version => {
|
||||||
var stdout_buffer: [1024]u8 = undefined;
|
var stdout_buffer: [1024]u8 = undefined;
|
||||||
@@ -61,9 +61,64 @@ fn deps(
|
|||||||
writer: *Io.Writer,
|
writer: *Io.Writer,
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
) !void {
|
) !void {
|
||||||
const feats: Features = try .scan(io, path);
|
var feats: Features = .{};
|
||||||
|
|
||||||
|
// try writer.print("path: {s}\n\n", .{path});
|
||||||
|
|
||||||
|
var dirs = std.mem.splitScalar(u8, path, std.fs.path.delimiter);
|
||||||
|
|
||||||
|
// std.debug.print("feats: {b}\n", .{@as(u2, @bitCast(feats))});
|
||||||
|
// std.debug.print("all feats: {b}\n", .{@as(u2, @bitCast(Features.all_features))});
|
||||||
|
|
||||||
|
loop: while (dirs.next()) |dir| {
|
||||||
|
// try writer.print("dir: {s}\n", .{dir});
|
||||||
|
// try writer.flush();
|
||||||
|
|
||||||
|
// FIXME: Need to handle this failure
|
||||||
|
const dirt = try Io.Dir.openDir(Io.Dir.cwd(), io, dir, .{ .follow_symlinks = true, .iterate = true });
|
||||||
|
defer dirt.close(io);
|
||||||
|
|
||||||
|
var dir_paths = dirt.iterate();
|
||||||
|
|
||||||
|
while (try dir_paths.next(io)) |file| {
|
||||||
|
// FIXME: Check if executable
|
||||||
|
// switch (file.kind) {
|
||||||
|
// .file, .sym_link => {
|
||||||
|
if (std.mem.eql(u8, std.fs.path.basename(file.name), "git")) {
|
||||||
|
feats.git = true;
|
||||||
|
// try writer.print("file: {s}\n", .{file.name});
|
||||||
|
// try writer.flush();
|
||||||
|
|
||||||
|
if (feats == Features.all_features) {
|
||||||
|
break :loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, std.fs.path.basename(file.name), "fd")) {
|
||||||
|
feats.fd = true;
|
||||||
|
// try writer.print("file: {s}\n", .{file.name});
|
||||||
|
// try writer.flush();
|
||||||
|
|
||||||
|
if (feats == Features.all_features) {
|
||||||
|
break :loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// },
|
||||||
|
|
||||||
|
// else => {},
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (path_contains(path, "git")) {
|
||||||
|
// feats.git = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (path_contains(path, "fd")) {
|
||||||
|
// feats.fd = true;
|
||||||
|
// }
|
||||||
|
|
||||||
// FIXME: Draw as a table
|
|
||||||
try writer.print("features: {}", .{feats});
|
try writer.print("features: {}", .{feats});
|
||||||
try writer.flush();
|
try writer.flush();
|
||||||
}
|
}
|
||||||
@@ -75,41 +130,6 @@ const Features = packed struct {
|
|||||||
.git = true,
|
.git = true,
|
||||||
.fd = 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(
|
||||||
|
|||||||
19
src/root.zig
19
src/root.zig
@@ -8,21 +8,13 @@ const Command = comma.Command;
|
|||||||
pub const root: Command = .new(.{
|
pub const root: Command = .new(.{
|
||||||
.name = "envr",
|
.name = "envr",
|
||||||
.subcommands = &.{
|
.subcommands = &.{
|
||||||
.{
|
.{ .name = "deps" },
|
||||||
.name = "deps",
|
|
||||||
.short = "Check for missing binaries",
|
|
||||||
.long =
|
|
||||||
\\envr relies on external binaries for certain functionality.
|
|
||||||
\\
|
|
||||||
\\ The deps command reports which binaries are available and which are not."
|
|
||||||
,
|
|
||||||
},
|
|
||||||
.{ .name = "version" },
|
.{ .name = "version" },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
test "enum type" {
|
test "enum type" {
|
||||||
const got: root.Type = @enumFromInt(2);
|
const got: root.Type = @enumFromInt(1);
|
||||||
|
|
||||||
try std.testing.expectEqual(.version, got);
|
try std.testing.expectEqual(.version, got);
|
||||||
}
|
}
|
||||||
@@ -33,10 +25,3 @@ test "parse version" {
|
|||||||
|
|
||||||
try std.testing.expectEqual(.version, cmd);
|
try std.testing.expectEqual(.version, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "parse unknown" {
|
|
||||||
const args = &[_][]const u8{"bad", "value"};
|
|
||||||
const cmd = root.parse(args);
|
|
||||||
|
|
||||||
try std.testing.expectEqual(.unknown, cmd);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user