diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4f98d23 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,31 @@ +matrix: + include: + - os: linux + dist: bionic + language: cpp + compiler: gcc + - os: osx + osx_image: xcode10.2 + language: cpp + compiler: clang + - os: windows + language: bash + env: CXX=cl.exe +install: + - | + if [[ $TRAVIS_OS_NAME == 'windows' ]]; then + choco install ninja cmake + elif [[ $TRAVIS_OS_NAME == 'osx' ]]; then + export PATH=~/Library/Python/3.7/bin:$PATH + pip3 install --user ninja cmake + else + pip install --user ninja cmake + fi +script: + - | + if [[ $TRAVIS_OS_NAME == 'windows' ]]; then + cmd.exe /C '"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 && cmake -Bbuild -G Ninja -DCMAKE_BUILD_TYPE=Release -DARGPARSE_BUILD_TESTS=ON && ninja -C build' + else + cmake -Bbuild -G Ninja -DCMAKE_BUILD_TYPE=Release -DARGPARSE_BUILD_TESTS=ON && ninja -C build + fi + - ./build/test/tests diff --git a/README.md b/README.md index aa794a1..01739e2 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ pprint

+

+ travis + + license + + version +

+ ## Highlights * Single header file diff --git a/include/argparse.hpp b/include/argparse.hpp index 7411918..74904cc 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -102,6 +102,11 @@ public: return *this; } + Argument& required() { + mIsRequired = true; + return *this; + } + Argument& implicit_value(std::any aImplicitValue) { mImplicitValue = std::move(aImplicitValue); mNumArgs = 0; @@ -152,19 +157,29 @@ public: if (mIsOptional) { if (mIsUsed && mValues.size() != mNumArgs && !mDefaultValue.has_value()) { std::stringstream stream; - stream << "error: " << mUsedName << ": expected " << mNumArgs << " argument(s). " - << mValues.size() << " provided.\n" << std::endl; + stream << "error: " << mUsedName << ": expected " << mNumArgs << " argument(s). " + << mValues.size() << " provided."; throw std::runtime_error(stream.str()); } else { // TODO: check if an implicit value was programmed for this argument + if (!mIsUsed && !mDefaultValue.has_value() && mIsRequired) { + std::stringstream stream; + stream << "error: " << mNames[0] << ": required."; + throw std::runtime_error(stream.str()); + } + if (mIsUsed && mIsRequired && mValues.size() == 0) { + std::stringstream stream; + stream << "error: " << mUsedName << ": no value provided."; + throw std::runtime_error(stream.str()); + } } } else { if (mValues.size() != mNumArgs && !mDefaultValue.has_value()) { std::stringstream stream; - stream << "error: " << mUsedName << ": expected " << mNumArgs << " argument(s). " - << mValues.size() << " provided.\n" << std::endl; + stream << "error: " << mUsedName << ": expected " << mNumArgs << " argument(s). " + << mValues.size() << " provided."; throw std::runtime_error(stream.str()); } } @@ -179,7 +194,11 @@ public: friend std::ostream& operator<<(std::ostream& stream, const Argument& argument) { std::stringstream nameStream; std::copy(std::begin(argument.mNames), std::end(argument.mNames), std::ostream_iterator(nameStream, " ")); - return stream << nameStream.str() << "\t" << argument.mHelp << "\n"; + stream << nameStream.str() << "\t" << argument.mHelp; + if (argument.mIsRequired) + stream << "[Required]"; + stream << "\n"; + return stream; } @@ -298,6 +317,7 @@ public: std::vector mRawValues; size_t mNumArgs = 1; bool mIsOptional = false; + bool mIsRequired = false; bool mIsUsed = false; // relevant for optional arguments. True if used by user public: diff --git a/test/main.cpp b/test/main.cpp index 2885d40..8d9aa37 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -10,3 +10,4 @@ #include #include #include +#include \ No newline at end of file diff --git a/test/test_required_arguments.hpp b/test/test_required_arguments.hpp new file mode 100644 index 0000000..f9ac26e --- /dev/null +++ b/test/test_required_arguments.hpp @@ -0,0 +1,45 @@ +#pragma once +#include +#include + +TEST_CASE("Parse required arguments which are not set and don't have default value.", "[required_arguments]") { + argparse::ArgumentParser program("test"); + program.add_argument("--output", "-o").required(); + REQUIRE_THROWS(program.parse_args({ "./main" })); +} + +TEST_CASE("Parse required arguments which are set as empty value and don't have default value.", "[required_arguments]") { + argparse::ArgumentParser program("test"); + program.add_argument("--output", "-o").required(); + REQUIRE_THROWS(program.parse_args({ "./main", "-o" })); +} + +TEST_CASE("Parse required arguments which are set as some value and don't have default value.", "[required_arguments]") { + argparse::ArgumentParser program("test"); + program.add_argument("--output", "-o").required(); + program.parse_args({ "./main", "-o", "filename" }); + REQUIRE(program.get("--output") == "filename"); + REQUIRE(program.get("-o") == "filename"); +} + +TEST_CASE("Parse required arguments which are not set and have default value.", "[required_arguments]") { + argparse::ArgumentParser program("test"); + program.add_argument("--output", "-o").required().default_value(std::string("filename")); + program.parse_args({ "./main" }); + REQUIRE(program.get("--output") == "filename"); + REQUIRE(program.get("-o") == "filename"); +} + +TEST_CASE("Parse required arguments which are set as empty and have default value.", "[required_arguments]") { + argparse::ArgumentParser program("test"); + program.add_argument("--output", "-o").required().default_value(std::string("filename")); + REQUIRE_THROWS(program.parse_args({ "./main", "-o" })); +} + +TEST_CASE("Parse required arguments which are set as some value and have default value.", "[required_arguments]") { + argparse::ArgumentParser program("test"); + program.add_argument("--output", "-o").required().default_value(std::string("filename")); + program.parse_args({ "./main", "-o", "anotherfile" }); + REQUIRE(program.get("--output") == "anotherfile"); + REQUIRE(program.get("-o") == "anotherfile"); +} \ No newline at end of file