68 std::vector<std::variant<std::string, uint16_t>> values;
72 constexpr explicit PreRelease(std::string_view s)
noexcept { from_string(s); }
74 constexpr std::strong_ordering operator<=>(
PreRelease const& other)
const noexcept {
75 for (std::size_t i = 0; i < std::min(values.size(), other.values.size()); ++i) {
76 if (std::holds_alternative<std::string>(values[i])) {
77 if (std::holds_alternative<std::string>(other.values[i])) {
78 if (std::get<std::string>(values[i]) != std::get<std::string>(other.values[i])) {
79 return std::get<std::string>(values[i]) <=> std::get<std::string>(other.values[i]);
82 return std::strong_ordering::greater;
85 if (std::holds_alternative<std::string>(other.values[i])) {
86 return std::strong_ordering::less;
88 if (std::get<std::uint16_t>(values[i]) != std::get<std::uint16_t>(other.values[i])) {
89 return std::get<std::uint16_t>(values[i]) <=> std::get<std::uint16_t>(other.values[i]);
94 return values.size() <=> other.values.size();
99 while (first != last && !detail::is_plus(*first)) {
102 std::string s{begin, first};
103 auto tokens = ll::string_utils::splitByPattern(s,
".");
104 for (
auto const& token : tokens) {
106 if (
auto result = detail::from_chars(token.data(), token.data() + token.length(), value); result) {
107 values.emplace_back(value);
109 values.emplace_back(std::string{token});
112 return {first, std::errc{}};
116 return from_chars(str.data(), str.data() + str.length());
119 constexpr PreRelease& from_string(std::string_view str)
noexcept {
120 from_string_noexcept(str).value();
124 [[nodiscard]]
constexpr std::string to_string()
const noexcept {
126 for (
auto const& value : values) {
127 if (std::holds_alternative<std::string>(value)) {
128 str += std::get<std::string>(value);
130 str += std::to_string(std::get<std::uint16_t>(value));
134 if (str.ends_with(
'.')) {
143 std::uint16_t major = 0;
144 std::uint16_t minor = 1;
145 std::uint16_t patch = 0;
146 std::optional<PreRelease> preRelease;
147 std::optional<std::string> build;
156 std::optional<PreRelease> prt = {},
157 std::optional<std::string> bu = {}
162 preRelease{std::move(prt)},
163 build{std::move(bu)} {}
169 std::string_view prt,
170 std::optional<std::string> bu = {}
176 build{std::move(bu)} {}
178 explicit constexpr Version(std::string_view str) :
Version() { from_string(str); }
181 if (first ==
nullptr || last ==
nullptr || (last - first) < detail::min_version_string_length) {
182 return {first, std::errc::invalid_argument};
185 if (
auto result = detail::from_chars(next, last, major); result) {
187 if (!detail::check_delimiter(next, last,
'.')) {
188 return {next, std::errc::invalid_argument};
194 if (
auto result = detail::from_chars(next, last, minor); result) {
196 if (!detail::check_delimiter(next, last,
'.')) {
197 return {next, std::errc::invalid_argument};
203 if (
auto result = detail::from_chars(next, last, patch); result) {
209 return {next, std::errc{}};
211 if (detail::check_delimiter(next, last,
'-')) {
213 auto result = pre.from_chars(++next, last);
214 if (!result)
return result;
215 if (pre.values.empty())
return {next, std::errc::invalid_argument};
218 if (result && next == last) {
219 return {next, std::errc{}};
222 if (detail::check_delimiter(next, last,
'+')) {
223 build = {++next,
static_cast<size_t>(last - next)};
224 if (build->empty()) {
225 return {
nullptr, std::errc::invalid_argument};
228 if (std::any_of(build->begin(), build->end(), [](
char c) {
229 return !detail::is_digit(c) && !detail::is_letter(c);
231 return {
nullptr, std::errc::invalid_argument};
235 return {next, std::errc{}};
238 return {first, std::errc::invalid_argument};
242 return from_chars(str.data(), str.data() + str.length());
245 constexpr Version& from_string(std::string_view str) {
246 from_string_noexcept(str).value();
250 [[nodiscard]] std::string to_string()
const {
252 str = fmt::format(
"{}.{}.{}", major, minor, patch);
255 str += preRelease->to_string();
264 [[nodiscard]]
constexpr std::strong_ordering operator<=>(
Version const& other)
const noexcept {
265 if (major != other.major) {
266 return major <=> other.major;
268 if (minor != other.minor) {
269 return minor <=> other.minor;
271 if (patch != other.patch) {
272 return patch <=> other.patch;
275 if (other.preRelease) {
276 return *preRelease <=> *other.preRelease;
278 return std::strong_ordering::less;
279 }
else if (other.preRelease) {
280 return std::strong_ordering::greater;
282 return std::strong_ordering::equal;
285 [[nodiscard]]
constexpr bool operator==(
Version const& other)
const noexcept {
286 return *this <=> other == std::strong_ordering::equal;
289 [[nodiscard]] [[maybe_unused]]
static constexpr bool valid(std::string_view str)
noexcept {
290 return Version{}.from_string_noexcept(str);
333struct hash<ll::data::Version> {
335 size_t seed{v.major};
336 ll::hash_utils::hashCombine(v.minor, seed);
337 ll::hash_utils::hashCombine(v.patch, seed);
338 ll::hash_utils::hashCombine(std::hash<std::optional<ll::data::PreRelease>>{}(v.preRelease), seed);
339 ll::hash_utils::hashCombine(std::hash<std::optional<std::string>>{}(v.build), seed);