Skip to content

Commit

Permalink
Merge pull request #524 from rakshasa/feature-flags
Browse files Browse the repository at this point in the history
Feature flags
  • Loading branch information
rakshasa committed Nov 30, 2016
2 parents 10ce686 + 5873e41 commit 3c42371
Show file tree
Hide file tree
Showing 11 changed files with 399 additions and 90 deletions.
110 changes: 38 additions & 72 deletions src/command_dynamic.cc
Expand Up @@ -44,6 +44,30 @@
#include "control.h"
#include "command_helpers.h"
#include "rpc/parse.h"
#include "rpc/parse_options.h"

static std::vector<std::pair<const char*, int>> object_storage_flags = {
{ "multi", rpc::object_storage::flag_multi_type },
{ "simple", rpc::object_storage::flag_function_type },
{ "value", rpc::object_storage::flag_value_type },
{ "bool", rpc::object_storage::flag_bool_type },
{ "string", rpc::object_storage::flag_string_type },
{ "list", rpc::object_storage::flag_list_type },

{ "static", rpc::object_storage::flag_static },
{ "private", rpc::object_storage::flag_private },
{ "const", rpc::object_storage::flag_constant },
{ "rlookup", rpc::object_storage::flag_rlookup }
};

static int
object_storage_parse_flag(const std::string& flag) {
for (auto f : object_storage_flags)
if (f.first == flag)
return f.second;

throw torrent::input_error("unknown flag");
}

