const std = @import("std"); // TODO: Move decompression logic to dedicated function to reduce code duplication. const XrpcError = struct { @"error": []const u8, message: ?[]const u8 = null, }; pub fn formatTimestamp(timestamp_seconds: u64, buf: *[24]u8) []const u8 { const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = timestamp_seconds }; const epoch_day = epoch_seconds.getEpochDay(); const day_seconds = epoch_seconds.getDaySeconds(); const year_day = epoch_day.calculateYearDay(); const month_day = year_day.calculateMonthDay(); return std.fmt.bufPrint(buf, "{d:0>4}-{d:0>2}-{d:0>2}T{d:0>2}:{d:0>2}:{d:0>2}Z", .{ year_day.year, @as(u8, @intFromEnum(month_day.month)), @as(u8, month_day.day_index) + 1, day_seconds.getHoursIntoDay(), day_seconds.getMinutesIntoHour(), day_seconds.getSecondsIntoMinute(), }) catch unreachable; // Should be unreachable since the buffer provided is the exact size. } pub fn checkXrpcResponse(alloc: std.mem.Allocator, response: *std.http.Client.Response) !void { switch (response.head.status) { .ok, .created => return, // Happy paths .unauthorized, .bad_request => { // The handling of "bad" responses are the same, we log and we crash. var decompress: std.http.Decompress = undefined; var decompress_buf: [4096]u8 = undefined; var reader_buf: [4096]u8 = undefined; const reader = response.readerDecompressing(&reader_buf, &decompress, &decompress_buf); const body = reader.allocRemaining(alloc, .limited(1024 * 1024)) catch { std.log.err("HTTP {d} (Failed to read body)\n", .{@intFromEnum(response.head.status)}); return error.RequestFailed; }; defer alloc.free(body); const parsed = std.json.parseFromSlice(XrpcError, alloc, body, .{ .ignore_unknown_fields = true, }) catch { std.log.err("HTTP {d}: {s}\n", .{ @intFromEnum(response.head.status), body }); return error.RequestFailed; }; defer parsed.deinit(); std.log.err("{s}: {s}", .{ parsed.value.@"error", parsed.value.message orelse "no details", }); return error.RequestFailed; }, else => |s| { std.log.err("unexpected HTTP {d}\n", .{@intFromEnum(s)}); return error.UnexpectedStatus; }, } }