diff --git a/Base/usr/share/man/man1/semver.md b/Base/usr/share/man/man1/semver.md new file mode 100644 index 000000000000000..9ad7ae02f91cd56 --- /dev/null +++ b/Base/usr/share/man/man1/semver.md @@ -0,0 +1,49 @@ +## Name + +semver - utility to compare + +## Synopsis + +```**sh +$ semver [--satisfies SPEC] [--separator SEPARATOR] [--bump BUMP_TYPE] +``` + +## Description + +Validate, compare, bump, or check if the versions satisfies the spec or not. + +## Options + +* `--help`: Display help message and exit +* `--version`: Display version +* `--satisfies`: Spec string to filter all the versions that satisfies it +* `-s`, `--separator`: Normal version part separator (default: `.`) +* `-b`, `--bump`: Part of the version to bump. You must choose from `major`, `minor`, `patch`, or `prerelease` + +## Arguments + +* `versions`: raw version strings to process + +## Examples + +```sh +# Try +$ mkdir serenity ; echo cool > serenity/cool.txt +$ rmdir serenity +rmdir: Directory not empty + +# Remove empty directory +$ mkdir example +$ ls -a example +. .. +$ rmdir example +$ ls -a example +example: No such file or directory + +# Removes foo/bar/baz/, foo/bar/ and foo/ +$ rmdir -p foo/bar/baz/ +``` + +## See also +* [`mkdir`(1)](help://man/1/mkdir) +* [`rm`(1)](help://man/1/rm) diff --git a/Userland/Utilities/CMakeLists.txt b/Userland/Utilities/CMakeLists.txt index 3a87027d8097a91..0fa3a44b54ce5bb 100644 --- a/Userland/Utilities/CMakeLists.txt +++ b/Userland/Utilities/CMakeLists.txt @@ -137,6 +137,7 @@ target_link_libraries(realpath PRIVATE LibFileSystem) target_link_libraries(run-tests PRIVATE LibCoredump LibDebug LibFileSystem LibRegex) target_link_libraries(rm PRIVATE LibFileSystem) target_link_libraries(sed PRIVATE LibRegex LibFileSystem) +target_link_libraries(semver PRIVATE LibSemVer) target_link_libraries(shot PRIVATE LibFileSystem LibGfx LibGUI LibIPC) target_link_libraries(slugify PRIVATE LibUnicode) target_link_libraries(sql PRIVATE LibFileSystem LibIPC LibLine LibSQL) diff --git a/Userland/Utilities/semver.cpp b/Userland/Utilities/semver.cpp new file mode 100644 index 000000000000000..29c37161e4c2ea8 --- /dev/null +++ b/Userland/Utilities/semver.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023, Gurkirat Singh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +ErrorOr serenity_main(Main::Arguments arguments) +{ + Vector versions; + StringView spec_sv; + StringView bump_type_sv; + StringView normal_version_separator_sv = "."sv; + + Core::ArgsParser parser; + parser.add_positional_argument(versions, "List of all the versions to process", "versions"); + parser.add_option(spec_sv, "Spec string to filter all the versions that satisfies it", "satisfies", '\0', "SPEC"); + parser.add_option(normal_version_separator_sv, "Normal version part separator (default: `.`)", "separator", 's', "SEPARATOR"); + parser.add_option(bump_type_sv, "Part of the version to bump. You must choose from `major`, `minor`, `patch`, or `prerelease`", "bump", 'b', "BUMP_TYPE"); + + if (!parser.parse(arguments)) + return Error::from_string_view("Unable to parse the arguments"sv); + + if (normal_version_separator_sv.is_empty()) + return Error::from_string_view("Omit the -s or --separator option to use the default instead"sv); + if (normal_version_separator_sv.length() > 1) + return Error::from_string_view("Normal version separator must be exactly 1 character long"sv); + + auto const normal_version_separator = normal_version_separator_sv[0]; + if (normal_version_separator != '.' && normal_version_separator != '-') + return Error::from_string_view("Only . or - are supported as normal version separator"sv); + + Vector parsed_semvers; + TRY(parsed_semvers.try_ensure_capacity(versions.size())); + + for (auto const& version : versions) + parsed_semvers.unchecked_append(TRY(SemVer::from_string_view(version, normal_version_separator))); + + if (!spec_sv.is_empty()) { + outln("Printing all the versions out of {} statisfies {} ---", parsed_semvers.size(), spec_sv); + + for (auto const& parsed_semver : parsed_semvers) { + if (parsed_semver.satisfies(spec_sv)) + outln("{}", parsed_semver); + } + + return 0; + } + + if (!bump_type_sv.is_empty()) { + + SemVer::BumpType bump_type; + if (bump_type_sv == "major"sv) + bump_type = SemVer::BumpType::Major; + else if (bump_type_sv == "minor"sv) + bump_type = SemVer::BumpType::Minor; + else if (bump_type_sv == "patch"sv) + bump_type = SemVer::BumpType::Patch; + else if (bump_type_sv == "prerelease"sv) + bump_type = SemVer::BumpType::Prerelease; + else + return Error::from_string_view("Bump type is invalid. Choose from `major`, `minor`, `patch` or `prerelease`"sv); + + outln("Bumping {} part of {} versions ---", bump_type_sv, parsed_semvers.size()); + + for (auto const& parsed_semver : parsed_semvers) + outln("{}", parsed_semver.bump(bump_type)); + + return 0; + } + + outln("Sorting {} versions in ascending order ---", parsed_semvers.size()); + quick_sort(parsed_semvers, [](SemVer::SemVer const& lhs, SemVer::SemVer const& rhs) { + return lhs < rhs; + }); + + for (auto const& parsed_semver : AK::ReverseWrapper::in_reverse(parsed_semvers)) + outln("{}", parsed_semver); + + return 0; +}