atproto pds in zig pds.zat.dev
pds atproto
Something went wrong. Try again.
Zig
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182const std = @import("std");const zat = @import("zat");
/// the data model's `$bytes` is RFC 4648 base64 with padding optional; the reference PDS/// sends it unpadded, so both forms must decode.pub fn decodeAtBase64(allocator: std.mem.Allocator, encoded: []const u8) ![]const u8 { var end = encoded.len; while (end > 0 and encoded[end - 1] == '=') end -= 1; const trimmed = encoded[0..end]; const bytes = try allocator.alloc(u8, try std.base64.standard_no_pad.Decoder.calcSizeForSlice(trimmed)); try std.base64.standard_no_pad.Decoder.decode(bytes, trimmed); return bytes;}
pub fn writeAlloc(allocator: std.mem.Allocator, value: zat.cbor.Value) ![]const u8 { var out: std.Io.Writer.Allocating = .init(allocator); defer out.deinit(); try writeValue(allocator, &out.writer, value); return out.toOwnedSlice();}
fn writeValue(allocator: std.mem.Allocator, writer: anytype, value: zat.cbor.Value) !void { switch (value) { .unsigned => |v| try writer.print("{d}", .{v}), .negative => |v| try writer.print("{d}", .{v}), .bytes => |bytes| { const encoded = try allocator.alloc(u8, std.base64.standard.Encoder.calcSize(bytes.len)); defer allocator.free(encoded); _ = std.base64.standard.Encoder.encode(encoded, bytes); try writer.print("{{\"$bytes\":{f}}}", .{std.json.fmt(encoded, .{})}); }, .text => |text| try writer.print("{f}", .{std.json.fmt(text, .{})}), // zat rejects NaN/Infinity at decode and asserts them unreachable at // encode, so anything reaching here is finite and renders as JSON. .float => |v| try writer.print("{f}", .{std.json.fmt(v, .{})}), .array => |items| { try writer.writeByte('['); for (items, 0..) |item, idx| { if (idx != 0) try writer.writeByte(','); try writeValue(allocator, writer, item); } try writer.writeByte(']'); }, .map => |entries| { try writer.writeByte('{'); for (entries, 0..) |entry, idx| { if (idx != 0) try writer.writeByte(','); try writer.print("{f}:", .{std.json.fmt(entry.key, .{})}); try writeValue(allocator, writer, entry.value); } try writer.writeByte('}'); }, .boolean => |v| try writer.print("{}", .{v}), .null => try writer.writeAll("null"), .cid => |cid| try writer.print( "{{\"$link\":{f}}}", .{std.json.fmt(try zat.multibase.base32lower.encode(allocator, cid.raw), .{})}, ), }}
test "float values render as JSON numbers" { const a = std.testing.allocator; const cases = [_]struct { v: f64, want: []const u8 }{ .{ .v = 1.5, .want = "1.5" }, .{ .v = -0.25, .want = "-0.25" }, .{ .v = 2.0, .want = "2e0" }, }; for (cases) |c| { const out = try writeAlloc(a, .{ .float = c.v }); defer a.free(out); // whatever the spelling, it must parse back as the same number const parsed = try std.json.parseFromSlice(std.json.Value, a, out, .{}); defer parsed.deinit(); try std.testing.expectEqual(c.v, switch (parsed.value) { .float => |f| f, .integer => |i| @as(f64, @floatFromInt(i)), else => return error.NotANumber, }); }}