atproto pds in zig
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175const std = @import("std");const store = @import("../../storage/store.zig");
pub const Signal = struct { style: []const u8, html: []const u8,};
pub fn fromState( allocator: std.mem.Allocator, residents: []const store.Resident, collections: []const store.CollectionSummary, activity: []const store.ActivityBucket,) !Signal { var hash = std.hash.Wyhash.init(0x7a_64_73); for (residents) |resident| { hash.update(resident.did); hash.update(resident.handle); var buf: [16]u8 = undefined; hash.update(try std.fmt.bufPrint(&buf, "{d}", .{resident.record_count})); } for (collections) |collection| { hash.update(collection.collection); var buf: [16]u8 = undefined; hash.update(try std.fmt.bufPrint(&buf, "{d}", .{collection.count})); } for (activity) |bucket| { var buf: [32]u8 = undefined; hash.update(try std.fmt.bufPrint(&buf, "{d}", .{bucket.count})); } const seed = hash.final(); const hue_a: u16 = @intCast(seed % 360); const hue_b: u16 = @intCast((seed >> 11) % 360); const hue_c: u16 = @intCast((seed >> 23) % 360); const style = try std.fmt.allocPrint( allocator, "--signal-a:hsl({d} 76% 62%);--signal-b:hsl({d} 84% 66%);--signal-c:hsl({d} 78% 65%);", .{ hue_a, hue_b, hue_c }, );
var html: std.Io.Writer.Allocating = .init(allocator); defer html.deinit();
try html.writer.writeAll("<div class=\"entropy\" aria-label=\"record write activity\">"); if (activity.len == 0) { try html.writer.writeAll("<span class=\"signal-empty\">waiting for writes</span>"); } else { var max_count: u64 = 1; var total_count: u64 = 0; for (activity) |bucket| { max_count = @max(max_count, bucket.count); total_count += bucket.count; } const first_label = try formatDate(allocator, activity[0].start_us); const last_label = try formatDate(allocator, activity[activity.len - 1].end_us); const range_label = try formatRange(allocator, activity[0].start_us, activity[activity.len - 1].end_us); try html.writer.print( \\<span class="entropy-label">{d} repo commits · {s}</span> \\<span class="entropy-bars" aria-hidden="true"> , .{ total_count, range_label }); var i: usize = 0; while (i < activity.len) : (i += 1) { const bucket = activity[i]; const height = scaledHeight(bucket.count, max_count); const offset = i % 3; const bucket_label = try formatBucketLabel(allocator, bucket); try html.writer.print( \\<i style="--h:{d}px;--y:{d}px" data-label="{s}"></i> , .{ height, offset, bucket_label }); } try html.writer.print( \\</span> \\<span class="entropy-axis"><span>{s}</span><span>{s}</span></span> , .{ first_label, last_label }); } try html.writer.writeAll("</div>"); return .{ .style = style, .html = try html.toOwnedSlice() };}
fn scaledHeight(count: u64, max_count: u64) u64 { if (count == 0) return 5; const count_root = std.math.sqrt(@as(f64, @floatFromInt(count))); const max_root = std.math.sqrt(@as(f64, @floatFromInt(@max(max_count, 1)))); return 7 + @as(u64, @intFromFloat(@round(count_root * 34.0 / max_root)));}
fn formatRange(allocator: std.mem.Allocator, first_us: u64, last_us: u64) ![]const u8 { const first = civilFromMicros(first_us); const last = civilFromMicros(last_us); if (first.year == last.year and first.month == last.month and first.day == last.day) { return std.fmt.allocPrint(allocator, "{s} {d}", .{ monthName(first.month), first.day }); } if (first.year == last.year) { return std.fmt.allocPrint(allocator, "{s} {d}-{s} {d}", .{ monthName(first.month), first.day, monthName(last.month), last.day }); } return std.fmt.allocPrint(allocator, "{s} {d} {d}-{s} {d} {d}", .{ monthName(first.month), first.day, first.year, monthName(last.month), last.day, last.year });}
fn formatDate(allocator: std.mem.Allocator, timestamp_us: u64) ![]const u8 { const civil = civilFromMicros(timestamp_us); return std.fmt.allocPrint(allocator, "{s} {d}", .{ monthName(civil.month), civil.day });}
fn formatBucketLabel(allocator: std.mem.Allocator, bucket: store.ActivityBucket) ![]const u8 { const start = civilFromMicros(bucket.start_us); const end = civilFromMicros(bucket.end_us); if (start.year == end.year and start.month == end.month and start.day == end.day) { return std.fmt.allocPrint( allocator, "{d} commits · {s} {d}, {d:0>2}:{d:0>2}-{d:0>2}:{d:0>2} UTC", .{ bucket.count, monthName(start.month), start.day, start.hour, start.minute, end.hour, end.minute }, ); } return std.fmt.allocPrint( allocator, "{d} commits · {s} {d}-{s} {d}", .{ bucket.count, monthName(start.month), start.day, monthName(end.month), end.day }, );}
const Civil = struct { year: i64, month: u8, day: u8, hour: u8, minute: u8,};
fn civilFromMicros(timestamp_us: u64) Civil { const timestamp_s: i64 = @intCast(timestamp_us / std.time.us_per_s); const days = @divFloor(timestamp_s, std.time.s_per_day); const seconds_of_day = @mod(timestamp_s, std.time.s_per_day); const date = civilFromDays(days); return .{ .year = date.year, .month = date.month, .day = date.day, .hour = @intCast(@divFloor(seconds_of_day, std.time.s_per_hour)), .minute = @intCast(@divFloor(@mod(seconds_of_day, std.time.s_per_hour), std.time.s_per_min)), };}
fn civilFromDays(days_since_epoch: i64) struct { year: i64, month: u8, day: u8 } { const z = days_since_epoch + 719468; const era = if (z >= 0) @divFloor(z, 146097) else @divFloor(z - 146096, 146097); const doe: u64 = @intCast(z - era * 146097); const yoe = @divFloor(doe - @divFloor(doe, 1460) + @divFloor(doe, 36524) - @divFloor(doe, 146096), 365); var year: i64 = @intCast(yoe); year += era * 400; const doy = doe - (365 * yoe + @divFloor(yoe, 4) - @divFloor(yoe, 100)); const mp = @divFloor(5 * doy + 2, 153); const day = doy - @divFloor(153 * mp + 2, 5) + 1; const month = if (mp < 10) mp + 3 else mp - 9; year += if (month <= 2) 1 else 0; return .{ .year = year, .month = @intCast(month), .day = @intCast(day) };}
fn monthName(month: u8) []const u8 { return switch (month) { 1 => "Jan", 2 => "Feb", 3 => "Mar", 4 => "Apr", 5 => "May", 6 => "Jun", 7 => "Jul", 8 => "Aug", 9 => "Sep", 10 => "Oct", 11 => "Nov", 12 => "Dec", else => "?", };}