This commit just shuffles the test for user commands closer to the definition of the user commands.
144 lines
5 KiB
Zig
144 lines
5 KiB
Zig
const std = @import("std");
|
|
|
|
const Parser = @import("root.zig").parser.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 init_substitute(author: []const u8, needle: []const u8, replacement: []const u8, all: bool) UserCommand {
|
|
return .{ .substitute = .{
|
|
.author = author,
|
|
.needle = needle,
|
|
.replacement = replacement,
|
|
.all = all,
|
|
} };
|
|
}
|
|
|
|
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 log_prefix = "parsing substitute command";
|
|
var parser = substitute;
|
|
parser, const delim = parser.take_char();
|
|
if (std.ascii.isAlphanumeric(delim)) {
|
|
std.log.debug("{s}: delimiter cannot be a whitespace: \"{s}\"", .{ log_prefix, text });
|
|
return null;
|
|
}
|
|
var result = parser.take_until_char(delim);
|
|
if (result == null) {
|
|
std.log.debug(
|
|
"{s}: cannot find typo, expecting a message on the form 's{}TYPO{}CORRECTION{}', but got {s}",
|
|
.{ log_prefix, delim, delim, delim, text },
|
|
);
|
|
return null;
|
|
}
|
|
parser, const typo = result.?;
|
|
result = parser.consume_char(delim).?.take_until_char(delim);
|
|
if (result == null) {
|
|
std.log.debug("{s}: missing an ending '/' in \"{s}\"", .{ log_prefix, text });
|
|
return null;
|
|
}
|
|
parser, const correction = result.?;
|
|
return .init_substitute(nick, typo, correction, false);
|
|
}
|
|
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 "can parse s/hello/world and report failure" {
|
|
try std.testing.expectEqualDeep(null, UserCommand.parse("jassob", "s/hello/world"));
|
|
}
|
|
|
|
test "can parse s/hello|world| and report failure" {
|
|
try std.testing.expectEqualDeep(null, 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"));
|
|
}
|
|
|
|
/// 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")) |join| {
|
|
if (join.consume_space()) |channel| {
|
|
if (channel.rest[0] != '#') {
|
|
return .{ .err = .{ .message = "channels must start with \"#\"" } };
|
|
}
|
|
return .{ .join = .{ .channel = join.rest } };
|
|
}
|
|
}
|
|
if (command.consume_str("backlog")) |backlog| {
|
|
if (backlog.consume_space()) |history| {
|
|
const historyOffset = std.fmt.parseInt(u16, history.rest, 10) catch |err| {
|
|
std.debug.print("failed to parse int ('{s}') with error: {}\n", .{ history.rest, err });
|
|
return null;
|
|
};
|
|
return .{ .backlog = .{ .history = historyOffset } };
|
|
}
|
|
}
|
|
std.log.debug("unknown command: \"{s}\"", .{command.rest});
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
test "parse admin commands" {
|
|
const cmd = AdminCommand.parse("!join badchannel") orelse unreachable;
|
|
try std.testing.expectEqual(
|
|
AdminCommand{ .err = .{ .message = "channels must start with \"#\"" } },
|
|
cmd,
|
|
);
|
|
}
|
|
|
|
test "parse backlog admin commands" {
|
|
const cmd = AdminCommand.parse("!backlog 1") orelse unreachable;
|
|
try std.testing.expectEqual(
|
|
AdminCommand{ .backlog = .{ .history = 1 } },
|
|
cmd,
|
|
);
|
|
}
|
|
|
|
test "parse unknown admin commands" {
|
|
const cmd = AdminCommand.parse("!history 1");
|
|
try std.testing.expectEqual(null, cmd);
|
|
}
|