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 |