69 std::vector<std::variant<std::string, uint16_t>> values;
71 constexpr PreRelease()
noexcept =
default;
72 constexpr ~PreRelease() =
default;
73 constexpr explicit PreRelease(std::string_view s)
noexcept { from_string(s); }
75 constexpr std::strong_ordering operator<=>(PreRelease
const& other)
const noexcept {
76 for (std::size_t i = 0; i < std::min(values.size(), other.values.size()); ++i) {
77 if (std::holds_alternative<std::string>(values[i])) {
78 if (std::holds_alternative<std::string>(other.values[i])) {
79 if (std::get<std::string>(values[i]) != std::get<std::string>(other.values[i])) {
80 return std::get<std::string>(values[i]) <=> std::get<std::string>(other.values[i]);
83 return std::strong_ordering::greater;
86 if (std::holds_alternative<std::string>(other.values[i])) {
87 return std::strong_ordering::less;
89 if (std::get<std::uint16_t>(values[i]) != std::get<std::uint16_t>(other.values[i])) {
90 return std::get<std::uint16_t>(values[i]) <=> std::get<std::uint16_t>(other.values[i]);
95 return values.size() <=> other.values.size();
100 while (first != last && !detail::is_plus(*first)) {
103 std::string s{begin, first};
104 auto tokens = ll::string_utils::splitByPattern(s,
".");
105 for (
auto const& token : tokens) {
107 if (
auto result = detail::from_chars(token.data(), token.data() + token.length(), value); result) {
108 values.emplace_back(value);
110 values.emplace_back(std::string{token});
113 return {first, std::errc{}};
117 return from_chars(str.data(), str.data() + str.length());
120 constexpr PreRelease& from_string(std::string_view str)
noexcept {
121 from_string_noexcept(str).value();
125 [[nodiscard]]
constexpr std::string to_string()
const noexcept {
127 for (
auto const& value : values) {
128 if (std::holds_alternative<std::string>(value)) {
129 str += std::get<std::string>(value);
131 str += std::to_string(std::get<std::uint16_t>(value));
135 if (str.ends_with(
'.')) {
144 std::uint16_t major = 0;
145 std::uint16_t minor = 1;
146 std::uint16_t patch = 0;
147 std::optional<PreRelease> preRelease;
148 std::optional<std::string> build;
150 constexpr Version() =
default;
151 constexpr ~Version() =
default;
157 std::optional<PreRelease> prt = {},
158 std::optional<std::string> bu = {}
163 preRelease{std::move(prt)},
164 build{std::move(bu)} {}
170 std::string_view prt,
171 std::optional<std::string> bu = {}
177 build{std::move(bu)} {}
179 explicit constexpr Version(std::string_view str) : Version() { from_string(str); }
182 if (first ==
nullptr || last ==
nullptr || (last - first) < detail::min_version_string_length) {
183 return {first, std::errc::invalid_argument};
186 if (
auto result = detail::from_chars(next, last, major); result) {
188 if (!detail::check_delimiter(next, last,
'.')) {
189 return {next, std::errc::invalid_argument};
195 if (
auto result = detail::from_chars(next, last, minor); result) {
197 if (!detail::check_delimiter(next, last,
'.')) {
198 return {next, std::errc::invalid_argument};
204 if (
auto result = detail::from_chars(next, last, patch); result) {
210 return {next, std::errc{}};
212 if (detail::check_delimiter(next, last,
'-')) {
214 auto result = pre.from_chars(++next, last);
215 if (!result)
return result;
216 if (pre.values.empty())
return {next, std::errc::invalid_argument};
219 if (result && next == last) {
220 return {next, std::errc{}};
223 if (detail::check_delimiter(next, last,
'+')) {
224 build = {++next,
static_cast<size_t>(last - next)};
225 if (build->empty()) {
226 return {
nullptr, std::errc::invalid_argument};
229 if (std::any_of(build->begin(), build->end(), [](
char c) {
230 return !detail::is_digit(c) && !detail::is_letter(c);
232 return {
nullptr, std::errc::invalid_argument};
236 return {next, std::errc{}};
239 return {first, std::errc::invalid_argument};
243 return from_chars(str.data(), str.data() + str.length());
246 constexpr Version& from_string(std::string_view str) {
247 from_string_noexcept(str).value();
251 [[nodiscard]] std::string to_string()
const {
253 str = fmt::format(
"{}.{}.{}", major, minor, patch);
256 str += preRelease->to_string();
265 [[nodiscard]]
constexpr std::strong_ordering operator<=>(Version
const& other)
const noexcept {
266 if (major != other.major) {
267 return major <=> other.major;
269 if (minor != other.minor) {
270 return minor <=> other.minor;
272 if (patch != other.patch) {
273 return patch <=> other.patch;
276 if (other.preRelease) {
277 return *preRelease <=> *other.preRelease;
279 return std::strong_ordering::less;
280 }
else if (other.preRelease) {
281 return std::strong_ordering::greater;
283 return std::strong_ordering::equal;
286 [[nodiscard]]
constexpr bool operator==(Version
const& other)
const noexcept {
287 return *this <=> other == std::strong_ordering::equal;
290 [[nodiscard]] [[maybe_unused]]
static constexpr bool valid(std::string_view str)
noexcept {
291 return Version{}.from_string_noexcept(str);