Lightweight 0.20251202.0
Loading...
Searching...
No Matches
SqlGuid.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../SqlColumnTypeDefinitions.hpp"
6#include "Core.hpp"
7
8#include <charconv>
9#include <format>
10#include <optional>
11#include <string>
12
13namespace Lightweight
14{
15
16/// Represents a GUID (Globally Unique Identifier).
17///
18/// @ingroup DataTypes
19struct LIGHTWEIGHT_API SqlGuid
20{
21 uint8_t data[16] {};
22
23 /// Creates a new non-empty GUID.
24 static SqlGuid Create() noexcept;
25
26 /// Parses a GUID from a string.
27 static std::optional<SqlGuid> TryParse(std::string_view const& text) noexcept;
28
29 /// Parses a GUID from a string. Use with caution and always prefer TryParse at all cost.
30 static SqlGuid constexpr UnsafeParse(std::string_view const& text) noexcept;
31
32 constexpr std::weak_ordering operator<=>(SqlGuid const& other) const noexcept = default;
33
34 constexpr bool operator==(SqlGuid const& other) const noexcept
35 {
36 return (*this <=> other) == std::weak_ordering::equivalent;
37 }
38
39 constexpr bool operator!=(SqlGuid const& other) const noexcept
40 {
41 return !(*this == other);
42 }
43
44 // Test if the GUID is non-empty.
45 constexpr explicit operator bool() const noexcept
46 {
47 return *this != SqlGuid {};
48 }
49
50 // Test if the GUID is empty.
51 constexpr bool operator!() const noexcept
52 {
53 return !static_cast<bool>(*this);
54 }
55};
56
57constexpr SqlGuid SqlGuid::UnsafeParse(std::string_view const& text) noexcept
58{
59 SqlGuid guid {};
60
61 // UUID format: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
62 // M is the version and N is the variant
63
64 // Check for length
65 if (text.size() != 36)
66 return { "\x01" };
67
68 // Check for dashes
69 if (text[8] != '-' || text[13] != '-' || text[18] != '-' || text[23] != '-')
70 return { "\x02" };
71
72 // Version must be 1, 2, 3, 4, or 5
73 auto const version = text[14];
74 if (!('1' <= version && version <= '5'))
75 return { "\x03" };
76
77 // Variant nibble at position 19 must be a valid hex digit
78 // We accept all variants (RFC 4122: 8-B, Microsoft: C-D, etc.)
79 auto const variant = text[19];
80 auto const isHexDigit = (variant >= '0' && variant <= '9') || (variant >= 'A' && variant <= 'F')
81 || (variant >= 'a' && variant <= 'f');
82 if (!isHexDigit)
83 return { "\x04" };
84
85 // clang-format off
86 size_t i = 0;
87 for (auto const index: { 0, 2, 4, 6,
88 9, 11,
89 14, 16,
90 19, 21,
91 24, 26, 28, 30, 32, 34 })
92 {
93 if (std::from_chars(text.data() + index, text.data() + index + 2, guid.data[i], 16).ec != std::errc())
94 return { "\x05" };
95 i++;
96 }
97 // clang-format on
98
99 return guid;
100}
101
102} // namespace Lightweight
103
104template <>
105struct std::formatter<Lightweight::SqlGuid>: std::formatter<std::string>
106{
107 LIGHTWEIGHT_FORCE_INLINE auto format(Lightweight::SqlGuid const& guid, format_context& ctx) const
108 -> format_context::iterator
109 {
110 // clang-format off
111 return formatter<std::string>::format(std::format(
112 "{:08X}-{:04X}-{:04X}-{:04X}-{:012X}",
113 (uint32_t) guid.data[3] | (uint32_t) guid.data[2] << 8 |
114 (uint32_t) guid.data[1] << 16 | (uint32_t) guid.data[0] << 24,
115 (uint16_t) guid.data[5] | (uint16_t) guid.data[4] << 8,
116 (uint16_t) guid.data[7] | (uint16_t) guid.data[6] << 8,
117 (uint16_t) guid.data[9] | (uint16_t) guid.data[8] << 8,
118 (uint64_t) guid.data[15] | (uint64_t) guid.data[14] << 8 |
119 (uint64_t) guid.data[13] << 16 | (uint64_t) guid.data[12] << 24 |
120 (uint64_t) guid.data[11] << 32 | (uint64_t) guid.data[10] << 40
121 ),
122 ctx
123 );
124 // clang-format on
125 }
126};
127
128namespace Lightweight
129{
130
131inline LIGHTWEIGHT_FORCE_INLINE std::string to_string(SqlGuid const& guid)
132{
133 return std::format("{}", guid);
134}
135
136template <>
137struct LIGHTWEIGHT_API SqlDataBinder<SqlGuid>
138{
139 static constexpr auto ColumnType = SqlColumnTypeDefinitions::Guid {};
140
141 static SQLRETURN InputParameter(SQLHSTMT stmt,
142 SQLUSMALLINT column,
143 SqlGuid const& value,
144 SqlDataBinderCallback& cb) noexcept;
145
146 static SQLRETURN OutputColumn(
147 SQLHSTMT stmt, SQLUSMALLINT column, SqlGuid* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept;
148
149 static SQLRETURN GetColumn(
150 SQLHSTMT stmt, SQLUSMALLINT column, SqlGuid* result, SQLLEN* indicator, SqlDataBinderCallback const& cb) noexcept;
151
152 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(SqlGuid const& value) noexcept
153 {
154 return std::format("{}", value);
155 }
156};
157
158} // namespace Lightweight
static SqlGuid constexpr UnsafeParse(std::string_view const &text) noexcept
Parses a GUID from a string. Use with caution and always prefer TryParse at all cost.
Definition SqlGuid.hpp:57
static SqlGuid Create() noexcept
Creates a new non-empty GUID.