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 @@
+
+
+
+
+
+
+
+
## 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