refactor: extract modules from bot.zig
This commit creates a bunch of new modules that contain code and tests for various concepts/implementations that used to exist inside bot.zig. Notable amongst these are: - buffer.zig, which contain the circular buffer containing both backlog and outbox messages. - parser.zig, which contain the parser used to parse commands from IRC messages.
This commit is contained in:
parent
508e084ddf
commit
e1e1938359
7 changed files with 694 additions and 285 deletions
106
src/commands.zig
Normal file
106
src/commands.zig
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
const std = @import("std");
|
||||
|
||||
const parser = @import("root.zig").parser;
|
||||
|
||||
/// UserCommand represents the commands that ordinary IRC users can use.
|
||||
pub const UserCommand = union(enum) {
|
||||
/// `s/<old-word>/<new-word>/`
|
||||
substitute: struct { author: []const u8, needle: []const u8, replacement: []const u8, all: bool = false },
|
||||
/// !help
|
||||
help: void,
|
||||
|
||||
pub fn parse(nick: []const u8, text: []const u8) ?UserCommand {
|
||||
const original = parser.init(text);
|
||||
if (original.consume_str("!help")) |_| {
|
||||
return .help;
|
||||
}
|
||||
|
||||
if (original.consume_char('s')) |substitute| {
|
||||
const delim_parser, const delim = substitute.take_char();
|
||||
if (std.ascii.isAlphanumeric(delim)) {
|
||||
std.log.debug("parsing substitute command: delimiter cannot be a whitespace: \"{s}\"", .{text});
|
||||
return null;
|
||||
}
|
||||
const typo_parser, const typo = delim_parser.take_until_char(delim);
|
||||
const correction_parser, const correction = typo_parser.consume_char(delim).?.take_until_char(delim);
|
||||
if (correction_parser.consume_char(delim) == null) {
|
||||
std.log.debug("parsing substitute command: missing an ending '/' in \"{s}\"", .{text});
|
||||
return null;
|
||||
}
|
||||
return .{
|
||||
.substitute = .{
|
||||
.author = nick,
|
||||
.needle = typo,
|
||||
.replacement = correction,
|
||||
.all = false,
|
||||
},
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/// AdminCommand are commands useful for debugging zigeru, since they
|
||||
/// are more spammy than others they are separated and only sent to
|
||||
/// #eru-admin.
|
||||
pub const AdminCommand = union(enum) {
|
||||
backlog: struct { history: u16 },
|
||||
status: void,
|
||||
join: struct { channel: []const u8 },
|
||||
err: struct { message: []const u8 },
|
||||
|
||||
pub fn parse(text: []const u8) ?AdminCommand {
|
||||
const original = parser.init(text);
|
||||
if (original.consume_char('!')) |command| {
|
||||
if (command.consume_str("status")) |_| {
|
||||
return .status;
|
||||
}
|
||||
if (command.consume_str("join").?.consume_char(' ')) |join| {
|
||||
if (join.rest[0] != '#') {
|
||||
return .{ .err = .{ .message = "channels must start with \"#\"" } };
|
||||
}
|
||||
return .{ .join = .{ .channel = join.rest } };
|
||||
}
|
||||
if (command.consume_str("backlog")) |backlog| {
|
||||
const history = std.fmt.parseInt(u16, backlog.rest, 10) catch |err| {
|
||||
std.debug.print("failed to parse int ('{s}') with error: {}\n", .{ backlog.rest, err });
|
||||
return null;
|
||||
};
|
||||
return .{ .backlog = .{ .history = history } };
|
||||
}
|
||||
std.log.debug("unknown command: \"{s}\"", .{command.rest});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
test "can parse !help successful" {
|
||||
try std.testing.expectEqual(
|
||||
UserCommand.help,
|
||||
UserCommand.parse("jassob", "!help"),
|
||||
);
|
||||
}
|
||||
|
||||
test "can parse s/hello/world/ successful" {
|
||||
try std.testing.expectEqualDeep(
|
||||
UserCommand{ .substitute = .{
|
||||
.author = "jassob",
|
||||
.needle = "hello",
|
||||
.replacement = "world",
|
||||
.all = false,
|
||||
} },
|
||||
UserCommand.parse("jassob", "s/hello/world/"),
|
||||
);
|
||||
}
|
||||
|
||||
test "correctly ignores non-messages when trying to parse" {
|
||||
try std.testing.expectEqualDeep(null, UserCommand.parse("jassob", "Hello, world"));
|
||||
}
|
||||
|
||||
test "parse admin commands" {
|
||||
const cmd = AdminCommand.parse("!join badchannel") orelse unreachable;
|
||||
try std.testing.expectEqual(
|
||||
AdminCommand{ .err = .{ .message = "channels must start with \"#\"" } },
|
||||
cmd,
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue