main: add very basic command line interface

There are three commands: one to write the default config, one to write
the embedded udev rules file, and one to actually run the program.

I might reformat the help text at some point. It's not very nice as-is.
This commit is contained in:
torque 2024-07-07 15:37:53 -07:00
parent c8511d8c92
commit 8fb6032a04
Signed by: torque
SSH Key Fingerprint: SHA256:nCrXefBNo6EbjNSQhv0nXmEg/VuNq3sMF5b8zETw3Tk

View File

@ -8,21 +8,69 @@ const udev = @import("udev_rules");
const log = std.log.scoped(.main);
fn printStderr(comptime fmt: []const u8, args: anytype) void {
std.debug.print(fmt ++ "\n", args);
}
const commands = .{
.install_udev = "install-udev-rules",
.write_config = "write-default-config",
.run = "run",
};
const command_help = .{
.install_udev = "[udev rules.d path]: Install the built-in LabJack u12 udev rules file. May require sudo privileges. Linux only.",
.write_config = "[path]: write the default configuration to a json file.",
.run = "[config path]: run the rotctl interface with the provided config.",
};
pub fn main() !u8 {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = std.process.argsAlloc(allocator) catch {
printStderr("Couldn't allocate arguments array", .{});
return 1;
};
defer std.process.argsFree(allocator, args);
if (args.len < 2) {
printHelp();
return 1;
}
if (std.mem.eql(u8, args[1], commands.install_udev)) {
if (args.len > 3) {
printHelp();
return 1;
}
return installUdevRules(if (args.len == 3) args[2] else null);
} else if (std.mem.eql(u8, args[1], commands.write_config)) {
if (args.len > 3) {
printHelp();
return 1;
}
Config.loadDefault(allocator);
return writeDefaultConfig(if (args.len == 3) args[2] else null);
} else if (std.mem.eql(u8, args[1], commands.run)) {
if (args.len > 3) {
printHelp();
return 1;
}
blk: {
const conf_file = std.fs.cwd().openFile("yaes.json", .{}) catch {
log.warn("Could not load config file yaes.json. Using default config.", .{});
const confpath = if (args.len == 3) args[2] else "yaes.json";
const conf_file = std.fs.cwd().openFile(confpath, .{}) catch {
log.warn("Could not load config file '{s}'. Using default config.", .{confpath});
Config.loadDefault(allocator);
break :blk;
};
defer conf_file.close();
Config.load(allocator, conf_file.reader()) catch {
log.err("Could not parse config file yaes.json. Good luck figuring out why.", .{});
Config.load(allocator, conf_file.reader()) catch |err| {
log.err("Could not parse config file '{s}': {s}.", .{ confpath, @errorName(err) });
return 1;
};
}
@ -37,4 +85,76 @@ pub fn main() !u8 {
};
return 0;
} else {
printHelp();
return 1;
}
}
fn installUdevRules(outpath: ?[]const u8) u8 {
const rules_path = outpath orelse "/etc/udev/rules.d";
var rules_d = std.fs.cwd().openDir(rules_path, .{}) catch |err| {
printStderr(
"could not open udev rules path '{s}': {s}",
.{ rules_path, @errorName(err) },
);
return 1;
};
defer rules_d.close();
rules_d.writeFile(.{ .sub_path = udev.rules_filename, .data = udev.rules }) catch |err| {
printStderr(
"could not write rules file '{s}{s}{s}': {s}",
.{
rules_path,
if (rules_path.len == 0)
"./"
else if (rules_path[rules_path.len - 1] == '/')
""
else
"/",
udev.rules_filename,
@errorName(err),
},
);
return 1;
};
return 0;
}
fn writeDefaultConfig(outarg: ?[]const u8) u8 {
const outpath = outarg orelse "yaes.json";
const outfile = std.fs.cwd().createFile(outpath, .{}) catch |err| {
printStderr("Could not write config file '{s}': {s}", .{ outpath, @errorName(err) });
return 1;
};
defer outfile.close();
std.json.stringify(Config.global.*, .{ .whitespace = .indent_4 }, outfile.writer()) catch |err| {
printStderr("Could not serialize config file '{s}': {s}", .{ outpath, @errorName(err) });
return 1;
};
printStderr("config written to {s}", .{outpath});
return 0;
}
fn printHelp() void {
printStderr(
\\Usage: yaes [command] [args...]
\\
\\ Control a Yaesu G-5500DC rotator through a LabJack U12 using the hamlib TCP interface.
\\
\\Available commands:
\\
, .{});
inline for (@typeInfo(@TypeOf(commands)).Struct.fields) |field| {
printStderr(
" {s} {s}",
.{ @field(commands, field.name), @field(command_help, field.name) },
);
}
}