| Uses: Niebler/range-v3 | Uses: Niebler/range-v3 | ||||
| Uses: nlohmann/json | Uses: nlohmann/json | ||||
| Uses: neo/buffer | Uses: neo/buffer | ||||
| Uses: neo/sqlite3 | |||||
| Uses: neo/sqlite3 | |||||
| Uses: semver/semver |
| Depends: range-v3 0.9.1 | Depends: range-v3 0.9.1 | ||||
| Depends: nlohmann-json 3.7.1 | Depends: nlohmann-json 3.7.1 | ||||
| Depends: neo-sqlite3 0.2.0 | Depends: neo-sqlite3 0.2.0 | ||||
| Depends: semver 0.1.0 | |||||
| Test-Driver: Catch-Main | Test-Driver: Catch-Main |
| # XXX: Don't depend on a moving revision! | # XXX: Don't depend on a moving revision! | ||||
| Remote-Package: neo-buffer 0.1.0; git url=https://github.com/vector-of-bool/neo-buffer.git ref=develop | Remote-Package: neo-buffer 0.1.0; git url=https://github.com/vector-of-bool/neo-buffer.git ref=develop | ||||
| Remote-Package: neo-sqlite3 0.2.0; git url=https://github.com/vector-of-bool/neo-sqlite3.git ref=0.2.0 | Remote-Package: neo-sqlite3 0.2.0; git url=https://github.com/vector-of-bool/neo-sqlite3.git ref=0.2.0 | ||||
| Remote-Package: semver 0.1.0; git url=https://github.com/vector-of-bool/semver.git ref=0.1.0 |
| #include <dds/util/fs.hpp> | #include <dds/util/fs.hpp> | ||||
| #include <string> | #include <string> | ||||
| #include <vector> | |||||
| #include <string_view> | #include <string_view> | ||||
| namespace dds { | namespace dds { |
| #include "./version.hpp" | |||||
| #include <algorithm> | |||||
| #include <array> | |||||
| #include <cassert> | |||||
| #include <charconv> | |||||
| using namespace semver; | |||||
| using siter = std::string_view::iterator; | |||||
| namespace { | |||||
| version parse(const char* ptr, std::size_t size) { | |||||
| version ret; | |||||
| // const auto str_begin = ptr; | |||||
| const auto str_end = ptr + size; | |||||
| std::from_chars_result fc_res; | |||||
| auto did_error = [&](int elem) { return fc_res.ec == std::errc::invalid_argument || elem < 0; }; | |||||
| // Parse major | |||||
| fc_res = std::from_chars(ptr, str_end, ret.major); | |||||
| if (did_error(ret.major) || fc_res.ptr == str_end || *fc_res.ptr != '.') { | |||||
| throw invalid_version(0); | |||||
| } | |||||
| // Parse minor | |||||
| ptr = fc_res.ptr + 1; | |||||
| fc_res = std::from_chars(ptr, str_end, ret.minor); | |||||
| if (did_error(ret.minor) || fc_res.ptr == str_end || *fc_res.ptr != '.') { | |||||
| throw invalid_version(ptr - str_end); | |||||
| } | |||||
| // Parse patch | |||||
| ptr = fc_res.ptr + 1; | |||||
| fc_res = std::from_chars(ptr, str_end, ret.patch); | |||||
| if (did_error(ret.patch)) { | |||||
| throw invalid_version(ptr - str_end); | |||||
| } | |||||
| if (fc_res.ptr != str_end) { | |||||
| assert(false && "More complex version numbers are not ready yet!"); | |||||
| throw invalid_version(-42); | |||||
| } | |||||
| return ret; | |||||
| } | |||||
| } // namespace | |||||
| version version::parse(std::string_view s) { return ::parse(s.data(), s.size()); } | |||||
| std::string version::to_string() const noexcept { | |||||
| std::array<char, 256> buffer; | |||||
| const auto buf_begin = buffer.data(); | |||||
| auto buf_ptr = buf_begin; | |||||
| const auto buf_end = buf_ptr + buffer.size(); | |||||
| auto conv_one = [&](auto n) { | |||||
| auto tc_res = std::to_chars(buf_ptr, buf_end, n); | |||||
| if (tc_res.ec == std::errc::value_too_large || tc_res.ptr == buf_end) { | |||||
| assert(false && "Our buffers weren't big enough! This is a bug!"); | |||||
| std::terminate(); | |||||
| } | |||||
| buf_ptr = tc_res.ptr; | |||||
| }; | |||||
| conv_one(major); | |||||
| *buf_ptr++ = '.'; | |||||
| conv_one(minor); | |||||
| *buf_ptr++ = '.'; | |||||
| conv_one(patch); | |||||
| return std::string(buf_begin, (buf_ptr - buf_begin)); | |||||
| } |
| #pragma once | |||||
| #include <stdexcept> | |||||
| #include <string> | |||||
| #include <string_view> | |||||
| #include <tuple> | |||||
| #include <vector> | |||||
| namespace semver { | |||||
| class invalid_version : public std::runtime_error { | |||||
| std::ptrdiff_t _offset = 0; | |||||
| public: | |||||
| invalid_version(std::ptrdiff_t n) | |||||
| : runtime_error("Invalid version number") | |||||
| , _offset(n) {} | |||||
| auto offset() const noexcept { return _offset; } | |||||
| }; | |||||
| struct version { | |||||
| int major = 0; | |||||
| int minor = 0; | |||||
| int patch = 0; | |||||
| static version parse(std::string_view s); | |||||
| std::string to_string() const noexcept; | |||||
| auto tie() const noexcept { return std::tie(major, minor, patch); } | |||||
| auto tie() noexcept { return std::tie(major, minor, patch); } | |||||
| }; | |||||
| inline bool operator!=(const version& lhs, const version& rhs) noexcept { | |||||
| return lhs.tie() != rhs.tie(); | |||||
| } | |||||
| inline bool operator<(const version& lhs, const version& rhs) noexcept { | |||||
| return lhs.tie() < rhs.tie(); | |||||
| } | |||||
| inline std::string to_string(const version& ver) noexcept { return ver.to_string(); } | |||||
| } // namespace semver |
| #include "./version.hpp" | |||||
| #include <catch2/catch.hpp> | |||||
| TEST_CASE("Parsing") { | |||||
| auto v1 = semver::version::parse("1.2.3"); | |||||
| CHECK(v1.major == 1); | |||||
| CHECK(v1.minor == 2); | |||||
| CHECK(v1.patch == 3); | |||||
| CHECK(v1.to_string() == "1.2.3"); | |||||
| v1.patch = 55; | |||||
| CHECK(v1.to_string() == "1.2.55"); | |||||
| v1.major = 999999; | |||||
| CHECK(v1.to_string() == "999999.2.55"); | |||||
| } |
| Compiler-ID: GNU | Compiler-ID: GNU | ||||
| C++-Version: C++17 | C++-Version: C++17 | ||||
| C-Compiler: gcc-8 | |||||
| C++-Compiler: g++-8 | |||||
| C-Compiler: gcc-9 | |||||
| C++-Compiler: g++-9 | |||||
| Flags: -D SPDLOG_COMPILED_LIB -fconcepts -Werror=return-type | Flags: -D SPDLOG_COMPILED_LIB -fconcepts -Werror=return-type | ||||
| Optimize: True | |||||
| # Debug: True | |||||
| # Optimize: True | |||||
| Debug: True | |||||
| # Link-Flags: -fsanitize=address | # Link-Flags: -fsanitize=address | ||||
| Link-Flags: -fuse-ld=lld | |||||
| Compiler-Launcher: ccache | Compiler-Launcher: ccache |