I’ve been building an IMAP client and have been running into a complicated cleanup scenario. I’m wondering if anyone has a good idea of how to handle this. The crux of it is as follows :
pub fn noop(session: *Session) !void {
// Write out the command
const tag: []const u8 = "N001";
try session.writer().print("{s} NOOP\r\n", .{tag});
try session.flush(); // Flush both buffers
// Do some processing that will read from the buffer
// May also allocate or parse that would result in an error
while (true) {
// Do processing, may fail from my bugs or unexpected input
break;
}
// Cleanup. This should happen if all is well, if a recoverable error occurs, but not for an unrecoverable error
// Need to make
const response_tag = try session.reader().take(4);
std.debug.assert(std.mem.eql(u8, tag, response_tag));
// Check if OK, NO, or BAD
_ = try session.reader().takeDelimiter('\n');
return;
}
More Concrete Example
pub fn noop(self: *Session) !void {
var tag_buf: [4]u8 = undefined;
const tag = std.fmt.bufPrint(&tag_buf, "S{d:0>3}", .{self.tag_id}) catch unreachable;
errdefer self.cleanup(tag);
const r = self.reader();
var w = self.writer();
try w.print("{s} NOOP\r\n", .{tag});
try self.flush();
// Logic for parsing optional untagged responses
// Would all start with `*`
while ((try r.peekByte()) == '*') {
// Parse it, including doing things like parsing integers or allocating memory which could fail
_ = try r.takeDelimiter('\n');
}
// Cleanup. We need to do this if recoverable so that the read buffer doesn't contain leftovers.
// Happy path is that the next 4 bytes would be the tag.
// Unhappy path is we need to skip over lines until we get to the tagged line
var line = try r.takeDelimiter('\n');
while (!std.mem.eql(u8, line[0..4], tag)) line = try r.takeDelimiter('\n');
// Parse the tagged value for OK, NO, or BAD
return;
}
Here is the challenge: how do I structure this so that the cleanup only happens for recoverable errors (OutOfMemory, integer parsing, etc.) but not for std.Io.Reader.Errors that indicate the stream is closed or some unrecoverable error.
Ideally this is repeatable for the other commands that IMAP supports, not just the NOOP command.