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