115 std::vector<std::variant<std::string, std::uint16_t>> 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<std::uint16_t>(values[i]) != std::get<std::uint16_t>(other.values[i])) {
134 return std::get<std::uint16_t>(values[i]) <=> std::get<std::uint16_t>(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, std::uint16_t>> 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);
163 std::uint16_t value{};
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<std::uint16_t>(value));
215 std::uint16_t major = 0;
216 std::uint16_t minor = 1;
217 std::uint16_t patch = 0;
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)} {}
241 std::string_view prt,
242 std::optional<std::string> bu = {}
248 build{std::move(bu)} {}
250 explicit constexpr Version(std::string_view str) : Version() { from_string(str); }
253 if (first ==
nullptr || last ==
nullptr || first >= last
254 || (last - first) < detail::min_version_string_length) {
255 return {first, std::errc::invalid_argument};
259 auto begin = current;
260 auto result = detail::from_chars(current, last, value);
264 if (result.ptr - begin > 1 && *begin ==
'0') {
265 return {begin, std::errc::invalid_argument};
267 current = result.ptr;
271 auto current = first;
272 std::uint16_t parsedMajor{};
273 std::uint16_t parsedMinor{};
274 std::uint16_t parsedPatch{};
276 if (
auto result = parseCorePart(current, parsedMajor); !result) {
279 if (!detail::check_delimiter(current, last,
'.')) {
280 return {current, std::errc::invalid_argument};
283 if (
auto result = parseCorePart(current, parsedMinor); !result) {
286 if (!detail::check_delimiter(current, last,
'.')) {
287 return {current, std::errc::invalid_argument};
290 if (
auto result = parseCorePart(current, parsedPatch); !result) {
294 std::optional<PreRelease> parsedPreRelease;
295 std::optional<std::string> parsedBuild;
296 if (detail::check_delimiter(current, last,
'-')) {
298 auto result = preRelease.from_chars(++current, last);
302 parsedPreRelease = std::move(preRelease);
303 current = result.ptr;
305 if (detail::check_delimiter(current, last,
'+')) {
306 auto buildBegin = ++current;
307 auto result = detail::validate_identifiers(buildBegin, last);
311 parsedBuild = std::string{buildBegin, last};
314 if (current != last) {
315 return {current, std::errc::invalid_argument};
321 preRelease = std::move(parsedPreRelease);
322 build = std::move(parsedBuild);
323 return {current, std::errc{}};
327 return from_chars(str.data(), str.data() + str.length());
330 constexpr Version& from_string(std::string_view str) {
331 from_string_noexcept(str).value();
335 [[nodiscard]]
constexpr std::string to_string()
const {
337 detail::append_number(str, major);
339 detail::append_number(str, minor);
341 detail::append_number(str, patch);
344 str += preRelease->to_string();
353 [[nodiscard]]
constexpr std::strong_ordering operator<=>(Version
const& other)
const noexcept {
354 if (major != other.major) {
355 return major <=> other.major;
357 if (minor != other.minor) {
358 return minor <=> other.minor;
360 if (patch != other.patch) {
361 return patch <=> other.patch;
364 if (other.preRelease) {
365 return *preRelease <=> *other.preRelease;
367 return std::strong_ordering::less;
369 if (other.preRelease) {
370 return std::strong_ordering::greater;
372 return std::strong_ordering::equal;
375 [[nodiscard]]
constexpr bool operator==(Version
const& other)
const noexcept {
376 return *this <=> other == std::strong_ordering::equal;
379 [[nodiscard]]
constexpr bool isIdenticalTo(Version
const& other)
const noexcept {
380 return *
this == other && build == other.build;
383 [[nodiscard]] [[maybe_unused]]
static constexpr bool valid(std::string_view str)
noexcept {
384 return Version{}.from_string_noexcept(str);