Skip to content

Commit

Permalink
Closes #19
Browse files Browse the repository at this point in the history
  • Loading branch information
p-ranav committed Apr 28, 2021
1 parent ffe3473 commit f23342a
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 47 deletions.
4 changes: 2 additions & 2 deletions include/structopt/app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class app {
details::visitor visitor;

public:
explicit app(const std::string &name, const std::string &version = "")
: visitor(name, version) {}
explicit app(const std::string &name, const std::string &version = "", const std::string& help = "")
: visitor(name, version, help) {}

template <typename T> T parse(const std::vector<std::string> &arguments) {
T argument_struct = T();
Expand Down
98 changes: 53 additions & 45 deletions include/structopt/visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace details {
struct visitor {
std::string name;
std::string version;
std::optional<std::string> help;
std::vector<std::string> field_names;
std::deque<std::string> positional_field_names; // mutated by parser
std::deque<std::string> positional_field_names_for_help;
Expand All @@ -33,6 +34,9 @@ struct visitor {
explicit visitor(const std::string &name, const std::string &version)
: name(name), version(version) {}

explicit visitor(const std::string &name, const std::string &version, const std::string& help)
: name(name), version(version), help(help) {}

// Visitor function for std::optional - could be an option or a flag
template <typename T>
inline typename std::enable_if<structopt::is_specialization<T, std::optional>::value,
Expand Down Expand Up @@ -85,63 +89,67 @@ struct visitor {
}

void print_help(std::ostream &os) const {
os << "\nUSAGE: " << name << " ";

if (flag_field_names.empty() == false) {
os << "[FLAGS] ";
}
if (help.has_value() && help.value().size() > 0) {
os << help.value();
} else {
os << "\nUSAGE: " << name << " ";

if (optional_field_names.empty() == false) {
os << "[OPTIONS] ";
}
if (flag_field_names.empty() == false) {
os << "[FLAGS] ";
}

if (nested_struct_field_names.empty() == false) {
os << "[SUBCOMMANDS] ";
}
if (optional_field_names.empty() == false) {
os << "[OPTIONS] ";
}

for (auto &field : positional_field_names_for_help) {
os << field << " ";
}
if (nested_struct_field_names.empty() == false) {
os << "[SUBCOMMANDS] ";
}

if (flag_field_names.empty() == false) {
os << "\n\nFLAGS:\n";
for (auto &flag : flag_field_names) {
os << " -" << flag[0] << ", --" << flag << "\n";
for (auto &field : positional_field_names_for_help) {
os << field << " ";
}
} else {
os << "\n";
}

if (optional_field_names.empty() == false) {
os << "\nOPTIONS:\n";
for (auto &option : optional_field_names) {

// Generate kebab case and present as option
auto kebab_case = option;
details::string_replace(kebab_case, "_", "-");
std::string long_form = "";
if (kebab_case != option) {
long_form = kebab_case;
} else {
long_form = option;
if (flag_field_names.empty() == false) {
os << "\n\nFLAGS:\n";
for (auto &flag : flag_field_names) {
os << " -" << flag[0] << ", --" << flag << "\n";
}
} else {
os << "\n";
}

os << " -" << option[0] << ", --" << long_form << " <" << option << ">"
<< "\n";
if (optional_field_names.empty() == false) {
os << "\nOPTIONS:\n";
for (auto &option : optional_field_names) {

// Generate kebab case and present as option
auto kebab_case = option;
details::string_replace(kebab_case, "_", "-");
std::string long_form = "";
if (kebab_case != option) {
long_form = kebab_case;
} else {
long_form = option;
}

os << " -" << option[0] << ", --" << long_form << " <" << option << ">"
<< "\n";
}
}
}

if (nested_struct_field_names.empty() == false) {
os << "\nSUBCOMMANDS:\n";
for (auto &sc : nested_struct_field_names) {
os << " " << sc << "\n";
if (nested_struct_field_names.empty() == false) {
os << "\nSUBCOMMANDS:\n";
for (auto &sc : nested_struct_field_names) {
os << " " << sc << "\n";
}
}
}

if (positional_field_names_for_help.empty() == false) {
os << "\nARGS:\n";
for (auto &arg : positional_field_names_for_help) {
os << " " << arg << "\n";
if (positional_field_names_for_help.empty() == false) {
os << "\nARGS:\n";
for (auto &arg : positional_field_names_for_help) {
os << " " << arg << "\n";
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,8 @@ target_link_libraries(nested_structures_2 PRIVATE structopt::structopt)
add_executable(printing_help printing_help.cpp)
target_link_libraries(printing_help PRIVATE structopt::structopt)

add_executable(printing_help_custom printing_help_custom.cpp)
target_link_libraries(printing_help_custom PRIVATE structopt::structopt)

add_executable(option_delimiters option_delimiters.cpp)
target_link_libraries(option_delimiters PRIVATE structopt::structopt)
25 changes: 25 additions & 0 deletions samples/printing_help_custom.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <structopt/app.hpp>

struct Options {
// positional arguments
std::string input_file;
std::string output_file;

// optional arguments
std::optional<std::string> bind_address;

// remaining arguments
std::vector<std::string> files;
};
STRUCTOPT(Options, input_file, output_file, bind_address, files);

int main(int argc, char *argv[]) {

try {
const std::string& custom_help = "Usage: ./my_app input_file output_file [--bind-address BIND_ADDRESS] [files...]\n";
auto options = structopt::app("my_app", "1.0.3", custom_help).parse<Options>(argc, argv);
} catch (structopt::exception &e) {
std::cout << e.what() << "\n";
std::cout << e.help();
}
}

0 comments on commit f23342a

Please sign in to comment.