diff options
| author | Leitwolf11 | 2026-11-05 06:29:20 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-11-05 06:29:20 +0300 |
| commit | 52490ff852f3d72ce9c9eb76898080223e9e17fd (patch) | |
| tree | 4dd64d7cdce703fa15956dfa51bf587b3f397153 /Sources/CommandLine.cpp | |
| download | tazelish-52490ff852f3d72ce9c9eb76898080223e9e17fd.tar.gz | |
Start Tazelish with its command line and version files
Tazelish installs Tazel and chooses which version of it runs, for those
who use Tazel alone. This first part holds everything that needs no
download:
- Versions: MAJOR.SEMIMAJOR.MINOR.PATCH without leading zeros,
unstable, nightly, and YYYY-MM-DD-unstable or -nightly with a date
the calendar has.
- The command line, read by hand into a typed command: install, use
and default with --force before or after the version, self
update|remove|purge, and help with or without a command. A wrong
command line ends with exit code 2.
- tazelish.txt and config.txt: the nearest tazelish.txt in the current
folder or above it comes first, then $HOME/.tazelish/config.txt or
%USERPROFILE%\.tazelish\config.txt. use and default refuse to write
over a file that exists unless --force is given.
install and self download from static.tazel.net, which this build does
not do; they say so and end with exit code 1.
C++17 with fmt, tested with Catch3, built by Bazel (clang-cl on
Windows, with the rules_cc patch the Tala repository carries). The code
throws no exceptions and builds with warnings as errors; the tests
leave out -Wconversion, which the headers of Catch3 do not pass. CI
tests on Linux, macOS and Windows.
Diffstat (limited to 'Sources/CommandLine.cpp')
| -rw-r--r-- | Sources/CommandLine.cpp | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/Sources/CommandLine.cpp b/Sources/CommandLine.cpp new file mode 100644 index 0000000..67288d9 --- /dev/null +++ b/Sources/CommandLine.cpp @@ -0,0 +1,218 @@ +// SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +#include <array> +#include <fmt/format.h> +#include <utility> + +#include "Progmasoft/Tazelish/CommandLine.hpp" + +namespace Progmasoft::Tazelish +{ + namespace + { + + constexpr std::array<std::pair<std::string_view, Topic>, 5> kTopics + = { { + { "install", Topic::kInstall }, + { "use", Topic::kUse }, + { "default", Topic::kDefault }, + { "self", Topic::kSelf }, + { "help", Topic::kHelp }, + } }; + + constexpr std::string_view kVersionForms + = "MAJOR.SEMIMAJOR.MINOR.PATCH, unstable, nightly, " + "YYYY-MM-DD-unstable or " + "YYYY-MM-DD-nightly"; + + Rejection + Reject(std::string message) + { + return Rejection{ std::move(message) }; + } + + // The one version of a command, written as the argument [text]. + std::variant<Version, Rejection> + ReadVersion(std::string_view command, std::string_view text) + { + if (auto version = ParseVersion(text)) + { + return *version; + } + return Reject( + fmt::format("'{}' is not a version; tazelish {} takes {}", + text, + command, + kVersionForms)); + } + + // `install <version>`. + ParseResult + ParseInstall(const std::vector<std::string_view> &rest) + { + if (rest.size() != 1) + { + return Reject("tazelish install takes one version, such as " + "tazelish install 0.4.0.3"); + } + auto version = ReadVersion("install", rest.front()); + if (auto *rejection = std::get_if<Rejection>(&version)) + { + return std::move(*rejection); + } + return Command{ Install{ std::get<Version>(version) } }; + } + + // `use|default [--force] <version>`, the flag before or after the + // version. + template<typename Writing> + ParseResult + ParseWriting(std::string_view command, + const std::vector<std::string_view> &rest) + { + bool force = false; + std::optional<std::string_view> text; + for (const std::string_view argument : rest) + { + if (argument == "--force") + { + if (force) + { + return Reject(fmt::format( + "--force is written twice in tazelish {}", + command)); + } + force = true; + } + else if (argument.size() > 1 && argument.front() == '-') + { + return Reject(fmt::format("'{}' is not an option of " + "tazelish {}; it takes --force", + argument, + command)); + } + else if (text) + { + return Reject( + fmt::format("tazelish {} takes one version", command)); + } + else + { + text = argument; + } + } + if (!text) + { + return Reject(fmt::format( + "tazelish {} takes a version, such as tazelish {} 0.4.0.3", + command, + command)); + } + auto version = ReadVersion(command, *text); + if (auto *rejection = std::get_if<Rejection>(&version)) + { + return std::move(*rejection); + } + return Command{ Writing{ std::get<Version>(version), force } }; + } + + // `self update|remove|purge`. + ParseResult + ParseSelf(const std::vector<std::string_view> &rest) + { + if (rest.size() == 1) + { + if (rest.front() == "update") + { + return Command{ Self{ SelfAction::kUpdate } }; + } + if (rest.front() == "remove") + { + return Command{ Self{ SelfAction::kRemove } }; + } + if (rest.front() == "purge") + { + return Command{ Self{ SelfAction::kPurge } }; + } + } + return Reject("tazelish self takes one of update, remove or purge"); + } + + // `help [command]`. + ParseResult + ParseHelp(const std::vector<std::string_view> &rest) + { + if (rest.empty()) + { + return Command{ Help{} }; + } + if (rest.size() == 1) + { + for (const auto &[name, topic] : kTopics) + { + if (rest.front() == name) + { + return Command{ Help{ topic } }; + } + } + return Reject( + fmt::format("'{}' is not a command of tazelish; write " + "tazelish help for the commands", + rest.front())); + } + return Reject("tazelish help takes at most one command"); + } + + } // namespace + + ParseResult + ParseCommandLine(const std::vector<std::string_view> &arguments) + { + if (arguments.empty()) + { + return Command{ Help{} }; + } + const std::string_view command = arguments.front(); + const std::vector<std::string_view> rest(arguments.begin() + 1, + arguments.end()); + if (command == "install") + { + return ParseInstall(rest); + } + if (command == "use") + { + return ParseWriting<Use>(command, rest); + } + if (command == "default") + { + return ParseWriting<Default>(command, rest); + } + if (command == "self") + { + return ParseSelf(rest); + } + if (command == "help") + { + return ParseHelp(rest); + } + return Reject(fmt::format( + "'{}' is not a command of tazelish; write tazelish help for the " + "commands", + command)); + } + + std::string_view + TopicName(Topic topic) + { + for (const auto &[name, candidate] : kTopics) + { + if (candidate == topic) + { + return name; + } + } + return {}; + } + +} // namespace Progmasoft::Tazelish |