feat(parser): implement on-complete callback functionality
Add on_complete event handler to trigger actions after argument parsing completes Move grep logic to separate function and register it as a completion callback Update argument getters and help text generation to be const methods
This commit is contained in:
2
TODO.md
2
TODO.md
@@ -24,6 +24,8 @@ After handle_parse(...) ends, we can call the on_parse_done callbacks. But this
|
||||
### Update: 6 Oct. 2025
|
||||
We now have storable arguments. Now we need to implement the on_parse_done callbacks, to achieve this feature.
|
||||
|
||||
Also the run after complete events are implemented.
|
||||
|
||||
# TODO 2: Positional arguments
|
||||
Positional arguments are arguments that are not prefixed with a dash. They are usually the arguments that are passed to the program.
|
||||
For example, in the command `grep "pattern" file.txt`, "pattern" and "file.txt" are positional arguments.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#ifndef ARGUMENT_PARSER_HPP
|
||||
@@ -163,23 +164,27 @@ namespace argument_parser {
|
||||
base_add_argument<void>(short_arg, long_arg, help_text, required);
|
||||
}
|
||||
|
||||
void on_complete(std::function<void(base_parser const&)> const& action) {
|
||||
on_complete_events.emplace_back(action);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<T> get_optional(std::string const& arg) {
|
||||
std::optional<T> get_optional(std::string const& arg) const {
|
||||
auto id = find_argument_id(arg);
|
||||
if (id.has_value()) {
|
||||
auto value = stored_arguments[id.value()];
|
||||
if (value.has_value()) return std::any_cast<T>(value);
|
||||
auto value = stored_arguments.find(id.value());
|
||||
if (value != stored_arguments.end() && value->second.has_value()) return std::any_cast<T>(value->second);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string build_help_text(std::initializer_list<conventions::convention const* const> convention_types) {
|
||||
std::string build_help_text(std::initializer_list<conventions::convention const* const> convention_types) const {
|
||||
std::stringstream ss;
|
||||
ss << "Usage: " << program_name << " [OPTIONS]...\n";
|
||||
|
||||
for (auto const& [id, arg] : argument_map) {
|
||||
auto short_arg = reverse_short_arguments[id];
|
||||
auto long_arg = reverse_long_arguments[id];
|
||||
auto short_arg = reverse_short_arguments.at(id);
|
||||
auto long_arg = reverse_long_arguments.at(id);
|
||||
ss << "\t";
|
||||
ss << "-" << short_arg << ", --" << long_arg;
|
||||
ss << "\t\t" << arg.help_text << "\n";
|
||||
@@ -198,7 +203,7 @@ namespace argument_parser {
|
||||
throw std::runtime_error("Unknown argument: " + arg.second);
|
||||
}
|
||||
|
||||
std::optional<int> find_argument_id(std::string const& arg) {
|
||||
std::optional<int> find_argument_id(std::string const& arg) const {
|
||||
auto long_pos = long_arguments.find(arg);
|
||||
auto short_post = short_arguments.find(arg);
|
||||
|
||||
@@ -246,9 +251,10 @@ namespace argument_parser {
|
||||
}
|
||||
}
|
||||
check_for_required_arguments(convention_types);
|
||||
fire_on_complete_events();
|
||||
}
|
||||
|
||||
void display_help(std::initializer_list<conventions::convention const* const> convention_types) {
|
||||
void display_help(std::initializer_list<conventions::convention const* const> convention_types) const {
|
||||
std::cout << build_help_text(convention_types);
|
||||
}
|
||||
|
||||
@@ -325,6 +331,12 @@ namespace argument_parser {
|
||||
}
|
||||
}
|
||||
|
||||
void fire_on_complete_events() {
|
||||
for(auto const& event : on_complete_events) {
|
||||
event(*this);
|
||||
}
|
||||
}
|
||||
|
||||
inline static std::atomic_int id_counter = 0;
|
||||
|
||||
std::unordered_map<int, std::any> stored_arguments;
|
||||
@@ -334,6 +346,8 @@ namespace argument_parser {
|
||||
std::unordered_map<std::string, int> long_arguments;
|
||||
std::unordered_map<int, std::string> reverse_long_arguments;
|
||||
|
||||
std::list<std::function<void(base_parser const&)>> on_complete_events;
|
||||
|
||||
friend class linux_parser;
|
||||
friend class windows_parser;
|
||||
friend class macos_parser;
|
||||
|
||||
40
src/main.cpp
40
src/main.cpp
@@ -1,3 +1,4 @@
|
||||
#include "argument_parser.hpp"
|
||||
#include <argparse>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
@@ -94,7 +95,7 @@ const auto cat = argument_parser::helpers::make_parametered_action<std::string>(
|
||||
file.close();
|
||||
});
|
||||
|
||||
auto grep(argument_parser::base_parser& parser, std::string const& filename, std::regex const& pattern) {
|
||||
auto grep(argument_parser::base_parser const& parser, std::string const& filename, std::regex const& pattern) {
|
||||
if (filename.empty()) {
|
||||
std::cerr << "Missing filename" << std::endl;
|
||||
parser.display_help(conventions);
|
||||
@@ -115,6 +116,23 @@ auto grep(argument_parser::base_parser& parser, std::string const& filename, std
|
||||
file.close();
|
||||
}
|
||||
|
||||
void run_grep(argument_parser::base_parser const& parser) {
|
||||
auto filename = parser.get_optional<std::string>("file");
|
||||
auto pattern = parser.get_optional<std::regex>("grep");
|
||||
|
||||
if (filename && pattern) {
|
||||
grep(parser, filename.value(), pattern.value());
|
||||
} else if (filename) {
|
||||
std::cerr << "Missing grep pattern" << std::endl;
|
||||
parser.display_help(conventions);
|
||||
exit(-1);
|
||||
} else if (pattern) {
|
||||
std::cerr << "Missing filename" << std::endl;
|
||||
parser.display_help(conventions);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto parser = argument_parser::parser{};
|
||||
|
||||
@@ -131,23 +149,9 @@ int main() {
|
||||
|
||||
parser.add_argument<std::vector<int>>("t", "test", "Test vector<int>", false);
|
||||
parser.add_argument<std::vector<std::string>>("ts", "test-strings", "Test vector<string>", false);
|
||||
|
||||
parser.on_complete(::run_grep);
|
||||
parser.handle_arguments(conventions);
|
||||
|
||||
auto filename = parser.get_optional<std::string>("file");
|
||||
auto pattern = parser.get_optional<std::regex>("grep");
|
||||
if (filename && pattern) {
|
||||
grep(parser, filename.value(), pattern.value());
|
||||
} else if (filename) {
|
||||
std::cerr << "Missing grep pattern" << std::endl;
|
||||
parser.display_help(conventions);
|
||||
exit(-1);
|
||||
} else if (pattern) {
|
||||
std::cerr << "Missing filename" << std::endl;
|
||||
parser.display_help(conventions);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
auto test = parser.get_optional<std::vector<int>>("test");
|
||||
if (test) {
|
||||
for (auto const& item : test.value()) {
|
||||
@@ -162,7 +166,5 @@ int main() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user