const Uuid = @This(); const std = @import("std"); data: u128, pub const nil = Uuid{ .data = @as(u128, 0) }; pub const string_len = 36; pub fn eql(lhs: ?Uuid, rhs: ?Uuid) bool { if (lhs == null and rhs == null) return true; if (lhs == null or rhs == null) return false; return lhs.?.data == rhs.?.data; } pub fn toCharArray(value: Uuid) [string_len]u8 { var buf: [string_len]u8 = undefined; _ = std.fmt.bufPrint(&buf, "{}", .{value}) catch unreachable; return buf; } pub fn toCharArrayZ(value: Uuid) [string_len + 1:0]u8 { var buf: [string_len + 1:0]u8 = undefined; _ = std.fmt.bufPrintZ(&buf, "{}", .{value}) catch unreachable; return buf; } pub fn format(value: Uuid, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { const arr = @bitCast([16]u8, value.data); try std.fmt.format(writer, "{x:0>2}{x:0>2}{x:0>2}{x:0>2}-{x:0>2}{x:0>2}-{x:0>2}{x:0>2}-{x:0>2}{x:0>2}-{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}{x:0>2}", .{ arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7], arr[8], arr[9], arr[10], arr[11], arr[12], arr[13], arr[14], arr[15], }); } pub const JsonParseAs = []const u8; pub fn jsonStringify(value: Uuid, _: std.json.StringifyOptions, writer: anytype) !void { try std.fmt.format(writer, "\"{}\"", .{value}); } pub const ParseError = error{ InvalidCharacter, InvalidLength, }; pub fn parse(str: []const u8) ParseError!Uuid { if (str.len != string_len and (str.len != string_len + 1 or str[str.len - 1] != 0)) return error.InvalidLength; var data: [16]u8 = undefined; var str_i: usize = 0; var i: usize = 0; while (i < 16 and str_i < str.len) : ({ i += 1; str_i += 2; }) { data[i] = std.fmt.parseInt(u8, str[str_i .. str_i + 2], 16) catch |err| switch (err) { error.InvalidCharacter => return error.InvalidCharacter, else => unreachable, }; if (i == 3 or i == 5 or i == 7 or i == 9) { if (str[str_i + 2] != '-') return error.InvalidCharacter; str_i += 1; } } return Uuid{ .data = @bitCast(u128, data) }; } pub fn randV4(rand: std.rand.Random) Uuid { var ret: [16]u8 = undefined; rand.bytes(&ret); // signify that this is a random v4 uuid ret[7] = (0b0100_0000) | (ret[7] & 0b1111); ret[9] = (0b1000_0000) | (ret[9] & 0b11_1111); return Uuid{ .data = @bitCast(u128, ret) }; } test "parse uuid" { try std.testing.expectEqual( Uuid.nil, try Uuid.parse("00000000-0000-0000-0000-000000000000"), ); try std.testing.expectEqual( Uuid{ .data = @as(u128, 0x4ba7b74522ad_1da8_c242_d312_60515ff7), }, try Uuid.parse("f75f5160-12d3-42c2-a81d-ad2245b7a74b"), ); } test "format uuid" { try std.testing.expectFmt("00000000-0000-0000-0000-000000000000", "{}", .{Uuid.nil}); const uuid = Uuid{ .data = @as(u128, 0x4ba7b74522ad_1da8_c242_d312_60515ff7), }; try std.testing.expectFmt("f75f5160-12d3-42c2-a81d-ad2245b7a74b", "{}", .{uuid}); try std.testing.expectError(error.InvalidLength, Uuid.parse("fsdfs")); try std.testing.expectError(error.InvalidCharacter, Uuid.parse("00000000-0000-0000-xxxx-000000000000")); try std.testing.expectError(error.InvalidLength, Uuid.parse("00000000-0000-0000-0000-000000000000fsdfs")); try std.testing.expectError(error.InvalidCharacter, Uuid.parse("00000000-0000x0000-0000-000000000000")); } test "roundtrip random uuid" { const test_seed = 12345; var test_prng = std.rand.DefaultPrng.init(test_seed); const uuid = Uuid.randV4(test_prng.random()); var buf: [36]u8 = undefined; var fbs = std.io.fixedBufferStream(&buf); try Uuid.format(uuid, "", .{}, fbs.writer()); const parsed = try Uuid.parse(&buf); try std.testing.expectEqual(uuid, parsed); }