aboutsummaryrefslogtreecommitdiff
path: root/libbutl/semantic-version.mxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-08-14 14:09:32 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-08-14 14:09:32 +0200
commit8561af5a2551ec453c9888125de273f2cc2940c0 (patch)
tree4458e955d33f9aaf7e904c8de717803a33ecc81f /libbutl/semantic-version.mxx
parent7c665d965c0ebb259849d5032faa0854c6ae94f2 (diff)
Add support for parsing semantic and semantic-like versions
Diffstat (limited to 'libbutl/semantic-version.mxx')
-rw-r--r--libbutl/semantic-version.mxx187
1 files changed, 187 insertions, 0 deletions
diff --git a/libbutl/semantic-version.mxx b/libbutl/semantic-version.mxx
new file mode 100644
index 0000000..d64eb82
--- /dev/null
+++ b/libbutl/semantic-version.mxx
@@ -0,0 +1,187 @@
+// file : libbutl/semantic-version.mxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef __cpp_modules
+#pragma once
+#endif
+
+// C includes.
+
+#ifndef __cpp_lib_modules
+#include <string>
+#include <cstddef> // size_t
+#include <cstdint> // uint*_t
+#include <utility> // move()
+#include <ostream>
+#endif
+
+// Other includes.
+
+#ifdef __cpp_modules
+export module butl.semantic_version;
+#ifdef __cpp_lib_modules
+import std.core;
+import std.io;
+#endif
+import butl.optional;
+#else
+#include <libbutl/optional.mxx>
+#endif
+
+#include <libbutl/export.hxx>
+
+// FreeBSD defines these macros in its <sys/types.h>.
+//
+#ifdef major
+# undef major
+#endif
+
+#ifdef minor
+# undef minor
+#endif
+
+LIBBUTL_MODEXPORT namespace butl
+{
+ // Semantic or semantic-like version.
+ //
+ // <major>.<minor>[.<patch>][<build>]
+ //
+ // 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.
+ //
+ // By default, a version containing the <build> component is considered
+ // valid only if separated from <patch> with '-' (semver pre-release) or '+'
+ // (semver build metadata). However, as discussed below, the list of valid
+ // separators can be customized to recognize other semver-like formats.
+ //
+ // Note also that the format of semver pre-release and build metadata are
+ // not validated.
+ //
+ struct LIBBUTL_SYMEXPORT semantic_version
+ {
+ std::uint64_t major = 0;
+ std::uint64_t minor = 0;
+ std::uint64_t patch = 0;
+ std::string build;
+
+ // Construct the semantic version from various representations. Throw
+ // std::invalid_argument if the format is not recognizable or components
+ // are invalid.
+ //
+ semantic_version () = default;
+
+ semantic_version (std::uint64_t major,
+ std::uint64_t minor,
+ 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
+ // "-+".
+ //
+ explicit
+ semantic_version (const std::string&, bool allow_build = true);
+
+ semantic_version (const std::string&, const char* build_separators);
+
+ // 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*);
+
+ std::string
+ string (bool ignore_build = false) const;
+
+ // Numeric representation in the AAABBBCCC0000 form, where:
+ //
+ // AAA - major version number
+ // BBB - minor version number
+ // CCC - patch version number
+ //
+ // See standard version for details.
+ //
+ explicit
+ semantic_version (std::uint64_t numeric, std::string build = "");
+
+ // If any of the major/minor/patch components is greater than 999, then
+ // throw std::invalid_argument. The build component is ignored.
+ //
+ std::uint64_t
+ numeric () const;
+
+ // Unless instructed to ignore, the build components are compared
+ // lexicographically.
+ //
+ int
+ compare (const semantic_version&, bool ignore_build = false) const;
+ };
+
+ // Try to parse a string as a semantic version returning nullopt if invalid.
+ //
+ optional<semantic_version>
+ parse_semantic_version (const std::string&, bool allow_build = true);
+
+ optional<semantic_version>
+ parse_semantic_version (const std::string&, const char* build_separators);
+
+ optional<semantic_version>
+ parse_semantic_version (const std::string&, std::size_t pos, bool = true);
+
+ optional<semantic_version>
+ parse_semantic_version (const std::string&, std::size_t pos, const char*);
+
+ // NOTE: comparison operators take the build component into account.
+ //
+ inline bool
+ operator< (const semantic_version& x, const semantic_version& y) noexcept
+ {
+ return x.compare (y) < 0;
+ }
+
+ inline bool
+ operator> (const semantic_version& x, const semantic_version& y) noexcept
+ {
+ return x.compare (y) > 0;
+ }
+
+ inline bool
+ operator== (const semantic_version& x, const semantic_version& y) noexcept
+ {
+ return x.compare (y) == 0;
+ }
+
+ inline bool
+ operator<= (const semantic_version& x, const semantic_version& y) noexcept
+ {
+ return x.compare (y) <= 0;
+ }
+
+ inline bool
+ operator>= (const semantic_version& x, const semantic_version& y) noexcept
+ {
+ return x.compare (y) >= 0;
+ }
+
+ inline bool
+ operator!= (const semantic_version& x, const semantic_version& y) noexcept
+ {
+ return !(x == y);
+ }
+
+ inline std::ostream&
+ operator<< (std::ostream& o, const semantic_version& x)
+ {
+ return o << x.string ();
+ }
+}
+
+#include <libbutl/semantic-version.ixx>