Refactor Options to use TagsHandlers; add --stream

This commit is contained in:
rr-
2016-02-24 22:13:28 +01:00
parent 24c2dae49e
commit 7b6ad95b35
3 changed files with 118 additions and 64 deletions

View File

@ -1,5 +1,8 @@
#include <getopt.h>
#include <regex>
#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<RemovalTagsHandler>(
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<ModificationTagsHandler>(
current_streamno, match[1], match[2]));
}
else
{
options.tags_handler.add_handler(
std::make_shared<InsertionTagsHandler>(
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<RemovalTagsHandler>(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");
}

View File

@ -3,6 +3,7 @@
#include <stdexcept>
#include <map>
#include <vector>
#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<std::string, std::string> to_add;
std::vector<std::string> to_delete;
CompositeTagsHandler tags_handler;
};
struct ArgumentError : std::runtime_error

View File

@ -1,6 +1,11 @@
#include "options.h"
#include <memory>
#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<char[]> string_to_uptr(const std::string &str)
{
@ -11,7 +16,7 @@ static std::unique_ptr<char[]> string_to_uptr(const std::string &str)
return ret;
}
static opustags::Options retrieve_options(std::vector<std::string> args)
static Options retrieve_options(std::vector<std::string> args)
{
// need to pass non-const char*, but we got const objects. make copies
std::vector<std::unique_ptr<char[]>> arg_holders;
@ -23,7 +28,17 @@ static opustags::Options retrieve_options(std::vector<std::string> 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<typename T> static T *get_handler(
const Options &options, const size_t index)
{
const auto handlers = options.tags_handler.get_handlers();
const auto handler = dynamic_cast<T*>(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<RemovalTagsHandler>(
retrieve_options({"--delete-all"}), 0)->get_tag_key().empty());
REQUIRE(get_handler<RemovalTagsHandler>(
retrieve_options({"-D"}), 0)->get_tag_key().empty());
}
SECTION("--delete") {
REQUIRE(
retrieve_options({"--delete", "ABC"}).to_delete
== std::vector<std::string>{"ABC"});
REQUIRE(
retrieve_options({"-d", "ABC"}).to_delete
== std::vector<std::string>{"ABC"});
REQUIRE(
retrieve_options({"-d", "ABC", "-d", "XYZ"}).to_delete
== (std::vector<std::string>{"ABC", "XYZ"}));
const auto opts = retrieve_options({"--delete", "A", "-d", "B"});
REQUIRE(get_handler<RemovalTagsHandler>(opts, 0)->get_tag_key() == "A");
REQUIRE(get_handler<RemovalTagsHandler>(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<std::string, std::string>{{"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<std::string, std::string>{
{"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<InsertionTagsHandler>(opts, 0);
const auto handler2 = get_handler<InsertionTagsHandler>(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<std::string, std::string>{{"ABC", "XYZ"}}));
REQUIRE(args1.to_delete == args2.to_delete);
REQUIRE(args1.to_delete == std::vector<std::string>{"ABC"});
const auto args3 = retrieve_options({"-s", "ABC=XYZ", "-s", "1=2"});
REQUIRE(args3.to_add == (std::map<std::string, std::string>{
{"ABC", "XYZ"},
{"1", "2"}}));
REQUIRE(args3.to_delete == (std::vector<std::string>{"ABC", "1"}));
const auto opts = retrieve_options({"--set", "A=1", "-s", "B=2"});
const auto handler1 = get_handler<ModificationTagsHandler>(opts, 0);
const auto handler2 = get_handler<ModificationTagsHandler>(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<RemovalTagsHandler>(retrieve_options({"-d", "xyz"}), 0)
->get_streamno() == StreamTagsHandler::ALL_STREAMS);
// ...unless the user supplies an option to use a specific stream
REQUIRE(
get_handler<RemovalTagsHandler>(
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<RemovalTagsHandler>(opts, 0);
const auto handler2 = get_handler<RemovalTagsHandler>(opts, 1);
REQUIRE(handler1->get_streamno() == 1);
REQUIRE(handler2->get_streamno() == 2);
}
}
}