From 397d710073eae9ad282bc0df9482a41d621acde5 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 25 Oct 2022 11:52:47 +0300 Subject: Allow omitting minor version number in semantic_version --- libbutl/git.cxx | 4 ++- libbutl/openssl.txx | 1 + libbutl/semantic-version.cxx | 70 +++++++++++++++++++++++++++++++++----------- libbutl/semantic-version.hxx | 70 ++++++++++++++++++++++++++++---------------- libbutl/semantic-version.ixx | 64 +++++++++++++++++++++++----------------- 5 files changed, 138 insertions(+), 71 deletions(-) (limited to 'libbutl') diff --git a/libbutl/git.cxx b/libbutl/git.cxx index cc10c91..f37e16a 100644 --- a/libbutl/git.cxx +++ b/libbutl/git.cxx @@ -36,7 +36,9 @@ namespace butl // MinGit: git version 2.16.1.windows.1 // if (s.compare (0, 12, "git version ") == 0) - return parse_semantic_version (s, 12, "" /* build_separators */); + return parse_semantic_version (s, 12, + semantic_version::allow_build, + "" /* build_separators */); return nullopt; } diff --git a/libbutl/openssl.txx b/libbutl/openssl.txx index 01e854c..f55432d 100644 --- a/libbutl/openssl.txx +++ b/libbutl/openssl.txx @@ -105,6 +105,7 @@ namespace butl optional ver ( parse_semantic_version (string (s, b, e != string::npos ? e - b : e), + semantic_version::allow_build, "" /* build_separators */)); if (!ver) diff --git a/libbutl/semantic-version.cxx b/libbutl/semantic-version.cxx index 3be382f..9e0a1ef 100644 --- a/libbutl/semantic-version.cxx +++ b/libbutl/semantic-version.cxx @@ -3,6 +3,7 @@ #include +#include #include // strchr() #include // move() #include // invalid_argument @@ -52,9 +53,9 @@ namespace butl } semantic_version:: - semantic_version (const std::string& s, size_t p, const char* bs) + semantic_version (const std::string& s, size_t p, flags fs, const char* bs) { - semantic_version_result r (parse_semantic_version_impl (s, p, bs)); + semantic_version_result r (parse_semantic_version_impl (s, p, fs, bs)); if (r.version) *this = move (*r.version); @@ -70,8 +71,27 @@ namespace butl uint64_t min = 0, uint64_t max = uint64_t (~0)); semantic_version_result - parse_semantic_version_impl (const string& s, size_t p, const char* bs) + parse_semantic_version_impl (const string& s, size_t p, + semantic_version::flags fs, + const char* bs) { + bool allow_build ((fs & semantic_version::allow_build) != 0); + + // If build separators are specified, then the allow_build flag must be + // specified explicitly. + // + assert (bs == nullptr || allow_build); + + if (allow_build && bs == nullptr) + bs = "-+"; + + bool require_minor ((fs & semantic_version::allow_omit_minor) == 0); + + if (!require_minor) + fs |= semantic_version::allow_omit_patch; + + bool require_patch ((fs & semantic_version::allow_omit_patch) == 0); + auto bail = [] (string m) { return semantic_version_result {nullopt, move (m)}; @@ -82,31 +102,47 @@ namespace butl if (!parse_uint64 (s, p, r.major)) return bail ("invalid major version"); - if (s[p] != '.') - return bail ("'.' expected after major version"); - - if (!parse_uint64 (s, ++p, r.minor)) - return bail ("invalid minor version"); - - if (s[p] == '.') + if (s[p] == '.') // Is there a minor version? { - // Treat it as build if failed to parse as patch (e.g., 1.2.alpha). + // Try to parse the minor version and treat it as build on failure + // (e.g., 1.alpha). // - if (!parse_uint64 (s, ++p, r.patch)) + if (parse_uint64 (s, ++p, r.minor)) { - //if (require_patch) - // return bail ("invalid patch version"); + if (s[p] == '.') // Is there a patch version? + { + // Try to parse the patch version and treat it as build on failure + // (e.g., 1.2.alpha). + // + if (parse_uint64 (s, ++p, r.patch)) + ; + else + { + if (require_patch) + return bail ("invalid patch version"); + + --p; + // Fall through. + } + } + else if (require_patch) + return bail ("'.' expected after minor version"); + } + else + { + if (require_minor) + return bail ("invalid minor version"); --p; // Fall through. } } - //else if (require_patch) - // return bail ("'.' expected after minor version"); + else if (require_minor) + return bail ("'.' expected after major version"); if (char c = s[p]) { - if (bs == nullptr || (*bs != '\0' && strchr (bs, c) == nullptr)) + if (!allow_build || (*bs != '\0' && strchr (bs, c) == nullptr)) return bail ("junk after version"); r.build.assign (s, p, string::npos); diff --git a/libbutl/semantic-version.hxx b/libbutl/semantic-version.hxx index 16f3d56..1dc7d1d 100644 --- a/libbutl/semantic-version.hxx +++ b/libbutl/semantic-version.hxx @@ -27,15 +27,9 @@ namespace butl { // Semantic or semantic-like version. // - // .[.][] + // [.[.]][] // - // If the patch component is absent, then it defaults to 0. - // - // @@ Currently there is no way to enforce the three-component version. - // Supporting this will require changing allow_build to a bit-wise - // flag. See parse_semantic_version_impl() for some sketched code. - // We may also want to pass these flags to string() to not print - // 0 patch. + // If the minor and patch components are absent, then they default to 0. // // By default, a version containing the component is considered // valid only if separated from with '-' (semver pre-release) or '+' @@ -63,23 +57,36 @@ namespace butl std::uint64_t patch, std::string build = ""); - // The build_separators argument can be NULL (no build component allowed), - // empty (any build component allowed), or a string of characters to allow - // as separators. When allow_build is true build_separators defaults to - // "-+". + // If the allow_build flag is specified, then build_separators argument + // can be a string of characters to allow as separators, empty (any build + // component allowed), or NULL (defaults to "-+"). // - explicit - semantic_version (const std::string&, bool allow_build = true); + // Note: allow_omit_minor implies allow_omit_patch. + // + enum flags + { + none = 0, // Exact .. form. + allow_omit_minor = 0x01, // Allow form. + allow_omit_patch = 0x02, // Allow . form. + allow_build = 0x04, // Allow ..- form. + }; - semantic_version (const std::string&, const char* build_separators); + explicit + semantic_version (const std::string&, + flags = none, + const char* build_separators = nullptr); // As above but parse from the specified position until the end of the // string. // - semantic_version (const std::string&, std::size_t pos, bool = true); - - semantic_version (const std::string&, std::size_t pos, const char*); + semantic_version (const std::string&, + std::size_t pos, + flags = none, + const char* = nullptr); + // @@ We may also want to pass allow_* flags not to print 0 minor/patch or + // maybe invent ignore_* flags. + // std::string string (bool ignore_build = false) const; @@ -116,16 +123,15 @@ namespace butl // Try to parse a string as a semantic version returning nullopt if invalid. // optional - parse_semantic_version (const std::string&, bool allow_build = true); + parse_semantic_version (const std::string&, + semantic_version::flags = semantic_version::none, + const char* build_separators = nullptr); optional - parse_semantic_version (const std::string&, const char* build_separators); - - optional - parse_semantic_version (const std::string&, std::size_t pos, bool = true); - - optional - parse_semantic_version (const std::string&, std::size_t pos, const char*); + parse_semantic_version (const std::string&, + std::size_t pos, + semantic_version::flags = semantic_version::none, + const char* = nullptr); // NOTE: comparison operators take the build component into account. // @@ -170,6 +176,18 @@ namespace butl { return o << x.string (); } + + inline semantic_version::flags + operator& (semantic_version::flags, semantic_version::flags); + + inline semantic_version::flags + operator| (semantic_version::flags, semantic_version::flags); + + inline semantic_version::flags + operator&= (semantic_version::flags&, semantic_version::flags); + + inline semantic_version::flags + operator|= (semantic_version::flags&, semantic_version::flags); } #include diff --git a/libbutl/semantic-version.ixx b/libbutl/semantic-version.ixx index 6bf7584..67cd8c0 100644 --- a/libbutl/semantic-version.ixx +++ b/libbutl/semantic-version.ixx @@ -15,23 +15,9 @@ namespace butl { } - // Note: the order is important to MinGW GCC (DLL linkage). - // inline semantic_version:: - semantic_version (const std::string& s, std::size_t p, bool ab) - : semantic_version (s, p, ab ? "-+" : nullptr) - { - } - - inline semantic_version:: - semantic_version (const std::string& s, const char* bs) - : semantic_version (s, 0, bs) - { - } - - inline semantic_version:: - semantic_version (const std::string& s, bool ab) - : semantic_version (s, ab ? "-+" : nullptr) + semantic_version (const std::string& s, flags fs, const char* bs) + : semantic_version (s, 0, fs, bs) { } @@ -42,29 +28,53 @@ namespace butl }; LIBBUTL_SYMEXPORT semantic_version_result - parse_semantic_version_impl (const std::string&, std::size_t, const char*); + parse_semantic_version_impl (const std::string&, + std::size_t, + semantic_version::flags, + const char*); inline optional - parse_semantic_version (const std::string& s, bool ab) + parse_semantic_version (const std::string& s, + semantic_version::flags fs, + const char* bs) { - return parse_semantic_version (s, ab ? "-+" : nullptr); + return parse_semantic_version_impl (s, 0, fs, bs).version; } inline optional - parse_semantic_version (const std::string& s, const char* bs) + parse_semantic_version (const std::string& s, + std::size_t p, + semantic_version::flags fs, + const char* bs) { - return parse_semantic_version_impl (s, 0, bs).version; + return parse_semantic_version_impl (s, p, fs, bs).version; } - inline optional - parse_semantic_version (const std::string& s, std::size_t p, bool ab) + inline semantic_version::flags + operator& (semantic_version::flags x, semantic_version::flags y) { - return parse_semantic_version (s, p, ab ? "-+" : nullptr); + return x &= y; } - inline optional - parse_semantic_version (const std::string& s, std::size_t p, const char* bs) + inline semantic_version::flags + operator| (semantic_version::flags x, semantic_version::flags y) + { + return x |= y; + } + + inline semantic_version::flags + operator&= (semantic_version::flags& x, semantic_version::flags y) + { + return x = static_cast ( + static_cast (x) & + static_cast (y)); + } + + inline semantic_version::flags + operator|= (semantic_version::flags& x, semantic_version::flags y) { - return parse_semantic_version_impl (s, p, bs).version; + return x = static_cast ( + static_cast (x) | + static_cast (y)); } } -- cgit v1.1