1//! https://github.com/ulid/spec
2
3const std = @import("std");
4const string = []const u8;
5const base32 = @import("./base32.zig");
6const extras = @import("extras");
7const time = @import("time");
8
9pub const Factory = struct {
10 epoch: i64,
11 rand: std.Random,
12
13 pub fn init(epoch: i64, rand: std.Random) Factory {
14 return Factory{
15 .epoch = epoch,
16 .rand = rand,
17 };
18 }
19
20 pub fn newULID(self: Factory) ULID {
21 const now = time.milliTimestamp();
22 return ULID{
23 .timestamp = std.math.cast(u48, now - self.epoch) orelse @panic("time.milliTimestamp() is higher than 281474976710655"), // this is Tue Aug 02 10889 05:31:50.655 UTC
24 .randomnes = self.rand.int(u80),
25 };
26 }
27};
28
29/// 01AN4Z07BY 79KA1307SR9X4MV3
30///
31/// |----------| |----------------|
32/// Timestamp Randomness
33/// 48bits 80bits
34pub const ULID = struct {
35 timestamp: u48,
36 randomnes: u80,
37
38 pub const BaseType = string;
39 pub const baseTypeName = "char(26)";
40
41 pub fn parse(alloc: std.mem.Allocator, value: BaseType) !ULID {
42 if (value.len != 26) return error.Ulid;
43 return ULID{
44 .timestamp = std.math.cast(u48, try extras.sliceToInt(u50, u5, try base32.decode(alloc, value[0..10]))) orelse return error.Ulid,
45 .randomnes = try extras.sliceToInt(u80, u5, try base32.decode(alloc, value[10..26])),
46 };
47 }
48
49 pub fn toString(self: ULID, alloc: std.mem.Allocator) !BaseType {
50 var res = try std.array_list.Managed(u8).initCapacity(alloc, 26);
51 defer res.deinit();
52 try res.appendSlice(&self.bytes());
53 return res.toOwnedSlice();
54 }
55
56 pub fn bindField(self: ULID) ![26]u8 {
57 return self.bytes();
58 }
59
60 pub const readField = parse;
61
62 pub fn bytes(self: ULID) [26]u8 {
63 var buf: [26]u8 = undefined;
64 base32.formatInt(u48, self.timestamp, buf[0..10]);
65 base32.formatInt(u80, self.randomnes, buf[10..26]);
66 return buf;
67 }
68
69 pub fn format(self: ULID, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
70 _ = fmt;
71 _ = options;
72 try writer.writeAll(&self.bytes());
73 }
74
75 pub fn nprint(self: ULID, writer: anytype) !void {
76 try writer.writeAll(&self.bytes());
77 }
78
79 pub fn stringifyJson(self: ULID, writer: anytype, options: std.json.Stringify.Options, json: type) !void {
80 return json.stringify(writer, &self.bytes(), options);
81 }
82
83 pub fn createdAt(self: ULID, epoch: u64) time.DateTime {
84 return .initUnixMs(epoch + self.timestamp);
85 }
86};