This smells like an anti-pattern, but I'm not able to see a better way at the moment. Am I missing something, or is this a normal way to "build" something based through a series of steps?
I'm talking about the times I do const old_path = path; defer allocator.free(old_path).
pub fn to_file_path(allocator: std.mem.Allocator, url_path: []const u8) ![]u8 {
std.debug.assert(std.mem.startsWith(u8, url_path, "/"));
// make it a ./ relative path
var path = try std.mem.concat(allocator, u8, &[_][]const u8{ &".".*, url_path });
// friendly urls
if (std.mem.endsWith(u8, path, "/")) {
const old_path = path;
defer allocator.free(old_path);
path = try std.fmt.allocPrint(allocator, "{s}{s}", .{ path, "index.html" });
}
// resolve the (relative) path to a file
const old_path = path;
defer allocator.free(old_path);
path = try std.fs.path.resolve(allocator, &[_][]const u8{path});
return path;
}