From d969a5985a8d6bcbb6625b2a99c0ad2637916f42 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 12 Mar 2018 21:41:06 +0300 Subject: Add support for version iteration --- libbpkg/manifest.cxx | 35 +++++++++++++++++------ libbpkg/manifest.hxx | 44 ++++++++++++++++++++++------- tests/package-version/driver.cxx | 60 +++++++++++++++++++++++++++++++--------- 3 files changed, 107 insertions(+), 32 deletions(-) diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx index a61dd82..03bf689 100644 --- a/libbpkg/manifest.cxx +++ b/libbpkg/manifest.cxx @@ -134,11 +134,16 @@ namespace bpkg // version // version:: - version (uint16_t e, std::string u, optional l, uint16_t r) + version (uint16_t e, + std::string u, + optional l, + uint16_t r, + uint32_t i) : epoch (e), upstream (move (u)), release (move (l)), revision (r), + iteration (i), canonical_upstream ( data_type (upstream.c_str (), data_type::parse::upstream). canonical_upstream), @@ -159,11 +164,14 @@ namespace bpkg if (revision != 0) throw invalid_argument ("revision for empty version"); + + if (iteration != 0) + throw invalid_argument ("iteration for empty version"); } - else if (release && release->empty () && revision != 0) - // Empty release signifies the earliest possible release. Revision is - // meaningless in such a context. - // + // Empty release signifies the earliest possible release. Revision and/or + // iteration are meaningless in such a context. + // + else if (release && release->empty () && (revision != 0 || iteration != 0)) throw invalid_argument ("revision for earliest possible release"); } @@ -444,7 +452,7 @@ namespace bpkg } string version:: - string (bool ignore_revision) const + string (bool ignore_revision, bool ignore_iteration) const { using std::to_string; // Hidden by to_string (repository_type). @@ -459,10 +467,19 @@ namespace bpkg v += *release; } - if (!ignore_revision && revision != 0) + if (!ignore_revision) { - v += '+'; - v += to_string (revision); + if (revision != 0) + { + v += '+'; + v += to_string (revision); + } + + if (!ignore_iteration && iteration != 0) + { + v += '#'; + v += to_string (iteration); + } } return v; diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index 4d21588..a44ffcc 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -37,6 +37,7 @@ namespace bpkg const std::string upstream; const butl::optional release; const std::uint16_t revision; + const std::uint32_t iteration; // Upstream part canonical representation. // @@ -49,7 +50,7 @@ namespace bpkg // Create a special empty version. It is less than any other valid // version (and is conceptually equivalent to 0-). // - version (): epoch (0), release (""), revision (0) {} + version (): epoch (0), release (""), revision (0), iteration (0) {} // Throw std::invalid_argument if the passed string is not a valid // version representation. @@ -60,24 +61,28 @@ namespace bpkg explicit version (const char* v): version (data_type (v, data_type::parse::full)) {} - // Create the version object from separate epoch, upstream, release, and - // revision parts. + // Create the version object from separate epoch, upstream, release, + // revision, and iteration parts. // // Note that it is possible (and legal) to create the special empty - // version via this interface as version(0, string(), string(), 0). + // version via this interface as version(0, string(), string(), 0, 0). // version (std::uint16_t epoch, std::string upstream, butl::optional release, - std::uint16_t revision); + std::uint16_t revision, + std::uint32_t iteration); version (version&&) = default; version (const version&) = default; version& operator= (version&&); version& operator= (const version&); + // If the revision is ignored, then the iteration (that semantically + // extends the revision) is also ignored, regardless of the argument. + // std::string - string (bool ignore_revision = false) const; + string (bool ignore_revision = false, bool ignore_iteration = false) const; bool operator< (const version& v) const noexcept {return compare (v) < 0;} @@ -97,8 +102,13 @@ namespace bpkg bool operator!= (const version& v) const noexcept {return compare (v) != 0;} + // If the revision is ignored, then the iteration is also ignored, + // regardless of the argument (see above for details). + // int - compare (const version& v, bool ignore_revision = false) const noexcept + compare (const version& v, + bool ignore_revision = false, + bool ignore_iteration = false) const noexcept { if (epoch != v.epoch) return epoch < v.epoch ? -1 : 1; @@ -109,8 +119,14 @@ namespace bpkg if (int c = canonical_release.compare (v.canonical_release)) return c; - if (!ignore_revision && revision != v.revision) - return revision < v.revision ? -1 : 1; + if (!ignore_revision) + { + if (revision != v.revision) + return revision < v.revision ? -1 : 1; + + if (!ignore_iteration && iteration != v.iteration) + return iteration < v.iteration ? -1 : 1; + } return 0; } @@ -119,8 +135,12 @@ namespace bpkg empty () const noexcept { bool e (upstream.empty ()); + assert (!e || - (epoch == 0 && release && release->empty () && revision == 0)); + (epoch == 0 && + release && release->empty () && + revision == 0 && iteration == 0)); + return e; } @@ -131,6 +151,9 @@ namespace bpkg data_type (const char*, parse); + // Note that there is no iteration component as it can't be present in + // the string representation passed to the ctor. + // std::uint16_t epoch; std::string upstream; butl::optional release; @@ -145,6 +168,7 @@ namespace bpkg upstream (std::move (d.upstream)), release (std::move (d.release)), revision (d.revision), + iteration (0), canonical_upstream (std::move (d.canonical_upstream)), canonical_release (std::move (d.canonical_release)) {} }; diff --git a/tests/package-version/driver.cxx b/tests/package-version/driver.cxx index 1cfad1e..4925711 100644 --- a/tests/package-version/driver.cxx +++ b/tests/package-version/driver.cxx @@ -40,11 +40,12 @@ namespace bpkg bad_version (uint16_t e, const string& u, const optional& l, - uint16_t r) + uint16_t r, + uint32_t i = 0) { try { - version bv (e, u, l, r); + version bv (e, u, l, r, i); return false; } catch (const invalid_argument&) @@ -54,15 +55,20 @@ namespace bpkg } static bool - bad_version (uint16_t e, const string& u, const char* l, uint16_t r) + bad_version (uint16_t e, + const string& u, + const char* l, + uint16_t r, + uint32_t i = 0) { - return bad_version (e, u, string (l), r); + return bad_version (e, u, string (l), r, i); } static bool test_constructor (const version& v) { - return v == version (v.epoch, v.upstream, v.release, v.revision); + return v == + version (v.epoch, v.upstream, v.release, v.revision, v.iteration); } int @@ -111,6 +117,7 @@ namespace bpkg assert (bad_version ("1.2.3-~")); // Invalid release. assert (bad_version ("0-")); // Illegal version. assert (bad_version ("0.0-")); // Same. + assert (bad_version ("1.2.3+1#1")); // Unexpected iteration. assert (bad_version (0, "1", "", 1)); // Revision for empty release. assert (bad_version (1, "1~1.1", "", 2)); // Epoch in upstream. @@ -120,9 +127,10 @@ namespace bpkg assert (bad_version (1, "1", "1.1-1", 2)); // Release in release. assert (bad_version (1, "1", "1.1+1", 2)); // Revision in release. - assert (bad_version (1, "", "", 0)); // Unexpected epoch. - assert (bad_version (0, "", "1", 0)); // Unexpected release. - assert (bad_version (0, "", "", 1)); // Unexpected revision. + assert (bad_version (1, "", "", 0)); // Unexpected epoch. + assert (bad_version (0, "", "1", 0)); // Unexpected release. + assert (bad_version (0, "", "", 1)); // Unexpected revision. + assert (bad_version (0, "", "", 0, 1)); // Unexpected iteration. { version v1; @@ -280,7 +288,7 @@ namespace bpkg } { - version v (1, "1", nullopt, 2); + version v (1, "1", nullopt, 2, 0); assert (v.string () == "1~1+2"); assert (!v.release); assert (v.canonical_release == "~"); @@ -288,13 +296,24 @@ namespace bpkg } { - version v (1, "1", string (), 0); + version v (1, "1", string (), 0, 0); assert (v.string () == "1~1-"); assert (v.release && v.release->empty ()); assert (v.canonical_release.empty ()); assert (test_constructor (v)); } + { + version v (1, "2.0", nullopt, 3, 4); + assert (v.string (false, false) == "1~2.0+3#4"); + assert (v.string (true, true) == "1~2.0"); + assert (v.string (true, false) == "1~2.0"); + assert (v.string (false, true) == "1~2.0+3"); + + assert (version (1, "2.0", nullopt, 0, 3).string () == "1~2.0#3"); + assert (version (1, "2.0", nullopt, 3, 0).string () == "1~2.0+3"); + } + assert (version ("a") == version ("a")); assert (version ("a") < version ("b")); assert (version ("a") < version ("aa")); @@ -335,9 +354,24 @@ namespace bpkg assert (version ("1.0-alpha") > version ("1.0-1")); assert (version ("1.0-alpha") == version ("1.0-alpha.0")); - assert (version (1, "2.0", nullopt, 3) == version ("1~2+3")); - assert (version (1, "2.0", string (), 0) == version ("1~2-")); - assert (version (0, "", string (), 0) == version ()); + assert (version (1, "2.0", nullopt, 3, 0) == version ("1~2+3")); + assert (version (1, "2.0", string (), 0, 0) == version ("1~2-")); + assert (version (0, "", string (), 0, 0) == version ()); + + assert (version (1, "2.0", nullopt, 3, 4).compare ( + version (1, "2.0", nullopt, 3, 4)) == 0); + + assert (version (1, "2.0", nullopt, 3, 4).compare ( + version (1, "2.0", nullopt, 4, 3)) < 0); + + assert (version (1, "2.0", nullopt, 3, 4).compare ( + version (1, "2.0", nullopt, 3, 5)) < 0); + + assert (version (1, "2.0", nullopt, 3, 4).compare ( + version (1, "2.0", nullopt, 3, 5), false, true) == 0); + + assert (version (1, "2.0", nullopt, 3, 4).compare ( + version (1, "2.0", nullopt, 5, 6), true) == 0); } catch (const exception& e) { -- cgit v1.1