From cad569de25f2f5511f0b7bf4147c88fffdd5c77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abd=C3=BCssamet=20ERSOYLU?= Date: Mon, 6 Oct 2025 11:17:14 +0400 Subject: [PATCH] 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 --- TODO.md | 2 ++ include/parser/argument_parser.hpp | 30 ++++++++++++++++------ src/main.cpp | 40 ++++++++++++++++-------------- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/TODO.md b/TODO.md index 98206a3..92e1454 100644 --- a/TODO.md +++ b/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. diff --git a/include/parser/argument_parser.hpp b/include/parser/argument_parser.hpp index 239a6c2..97a8990 100644 --- a/include/parser/argument_parser.hpp +++ b/include/parser/argument_parser.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #ifndef ARGUMENT_PARSER_HPP @@ -163,23 +164,27 @@ namespace argument_parser { base_add_argument(short_arg, long_arg, help_text, required); } + void on_complete(std::function const& action) { + on_complete_events.emplace_back(action); + } + template - std::optional get_optional(std::string const& arg) { + std::optional 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(value); + auto value = stored_arguments.find(id.value()); + if (value != stored_arguments.end() && value->second.has_value()) return std::any_cast(value->second); } return std::nullopt; } - std::string build_help_text(std::initializer_list convention_types) { + std::string build_help_text(std::initializer_list 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 find_argument_id(std::string const& arg) { + std::optional 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 convention_types) { + void display_help(std::initializer_list 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 stored_arguments; @@ -334,6 +346,8 @@ namespace argument_parser { std::unordered_map long_arguments; std::unordered_map reverse_long_arguments; + std::list> on_complete_events; + friend class linux_parser; friend class windows_parser; friend class macos_parser; diff --git a/src/main.cpp b/src/main.cpp index 105c326..3bff33d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#include "argument_parser.hpp" #include #include #include @@ -94,7 +95,7 @@ const auto cat = argument_parser::helpers::make_parametered_action( 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("file"); + auto pattern = parser.get_optional("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>("t", "test", "Test vector", false); parser.add_argument>("ts", "test-strings", "Test vector", false); - + parser.on_complete(::run_grep); parser.handle_arguments(conventions); - - auto filename = parser.get_optional("file"); - auto pattern = parser.get_optional("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>("test"); if (test) { for (auto const& item : test.value()) { @@ -162,7 +166,5 @@ int main() { } } - - return 0; } \ No newline at end of file