From dad361a3415e88475a78d1b2702133629fb6e548 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 27 Apr 2017 15:11:56 +0300 Subject: Add standard_version_constraint struct --- butl/standard-version | 107 +++++++++++++++++++++++++--- butl/standard-version.cxx | 178 +++++++++++++++++++++++++++++++++++++++++++++- butl/standard-version.ixx | 14 ++-- 3 files changed, 279 insertions(+), 20 deletions(-) (limited to 'butl') diff --git a/butl/standard-version b/butl/standard-version index 0888900..c9637d8 100644 --- a/butl/standard-version +++ b/butl/standard-version @@ -12,6 +12,8 @@ #include +#include + namespace butl { // The build2 "standard version": @@ -34,10 +36,13 @@ namespace butl std::string snapshot_id; // Empty if not specified. std::uint16_t revision = 0; // 0 if not specified. - std::uint16_t major () const; - std::uint16_t minor () const; - std::uint16_t patch () const; - std::uint16_t pre_release () const; // Note: 0 is ambiguous (-a.0.z). + std::uint16_t major () const noexcept; + std::uint16_t minor () const noexcept; + std::uint16_t patch () const noexcept; + + // Note: 0 is ambiguous (-a.0.z). + // + std::uint16_t pre_release () const noexcept; // Note: return empty if the corresponding component is unspecified. // @@ -47,14 +52,14 @@ namespace butl std::string string_pre_release () const; // Pre-release part only (a.1). std::string string_snapshot () const; // Snapshot part only (1234.1f23). - bool empty () const {return version == 0;} + bool empty () const noexcept {return version == 0;} - bool alpha () const; - bool beta () const; - bool snapshot () const {return snapshot_sn != 0;} + bool alpha () const noexcept; + bool beta () const noexcept; + bool snapshot () const noexcept {return snapshot_sn != 0;} int - compare (const standard_version&) const; + compare (const standard_version&) const noexcept; // Parse the version. Throw std::invalid_argument if the format is not // recognizable or components are invalid. @@ -88,13 +93,37 @@ namespace butl }; inline bool - operator== (const standard_version& x, const standard_version& y) + operator< (const standard_version& x, const standard_version& y) noexcept + { + return x.compare (y) < 0; + } + + inline bool + operator> (const standard_version& x, const standard_version& y) noexcept + { + return x.compare (y) > 0; + } + + inline bool + operator== (const standard_version& x, const standard_version& y) noexcept { return x.compare (y) == 0; } inline bool - operator!= (const standard_version& x, const standard_version& y) + operator<= (const standard_version& x, const standard_version& y) noexcept + { + return x.compare (y) <= 0; + } + + inline bool + operator>= (const standard_version& x, const standard_version& y) noexcept + { + return x.compare (y) >= 0; + } + + inline bool + operator!= (const standard_version& x, const standard_version& y) noexcept { return !(x == y); } @@ -104,6 +133,62 @@ namespace butl { return o << x.string (); } + + // The build2 "standard version" constraint: + // + // ('==' | '>' | '<' | '>=' | '<=') + // ('(' | '[') (')' | ']') + // + struct LIBBUTL_EXPORT standard_version_constraint + { + butl::optional min_version; + butl::optional max_version; + bool min_open; + bool max_open; + + // Parse the version constraint. Throw std::invalid_argument on error. + // + explicit + standard_version_constraint (const std::string&); + + // Throw std::invalid_argument if the specified version range is invalid. + // + standard_version_constraint ( + butl::optional min_version, bool min_open, + butl::optional max_version, bool max_open); + + standard_version_constraint (const standard_version& v) + : standard_version_constraint (v, false, v, false) {} + + standard_version_constraint () = default; + + std::string + string () const; + + bool + empty () const noexcept {return !min_version && !max_version;} + }; + + inline bool + operator== (const standard_version_constraint& x, + const standard_version_constraint& y) + { + return x.min_version == y.min_version && x.max_version == y.max_version && + x.min_open == y.min_open && x.max_open == y.max_open; + } + + inline bool + operator!= (const standard_version_constraint& x, + const standard_version_constraint& y) + { + return !(x == y); + } + + inline std::ostream& + operator<< (std::ostream& o, const standard_version_constraint& x) + { + return o << x.string (); + } } #include diff --git a/butl/standard-version.cxx b/butl/standard-version.cxx index 7e144a6..552aac8 100644 --- a/butl/standard-version.cxx +++ b/butl/standard-version.cxx @@ -4,8 +4,10 @@ #include +#include #include // strtoull() #include // size_t +#include // move() #include // invalid_argument #include // alnum() @@ -90,7 +92,7 @@ namespace butl ep = *e == '~'; } - size_t p (0), n (s.size ()); + size_t p (0); if (ep) { @@ -152,7 +154,7 @@ namespace butl if (s[p] == '+') revision = parse_num (s, ++p, "invalid revision", 1, uint16_t (~0)); - if (p != n) + if (p != s.size ()) bail ("junk after version"); if (ab != 0) @@ -330,4 +332,176 @@ namespace butl return r; } + + // standard_version_constraint + // + standard_version_constraint:: + standard_version_constraint (const std::string& s) + { + using std::string; // Not to confuse with string(). + + auto bail = [] (const string& m) {throw invalid_argument (m);}; + const char* spaces (" \t"); + + size_t p (0); + char c (s[p]); + + if (c == '(' || c == '[') // Can be '\0'. + { + bool min_open = c == '('; + + p = s.find_first_not_of (spaces, ++p); + if (p == string::npos) + bail ("no min version"); + + size_t e (s.find_first_of (spaces, p)); + + standard_version min_version; + + try + { + min_version = standard_version (s.substr (p, e - p)); + } + catch (const invalid_argument& e) + { + bail (string ("invalid min version: ") + e.what ()); + } + + p = s.find_first_not_of (spaces, e); + if (p == string::npos) + bail ("no max version"); + + e = s.find_first_of (" \t])", p); + + standard_version max_version; + + try + { + max_version = standard_version (s.substr (p, e - p)); + } + catch (const invalid_argument& e) + { + bail (string ("invalid max version: ") + e.what ()); + } + + p = s.find_first_of ("])", e); + if (p == string::npos) + bail ("no closing bracket"); + + bool max_open = s[p] == ')'; + + if (++p != s.size ()) + bail ("junk after constraint"); + + // Verify and copy the constraint. + // + *this = standard_version_constraint (min_version, min_open, + max_version, max_open); + } + else + { + enum comparison {eq, lt, gt, le, ge}; + comparison operation (eq); // Uninitialized warning. + + if (s.compare (0, p = 2, "==") == 0) + operation = eq; + else if (s.compare (0, p = 2, ">=") == 0) + operation = ge; + else if (s.compare (0, p = 2, "<=") == 0) + operation = le; + else if (s.compare (0, p = 1, ">") == 0) + operation = gt; + else if (s.compare (0, p = 1, "<") == 0) + operation = lt; + else + bail ("invalid constraint"); + + p = s.find_first_not_of (spaces, p); + + if (p == string::npos) + bail ("no version"); + + standard_version v; + + try + { + v = standard_version (s.substr (p)); + } + catch (const invalid_argument& e) + { + bail (string ("invalid version: ") + e.what ()); + } + + // Verify and copy the constraint. + // + switch (operation) + { + case comparison::eq: + *this = standard_version_constraint (v); + break; + case comparison::lt: + *this = standard_version_constraint (nullopt, true, move (v), true); + break; + case comparison::le: + *this = standard_version_constraint (nullopt, true, move (v), false); + break; + case comparison::gt: + *this = standard_version_constraint (move (v), true, nullopt, true); + break; + case comparison::ge: + *this = standard_version_constraint (move (v), false, nullopt, true); + break; + } + } + } + + standard_version_constraint:: + standard_version_constraint (optional mnv, bool mno, + optional mxv, bool mxo) + : min_version (move (mnv)), + max_version (move (mxv)), + min_open (mno), + max_open (mxo) + { + assert ( + // Min and max versions can't both be absent. + // + (min_version || max_version) && + + // Version should be non-empty. + // + (!min_version || !min_version->empty ()) && + (!max_version || !max_version->empty ()) && + + // Absent version endpoint (infinity) should be open. + // + (min_version || min_open) && (max_version || max_open)); + + if (min_version && max_version) + { + if (*min_version > *max_version) + throw invalid_argument ("min version is greater than max version"); + + if (*min_version == *max_version && (min_open || max_open)) + throw invalid_argument ("equal version endpoints not closed"); + } + } + + string standard_version_constraint:: + string () const + { + assert (!empty ()); + + if (!min_version) + return (max_open ? "< " : "<= ") + max_version->string (); + + if (!max_version) + return (min_open ? "> " : ">= ") + min_version->string (); + + if (*min_version == *max_version) + return "== " + min_version->string (); + + return (min_open ? '(' : '[') + min_version->string () + ' ' + + max_version->string () + (max_open ? ')' : ']'); + } } diff --git a/butl/standard-version.ixx b/butl/standard-version.ixx index e414deb..b01faef 100644 --- a/butl/standard-version.ixx +++ b/butl/standard-version.ixx @@ -18,7 +18,7 @@ namespace butl } inline std::uint16_t standard_version:: - major () const + major () const noexcept { std::uint64_t v (version / 10); std::uint64_t ab (v % 1000); @@ -29,7 +29,7 @@ namespace butl } inline std::uint16_t standard_version:: - minor () const + minor () const noexcept { std::uint64_t v (version / 10); std::uint64_t ab (v % 1000); @@ -40,7 +40,7 @@ namespace butl } inline std::uint16_t standard_version:: - patch () const + patch () const noexcept { std::uint64_t v (version / 10); std::uint64_t ab (v % 1000); @@ -51,7 +51,7 @@ namespace butl } inline std::uint16_t standard_version:: - pre_release () const + pre_release () const noexcept { std::uint64_t ab (version / 10 % 1000); if (ab > 500) @@ -61,21 +61,21 @@ namespace butl } inline bool standard_version:: - alpha () const + alpha () const noexcept { std::uint64_t abe (version % 10000); return abe > 0 && abe < 5000; } inline bool standard_version:: - beta () const + beta () const noexcept { std::uint64_t abe (version % 10000); return abe > 5000; } inline int standard_version:: - compare (const standard_version& v) const + compare (const standard_version& v) const noexcept { if (epoch != v.epoch) return epoch < v.epoch ? -1 : 1; -- cgit v1.1