Lightweight 0.20250904.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 must be 8, 9, A, or B
78 auto const variant = text[21];
79 if (variant != '8' && variant != '9' && variant != 'A' && variant != 'B' && variant != 'a' && variant != 'b')
80 return { "\x04" };
81
82 // clang-format off
83 size_t i = 0;
84 for (auto const index: { 0, 2, 4, 6,
85 9, 11,
86 14, 16,
87 21, 19,
88 24, 26, 28, 30, 32, 34 })
89 {
90 if (std::from_chars(text.data() + index, text.data() + index + 2, guid.data[i], 16).ec != std::errc())
91 return { "\x05" };
92 i++;
93 }
94 // clang-format on
95
96 return guid;
97}
98
99} // namespace Lightweight
100
101template <>
102struct std::formatter<Lightweight::SqlGuid>: std::formatter<std::string>
103{
104 LIGHTWEIGHT_FORCE_INLINE auto format(Lightweight::SqlGuid const& guid, format_context& ctx) const
105 -> format_context::iterator
106 {
107 // clang-format off
108 return formatter<std::string>::format(std::format(
109 "{:08X}-{:04X}-{:04X}-{:04X}-{:012X}",
110 (uint32_t) guid.data[3] | (uint32_t) guid.data[2] << 8 |
111 (uint32_t) guid.data[1] << 16 | (uint32_t) guid.data[0] << 24,
112 (uint16_t) guid.data[5] | (uint16_t) guid.data[4] << 8,
113 (uint16_t) guid.data[7] | (uint16_t) guid.data[6] << 8,
114 (uint16_t) guid.data[8] | (uint16_t) guid.data[9] << 8,
115 (uint64_t) guid.data[15] | (uint64_t) guid.data[14] << 8 |
116 (uint64_t) guid.data[13] << 16 | (uint64_t) guid.data[12] << 24 |
117 (uint64_t) guid.data[11] << 32 | (uint64_t) guid.data[10] << 40
118 ),
119 ctx
120 );
121 // clang-format on
122 }
123};
124
125namespace Lightweight
126{
127
128inline LIGHTWEIGHT_FORCE_INLINE std::string to_string(SqlGuid const& guid)
129{
130 return std::format("{}", guid);
131}
132
133template <>
134struct LIGHTWEIGHT_API SqlDataBinder<SqlGuid>
135{
136 static constexpr auto ColumnType = SqlColumnTypeDefinitions::Guid {};
137
138 static SQLRETURN InputParameter(SQLHSTMT stmt,
139 SQLUSMALLINT column,
140 SqlGuid const& value,
141 SqlDataBinderCallback& cb) noexcept;
142
143 static SQLRETURN OutputColumn(
144 SQLHSTMT stmt, SQLUSMALLINT column, SqlGuid* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept;
145
146 static SQLRETURN GetColumn(
147 SQLHSTMT stmt, SQLUSMALLINT column, SqlGuid* result, SQLLEN* indicator, SqlDataBinderCallback const& cb) noexcept;
148
149 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(SqlGuid const& value) noexcept
150 {
151 return std::format("{}", value);
152 }
153};
154
155} // 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.