diff --git a/src/options.cc b/src/options.cc index 3f1ecb6..bebe875 100644 --- a/src/options.cc +++ b/src/options.cc @@ -1,5 +1,8 @@ #include #include +#include "tags_handlers/insertion_tags_handler.h" +#include "tags_handlers/modification_tags_handler.h" +#include "tags_handlers/removal_tags_handler.h" #include "options.h" using namespace opustags; @@ -12,7 +15,6 @@ ArgumentError::ArgumentError(const std::string &message) Options::Options() : show_help(false), overwrite(false), - delete_all(false), set_all(false) { } @@ -20,23 +22,32 @@ Options::Options() : Options opustags::parse_args(const int argc, char **argv) { static const auto short_def = "ho:i::yd:a:s:DS"; + static const option long_def[] = { - {"help", no_argument, 0, 'h'}, - {"output", required_argument, 0, 'o'}, - {"in-place", optional_argument, 0, 'i'}, - {"overwrite", no_argument, 0, 'y'}, - {"delete", required_argument, 0, 'd'}, - {"add", required_argument, 0, 'a'}, - {"set", required_argument, 0, 's'}, - {"delete-all", no_argument, 0, 'D'}, - {"set-all", no_argument, 0, 'S'}, + {"help", no_argument, 0, 'h'}, + {"output", required_argument, 0, 'o'}, + {"in-place", optional_argument, 0, 'i'}, + {"overwrite", no_argument, 0, 'y'}, + {"delete", required_argument, 0, 'd'}, + {"add", required_argument, 0, 'a'}, + {"stream", required_argument, 0, 0}, + {"set", required_argument, 0, 's'}, + {"delete-all", no_argument, 0, 'D'}, + {"set-all", no_argument, 0, 'S'}, + {nullptr, 0, 0, 0} }; Options options; + + int current_streamno = StreamTagsHandler::ALL_STREAMS; + int option_index; char c; optind = 0; - while ((c = getopt_long(argc, argv, short_def, long_def, nullptr)) != -1) { + + while ((c = getopt_long( + argc, argv, short_def, long_def, &option_index)) != -1) { + const std::string arg(optarg == nullptr ? "" : optarg); switch (c) { @@ -59,7 +70,9 @@ Options opustags::parse_args(const int argc, char **argv) case 'd': if (arg.find('=') != std::string::npos) throw ArgumentError("Invalid field: '" + arg + "'"); - options.to_delete.push_back(arg); + options.tags_handler.add_handler( + std::make_shared( + current_streamno, arg)); break; case 'a': @@ -69,9 +82,18 @@ Options opustags::parse_args(const int argc, char **argv) std::regex regex("^(\\w+)=(.*)$"); if (!std::regex_match(arg, match, regex)) throw ArgumentError("Invalid field: '" + arg + "'"); - options.to_add[match[1]] = match[2]; if (c == 's') - options.to_delete.push_back(match[1]); + { + options.tags_handler.add_handler( + std::make_shared( + current_streamno, match[1], match[2])); + } + else + { + options.tags_handler.add_handler( + std::make_shared( + current_streamno, match[1], match[2])); + } break; } @@ -80,9 +102,18 @@ Options opustags::parse_args(const int argc, char **argv) break; case 'D': - options.delete_all = true; + options.tags_handler.add_handler( + std::make_shared(current_streamno)); break; + case 0: + { + std::string long_arg = long_def[option_index].name; + if (long_arg == "stream") + current_streamno = atoi(optarg); + break; + } + default: throw ArgumentError("Invalid flag"); } diff --git a/src/options.h b/src/options.h index afc4adf..867e57e 100644 --- a/src/options.h +++ b/src/options.h @@ -3,6 +3,7 @@ #include #include #include +#include "tags_handlers/composite_tags_handler.h" namespace opustags { @@ -12,13 +13,12 @@ namespace opustags bool show_help; bool overwrite; - bool delete_all; bool set_all; - std::string in_place; // string? + std::string in_place; std::string path_out; - std::map to_add; - std::vector to_delete; + + CompositeTagsHandler tags_handler; }; struct ArgumentError : std::runtime_error diff --git a/tests/options_test.cc b/tests/options_test.cc index e9230b3..4dc2d8b 100644 --- a/tests/options_test.cc +++ b/tests/options_test.cc @@ -1,6 +1,11 @@ #include "options.h" #include #include "catch.h" +#include "tags_handlers/modification_tags_handler.h" +#include "tags_handlers/insertion_tags_handler.h" +#include "tags_handlers/removal_tags_handler.h" + +using namespace opustags; static std::unique_ptr string_to_uptr(const std::string &str) { @@ -11,7 +16,7 @@ static std::unique_ptr string_to_uptr(const std::string &str) return ret; } -static opustags::Options retrieve_options(std::vector args) +static Options retrieve_options(std::vector args) { // need to pass non-const char*, but we got const objects. make copies std::vector> arg_holders; @@ -23,7 +28,17 @@ static opustags::Options retrieve_options(std::vector args) for (size_t i = 0; i < arg_holders.size(); i++) plain_args[i] = arg_holders[i].get(); - return opustags::parse_args(arg_holders.size(), plain_args.get()); + return parse_args(arg_holders.size(), plain_args.get()); +} + +// retrieve a specific TagsHandler from the CompositeTagsHandler in options +template static T *get_handler( + const Options &options, const size_t index) +{ + const auto handlers = options.tags_handler.get_handlers(); + const auto handler = dynamic_cast(handlers.at(index).get()); + REQUIRE(handler); + return handler; } TEST_CASE("Options parsing test") @@ -46,12 +61,6 @@ TEST_CASE("Options parsing test") REQUIRE(!retrieve_options({}).set_all); } - SECTION("--delete-all") { - REQUIRE(retrieve_options({"--delete-all"}).delete_all); - REQUIRE(retrieve_options({"-D"}).delete_all); - REQUIRE(!retrieve_options({}).delete_all); - } - SECTION("--in-place") { REQUIRE(retrieve_options({"-i"}).in_place == ".otmp"); REQUIRE(retrieve_options({"--in-place"}).in_place == ".otmp"); @@ -68,52 +77,66 @@ TEST_CASE("Options parsing test") REQUIRE_THROWS(retrieve_options({"--delete", "invalid="})); } + SECTION("--delete-all") { + REQUIRE(get_handler( + retrieve_options({"--delete-all"}), 0)->get_tag_key().empty()); + + REQUIRE(get_handler( + retrieve_options({"-D"}), 0)->get_tag_key().empty()); + } + SECTION("--delete") { - REQUIRE( - retrieve_options({"--delete", "ABC"}).to_delete - == std::vector{"ABC"}); - REQUIRE( - retrieve_options({"-d", "ABC"}).to_delete - == std::vector{"ABC"}); - REQUIRE( - retrieve_options({"-d", "ABC", "-d", "XYZ"}).to_delete - == (std::vector{"ABC", "XYZ"})); + const auto opts = retrieve_options({"--delete", "A", "-d", "B"}); + REQUIRE(get_handler(opts, 0)->get_tag_key() == "A"); + REQUIRE(get_handler(opts, 1)->get_tag_key() == "B"); REQUIRE_THROWS(retrieve_options({"--delete", "invalid="})); } SECTION("--add") { - const auto args1 = retrieve_options({"--add", "ABC=XYZ"}); - const auto args2 = retrieve_options({"-a", "ABC=XYZ"}); - REQUIRE(args1.to_add == args2.to_add); - REQUIRE(args1.to_add - == (std::map{{"ABC", "XYZ"}})); - REQUIRE(args1.to_delete.empty()); - REQUIRE(args2.to_delete.empty()); - - const auto args3 = retrieve_options({"-a", "ABC=XYZ", "-a", "1=2"}); - REQUIRE(args3.to_add == (std::map{ - {"ABC", "XYZ"}, - {"1", "2"}})); - REQUIRE(args3.to_delete.empty()); - + const auto opts = retrieve_options({"--add", "A=1", "-a", "B=2"}); + const auto handler1 = get_handler(opts, 0); + const auto handler2 = get_handler(opts, 1); + REQUIRE(handler1->get_tag_key() == "A"); + REQUIRE(handler1->get_tag_value() == "1"); + REQUIRE(handler2->get_tag_key() == "B"); + REQUIRE(handler2->get_tag_value() == "2"); REQUIRE_THROWS(retrieve_options({"--add", "invalid"})); } SECTION("--set") { - const auto args1 = retrieve_options({"--set", "ABC=XYZ"}); - const auto args2 = retrieve_options({"-s", "ABC=XYZ"}); - REQUIRE(args1.to_add == args2.to_add); - REQUIRE(args1.to_add - == (std::map{{"ABC", "XYZ"}})); - REQUIRE(args1.to_delete == args2.to_delete); - REQUIRE(args1.to_delete == std::vector{"ABC"}); - - const auto args3 = retrieve_options({"-s", "ABC=XYZ", "-s", "1=2"}); - REQUIRE(args3.to_add == (std::map{ - {"ABC", "XYZ"}, - {"1", "2"}})); - REQUIRE(args3.to_delete == (std::vector{"ABC", "1"})); - + const auto opts = retrieve_options({"--set", "A=1", "-s", "B=2"}); + const auto handler1 = get_handler(opts, 0); + const auto handler2 = get_handler(opts, 1); + REQUIRE(handler1->get_tag_key() == "A"); + REQUIRE(handler1->get_tag_value() == "1"); + REQUIRE(handler2->get_tag_key() == "B"); + REQUIRE(handler2->get_tag_value() == "2"); REQUIRE_THROWS(retrieve_options({"--set", "invalid"})); } + + SECTION("--stream") { + // by default, use all the streams + REQUIRE( + get_handler(retrieve_options({"-d", "xyz"}), 0) + ->get_streamno() == StreamTagsHandler::ALL_STREAMS); + + // ...unless the user supplies an option to use a specific stream + REQUIRE( + get_handler( + retrieve_options({"--stream", "1", "-d", "xyz"}), 0) + ->get_streamno() == 1); + + // ...which can be combined multiple times + { + const auto opts = retrieve_options({ + "--stream", "1", + "-d", "xyz", + "--stream", "2", + "-d", "abc"}); + const auto handler1 = get_handler(opts, 0); + const auto handler2 = get_handler(opts, 1); + REQUIRE(handler1->get_streamno() == 1); + REQUIRE(handler2->get_streamno() == 2); + } + } }