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 | |
| 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.
| -rw-r--r-- | .bazelrc | 43 | ||||
| -rw-r--r-- | .bazelversion | 1 | ||||
| -rw-r--r-- | .clang-format | 123 | ||||
| -rw-r--r-- | .gitattributes | 4 | ||||
| -rw-r--r-- | .github/workflows/ci.yml | 48 | ||||
| -rw-r--r-- | .github/workflows/zizmor.yml | 38 | ||||
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | BUILD.bazel | 24 | ||||
| -rw-r--r-- | Build/BUILD.bazel | 4 | ||||
| -rw-r--r-- | Build/rules_cc_clangcl_dumpbin.patch | 9 | ||||
| -rw-r--r-- | Headers/Progmasoft/Tazelish/CommandLine.hpp | 91 | ||||
| -rw-r--r-- | Headers/Progmasoft/Tazelish/Help.hpp | 21 | ||||
| -rw-r--r-- | Headers/Progmasoft/Tazelish/Version.hpp | 83 | ||||
| -rw-r--r-- | Headers/Progmasoft/Tazelish/VersionFiles.hpp | 82 | ||||
| -rw-r--r-- | LICENSE.txt | 373 | ||||
| -rw-r--r-- | LICENSES/AdditionRef-Progmasoft-Exception-2.0.txt | 118 | ||||
| -rw-r--r-- | MODULE.bazel | 27 | ||||
| -rw-r--r-- | MODULE.bazel.lock | 433 | ||||
| -rw-r--r-- | README.md | 51 | ||||
| -rw-r--r-- | Sources/CommandLine.cpp | 218 | ||||
| -rw-r--r-- | Sources/Help.cpp | 105 | ||||
| -rw-r--r-- | Sources/Main.cpp | 140 | ||||
| -rw-r--r-- | Sources/Version.cpp | 196 | ||||
| -rw-r--r-- | Sources/VersionFiles.cpp | 195 | ||||
| -rw-r--r-- | Tests/BUILD.bazel | 17 | ||||
| -rw-r--r-- | Tests/CommandLineTest.cpp | 91 | ||||
| -rw-r--r-- | Tests/VersionFilesTest.cpp | 125 | ||||
| -rw-r--r-- | Tests/VersionTest.cpp | 68 |
28 files changed, 2734 insertions, 0 deletions
diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..7714124 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,43 @@ +# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +# SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +common --enable_bzlmod +common --enable_platform_specific_config + +build --incompatible_strict_action_env +build --verbose_failures + +# The code of Tazelish throws no exceptions and is held to the warnings below; +# the dependencies keep their own settings. The tests leave out -Wconversion, +# which the headers of Catch3 do not pass. +# rules_cc finds clang-cl through BAZEL_LLVM, which each machine sets; no +# path of a machine is written here. +build:windows --repo_env=BAZEL_LLVM +build:windows --repo_env=USE_CLANG_CL=1 +build:windows --compiler=clang-cl +build:windows --cxxopt=/std:c++17 +build:windows --host_cxxopt=/std:c++17 +build:windows --cxxopt=/utf-8 +build:windows --cxxopt=/Zc:__cplusplus +build:windows --features=static_link_msvcrt +build:windows --per_file_copt=^Sources/.*@/clang:-Wall,/clang:-Wextra,/clang:-Wpedantic,/clang:-Werror,/clang:-Wshadow,/clang:-Wconversion,/clang:-Wnon-virtual-dtor,/clang:-Wimplicit-fallthrough +build:windows --per_file_copt=^Tests/.*@/clang:-Wall,/clang:-Wextra,/clang:-Wpedantic,/clang:-Werror,/clang:-Wshadow,/clang:-Wnon-virtual-dtor,/clang:-Wimplicit-fallthrough +build:windows --per_file_copt=^Sources/.*@/EHs-c-,/clang:-fno-exceptions + +build:linux --compiler=clang +build:linux --repo_env=CC=clang +build:linux --repo_env=BAZEL_COMPILER=clang +build:linux --cxxopt=-std=c++17 +build:linux --host_cxxopt=-std=c++17 +build:linux --per_file_copt=^Sources/.*@-Wall,-Wextra,-Wpedantic,-Werror,-Wshadow,-Wconversion,-Wnon-virtual-dtor,-Wimplicit-fallthrough +build:linux --per_file_copt=^Tests/.*@-Wall,-Wextra,-Wpedantic,-Werror,-Wshadow,-Wnon-virtual-dtor,-Wimplicit-fallthrough +build:linux --per_file_copt=^Sources/.*@-fno-exceptions + +build:macos --compiler=clang +build:macos --cxxopt=-std=c++17 +build:macos --host_cxxopt=-std=c++17 +build:macos --per_file_copt=^Sources/.*@-Wall,-Wextra,-Wpedantic,-Werror,-Wshadow,-Wconversion,-Wnon-virtual-dtor,-Wimplicit-fallthrough +build:macos --per_file_copt=^Tests/.*@-Wall,-Wextra,-Wpedantic,-Werror,-Wshadow,-Wnon-virtual-dtor,-Wimplicit-fallthrough +build:macos --per_file_copt=^Sources/.*@-fno-exceptions + +test --test_output=errors diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..deeb3d6 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +9.2.0 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5efabd4 --- /dev/null +++ b/.clang-format @@ -0,0 +1,123 @@ +# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +# SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +--- +# Progmasoft C++ Style format profile, pinned to LLVM/Clang 23.1.0. +# Naming is intentionally outside clang-format and remains governed by the +# PascalCase/camelCase conventions of Progmasoft C++ Style. +Language: Cpp +BasedOnStyle: LLVM +Standard: c++17 + +UseTab: Never +IndentWidth: 4 +ContinuationIndentWidth: 4 +ConstructorInitializerIndentWidth: 4 +AccessModifierOffset: -4 +IndentAccessModifiers: false + +# Keep formatted C++ source within the agreed 80-column width. +ColumnLimit: 80 + +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterExternBlock: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false + +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None +AllowShortLoopsOnASingleLine: false +AllowShortNamespacesOnASingleLine: false +AllowShortRecordOnASingleLine: Never + +# Function declarations and definitions keep the return type on its own line. +BreakAfterReturnType: All + +PackArguments: + BinPack: OnePerLine +PackParameters: + BinPack: OnePerLine +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false + +BreakBeforeBinaryOperators: All +BreakBeforeTernaryOperators: true +AlignOperands: Align + +BreakConstructorInitializers: BeforeComma +PackConstructorInitializers: Never +BreakInheritanceList: BeforeComma + +PointerAlignment: Right +DerivePointerAlignment: false +QualifierAlignment: Leave + +SpaceBeforeParens: ControlStatements +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false +SpaceBeforeCpp11BracedList: false +SpacesInAngles: Never +SpacesInSquareBrackets: false +SpacesInParens: Never +SpaceInEmptyBraces: Never + +Cpp11BracedListStyle: Block + +SpaceAfterTemplateKeyword: false +BreakTemplateDeclarations: Yes + +IndentCaseLabels: true +IndentCaseBlocks: false + +NamespaceIndentation: All +FixNamespaceComments: true +ShortNamespaceLines: 0 +CompactNamespaces: false + +IndentPPDirectives: AfterHash +PPIndentWidth: 4 + +# System headers precede project headers, with alphabetical ordering per group. +IncludeBlocks: Regroup +SortIncludes: CaseSensitive +IncludeIsMainRegex: '$^' +IncludeCategories: + - Regex: '^<.*>$' + Priority: 1 + SortPriority: 1 + - Regex: '^".*"$' + Priority: 2 + SortPriority: 2 + +MaxEmptyLinesToKeep: 1 +KeepEmptyLines: + AtStartOfBlock: false + +ReflowComments: true +SortUsingDeclarations: Lexicographic + +# Match the dominant line-ending convention of each input file. +LineEnding: DeriveLF +InsertNewlineAtEOF: true +... diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..75cf851 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +# SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +* text=auto eol=lf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7d357e0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +# SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +concurrency: + group: tazelish-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: Test (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, macos-15, windows-2025] + runs-on: ${{ matrix.os }} + timeout-minutes: 45 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + # The hosted Windows image carries LLVM, whose clang-cl the Bazel + # configuration uses. + - if: runner.os == 'Windows' + shell: pwsh + run: '"BAZEL_LLVM=C:\Program Files\LLVM" | Out-File -FilePath $env:GITHUB_ENV -Append' + - run: bazelisk test //... + - run: bazelisk build //:tazelish + + required: + name: CI Required + if: always() + needs: [test] + runs-on: ubuntu-24.04 + timeout-minutes: 5 + steps: + - env: + RESULT: ${{ needs.test.result }} + run: test "$RESULT" = success diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000..b81dac0 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +# SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +name: Workflow Security + +# zizmor reads the workflows of this repository for what makes a workflow +# unsafe: a token that is wider than its job needs, text from a pull request +# that reaches a shell, an action that is not pinned. Its findings are +# uploaded to code scanning. + +on: + pull_request: + push: + branches: + - main + +permissions: {} + +concurrency: + group: zizmor-${{ github.ref }} + cancel-in-progress: true + +jobs: + zizmor: + name: zizmor + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + actions: read + contents: read + security-events: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + + - name: Audit the workflows + uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fcb785f --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +# SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +/bazel-* +/.cache/ +/compile_commands.json diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..badab6b --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +# SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") + +cc_library( + name = "tazelish_library", + srcs = [ + "Sources/CommandLine.cpp", + "Sources/Help.cpp", + "Sources/Version.cpp", + "Sources/VersionFiles.cpp", + ], + hdrs = glob(["Headers/Progmasoft/Tazelish/*.hpp"]), + includes = ["Headers"], + visibility = ["//Tests:__pkg__"], + deps = ["@fmt"], +) + +cc_binary( + name = "tazelish", + srcs = ["Sources/Main.cpp"], + deps = [":tazelish_library"], +) diff --git a/Build/BUILD.bazel b/Build/BUILD.bazel new file mode 100644 index 0000000..894380b --- /dev/null +++ b/Build/BUILD.bazel @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +# SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +exports_files(["rules_cc_clangcl_dumpbin.patch"]) diff --git a/Build/rules_cc_clangcl_dumpbin.patch b/Build/rules_cc_clangcl_dumpbin.patch new file mode 100644 index 0000000..66c138f --- /dev/null +++ b/Build/rules_cc_clangcl_dumpbin.patch @@ -0,0 +1,9 @@ +--- cc/private/toolchain/windows_cc_configure.bzl ++++ cc/private/toolchain/windows_cc_configure.bzl +@@ -722,5 +722,6 @@ + if not build_tools["LIB"]: + build_tools["LIB"] = find_msvc_tool(repository_ctx, vc_path, "lib.exe", "x64") ++ build_tools["DUMPBIN"] = find_msvc_tool(repository_ctx, vc_path, "dumpbin.exe", "x64") + else: + build_tools = _find_msvc_tools(repository_ctx, vc_path, target_arch) + diff --git a/Headers/Progmasoft/Tazelish/CommandLine.hpp b/Headers/Progmasoft/Tazelish/CommandLine.hpp new file mode 100644 index 0000000..783dfde --- /dev/null +++ b/Headers/Progmasoft/Tazelish/CommandLine.hpp @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +/// \file +/// The command line of Tazelish, read into a typed command. Nothing after the +/// parser looks at the text of an argument again. + +#pragma once + +#include <optional> +#include <string> +#include <string_view> +#include <variant> +#include <vector> + +#include "Progmasoft/Tazelish/Version.hpp" + +namespace Progmasoft::Tazelish +{ + + /// `tazelish install <version>`. + struct Install + { + Version version; + }; + + /// `tazelish use [--force] <version>`: writes `./tazelish.txt`. + struct Use + { + Version version; + bool force = false; + }; + + /// `tazelish default [--force] <version>`: writes the `config.txt` of the + /// user. + struct Default + { + Version version; + bool force = false; + }; + + /// What `tazelish self` does to Tazelish itself. + enum class SelfAction + { + kUpdate, + kRemove, + kPurge, + }; + + /// `tazelish self update|remove|purge`. + struct Self + { + SelfAction action = SelfAction::kUpdate; + }; + + /// The commands `tazelish help <command>` explains. + enum class Topic + { + kInstall, + kUse, + kDefault, + kSelf, + kHelp, + }; + + /// `tazelish help [command]`; without a topic, the general help. + struct Help + { + std::optional<Topic> topic; + }; + + using Command = std::variant<Install, Use, Default, Self, Help>; + + /// A command line that is wrong, and what is wrong with it. + struct Rejection + { + std::string message; + }; + + using ParseResult = std::variant<Command, Rejection>; + + /// The command [arguments] ask for; [arguments] leave out the name of the + /// program. No arguments ask for the general help. + [[nodiscard]] ParseResult + ParseCommandLine(const std::vector<std::string_view> &arguments); + + /// The name of a topic, as `tazelish help` takes it. + [[nodiscard]] std::string_view + TopicName(Topic topic); + +} // namespace Progmasoft::Tazelish diff --git a/Headers/Progmasoft/Tazelish/Help.hpp b/Headers/Progmasoft/Tazelish/Help.hpp new file mode 100644 index 0000000..d7254c7 --- /dev/null +++ b/Headers/Progmasoft/Tazelish/Help.hpp @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +/// \file +/// The text `tazelish help` prints. + +#pragma once + +#include <optional> +#include <string_view> + +#include "Progmasoft/Tazelish/CommandLine.hpp" + +namespace Progmasoft::Tazelish +{ + + /// The general help without a topic, or the help of one command. + [[nodiscard]] std::string_view + HelpText(std::optional<Topic> topic); + +} // namespace Progmasoft::Tazelish diff --git a/Headers/Progmasoft/Tazelish/Version.hpp b/Headers/Progmasoft/Tazelish/Version.hpp new file mode 100644 index 0000000..01c0392 --- /dev/null +++ b/Headers/Progmasoft/Tazelish/Version.hpp @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +/// \file +/// The versions of Tazel a command line or a version file can name. + +#pragma once + +#include <cstdint> +#include <optional> +#include <string> +#include <string_view> +#include <variant> + +namespace Progmasoft::Tazelish +{ + + /// A release of Tazel: `MAJOR.SEMIMAJOR.MINOR.PATCH`. + struct Release + { + std::uint32_t majorNumber = 0; + std::uint32_t semimajorNumber = 0; + std::uint32_t minorNumber = 0; + std::uint32_t patchNumber = 0; + }; + + /// The two kinds of build that are not releases. + enum class Channel + { + kUnstable, + kNightly, + }; + + /// The newest build of a channel: `unstable` or `nightly`. + struct Newest + { + Channel channel = Channel::kUnstable; + }; + + /// The build of a channel from one day: `YYYY-MM-DD-unstable`. + struct Dated + { + std::uint16_t year = 0; + std::uint8_t month = 0; + std::uint8_t day = 0; + Channel channel = Channel::kUnstable; + }; + + [[nodiscard]] inline bool + operator==(const Release &left, const Release &right) + { + return left.majorNumber == right.majorNumber + && left.semimajorNumber == right.semimajorNumber + && left.minorNumber == right.minorNumber + && left.patchNumber == right.patchNumber; + } + + [[nodiscard]] inline bool + operator==(const Newest &left, const Newest &right) + { + return left.channel == right.channel; + } + + [[nodiscard]] inline bool + operator==(const Dated &left, const Dated &right) + { + return left.year == right.year && left.month == right.month + && left.day == right.day && left.channel == right.channel; + } + + /// A version as it is written after `tazelish install`. + using Version = std::variant<Release, Newest, Dated>; + + /// The version [text] names, or nothing when it names none. Numbers have no + /// leading zeros, and a date has to exist in the calendar. + [[nodiscard]] std::optional<Version> + ParseVersion(std::string_view text); + + /// The text of [version], the way ParseVersion reads it. + [[nodiscard]] std::string + FormatVersion(const Version &version); + +} // namespace Progmasoft::Tazelish diff --git a/Headers/Progmasoft/Tazelish/VersionFiles.hpp b/Headers/Progmasoft/Tazelish/VersionFiles.hpp new file mode 100644 index 0000000..5df9326 --- /dev/null +++ b/Headers/Progmasoft/Tazelish/VersionFiles.hpp @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 + +/// \file +/// The files that name the version of Tazel to run: `tazelish.txt` of a +/// project, written by `tazelish use`, and `config.txt` of the user, written +/// by `tazelish default`. Both hold one version; `tazelish.txt` comes first. + +#pragma once + +#include <filesystem> +#include <optional> +#include <string> +#include <string_view> +#include <variant> + +#include "Progmasoft/Tazelish/Version.hpp" + +namespace Progmasoft::Tazelish +{ + + constexpr std::string_view kProjectFileName = "tazelish.txt"; + constexpr std::string_view kStateDirectoryName = ".tazelish"; + constexpr std::string_view kConfigFileName = "config.txt"; + + /// A version file that could not be read or written, and why. + struct FileError + { + std::filesystem::path file; + std::string message; + }; + + /// Which file named the version. + enum class VersionSource + { + kProject, + kUser, + }; + + /// The version to run and the file that named it. + struct Selection + { + Version version; + std::filesystem::path file; + VersionSource source = VersionSource::kProject; + }; + + /// No file names a version. + struct NoSelection + {}; + + /// The home folder of the user: `%USERPROFILE%` on Windows, `$HOME` + /// elsewhere; nothing when the variable is unset or empty. + [[nodiscard]] std::optional<std::filesystem::path> + HomeDirectory(); + + /// `<home>/.tazelish`, where Tazelish keeps what it owns. + [[nodiscard]] std::filesystem::path + StateDirectory(const std::filesystem::path &home); + + /// `<home>/.tazelish/config.txt`. + [[nodiscard]] std::filesystem::path + ConfigFile(const std::filesystem::path &home); + + /// The version [file] names; NoSelection when it does not exist. + [[nodiscard]] std::variant<NoSelection, Version, FileError> + ReadVersionFile(const std::filesystem::path &file); + + /// The version to run in [directory]: the nearest `tazelish.txt` in it or a + /// folder above it, else the `config.txt` of [home]. + [[nodiscard]] std::variant<NoSelection, Selection, FileError> + SelectVersion(const std::filesystem::path &directory, + const std::optional<std::filesystem::path> &home); + + /// Writes [version] to [file], creating its folder. An existing file is an + /// error unless [force] is set. + [[nodiscard]] std::optional<FileError> + WriteVersionFile(const std::filesystem::path &file, + const Version &version, + bool force); + +} // namespace Progmasoft::Tazelish diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d0a1fa1 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software wit |