50 enum class Op { Any, Eq, Neq, Gt, Gte, Lt, Lte, Caret, Tilde };
58 std::vector<Matcher> matchers_;
67 vc.raw_ = std::string{s};
69 for (
const auto part : split(s)) {
70 vc.matchers_.push_back(parse_single(part));
72 if (vc.matchers_.empty()) {
73 vc.matchers_.push_back(Matcher{});
80 return std::ranges::all_of(matchers_, [&v](
const Matcher& m) {
return matches(m, v); });
84 [[nodiscard]]
const std::string&
raw() const noexcept {
95 return raw_ == o.raw_;
99 [[nodiscard]]
static bool matches(
const Matcher& m,
const SemVer& v) {
116 return caret_matches(m.ver, v);
118 return tilde_matches(m.ver, v);
125 [[nodiscard]]
static bool caret_matches(
const SemVer& constraint,
const SemVer& v) {
126 if (v < constraint) {
129 if (constraint.major != 0) {
130 return v.major == constraint.major;
132 if (constraint.minor != 0) {
133 return v.major == 0 && v.minor == constraint.minor;
135 return v.major == 0 && v.minor == 0 && v.patch == constraint.patch;
139 [[nodiscard]]
static bool tilde_matches(
const SemVer& constraint,
const SemVer& v) {
140 if (v < constraint) {
143 return v.major == constraint.major && v.minor == constraint.minor;
147 [[nodiscard]]
static Matcher parse_single(std::string_view s) {
148 while (!s.empty() && s.front() ==
' ') {
151 while (!s.empty() && s.back() ==
' ') {
155 if (s.empty() || s ==
"*") {
160 if (s.starts_with(
">=")) {
161 return Matcher{.op = Op::Gte, .ver = require(s.substr(2), s)};
163 if (s.starts_with(
"<=")) {
164 return Matcher{.op = Op::Lte, .ver = require(s.substr(2), s)};
166 if (s.starts_with(
"!=")) {
167 return Matcher{.op = Op::Neq, .ver = require(s.substr(2), s)};
169 if (s.starts_with(
"^")) {
170 return Matcher{.op = Op::Caret, .ver = require(s.substr(1), s)};
172 if (s.starts_with(
"~")) {
173 return Matcher{.op = Op::Tilde, .ver = require(s.substr(1), s)};
175 if (s.starts_with(
">")) {
176 return Matcher{.op = Op::Gt, .ver = require(s.substr(1), s)};
178 if (s.starts_with(
"<")) {
179 return Matcher{.op = Op::Lt, .ver = require(s.substr(1), s)};
181 return Matcher{.op = Op::Eq, .ver = require(s, s)};
185 [[nodiscard]]
static SemVer require(
const std::string_view ver,
const std::string_view token) {
188 throw std::invalid_argument{
"commons: invalid version in constraint: " +
197 [[nodiscard]]
static std::vector<std::string_view> split(std::string_view s) {
198 std::vector<std::string_view> parts;
200 while (!s.empty() && s.front() ==
' ') {
208 bool in_version =
false;
209 for (usize i = 0; i < s.size(); ++i) {
214 while (next < s.size() && s[next] ==
' ') {
217 if (next < s.size()) {
218 if (
const char nc = s[next]; nc ==
'^' || nc ==
'~' || nc ==
'>' || nc ==
'<' ||
219 nc ==
'!' || nc ==
'=' ||
220 (nc >=
'0' && nc <=
'9')) {
229 parts.push_back(s.substr(0, end));
230 s.remove_prefix(end);
269struct std::formatter<comms::VersionConstraint> {
270 constexpr auto parse(
const std::format_parse_context& ctx) {
271 const auto* it = ctx.begin();
272 if (it != ctx.end() && *it !=
'}') {
273 throw std::format_error(
"commons: VersionConstraint takes no format spec");
279 return std::format_to(ctx.out(),
"{}", v.
raw());
A semver range constraint (npm ^/~, comparisons, space-separated intersection).
Definition version_constraint.hpp:49
const std::string & raw() const noexcept
The original range string this constraint was parsed from.
Definition version_constraint.hpp:84
std::string to_string(const Color &c)
Color as its canonical hex string (#RRGGBB, or #RRGGBBAA when not opaque).
Definition color.hpp:1388