Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Footer callback #309

Merged
merged 1 commit into from
Aug 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ There are several options that are supported on the main app and subcommands and
- `.positionals_at_end()`: 🆕 Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered.
- `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognized item. It is ideal for allowing your app or subcommand to be a "prefix" to calling another app.
- `.footer(message)`: Set text to appear at the bottom of the help string.
- `.footer(std::string())`: 🚧 Set a callback to generate a string that will appear at the end of the help string.
- `.set_help_flag(name, message)`: Set the help flag name and message, returns a pointer to the created option.
- `.set_help_all_flag(name, message)`: Set the help all flag name and message, returns a pointer to the created option. Expands subcommands.
- `.failure_message(func)`: Set the failure message function. Two provided: `CLI::FailureMessage::help` and `CLI::FailureMessage::simple` (the default).
Expand Down
49 changes: 27 additions & 22 deletions include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ class App {
/// Footer to put after all options in the help output INHERITABLE
std::string footer_;

/// This is a function that generates a footer to put after all other options in help output
std::function<std::string()> footer_callback_;

/// A pointer to the help flag if there is one INHERITABLE
Option *help_ptr_{nullptr};

Expand Down Expand Up @@ -461,9 +464,8 @@ class App {
option->capture_default_str();

return option.get();

} else
throw OptionAlreadyAdded(myopt.get_name());
}
throw OptionAlreadyAdded(myopt.get_name());
}

/// Add option for non-vectors (duplicate copy needed without defaulted to avoid `iostream << value`)
Expand Down Expand Up @@ -950,6 +952,7 @@ class App {
remove_option(config_ptr_);
config_name_ = "";
config_required_ = false; // Not really needed, but complete
config_ptr_ = nullptr; // need to remove the config_ptr completely
}

// Only add config if option passed
Expand Down Expand Up @@ -1423,25 +1426,23 @@ class App {
/// Removes an option from the excludes list of this subcommand
bool remove_excludes(Option *opt) {
auto iterator = std::find(std::begin(exclude_options_), std::end(exclude_options_), opt);
if(iterator != std::end(exclude_options_)) {
exclude_options_.erase(iterator);
return true;
} else {
if(iterator == std::end(exclude_options_)) {
return false;
}
exclude_options_.erase(iterator);
return true;
}

/// Removes a subcommand from this excludes list of this subcommand
bool remove_excludes(App *app) {
auto iterator = std::find(std::begin(exclude_subcommands_), std::end(exclude_subcommands_), app);
if(iterator != std::end(exclude_subcommands_)) {
auto other_app = *iterator;
exclude_subcommands_.erase(iterator);
other_app->remove_excludes(this);
return true;
} else {
if(iterator == std::end(exclude_subcommands_)) {
return false;
}
auto other_app = *iterator;
exclude_subcommands_.erase(iterator);
other_app->remove_excludes(this);
return true;
}

///@}
Expand All @@ -1453,7 +1454,11 @@ class App {
footer_ = std::move(footer_string);
return this;
}

/// Set footer.
App *footer(std::function<std::string()> footer_function) {
footer_callback_ = std::move(footer_function);
return this;
}
/// Produce a string that could be read in as a config of the current values of the App. Set default_also to
/// include default arguments. Prefix will add a string to the beginning of each option.
std::string config_to_str(bool default_also = false, bool write_description = false) const {
Expand All @@ -1470,10 +1475,10 @@ class App {

// Delegate to subcommand if needed
auto selected_subcommands = get_subcommands();
if(!selected_subcommands.empty())
if(!selected_subcommands.empty()) {
return selected_subcommands.at(0)->help(prev, mode);
else
return formatter_->make_help(this, prev, mode);
}
return formatter_->make_help(this, prev, mode);
}

///@}
Expand Down Expand Up @@ -1592,8 +1597,8 @@ class App {
/// Get the group of this subcommand
const std::string &get_group() const { return group_; }

/// Get footer.
const std::string &get_footer() const { return footer_; }
/// Generate and return the footer.
std::string get_footer() const { return (footer_callback_) ? footer_callback_() + '\n' + footer_ : footer_; }

/// Get the required min subcommand value
size_t get_require_subcommand_min() const { return require_subcommand_min_; }
Expand Down Expand Up @@ -2090,7 +2095,7 @@ class App {
if(!(allow_extras_ || prefix_command_)) {
size_t num_left_over = remaining_size();
if(num_left_over > 0) {
throw ExtrasError(remaining(false));
throw ExtrasError(name_, remaining(false));
}
}

Expand All @@ -2107,7 +2112,7 @@ class App {
size_t num_left_over = remaining_size();
if(num_left_over > 0) {
args = remaining(false);
throw ExtrasError(args);
throw ExtrasError(name_, args);
}
}

Expand Down Expand Up @@ -2372,7 +2377,7 @@ class App {
}

if(positionals_at_end_) {
throw CLI::ExtrasError(args);
throw CLI::ExtrasError(name_, args);
}
/// If this is an option group don't deal with it
if(parent_ != nullptr && name_.empty()) {
Expand Down
3 changes: 2 additions & 1 deletion include/CLI/ConfigFwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ class Config {
/// This converter works with INI files
class ConfigINI : public Config {
public:
std::string to_config(const App *, bool default_also, bool write_description, std::string prefix) const override;
std::string
to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override;

std::vector<ConfigItem> from_config(std::istream &input) const override {
std::string line;
Expand Down
30 changes: 18 additions & 12 deletions include/CLI/Error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,32 +206,32 @@ class RequiredError : public ParseError {
CLI11_ERROR_DEF(ParseError, RequiredError)
explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
static RequiredError Subcommand(size_t min_subcom) {
if(min_subcom == 1)
if(min_subcom == 1) {
return RequiredError("A subcommand");
else
return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands",
ExitCodes::RequiredError);
}
return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands",
ExitCodes::RequiredError);
}
static RequiredError Option(size_t min_option, size_t max_option, size_t used, const std::string &option_list) {
if((min_option == 1) && (max_option == 1) && (used == 0))
return RequiredError("Exactly 1 option from [" + option_list + "]");
else if((min_option == 1) && (max_option == 1) && (used > 1))
if((min_option == 1) && (max_option == 1) && (used > 1))
return RequiredError("Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) +
" were given",
ExitCodes::RequiredError);
else if((min_option == 1) && (used == 0))
if((min_option == 1) && (used == 0))
return RequiredError("At least 1 option from [" + option_list + "]");
else if(used < min_option)
if(used < min_option)
return RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " +
std::to_string(used) + "were given from [" + option_list + "]",
ExitCodes::RequiredError);
else if(max_option == 1)
if(max_option == 1)
return RequiredError("Requires at most 1 options be given from [" + option_list + "]",
ExitCodes::RequiredError);
else
return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " +
std::to_string(used) + "were given from [" + option_list + "]",
ExitCodes::RequiredError);

return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " +
std::to_string(used) + "were given from [" + option_list + "]",
ExitCodes::RequiredError);
}
};

Expand Down Expand Up @@ -279,6 +279,12 @@ class ExtrasError : public ParseError {
: "The following argument was not expected: ") +
detail::rjoin(args, " "),
ExitCodes::ExtrasError) {}
ExtrasError(const std::string &name, std::vector<std::string> args)
: ExtrasError(name,
(args.size() > 1 ? "The following arguments were not expected: "
: "The following argument was not expected: ") +
detail::rjoin(args, " "),
ExitCodes::ExtrasError) {}
};

/// Thrown when extra values are found in an INI file
Expand Down
18 changes: 9 additions & 9 deletions include/CLI/Formatter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ inline std::string Formatter::make_positionals(const App *app) const {

if(opts.empty())
return std::string();
else
return make_group(get_label("Positionals"), true, opts);

return make_group(get_label("Positionals"), true, opts);
}

inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
Expand Down Expand Up @@ -126,10 +126,10 @@ inline std::string Formatter::make_usage(const App *app, std::string name) const

inline std::string Formatter::make_footer(const App *app) const {
std::string footer = app->get_footer();
if(!footer.empty())
return footer + "\n";
else
return "";
if(footer.empty()) {
return std::string{};
}
return footer + "\n";
}

inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
Expand All @@ -151,7 +151,7 @@ inline std::string Formatter::make_help(const App *app, std::string name, AppFor
out << make_positionals(app);
out << make_groups(app, mode);
out << make_subcommands(app, mode);
out << make_footer(app);
out << '\n' << make_footer(app);

return out.str();
}
Expand Down Expand Up @@ -222,8 +222,8 @@ inline std::string Formatter::make_expanded(const App *sub) const {
inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
if(is_positional)
return opt->get_name(true, false);
else
return opt->get_name(false, true);

return opt->get_name(false, true);
}

inline std::string Formatter::make_option_opts(const Option *opt) const {
Expand Down
2 changes: 1 addition & 1 deletion include/CLI/FormatterFwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class Formatter : public FormatterBase {
virtual std::string make_usage(const App *app, std::string name) const;

/// This puts everything together
std::string make_help(const App *, std::string, AppFormatMode) const override;
std::string make_help(const App * /*app*/, std::string, AppFormatMode) const override;

///@}
/// @name Options
Expand Down
Loading