From 775f4a59988393db543624aab15f445635800e4e Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sat, 12 Jul 2025 17:23:27 +0100 Subject: [PATCH 1/3] feat: support premajor, preminor and prepatch inc operations --- include/version_weaver.h | 8 ++++---- src/version_weaver.cpp | 36 +++++++++++++++++++++++++----------- tests/basictests.cpp | 15 +++++++++++++++ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/include/version_weaver.h b/include/version_weaver.h index f6cce60..a742760 100644 --- a/include/version_weaver.h +++ b/include/version_weaver.h @@ -95,12 +95,12 @@ enum release_type { MAJOR, MINOR, PATCH, + PRE_MAJOR, + PRE_MINOR, + PRE_PATCH, // TODO: also support - // - PRE_MAJOR - // - PRE_MINOR - // - PRE_PATCH - // - PRE_RELEASE // - RELEASE + // - PRE_RELEASE }; // Increment the version according to the provided release type. diff --git a/src/version_weaver.cpp b/src/version_weaver.cpp index 661de80..0b6a620 100644 --- a/src/version_weaver.cpp +++ b/src/version_weaver.cpp @@ -36,8 +36,11 @@ std::string minimum(std::string_view range) { return ""; } std::expected inc(version input, release_type release_type) { + version_weaver::version result; + std::string incremented; switch (release_type) { - case MAJOR: { + case MAJOR: + case PRE_MAJOR: { int major_int; auto [ptr, ec] = std::from_chars(input.major.data(), @@ -46,11 +49,12 @@ std::expected inc(version input, return std::unexpected(parse_error::INVALID_MAJOR); } auto incremented_major_int = major_int + 1; - auto major = std::to_string(incremented_major_int); - auto new_version = version_weaver::version{major, "0", "0"}; - return new_version; + incremented = std::move(std::to_string(incremented_major_int)); + result = version_weaver::version{incremented, "0", "0"}; + break; } - case MINOR: { + case MINOR: + case PRE_MINOR: { int minor_int; auto [ptr, ec] = std::from_chars(input.minor.data(), @@ -59,11 +63,13 @@ std::expected inc(version input, return std::unexpected(parse_error::INVALID_MINOR); } auto incremented_minor_int = minor_int + 1; - return version_weaver::version{ - input.major, std::to_string(incremented_minor_int), "0"}; + incremented = std::move(std::to_string(incremented_minor_int)); + result = version_weaver::version{input.major, incremented, "0"}; + break; } - case PATCH: { - if (input.pre_release) { + case PATCH: + case PRE_PATCH: { + if (input.pre_release && release_type != PRE_PATCH) { return version_weaver::version{input.major, input.minor, input.patch}; } int patch_int; @@ -74,12 +80,20 @@ std::expected inc(version input, return std::unexpected(parse_error::INVALID_PATCH); } auto incremented_patch_int = patch_int + 1; - return version_weaver::version{input.major, input.minor, - std::to_string(incremented_patch_int)}; + incremented = std::move(std::to_string(incremented_patch_int)); + result = version_weaver::version{input.major, input.minor, incremented}; + break; } default: return std::unexpected(parse_error::INVALID_RELEASE_TYPE); } + + if (release_type == PRE_MAJOR || release_type == PRE_MINOR || + release_type == PRE_PATCH) { + result.pre_release = "0"; + } + + return result; } constexpr inline void trim_whitespace(std::string_view* input) noexcept { diff --git a/tests/basictests.cpp b/tests/basictests.cpp index 9d7a9f6..bdad2ce 100644 --- a/tests/basictests.cpp +++ b/tests/basictests.cpp @@ -229,6 +229,21 @@ std::vector inc_values = { {version_weaver::version{"1", "2", "3", "alpha.0.beta"}, "1.2.3-alpha.0.beta", version_weaver::release_type::PATCH, "1.2.3", version_weaver::version{"1", "2", "3"}}, + {version_weaver::version{"1", "2", "0"}, "1.2.0", + version_weaver::release_type::PRE_MAJOR, "2.0.0-0", + version_weaver::version{"2", "0", "0", "0"}}, + {version_weaver::version{"1", "2", "0"}, "1.2.0", + version_weaver::release_type::PRE_MINOR, "1.3.0-0", + version_weaver::version{"1", "3", "0", "0"}}, + {version_weaver::version{"1", "2", "3", "1"}, "1.2.3-1", + version_weaver::release_type::PRE_MINOR, "1.3.0-0", + version_weaver::version{"1", "3", "0", "0"}}, + {version_weaver::version{"1", "2", "0"}, "1.2.0", + version_weaver::release_type::PRE_PATCH, "1.2.1-0", + version_weaver::version{"1", "2", "1", "0"}}, + {version_weaver::version{"1", "2", "0", "1"}, "1.2.0-1", + version_weaver::release_type::PRE_PATCH, "1.2.1-0", + version_weaver::version{"1", "2", "1", "0"}}, }; TEST(basictests, inc) { From a4c39a05c757e94923bcbc7390860efa61825bba Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sat, 12 Jul 2025 17:46:37 +0100 Subject: [PATCH 2/3] feat: support release inc operations --- include/version_weaver.h | 2 +- src/version_weaver.cpp | 6 ++++++ tests/basictests.cpp | 12 ++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/version_weaver.h b/include/version_weaver.h index a742760..9f18706 100644 --- a/include/version_weaver.h +++ b/include/version_weaver.h @@ -98,8 +98,8 @@ enum release_type { PRE_MAJOR, PRE_MINOR, PRE_PATCH, + RELEASE, // TODO: also support - // - RELEASE // - PRE_RELEASE }; diff --git a/src/version_weaver.cpp b/src/version_weaver.cpp index 0b6a620..7ba0eb5 100644 --- a/src/version_weaver.cpp +++ b/src/version_weaver.cpp @@ -84,6 +84,12 @@ std::expected inc(version input, result = version_weaver::version{input.major, input.minor, incremented}; break; } + case RELEASE: { + if (!input.pre_release.has_value()) { + return std::unexpected(parse_error::INVALID_INPUT); + } + return version_weaver::version{input.major, input.minor, input.patch}; + }; default: return std::unexpected(parse_error::INVALID_RELEASE_TYPE); } diff --git a/tests/basictests.cpp b/tests/basictests.cpp index bdad2ce..7aaa6a1 100644 --- a/tests/basictests.cpp +++ b/tests/basictests.cpp @@ -244,6 +244,18 @@ std::vector inc_values = { {version_weaver::version{"1", "2", "0", "1"}, "1.2.0-1", version_weaver::release_type::PRE_PATCH, "1.2.1-0", version_weaver::version{"1", "2", "1", "0"}}, + {version_weaver::version{"1", "0", "0", "1"}, "1.0.0-1", + version_weaver::release_type::RELEASE, "1.0.0", + version_weaver::version{"1", "0", "0"}}, + {version_weaver::version{"1", "2", "0", "1"}, "1.2.0-1", + version_weaver::release_type::RELEASE, "1.2.0", + version_weaver::version{"1", "2", "0"}}, + {version_weaver::version{"1", "2", "3", "1"}, "1.2.3-1", + version_weaver::release_type::RELEASE, "1.2.3", + version_weaver::version{"1", "2", "3"}}, + {version_weaver::version{"1", "2", "3"}, "1.2.3", + version_weaver::release_type::RELEASE, "1.2.3", + std::unexpected(version_weaver::parse_error::INVALID_INPUT)}, }; TEST(basictests, inc) { From 304a0efaa5a6dfad2059d06bab29bc0450ad9045 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sat, 12 Jul 2025 18:03:23 +0100 Subject: [PATCH 3/3] feat: support prerelease inc operations (but only on int prerelease values) --- include/version_weaver.h | 4 ++-- src/version_weaver.cpp | 25 ++++++++++++++++++++++--- tests/basictests.cpp | 6 ++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/include/version_weaver.h b/include/version_weaver.h index 9f18706..b9e9a1f 100644 --- a/include/version_weaver.h +++ b/include/version_weaver.h @@ -81,6 +81,7 @@ enum parse_error { INVALID_MAJOR, INVALID_MINOR, INVALID_PATCH, + INVALID_PRERELEASE, INVALID_RELEASE_TYPE, }; @@ -99,8 +100,7 @@ enum release_type { PRE_MINOR, PRE_PATCH, RELEASE, - // TODO: also support - // - PRE_RELEASE + PRE_RELEASE, }; // Increment the version according to the provided release type. diff --git a/src/version_weaver.cpp b/src/version_weaver.cpp index 7ba0eb5..64c3c70 100644 --- a/src/version_weaver.cpp +++ b/src/version_weaver.cpp @@ -68,10 +68,29 @@ std::expected inc(version input, break; } case PATCH: - case PRE_PATCH: { - if (input.pre_release && release_type != PRE_PATCH) { + case PRE_PATCH: + case PRE_RELEASE: { + if (input.pre_release.has_value() && release_type == PATCH) { return version_weaver::version{input.major, input.minor, input.patch}; } + if (release_type == PRE_RELEASE && input.pre_release.has_value()) { + // TODO: support non-int pre_releases as well + // (see: + // https://github.com/npm/node-semver/blob/d17aebf8/test/fixtures/increments.js#L22-L36) + auto pre_release_value = input.pre_release.value(); + int prerelease_int; + auto [ptr, ec] = + std::from_chars(pre_release_value.data(), + pre_release_value.data() + pre_release_value.size(), + prerelease_int); + if (ec != std::errc()) { + return std::unexpected(parse_error::INVALID_PRERELEASE); + } + auto incremented_prerelease_int = prerelease_int + 1; + incremented = std::move(std::to_string(incremented_prerelease_int)); + return version_weaver::version{input.major, input.minor, input.patch, + incremented}; + } int patch_int; auto [ptr, ec] = std::from_chars(input.patch.data(), @@ -95,7 +114,7 @@ std::expected inc(version input, } if (release_type == PRE_MAJOR || release_type == PRE_MINOR || - release_type == PRE_PATCH) { + release_type == PRE_PATCH || release_type == PRE_RELEASE) { result.pre_release = "0"; } diff --git a/tests/basictests.cpp b/tests/basictests.cpp index 7aaa6a1..e1aea0e 100644 --- a/tests/basictests.cpp +++ b/tests/basictests.cpp @@ -256,6 +256,12 @@ std::vector inc_values = { {version_weaver::version{"1", "2", "3"}, "1.2.3", version_weaver::release_type::RELEASE, "1.2.3", std::unexpected(version_weaver::parse_error::INVALID_INPUT)}, + {version_weaver::version{"1", "2", "4"}, "1.2.4", + version_weaver::release_type::PRE_RELEASE, "1.2.5-0", + version_weaver::version{"1", "2", "5", "0"}}, + {version_weaver::version{"1", "2", "3", "0"}, "1.2.3-0", + version_weaver::release_type::PRE_RELEASE, "1.2.3-1", + version_weaver::version{"1", "2", "3", "1"}}, }; TEST(basictests, inc) {