std::string
system_method_generate_command(torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last) {
Expand Down Expand Up @@ -244,91 +268,33 @@ system_method_insert(const torrent::Object::list_type& args) {
throw torrent::input_error("Invalid key.");

int flags = rpc::CommandMap::flag_delete_key | rpc::CommandMap::flag_modifiable | rpc::CommandMap::flag_public_xmlrpc;
int new_flags = rpc::parse_option_flags(itrArgs->as_string(), std::bind(&object_storage_parse_flag, std::placeholders::_1));

const std::string& options = itrArgs->as_string();

// TODO: Replace find with a method that does '|' separated search
// for all valid options, and then cross-checks that unmixable ones
// aren't in there. Use a bitfield.

if (options.find("private") != std::string::npos)
if ((new_flags & rpc::object_storage::flag_private))
flags &= ~rpc::CommandMap::flag_public_xmlrpc;
if (options.find("const") != std::string::npos)
flags &= ~rpc::CommandMap::flag_modifiable;

if (options.find("multi") != std::string::npos) {
torrent::Object::list_type new_args;
new_args.push_back(rawKey);
new_args.push_back(system_method_generate_command(++itrArgs, args.end()));

int new_flags = rpc::object_storage::flag_multi_type;

if (options.find("static") != std::string::npos)
new_flags |= rpc::object_storage::flag_static;
if (options.find("private") != std::string::npos)
new_flags |= rpc::object_storage::flag_private;
if (options.find("const") != std::string::npos)
new_flags |= rpc::object_storage::flag_constant;
if (options.find("rlookup") != std::string::npos)
new_flags |= rpc::object_storage::flag_rlookup;
if ((new_flags & rpc::object_storage::flag_constant))
flags &= ~rpc::CommandMap::flag_modifiable;

return system_method_insert_object(new_args, new_flags);
torrent::Object::list_type new_args;
new_args.push_back(rawKey);

} else if (options.find("simple") != std::string::npos) {
torrent::Object::list_type new_args;
new_args.push_back(rawKey);
if ((new_flags & rpc::object_storage::flag_function_type) ||
(new_flags & rpc::object_storage::flag_multi_type)) {
new_args.push_back(system_method_generate_command(++itrArgs, args.end()));

int new_flags = rpc::object_storage::flag_function_type;

if (options.find("static") != std::string::npos)
new_flags |= rpc::object_storage::flag_static;
if (options.find("private") != std::string::npos)
new_flags |= rpc::object_storage::flag_private;
if (options.find("const") != std::string::npos)
new_flags |= rpc::object_storage::flag_constant;

return system_method_insert_object(new_args, new_flags);

} else if (options.find("value") != std::string::npos ||
options.find("bool") != std::string::npos ||
options.find("string") != std::string::npos ||
options.find("list") != std::string::npos) {
torrent::Object::list_type new_args;
new_args.push_back(rawKey);

} else if ((new_flags & rpc::object_storage::flag_value_type) ||
(new_flags & rpc::object_storage::flag_bool_type) ||
(new_flags & rpc::object_storage::flag_string_type) ||
(new_flags & rpc::object_storage::flag_list_type)) {
if (++itrArgs != args.end())
new_args.insert(new_args.end(), itrArgs, args.end());

int new_flags;

if (options.find("value") != std::string::npos)
new_flags = rpc::object_storage::flag_value_type;
else if (options.find("bool") != std::string::npos)
new_flags = rpc::object_storage::flag_bool_type;
else if (options.find("string") != std::string::npos)
new_flags = rpc::object_storage::flag_string_type;
else if (options.find("list") != std::string::npos)
new_flags = rpc::object_storage::flag_list_type;
else if (options.find("simple") != std::string::npos)
new_flags = rpc::object_storage::flag_function_type;
else
throw torrent::input_error("No support for 'list' variable type.");

if (options.find("static") != std::string::npos)
new_flags |= rpc::object_storage::flag_static;
if (options.find("private") != std::string::npos)
new_flags |= rpc::object_storage::flag_private;
if (options.find("const") != std::string::npos)
new_flags |= rpc::object_storage::flag_constant;

return system_method_insert_object(new_args, new_flags);

} else {
// THROW.
throw torrent::input_error("No object type specified.");
}

return torrent::Object();
return system_method_insert_object(new_args, new_flags);
}

// method.erase <> {name}
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/Makefile.am
Expand Up @@ -20,6 +20,8 @@ libsub_rpc_a_SOURCES = \
parse.h \
parse_commands.cc \
parse_commands.h \
parse_options.cc \
parse_options.h \
scgi.cc \
scgi.h \
scgi_task.cc \
Expand Down
24 changes: 12 additions & 12 deletions src/rpc/object_storage.h
Expand Up @@ -92,18 +92,18 @@ class object_storage : private object_storage_base_type {

static const unsigned int flag_generic_type = 0x1;
static const unsigned int flag_bool_type = 0x2;
static const unsigned int flag_value_type = 0x3;
static const unsigned int flag_string_type = 0x4;
static const unsigned int flag_list_type = 0x5;
static const unsigned int flag_function_type = 0x6;
static const unsigned int flag_multi_type = 0x7;

static const unsigned int mask_type = 0xf;

static const unsigned int flag_constant = 0x10;
static const unsigned int flag_static = 0x20;
static const unsigned int flag_private = 0x40;
static const unsigned int flag_rlookup = 0x80;
static const unsigned int flag_value_type = 0x4;
static const unsigned int flag_string_type = 0x8;
static const unsigned int flag_list_type = 0x10;
static const unsigned int flag_function_type = 0x20;
static const unsigned int flag_multi_type = 0x40;

static const unsigned int mask_type = 0xff;

static const unsigned int flag_constant = 0x100;
static const unsigned int flag_static = 0x200;
static const unsigned int flag_private = 0x400;
static const unsigned int flag_rlookup = 0x800;

static const size_t key_size = key_type::max_size;

Expand Down
124 changes: 124 additions & 0 deletions src/rpc/parse_options.cc
@@ -0,0 +1,124 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY

#include "parse_options.h"

#include <algorithm>
#include <locale>
#include <torrent/exceptions.h>

namespace rpc {

int
parse_option_flag(const std::string& option, std::function<int (const std::string&)> ftor) {
auto first = option.begin();
auto last = option.end();

first = std::find_if(first, last, [](char c) { return !std::isspace(c, std::locale::classic()); });

if (first == last)
throw torrent::input_error(option);

auto next = std::find_if(first, last, [](char c) { return !std::isalnum(c, std::locale::classic()) && c != '_'; });

if (first == next)
throw torrent::input_error(option);

if (std::find_if(next, last, [](char c) { return !std::isspace(c, std::locale::classic()); }) != last)
throw torrent::input_error(option);

return ftor(std::string(first, next));
}

int
parse_option_flags(const std::string& option, std::function<int (const std::string&)> ftor, int flags) {
auto first = option.begin();
auto last = option.end();

while (first != last) {
first = std::find_if(first, last, [](char c) { return !std::isspace(c, std::locale::classic()); });

if (first == last)
break;

auto next = std::find_if(first, last, [](char c) { return !std::isalnum(c, std::locale::classic()) && c != '_'; });

if (first == next)
throw torrent::input_error(option);

flags |= ftor(std::string(first, next));

first = std::find_if(next, last, [](char c) { return !std::isspace(c, std::locale::classic()); });

if (first == last)
break;

if (*first++ != '|' || first == last)
throw torrent::input_error(option);
}

return flags;
}

void
parse_option_for_each(const std::string& option, std::function<int (const std::string&)> ftor) {
auto first = option.begin();
auto last = option.end();

while (first != last) {
first = std::find_if(first, last, [](char c) { return !std::isspace(c, std::locale::classic()); });

if (first == last)
break;

auto next = std::find_if(first, last, [](char c) { return !std::isalnum(c, std::locale::classic()) && c != '_'; });

if (first == next)
throw torrent::input_error(option);

ftor(std::string(first, next));

first = std::find_if(next, last, [](char c) { return !std::isspace(c, std::locale::classic()); });

if (first == last)
break;

if (*first++ != '|' || first == last)
throw torrent::input_error(option);
}
}

}
53 changes: 53 additions & 0 deletions src/rpc/parse_options.h
@@ -0,0 +1,53 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY

#ifndef RTORRENT_RPC_PARSE_OPTIONS_H
#define RTORRENT_RPC_PARSE_OPTIONS_H

#include <cstring>
#include <functional>
#include <string>

namespace rpc {

int parse_option_flag(const std::string& option, std::function<int (const std::string&)> ftor);
int parse_option_flags(const std::string& option, std::function<int (const std::string&)> ftor, int flags = int());

void parse_option_for_each(const std::string& option, std::function<void (const std::string&)> ftor);

}

#endif
2 changes: 2 additions & 0 deletions test/Makefile.am
Expand Up @@ -20,6 +20,8 @@ rtorrentTest_SOURCES = \
rpc/command_slot_test.h \
rpc/object_storage_test.cc \
rpc/object_storage_test.h \
rpc/test_parse_options.cc \
rpc/test_parse_options.h \
src/command_dynamic_test.cc \
src/command_dynamic_test.h \
main.cc
Expand Down
7 changes: 7 additions & 0 deletions test/helpers/assert.h
@@ -0,0 +1,7 @@
#ifndef HELPERS_ASSERT_H
#define HELPERS_ASSERT_H

#define ASSERT_CATCH_INPUT_ERROR(some_code) \
try { some_code; CPPUNIT_ASSERT("torrent::input_error not caught" && false); } catch (torrent::input_error& e) { }

#endif
4 changes: 1 addition & 3 deletions test/rpc/object_storage_test.cc
@@ -1,12 +1,10 @@
#include "config.h"

#include "object_storage_test.h"
#include "helpers/assert.h"

CPPUNIT_TEST_SUITE_REGISTRATION(ObjectStorageTest);

#define ASSERT_CATCH_INPUT_ERROR(some_code) \
try { some_code; CPPUNIT_ASSERT("torrent::input_error not caught" && false); } catch (torrent::input_error& e) { }

void
ObjectStorageTest::test_basics() {
rpc::object_storage::iterator itr;
Expand Down

0 comments on commit 3c42371

Please sign in to comment.