115 std::vector<std::variant<std::string, uint16>> values;
117 constexpr PreRelease() =
default;
118 constexpr ~PreRelease() =
default;
119 constexpr explicit PreRelease(std::string_view str) { from_string(str); }
121 constexpr std::strong_ordering operator<=>(PreRelease
const& other)
const noexcept {
122 for (std::size_t i = 0; i < std::min(values.size(), other.values.size()); ++i) {
123 if (std::holds_alternative<std::string>(values[i])) {
124 if (std::holds_alternative<std::string>(other.values[i])) {
125 if (std::get<std::string>(values[i]) != std::get<std::string>(other.values[i])) {
126 return std::get<std::string>(values[i]) <=> std::get<std::string>(other.values[i]);
129 return std::strong_ordering::greater;
131 }
else if (std::holds_alternative<std::string>(other.values[i])) {
132 return std::strong_ordering::less;
133 }
else if (std::get<uint16>(values[i]) != std::get<uint16>(other.values[i])) {
134 return std::get<uint16>(values[i]) <=> std::get<uint16>(other.values[i]);
137 return values.size() <=> other.values.size();
140 constexpr bool operator==(PreRelease
const& other)
const noexcept =
default;
143 if (first ==
nullptr || last ==
nullptr || first >= last) {
144 return {first, std::errc::invalid_argument};
147 std::vector<std::variant<std::string, uint16>> parsed;
148 auto current = first;
149 while (current != last && *current !=
'+') {
150 auto identifierBegin = current;
151 while (current != last && *current !=
'.' && *current !=
'+') {
152 if (!detail::is_identifier_char(*current)) {
153 return {current, std::errc::invalid_argument};
157 if (identifierBegin == current) {
158 return {current, std::errc::invalid_argument};
161 bool numeric = std::all_of(identifierBegin, current, detail::is_digit);
164 auto result = detail::parse_numeric_identifier(identifierBegin, current, value);
168 parsed.emplace_back(value);
170 parsed.emplace_back(std::string{identifierBegin, current});
173 if (current != last && *current ==
'.') {
175 if (current == last || *current ==
'+' || *current ==
'.') {
176 return {current, std::errc::invalid_argument};
181 values = std::move(parsed);
182 return {current, std::errc{}};
186 auto result = from_chars(str.data(), str.data() + str.length());
187 if (result && result.ptr != str.data() + str.length()) {
188 return {result.ptr, std::errc::invalid_argument};
193 constexpr PreRelease& from_string(std::string_view str) {
194 from_string_noexcept(str).value();
198 [[nodiscard]]
constexpr std::string to_string()
const {
200 for (
auto const& value : values) {
204 if (std::holds_alternative<std::string>(value)) {
205 str += std::get<std::string>(value);
207 detail::append_number(str, std::get<uint16>(value));
218 std::optional<PreRelease> preRelease;
219 std::optional<std::string> build;
221 constexpr Version() =
default;
222 constexpr ~Version() =
default;
228 std::optional<PreRelease> prt = {},
229 std::optional<std::string> bu = {}
234 preRelease{std::move(prt)},
235 build{std::move(bu)} {}
237 constexpr Version(uint16 mj, uint16 mn, uint16 pt, std::string_view prt, std::optional<std::string> bu = {})
242 build{std::move(bu)} {}
244 explicit constexpr Version(std::string_view str) : Version() { from_string(str); }
247 if (first ==
nullptr || last ==
nullptr || first >= last
248 || (last - first) < detail::min_version_string_length) {
249 return {first, std::errc::invalid_argument};
253 auto begin = current;
254 auto result = detail::from_chars(current, last, value);
258 if (result.ptr - begin > 1 && *begin ==
'0') {
259 return {begin, std::errc::invalid_argument};
261 current = result.ptr;
265 auto current = first;
266 uint16 parsedMajor{};
267 uint16 parsedMinor{};
268 uint16 parsedPatch{};
270 if (
auto result = parseCorePart(current, parsedMajor); !result) {
273 if (!detail::check_delimiter(current, last,
'.')) {
274 return {current, std::errc::invalid_argument};
277 if (
auto result = parseCorePart(current, parsedMinor); !result) {
280 if (!detail::check_delimiter(current, last,
'.')) {
281 return {current, std::errc::invalid_argument};
284 if (
auto result = parseCorePart(current, parsedPatch); !result) {
288 std::optional<PreRelease> parsedPreRelease;
289 std::optional<std::string> parsedBuild;
290 if (detail::check_delimiter(current, last,
'-')) {
292 auto result = preRelease.from_chars(++current, last);
296 parsedPreRelease = std::move(preRelease);
297 current = result.ptr;
299 if (detail::check_delimiter(current, last,
'+')) {
300 auto buildBegin = ++current;
301 auto result = detail::validate_identifiers(buildBegin, last);
305 parsedBuild = std::string{buildBegin, last};
308 if (current != last) {
309 return {current, std::errc::invalid_argument};
315 preRelease = std::move(parsedPreRelease);
316 build = std::move(parsedBuild);
317 return {current, std::errc{}};
321 return from_chars(str.data(), str.data() + str.length());
324 constexpr Version& from_string(std::string_view str) {
325 from_string_noexcept(str).value();
329 [[nodiscard]]
constexpr std::string to_string()
const {
331 detail::append_number(str, major);
333 detail::append_number(str, minor);
335 detail::append_number(str, patch);
338 str += preRelease->to_string();
347 [[nodiscard]]
constexpr std::strong_ordering operator<=>(Version
const& other)
const noexcept {
348 if (major != other.major) {
349 return major <=> other.major;
351 if (minor != other.minor) {
352 return minor <=> other.minor;
354 if (patch != other.patch) {
355 return patch <=> other.patch;
358 if (other.preRelease) {
359 return *preRelease <=> *other.preRelease;
361 return std::strong_ordering::less;
363 if (other.preRelease) {
364 return std::strong_ordering::greater;
366 return std::strong_ordering::equal;
369 [[nodiscard]]
constexpr bool operator==(Version
const& other)
const noexcept {
370 return *this <=> other == std::strong_ordering::equal;
373 [[nodiscard]]
constexpr bool isIdenticalTo(Version
const& other)
const noexcept {
374 return *
this == other && build == other.build;
377 [[nodiscard]] [[maybe_unused]]
static constexpr bool valid(std::string_view str)
noexcept {
378 return Version{}.from_string_noexcept(str);