| 1 | //! https://github.com/ulid/spec |
| 2 | |
| 3 | const std = @import("std"); |
| 4 | const string = []const u8; |
| 5 | const base32 = @import("./base32.zig"); |
| 6 | const extras = @import("extras"); |
| 7 | const time = @import("time"); |
| 8 | |
| 9 | pub 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 |
| 34 | pub 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 | }; |