<commit_before>#include "vast/console.h"

#include <cassert>
#include <iomanip>
#include <cppa/cppa.hpp>
#include "vast/event.h"
#include "vast/util/color.h"
#include "vast/util/parse.h"

using namespace cppa;

namespace vast {

namespace {

// Generates a callback function for a mode or command.
struct help_printer
{
  template <typename T>
  auto operator()(T x) const -> util::command_line::callback
  {
    return [=](std::string) -> util::result<bool>
    {
      std::cerr
        << "\noptions for "
        << util::color::cyan << x->name() << util::color::reset << ":\n\n"
        << x->help(4)
        << std::endl;

      return true;
    };
  }
};

help_printer help;

struct cow_event_less_than
{
  bool operator()(cow<event> const& x, cow<event> const& y) const
  {
    return *x < *y;
  }
};

cow_event_less_than cow_event_lt;

} // namespace <anonymous>

console::console(cppa::actor_ptr search, path dir)
  : dir_{std::move(dir)},
    search_{std::move(search)}
{
  if (! exists(dir_))
    if (mkdir(dir_))
      VAST_LOG_ACTOR_ERROR("failed to create console directory: " << dir_);

  auto main
    = cmdline_.mode_add("main", "::: ", to<std::string>(dir_ / "history_main"));

  main->on_unknown_command(help(main));

  main->add("exit", "exit the console")->on(
      [=](std::string) -> util::result<bool>
      {
        quit(exit::stop);
        return {};
      });

  auto set = main->add("set", "adjust console settings");
  set->on(help(set));

  set->add("batch-size", "number of results to display")->on(
      [=](std::string args) -> util::result<bool>
      {
        uint64_t n;
        auto begin = args.begin();
        if (! extract(begin, args.end(), n))
        {
          opts_.batch_size = n;
          return true;
        }
        else
        {
          print(error) << "batch-size requires numeric argument" << std::endl;
          return false;
        }
      });

  auto auto_follow = set->add("auto-follow",
                              "enter interactive control mode after query creation");
  auto_follow->on(
      [=](std::string args) -> util::result<bool>
      {
        match_split(args, ' ')(
            on("T") >> [=](std::string const&)
            {
              opts_.auto_follow = true;
            },
            on("F") >> [=](std::string const&)
            {
              opts_.auto_follow = false;
            },
            others() >> [=]
            {
              print(error) << "need 'T' or 'F' as argument" << std::endl;
              return false;
            });

        return true;
      });

  set->add("show", "display the current settings")->on(
      [=](std::string) -> util::result<bool>
      {
        std::cerr
          << "batch-size = " << opts_.batch_size << '\n'
          << "auto-follow = " << (opts_.auto_follow ? "T" : "F")
          << std::endl;

        return true;
      });

  main->add("ask", "enter query mode")->on(
      [=](std::string) -> util::result<bool>
      {
        cmdline_.mode_push("ask");
        return true;
      });

  main->add("list", "list existing queries")->on(
      [=](std::string) -> util::result<bool>
      {
        for (auto& p : results_)
          std::cout
            << (&p.second == current_result_ ? " * " : "   ")
            << p.second.id() << '\t'
            << p.second.size()
            << std::endl;

        return true;
      });

  main->add("query", "enter a query")->on(
      [=](std::string args) -> util::result<bool>
      {
        if (args.empty())
        {
          print(error) << "missing query UUID" << std::endl;
          return false;
        }

        auto r = to_result(args);
        if (r.first)
        {
          VAST_LOG_ACTOR_DEBUG("enters query " << r.second->id());
          current_result_ = r.second;
          current_query_ = r.first;
          send(self, atom("key"), atom("get"));
          return {};
        }

        return true;
      });

  auto ask = cmdline_.mode_add("ask", "-=> ",
                               to<std::string>(dir_ / "history_query"));

  ask->add("exit", "leave query asking mode")->on(
      [=](std::string) -> util::result<bool>
      {
        cmdline_.mode_pop();
        return true;
      });

  ask->on_unknown_command(
      [=](std::string args) -> util::result<bool>
      {
        if (args.empty())
          return true;

        sync_send(search_, atom("query"), atom("create"), args).then(
            on(atom("EXITED"), arg_match) >> [=](uint32_t reason)
            {
              print(error)
                << "search terminated with exit code " << reason << std::endl;

              send_exit(self, exit::error);
            },
            on_arg_match >> [=](std::string const& failure) // TODO: use error class.
            {
              print(error) << "syntax error: " << failure << std::endl;
              show_prompt();
            },
            on_arg_match >> [=](actor_ptr const& qry, expr::ast const& ast)
            {
              assert(! results_.count(qry));
              assert(qry);
              assert(ast);

              // FIXME: remove
              //cmdline_.append_to_history(args);
              monitor(qry);
              current_query_ = qry;
              current_result_ = &results_.emplace(qry, ast).first->second;
              cmdline_.mode_pop();
              std::cerr
                << "new query " << current_result_->id()
                << " -> " << ast << std::endl;

              if (opts_.auto_follow)
              {
                follow_mode_ = true;
                send(self, atom("key"), atom("get"));
              }
              else
              {
                show_prompt();
              }
            },
            others() >> [=]
            {
              VAST_LOG_ACTOR_ERROR("got unexpected message: " <<
                                   to_string(last_dequeued()));
              show_prompt();
            });

      return {};
    });

  cmdline_.mode_push("main");
}

console::result::result(expr::ast ast)
  : ast_{std::move(ast)}
{
}

void console::result::add(cow<event> e)
{
  auto i = std::lower_bound(events_.begin(), events_.end(), e, cow_event_lt);
  assert(i == events_.end() || cow_event_lt(e, *i));
  events_.insert(i, e);
}

size_t console::result::apply(size_t n, std::function<void(event const&)> f)
{
  size_t i = 0;
  while (i < n && pos_ < events_.size())
  {
    f(*events_[pos_++]);
    ++i;
  }
  return i;
}

size_t console::result::seek_forward(size_t n)
{
  if (pos_ + n >= events_.size())
  {
    auto seeking = static_cast<size_t>(events_.size() - pos_);
    pos_ = events_.size();
    return seeking;
  }
  else
  {
    pos_ += n;
    return n;
  }
}

size_t console::result::seek_backward(size_t n)
{
  if (n > pos_)
  {
    auto old = pos_;
    pos_ = 0;
    return static_cast<size_t>(old);
  }
  else
  {
    pos_ -= n;
    return n;
  }
}

expr::ast const& console::result::ast() const
{
  return ast_;
}

size_t console::result::size() const
{
  return events_.size();
}

void console::act()
{
  chaining(false);

  auto keystroke_monitor = spawn<detached+linked>(
      [=]
      {
        become(
            on(atom("get")) >> [=]
            {
              char c;
              return make_any_tuple(atom("key"), cmdline_.get(c) ? c : 'q');
            });
      });

  become(
      on(atom("DOWN"), arg_match) >> [=](uint32_t)
      {
        VAST_LOG_ACTOR_ERROR("got DOWN from query @" << last_sender()->id());
        // TODO: seal corresponding result
      },
      on(atom("done")) >> [=]
      {
        VAST_LOG_ACTOR_DEBUG("got done from query @" << last_sender()->id());
        show_prompt();
        // TODO: seal corresponding query.
      },
      on(atom("prompt")) >> [=]
      {
        // An empty result means that we should not go back to the prompt.
        auto result = cmdline_.process();
        if (result.engaged())
        {
          // TODO: give visual clue if result failed.

          show_prompt();
        }
        else if (result.failed())
        {
          print(error) << result.failure() << std::endl;
          show_prompt();
        }
      },
      on_arg_match >> [=](event const&)
      {
        auto i = results_.find(last_sender());
        assert(i != results_.end());
        auto r = &i->second;
        cow<event> ce = *tuple_cast<event>(last_dequeued());
        r->add(ce);
        if (r == current_result_ && follow_mode_)
          std::cout << *ce << std::endl;
      },
      on(atom("key"), atom("get")) >> [=]
      {
        send(keystroke_monitor, atom("get"));
      },
      on(atom("key"), arg_match) >> [=](char key)
      {
        switch (key)
        {
          default:
            {
              print(error)
                << "invalid key: '" << key << "', press '?' for help"
                << std::endl;
            }
            break;
          case '?':
            {
              std::cerr
                << "interactive query control mode:\n\n"
                << "    <space>  display the next batch of available results\n"
                << "       e     ask query for more results\n"
                << "       f     toggle follow mode\n"
                << "       j     seek one batch forward\n"
                << "       k     seek one batch backword\n"
                << "       ?     display this help\n"
                << "       q     leave query control mode\n"
                << std::endl;
            }
            break;
          case ' ':
            {
              auto n = current_result_->apply(
                  opts_.batch_size,
                  [](event const& e) { std::cout << e << std::endl; });
              if (n == 0)
                print(query) << "reached end of results" << std::endl;
            }
            break;
          case 'e':
            {
              send(current_query_, atom("extract"));
              print(query) << "asks for more results" << std::endl;
            }
            break;
          case 'f':
            {
              follow_mode_ = ! follow_mode_;
              print(query)
                << "toggled follow-mode to " << (follow_mode_ ? "on" : "off")
                << std::endl;
            }
            break;
          case 'j':
            {
              auto n = current_result_->seek_forward(opts_.batch_size);
              print(query) << "seeked +" << n << " events" << std::endl;
            }
            break;
          case 'k':
            {
              auto n = current_result_->seek_backward(opts_.batch_size);
              print(query) << "seeked -" << n << " events" << std::endl;
            }
            break;
          case '':
          case '\n':
          case 'q':
            {
              follow_mode_ = false;
              show_prompt();
            }
            return;
        }
        send(keystroke_monitor, atom("get"));
      },
      others() >> [=]
      {
        VAST_LOG_ACTOR_ERROR("got unexpected message from @" <<
                             last_sender()->id() << ": " <<
                             to_string(last_dequeued()));
      });
}

char const* console::description() const
{
  return "console";
}

std::ostream& console::print(print_mode mode) const
{
  switch (mode)
  {
    default:
      std::cerr << util::color::red << "[???] ";
      break;
    case error:
      std::cerr << util::color::red << "[error] ";
      break;
    case query:
      std::cerr
        << util::color::cyan << "[query " << current_result_->id() << "] ";
      break;
  }

  std::cerr << util::color::reset;

  return std::cerr;
}

void console::show_prompt(size_t ms)
{
  // The delay allows for logging messages to trickle through first
  // before we print the prompt.
  delayed_send(self, std::chrono::milliseconds(ms), atom("prompt"));
}

std::pair<actor_ptr, console::result*>
console::to_result(std::string const& str)
{
  std::vector<result*> matches;
  actor_ptr qry = nullptr;
  for (auto& p : results_)
  {
    auto candidate = to<std::string>(p.second.id());
    auto i = std::mismatch(str.begin(), str.end(), candidate.begin());
    if (i.first == str.end())
    {
      qry = p.first;
      matches.push_back(&p.second);
    }
  }
  if (matches.empty())
    print(error) << "no such query: " << str << std::endl;
  else if (matches.size() > 1)
    print(error) << "ambiguous query: " << str << std::endl;
  else
    return {qry, matches[0]};
  return {nullptr, nullptr};
}

} // namespace vast
<commit_msg>Small cosmetic touch-ups.<commit_after>#include "vast/console.h"

#include <cassert>
#include <iomanip>
#include <cppa/cppa.hpp>
#include "vast/event.h"
#include "vast/util/color.h"
#include "vast/util/parse.h"

using namespace cppa;

namespace vast {

namespace {

// Generates a callback function for a mode or command.
struct help_printer
{
  template <typename T>
  auto operator()(T x) const -> util::command_line::callback
  {
    return [=](std::string) -> util::result<bool>
    {
      std::cerr
        << "\noptions for "
        << util::color::cyan << x->name() << util::color::reset << ":\n\n"
        << x->help(4)
        << std::endl;

      return true;
    };
  }
};

help_printer help;

struct cow_event_less_than
{
  bool operator()(cow<event> const& x, cow<event> const& y) const
  {
    return *x < *y;
  }
};

cow_event_less_than cow_event_lt;

} // namespace <anonymous>

console::console(cppa::actor_ptr search, path dir)
  : dir_{std::move(dir)},
    search_{std::move(search)}
{
  if (! exists(dir_))
    if (mkdir(dir_))
      VAST_LOG_ACTOR_ERROR("failed to create console directory: " << dir_);

  auto main
    = cmdline_.mode_add("main", "::: ", to<std::string>(dir_ / "history_main"));

  main->on_unknown_command(help(main));

  main->add("exit", "exit the console")->on(
      [=](std::string) -> util::result<bool>
      {
        quit(exit::stop);
        return {};
      });

  auto set = main->add("set", "adjust console settings");
  set->on(help(set));

  set->add("batch-size", "number of results to display")->on(
      [=](std::string args) -> util::result<bool>
      {
        uint64_t n;
        auto begin = args.begin();
        if (! extract(begin, args.end(), n))
        {
          opts_.batch_size = n;
          return true;
        }
        else
        {
          print(error) << "batch-size requires numeric argument" << std::endl;
          return false;
        }
      });

  auto auto_follow = set->add("auto-follow",
                              "enter interactive control mode after query creation");
  auto_follow->on(
      [=](std::string args) -> util::result<bool>
      {
        match_split(args, ' ')(
            on("T") >> [=](std::string const&)
            {
              opts_.auto_follow = true;
            },
            on("F") >> [=](std::string const&)
            {
              opts_.auto_follow = false;
            },
            others() >> [=]
            {
              print(error) << "need 'T' or 'F' as argument" << std::endl;
              return false;
            });

        return true;
      });

  set->add("show", "display the current settings")->on(
      [=](std::string) -> util::result<bool>
      {
        std::cerr
          << "batch-size = " << opts_.batch_size << '\n'
          << "auto-follow = " << (opts_.auto_follow ? "T" : "F")
          << std::endl;

        return true;
      });

  main->add("ask", "enter query mode")->on(
      [=](std::string) -> util::result<bool>
      {
        cmdline_.mode_push("ask");
        return true;
      });

  main->add("list", "list existing queries")->on(
      [=](std::string) -> util::result<bool>
      {
        for (auto& p : results_)
          std::cerr
            << util::color::green
            << (&p.second == current_result_ ? " * " : "   ")
            << util::color::reset
            << p.second.id() << ": "
            << p.second.size() << " events"
            << std::endl;

        return true;
      });

  main->add("query", "enter a query")->on(
      [=](std::string args) -> util::result<bool>
      {
        if (args.empty())
        {
          print(error) << "missing query UUID" << std::endl;
          return false;
        }

        auto r = to_result(args);
        if (r.first)
        {
          VAST_LOG_ACTOR_DEBUG("enters query " << r.second->id());
          current_result_ = r.second;
          current_query_ = r.first;
          send(self, atom("key"), atom("get"));
          return {};
        }

        return true;
      });

  auto ask = cmdline_.mode_add("ask", "-=> ",
                               to<std::string>(dir_ / "history_query"));

  ask->add("exit", "leave query asking mode")->on(
      [=](std::string) -> util::result<bool>
      {
        cmdline_.mode_pop();
        return true;
      });

  ask->on_unknown_command(
      [=](std::string args) -> util::result<bool>
      {
        if (args.empty())
          return true;

        sync_send(search_, atom("query"), atom("create"), args).then(
            on(atom("EXITED"), arg_match) >> [=](uint32_t reason)
            {
              print(error)
                << "search terminated with exit code " << reason << std::endl;

              send_exit(self, exit::error);
            },
            on_arg_match >> [=](std::string const& failure) // TODO: use error class.
            {
              print(error) << "syntax error: " << failure << std::endl;
              show_prompt();
            },
            on_arg_match >> [=](actor_ptr const& qry, expr::ast const& ast)
            {
              assert(! results_.count(qry));
              assert(qry);
              assert(ast);

              // FIXME: remove
              //cmdline_.append_to_history(args);
              monitor(qry);
              current_query_ = qry;
              current_result_ = &results_.emplace(qry, ast).first->second;
              cmdline_.mode_pop();
              std::cerr
                << "new query " << current_result_->id()
                << " -> " << ast << std::endl;

              if (opts_.auto_follow)
              {
                follow_mode_ = true;
                send(self, atom("key"), atom("get"));
              }
              else
              {
                show_prompt();
              }
            },
            others() >> [=]
            {
              VAST_LOG_ACTOR_ERROR("got unexpected message: " <<
                                   to_string(last_dequeued()));
              show_prompt();
            });

      return {};
    });

  cmdline_.mode_push("main");
}

console::result::result(expr::ast ast)
  : ast_{std::move(ast)}
{
}

void console::result::add(cow<event> e)
{
  auto i = std::lower_bound(events_.begin(), events_.end(), e, cow_event_lt);
  assert(i == events_.end() || cow_event_lt(e, *i));
  events_.insert(i, e);
}

size_t console::result::apply(size_t n, std::function<void(event const&)> f)
{
  size_t i = 0;
  while (i < n && pos_ < events_.size())
  {
    f(*events_[pos_++]);
    ++i;
  }
  return i;
}

size_t console::result::seek_forward(size_t n)
{
  if (pos_ + n >= events_.size())
  {
    auto seeking = static_cast<size_t>(events_.size() - pos_);
    pos_ = events_.size();
    return seeking;
  }
  else
  {
    pos_ += n;
    return n;
  }
}

size_t console::result::seek_backward(size_t n)
{
  if (n > pos_)
  {
    auto old = pos_;
    pos_ = 0;
    return static_cast<size_t>(old);
  }
  else
  {
    pos_ -= n;
    return n;
  }
}

expr::ast const& console::result::ast() const
{
  return ast_;
}

size_t console::result::size() const
{
  return events_.size();
}

void console::act()
{
  chaining(false);

  auto keystroke_monitor = spawn<detached+linked>(
      [=]
      {
        become(
            on(atom("get")) >> [=]
            {
              char c;
              return make_any_tuple(atom("key"), cmdline_.get(c) ? c : 'q');
            });
      });

  become(
      on(atom("DOWN"), arg_match) >> [=](uint32_t)
      {
        VAST_LOG_ACTOR_ERROR("got DOWN from query @" << last_sender()->id());
        // TODO: seal corresponding result
      },
      on(atom("done")) >> [=]
      {
        VAST_LOG_ACTOR_DEBUG("got done from query @" << last_sender()->id());
        show_prompt();
        // TODO: seal corresponding query.
      },
      on(atom("prompt")) >> [=]
      {
        // An empty result means that we should not go back to the prompt.
        auto result = cmdline_.process();
        if (result.engaged())
        {
          // TODO: give visual clue if result failed.

          show_prompt();
        }
        else if (result.failed())
        {
          print(error) << result.failure() << std::endl;
          show_prompt();
        }
      },
      on_arg_match >> [=](event const&)
      {
        auto i = results_.find(last_sender());
        assert(i != results_.end());
        auto r = &i->second;
        cow<event> ce = *tuple_cast<event>(last_dequeued());
        r->add(ce);
        if (r == current_result_ && follow_mode_)
          std::cout << *ce << std::endl;
      },
      on(atom("key"), atom("get")) >> [=]
      {
        send(keystroke_monitor, atom("get"));
      },
      on(atom("key"), arg_match) >> [=](char key)
      {
        switch (key)
        {
          default:
            {
              print(error)
                << "invalid key: '" << key << "', press '?' for help"
                << std::endl;
            }
            break;
          case '?':
            {
              std::cerr
                << "interactive query control mode:\n\n"
                << "    <space>  display the next batch of available results\n"
                << "       e     ask query for more results\n"
                << "       f     toggle follow mode\n"
                << "       j     seek one batch forward\n"
                << "       k     seek one batch backword\n"
                << "       ?     display this help\n"
                << "       q     leave query control mode\n"
                << std::endl;
            }
            break;
          case ' ':
            {
              auto n = current_result_->apply(
                  opts_.batch_size,
                  [](event const& e) { std::cout << e << std::endl; });
              if (n == 0)
                print(query) << "reached end of results" << std::endl;
            }
            break;
          case 'e':
            {
              send(current_query_, atom("extract"));
              print(query) << "asks for more results" << std::endl;
            }
            break;
          case 'f':
            {
              follow_mode_ = ! follow_mode_;
              print(query)
                << "toggled follow-mode to "
                << util::color::cyan
                << (follow_mode_ ? "on" : "off")
                << util::color::reset << std::endl;
            }
            break;
          case 'j':
            {
              auto n = current_result_->seek_forward(opts_.batch_size);
              print(query) << "seeked +" << n << " events" << std::endl;
            }
            break;
          case 'k':
            {
              auto n = current_result_->seek_backward(opts_.batch_size);
              print(query) << "seeked -" << n << " events" << std::endl;
            }
            break;
          case '':
          case '\n':
          case 'q':
            {
              follow_mode_ = false;
              show_prompt();
            }
            return;
        }
        send(keystroke_monitor, atom("get"));
      },
      others() >> [=]
      {
        VAST_LOG_ACTOR_ERROR("got unexpected message from @" <<
                             last_sender()->id() << ": " <<
                             to_string(last_dequeued()));
      });
}

char const* console::description() const
{
  return "console";
}

std::ostream& console::print(print_mode mode) const
{
  switch (mode)
  {
    default:
      std::cerr << util::color::red << "[???] ";
      break;
    case error:
      std::cerr << util::color::red << "[error] ";
      break;
    case query:
      std::cerr
        << util::color::cyan << "[query " << current_result_->id() << "] ";
      break;
  }

  std::cerr << util::color::reset;

  return std::cerr;
}

void console::show_prompt(size_t ms)
{
  // The delay allows for logging messages to trickle through first
  // before we print the prompt.
  delayed_send(self, std::chrono::milliseconds(ms), atom("prompt"));
}

std::pair<actor_ptr, console::result*>
console::to_result(std::string const& str)
{
  std::vector<result*> matches;
  actor_ptr qry = nullptr;
  for (auto& p : results_)
  {
    auto candidate = to<std::string>(p.second.id());
    auto i = std::mismatch(str.begin(), str.end(), candidate.begin());
    if (i.first == str.end())
    {
      qry = p.first;
      matches.push_back(&p.second);
    }
  }
  if (matches.empty())
    print(error) << "no such query: " << str << std::endl;
  else if (matches.size() > 1)
    print(error) << "ambiguous query: " << str << std::endl;
  else
    return {qry, matches[0]};
  return {nullptr, nullptr};
}

} // namespace vast
<|endoftext|>


<commit_before>#include <dbus/dbus-glib.h>
#include <webkit2/webkit-web-extension.h>
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSStringRef.h>
#include <string.h>
#include "dbus.h"
#include "utils.h"

static GDBusConnection* connection;

static void dispatch_ignore_event(WebKitWebPage* page, gchar* eventName, const gchar* uri) {
	gchar* ignoreName = g_strconcat("r", eventName, NULL);
	WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
	WebKitDOMDOMWindow* window = webkit_dom_document_get_default_view(document);
	GError* error = NULL;
	WebKitDOMEvent* event = webkit_dom_document_create_event(document, "KeyboardEvent", &error);
	g_object_unref(document);
	if (error != NULL) {
		g_printerr("Cannot create event in dispatch_ignore_event: %s\n", error->message);
		g_error_free(error);
		return;
	}
	webkit_dom_keyboard_event_init_keyboard_event(
		(WebKitDOMKeyboardEvent*)event,	ignoreName,	FALSE, TRUE,
		window, uri, 0, FALSE, FALSE, FALSE, FALSE, FALSE
	);
	webkit_dom_event_target_dispatch_event(WEBKIT_DOM_EVENT_TARGET(window), event, &error);
	g_object_unref(window);
	g_object_unref(event);
	if (error != NULL) {
		g_printerr("Cannot dispatch event in dispatch_ignore_event: %s\n", error->message);
		g_error_free(error);
	}
}

static gboolean web_page_send_request(WebKitWebPage* page, WebKitURIRequest* request, WebKitURIResponse* redirected_response, gpointer data) {
	GError* error = NULL;
	// ignore redirected requests - it's transparent to the user
	if (redirected_response != NULL) {
		return FALSE;
	}
	gchar* eventName = (gchar*)data;
	const char* uri = webkit_uri_request_get_uri(request);
	SoupMessageHeaders* headers = webkit_uri_request_get_http_headers(request);

	GVariantDict dictIn;
	GVariant* variantIn = soup_headers_to_gvariant_dict(headers);
	g_variant_dict_init(&dictIn, variantIn);
	g_variant_dict_insert(&dictIn, "uri", "s", uri);
	variantIn = g_variant_dict_end(&dictIn);

	GVariant* tuple[1];
	tuple[0] = variantIn;

	GVariant* results = g_dbus_connection_call_sync(connection, NULL, DBUS_OBJECT_WKGTK, DBUS_INTERFACE_WKGTK, "HandleRequest", g_variant_new_tuple(tuple, 1), G_VARIANT_TYPE_TUPLE, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);

	g_variant_dict_clear(&dictIn);

	if (results == NULL) {
		g_printerr("ERR g_dbus_connection_call_sync %s\n", error->message);
		g_error_free(error);
		return FALSE;
	}

	GVariantDict dictOut;
	g_variant_dict_init(&dictOut, g_variant_get_child_value(results, 0));

	const gchar* newuri = NULL;
	const gchar* cancel = NULL;
	const gchar* ignore = NULL;

	gboolean ret = FALSE;
	if (g_variant_dict_lookup(&dictOut, "cancel", "s", &cancel)
	&& cancel != NULL && !g_strcmp0(cancel, "1")) {
		// returning TRUE blocks requests - it's better to set an empty uri - it sets status to 0;
		webkit_uri_request_set_uri(request, "");
	} else {
		if (g_variant_dict_lookup(&dictOut, "uri", "s", &newuri) && newuri != NULL) {
			webkit_uri_request_set_uri(request, newuri);
		}
		if (g_variant_dict_lookup(&dictOut, "ignore", "s", &ignore)
		&& ignore != NULL && !g_strcmp0(ignore, "1")) {
			dispatch_ignore_event(page, eventName, uri);
		}
	}

	g_variant_dict_remove(&dictOut, "uri");

	results = g_variant_dict_end(&dictOut);
	update_soup_headers_with_dict(headers, results);
	g_variant_unref(results);

	return ret;
}

static void web_page_created_callback(WebKitWebExtension* extension, WebKitWebPage* web_page, gpointer data) {
	g_signal_handlers_disconnect_by_data(web_page, data);
	g_signal_connect(web_page, "send-request", G_CALLBACK(web_page_send_request), data);
}

static gboolean event_listener(WebKitDOMDOMWindow* view, WebKitDOMEvent* event, gpointer data) {
	char* message = webkit_dom_keyboard_event_get_key_identifier((WebKitDOMKeyboardEvent*)event);
	GError* error = NULL;
	g_dbus_connection_call_sync(connection, NULL, DBUS_OBJECT_WKGTK, DBUS_INTERFACE_WKGTK,
		"NotifyEvent", g_variant_new("(s)", message), G_VARIANT_TYPE("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL,
		&error);
	if (error != NULL) {
		g_printerr("Failed to finish dbus call: %s\n", error->message);
		g_error_free(error);
	}
	g_free(message);
	return TRUE;
}

static void window_object_cleared_callback(WebKitScriptWorld* world, WebKitWebPage* page, WebKitFrame* frame, gchar* eventName) {
	WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
	WebKitDOMDOMWindow* window = webkit_dom_document_get_default_view(document);
	webkit_dom_event_target_add_event_listener(WEBKIT_DOM_EVENT_TARGET(window), eventName, G_CALLBACK(event_listener), false, NULL);
}

extern "C" {
	G_MODULE_EXPORT void webkit_web_extension_initialize_with_user_data(WebKitWebExtension* extension, const GVariant* constData) {
		gchar* address = NULL;
		gchar* eventName = NULL;
		g_variant_get((GVariant*)constData, "(ss)", &address, &eventName);

		g_signal_connect(webkit_script_world_get_default(), "window-object-cleared", G_CALLBACK(window_object_cleared_callback), eventName);

		g_signal_connect(extension, "page-created", G_CALLBACK(web_page_created_callback), eventName);

		GError* error = NULL;
		connection = g_dbus_connection_new_for_address_sync(address, G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, NULL, NULL, &error);
		if (connection == NULL) {
			g_printerr("Failed to open connection to bus: %s\n", error->message);
			g_error_free(error);
		}
	}
}
<commit_msg>Do not unref document too much<commit_after>#include <dbus/dbus-glib.h>
#include <webkit2/webkit-web-extension.h>
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSStringRef.h>
#include <string.h>
#include "dbus.h"
#include "utils.h"

static GDBusConnection* connection;

static void dispatch_ignore_event(WebKitWebPage* page, gchar* eventName, const gchar* uri) {
	gchar* ignoreName = g_strconcat("r", eventName, NULL);
	WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
	WebKitDOMDOMWindow* window = webkit_dom_document_get_default_view(document);
	GError* error = NULL;
	WebKitDOMEvent* event = webkit_dom_document_create_event(document, "KeyboardEvent", &error);
	if (error != NULL) {
		g_printerr("Cannot create event in dispatch_ignore_event: %s\n", error->message);
		g_error_free(error);
		return;
	}
	webkit_dom_keyboard_event_init_keyboard_event(
		(WebKitDOMKeyboardEvent*)event,	ignoreName,	FALSE, TRUE,
		window, uri, 0, FALSE, FALSE, FALSE, FALSE, FALSE
	);
	webkit_dom_event_target_dispatch_event(WEBKIT_DOM_EVENT_TARGET(window), event, &error);
	g_object_unref(window);
	g_object_unref(event);
	if (error != NULL) {
		g_printerr("Cannot dispatch event in dispatch_ignore_event: %s\n", error->message);
		g_error_free(error);
	}
}

static gboolean web_page_send_request(WebKitWebPage* page, WebKitURIRequest* request, WebKitURIResponse* redirected_response, gpointer data) {
	GError* error = NULL;
	// ignore redirected requests - it's transparent to the user
	if (redirected_response != NULL) {
		return FALSE;
	}
	gchar* eventName = (gchar*)data;
	const char* uri = webkit_uri_request_get_uri(request);
	SoupMessageHeaders* headers = webkit_uri_request_get_http_headers(request);

	GVariantDict dictIn;
	GVariant* variantIn = soup_headers_to_gvariant_dict(headers);
	g_variant_dict_init(&dictIn, variantIn);
	g_variant_dict_insert(&dictIn, "uri", "s", uri);
	variantIn = g_variant_dict_end(&dictIn);

	GVariant* tuple[1];
	tuple[0] = variantIn;

	GVariant* results = g_dbus_connection_call_sync(connection, NULL, DBUS_OBJECT_WKGTK, DBUS_INTERFACE_WKGTK, "HandleRequest", g_variant_new_tuple(tuple, 1), G_VARIANT_TYPE_TUPLE, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);

	g_variant_dict_clear(&dictIn);

	if (results == NULL) {
		g_printerr("ERR g_dbus_connection_call_sync %s\n", error->message);
		g_error_free(error);
		return FALSE;
	}

	GVariantDict dictOut;
	g_variant_dict_init(&dictOut, g_variant_get_child_value(results, 0));

	const gchar* newuri = NULL;
	const gchar* cancel = NULL;
	const gchar* ignore = NULL;

	gboolean ret = FALSE;
	if (g_variant_dict_lookup(&dictOut, "cancel", "s", &cancel)
	&& cancel != NULL && !g_strcmp0(cancel, "1")) {
		// returning TRUE blocks requests - it's better to set an empty uri - it sets status to 0;
		webkit_uri_request_set_uri(request, "");
	} else {
		if (g_variant_dict_lookup(&dictOut, "uri", "s", &newuri) && newuri != NULL) {
			webkit_uri_request_set_uri(request, newuri);
		}
		if (g_variant_dict_lookup(&dictOut, "ignore", "s", &ignore)
		&& ignore != NULL && !g_strcmp0(ignore, "1")) {
			dispatch_ignore_event(page, eventName, uri);
		}
	}

	g_variant_dict_remove(&dictOut, "uri");

	results = g_variant_dict_end(&dictOut);
	update_soup_headers_with_dict(headers, results);
	g_variant_unref(results);

	return ret;
}

static void web_page_created_callback(WebKitWebExtension* extension, WebKitWebPage* web_page, gpointer data) {
	g_signal_handlers_disconnect_by_data(web_page, data);
	g_signal_connect(web_page, "send-request", G_CALLBACK(web_page_send_request), data);
}

static gboolean event_listener(WebKitDOMDOMWindow* view, WebKitDOMEvent* event, gpointer data) {
	char* message = webkit_dom_keyboard_event_get_key_identifier((WebKitDOMKeyboardEvent*)event);
	GError* error = NULL;
	g_dbus_connection_call_sync(connection, NULL, DBUS_OBJECT_WKGTK, DBUS_INTERFACE_WKGTK,
		"NotifyEvent", g_variant_new("(s)", message), G_VARIANT_TYPE("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL,
		&error);
	if (error != NULL) {
		g_printerr("Failed to finish dbus call: %s\n", error->message);
		g_error_free(error);
	}
	g_free(message);
	return TRUE;
}

static void window_object_cleared_callback(WebKitScriptWorld* world, WebKitWebPage* page, WebKitFrame* frame, gchar* eventName) {
	WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
	WebKitDOMDOMWindow* window = webkit_dom_document_get_default_view(document);
	webkit_dom_event_target_add_event_listener(WEBKIT_DOM_EVENT_TARGET(window), eventName, G_CALLBACK(event_listener), false, NULL);
}

extern "C" {
	G_MODULE_EXPORT void webkit_web_extension_initialize_with_user_data(WebKitWebExtension* extension, const GVariant* constData) {
		gchar* address = NULL;
		gchar* eventName = NULL;
		g_variant_get((GVariant*)constData, "(ss)", &address, &eventName);

		g_signal_connect(webkit_script_world_get_default(), "window-object-cleared", G_CALLBACK(window_object_cleared_callback), eventName);

		g_signal_connect(extension, "page-created", G_CALLBACK(web_page_created_callback), eventName);

		GError* error = NULL;
		connection = g_dbus_connection_new_for_address_sync(address, G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, NULL, NULL, &error);
		if (connection == NULL) {
			g_printerr("Failed to open connection to bus: %s\n", error->message);
			g_error_free(error);
		}
	}
}
<|endoftext|>


<commit_before>/* <x0/response.cpp>
 *
 * This file is part of the x0 web server project and is released under LGPL-3.
 *
 * (c) 2009 Chrisitan Parpart <trapni@gentoo.org>
 */

#include <x0/response.hpp>
#include <x0/server.hpp>
#include <x0/io/file.hpp>
#include <x0/io/file_source.hpp>
#include <x0/io/buffer_source.hpp>
#include <x0/io/chunked_filter.hpp>
#include <x0/strutils.hpp>
#include <x0/types.hpp>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <strings.h>						// strcasecmp
#include <string>
#include <algorithm>

namespace x0 {

using boost::algorithm::iequals;

char response::status_codes[512][4];

response::~response()
{
	//DEBUG("~response(%p, conn=%p)", this, connection_.get());
	delete request_;
}

/** checks wether given code MUST NOT have a response body. */
static inline bool content_forbidden(int code)
{
	switch (code)
	{
		case response::continue_:
		case response::switching_protocols:
		case response::no_content:
		case response::reset_content:
		case response::not_modified:
			return true;
		default:
			return false;
	}
}

template<typename T>
inline std::string make_str(T value)
{
	return boost::lexical_cast<std::string>(value);
}

source_ptr response::make_default_content()
{
	if (content_forbidden(status)) // || !equals(request_->method, "GET"))
		return source_ptr();

	std::string filename(connection_->server().config()["ErrorDocuments"][make_str(status)].as<std::string>());
	fileinfo_ptr fi(connection_->server().fileinfo(filename));
	if (fi->exists())
	{
		file_ptr f(new file(fi));

		headers.set("Content-Type", fi->mimetype());
		headers.set("Content-Length", boost::lexical_cast<std::string>(fi->size()));

		return source_ptr(new file_source(f));
	}
	else
	{
		const char *codeStr = status_cstr(status);
		char buf[1024];

		int nwritten = snprintf(buf, sizeof(buf),
			""
			"<title>%s</title>"
			"
%d %s
"
			"",
			codeStr, status(), codeStr
		);

		headers.set("Content-Type", "text/html");
		headers.set("Content-Length", boost::lexical_cast<std::string>(nwritten));

		return source_ptr(new buffer_source(buffer::from_copy(buf, nwritten)));
	}
}

source_ptr response::serialize()
{
	buffer buffers;

	if (!status)
	{
		status = response::ok;
	}

	if (!headers.contains("Content-Type"))
	{
		headers.push_back("Content-Type", "text/plain"); //!< \todo pass "default" content-type instead!
	}

	if (!headers.contains("Content-Length") && !content_forbidden(status))
	{
		headers.set("Connection", "closed");
	}
	else if (!headers.contains("Connection"))
	{
		if (iequals(request_->header("Connection"), "keep-alive"))
			headers.push_back("Connection", "keep-alive");
		else
			headers.push_back("Connection", "closed");
	}

	// post-response hook
	connection_->server().post_process(const_cast<x0::request *>(request_), this);

	// enable chunked transfer encoding if required and possible
	if (!headers.contains("Content-Length"))
	{
		if (request_->supports_protocol(1, 1)
			&& !headers.contains("Transfer-Encoding"))
		{
			headers.push_back("Transfer-Encoding", "chunked");
			filter_chain.push_back(std::make_shared<chunked_filter>());
		}
		else
		{
			headers.set("Connection", "closed");
		}
	}

	if (request_->supports_protocol(1, 1))
		buffers.push_back("HTTP/1.1 ");
	else if (request_->supports_protocol(1, 0))
		buffers.push_back("HTTP/1.0 ");
	else
		buffers.push_back("HTTP/0.9 ");

	buffers.push_back(status_codes[status]);
	buffers.push_back(' ');
	buffers.push_back(status_cstr(status));
	buffers.push_back("\r\n");

	std::for_each(headers.begin(), headers.end(), [&](response_header& h) {
		buffers.push_back(h.name.data(), h.name.size());
		buffers.push_back(": ");
		buffers.push_back(h.value.data(), h.value.size());
		buffers.push_back("\r\n");
	});

	buffers.push_back("\r\n");

	return std::make_shared<buffer_source>(std::move(buffers));
}

response::response(connection_ptr connection, x0::request *request, int _status) :
	connection_(connection),
	request_(request),
	headers_sent_(false),
	status(_status),
	headers()
{
	//DEBUG("response(%p, conn=%p)", this, connection_.get());

	headers.push_back("Date", connection_->server().now().http_str().str());
	headers.push_back("Server", connection_->server().tag());
}

const char *response::status_cstr(int value)
{
	switch (value)
	{
		case 200: return "Ok";
		case 201: return "Created";
		case 202: return "Accepted";
		case 204: return "No Content";
		case 206: return "Partial Content";
		case 300: return "Multiple Choices";
		case 301: return "Moved Permanently";
		case 302: return "Moved Temporarily";
		case 304: return "Not Modified";
		case 400: return "Bad Request";
		case 401: return "Unauthorized";
		case 403: return "Forbidden";
		case 404: return "Not Found";
		case 416: return "Requested Range Not Satisfiable";
		case 500: return "Internal Server Error";
		case 501: return "Not_Implemented";
		case 502: return "Bad Gateway";
		case 503: return "Service Unavailable";
		default: return "";
	}
}

std::string response::status_str(int value)
{
	return std::string(status_cstr(value));
}

/** handler, being invoked when this response has been fully flushed and is considered done.
 */
void response::finished(const asio::error_code& ec)
{
//	DEBUG("response(%p).finished(%s)", this, ec.message().c_str());

	{
		server& srv = request_->connection.server();

		// log request/response
		srv.request_done(const_cast<x0::request *>(request_), this);
	}

	if (!ec)
	{
		if (strcasecmp(headers["Connection"].c_str(), "keep-alive") == 0)
		{
			connection_->resume();
		}
	}

	delete this;
}

void response::initialize()
{
	// pre-compute string representations of status codes for use in response serialization
	for (std::size_t i = 0; i < sizeof(status_codes) / sizeof(*status_codes); ++i)
		snprintf(status_codes[i], sizeof(*status_codes), "%03ld", i);
}

} // namespace x0
<commit_msg>core: favor std::make_shared<T>(...)<commit_after>/* <x0/response.cpp>
 *
 * This file is part of the x0 web server project and is released under LGPL-3.
 *
 * (c) 2009 Chrisitan Parpart <trapni@gentoo.org>
 */

#include <x0/response.hpp>
#include <x0/server.hpp>
#include <x0/io/file.hpp>
#include <x0/io/file_source.hpp>
#include <x0/io/buffer_source.hpp>
#include <x0/io/chunked_filter.hpp>
#include <x0/strutils.hpp>
#include <x0/types.hpp>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <strings.h>						// strcasecmp
#include <string>
#include <algorithm>

namespace x0 {

using boost::algorithm::iequals;

char response::status_codes[512][4];

response::~response()
{
	//DEBUG("~response(%p, conn=%p)", this, connection_.get());
	delete request_;
}

/** checks wether given code MUST NOT have a response body. */
static inline bool content_forbidden(int code)
{
	switch (code)
	{
		case response::continue_:
		case response::switching_protocols:
		case response::no_content:
		case response::reset_content:
		case response::not_modified:
			return true;
		default:
			return false;
	}
}

template<typename T>
inline std::string make_str(T value)
{
	return boost::lexical_cast<std::string>(value);
}

source_ptr response::make_default_content()
{
	if (content_forbidden(status)) // || !equals(request_->method, "GET"))
		return source_ptr();

	std::string filename(connection_->server().config()["ErrorDocuments"][make_str(status)].as<std::string>());
	fileinfo_ptr fi(connection_->server().fileinfo(filename));
	if (fi->exists())
	{
		file_ptr f(new file(fi));

		headers.set("Content-Type", fi->mimetype());
		headers.set("Content-Length", boost::lexical_cast<std::string>(fi->size()));

		return std::make_shared<file_source>(f);
	}
	else
	{
		const char *codeStr = status_cstr(status);
		char buf[1024];

		int nwritten = snprintf(buf, sizeof(buf),
			""
			"<title>%s</title>"
			"
%d %s
"
			"",
			codeStr, status(), codeStr
		);

		headers.set("Content-Type", "text/html");
		headers.set("Content-Length", boost::lexical_cast<std::string>(nwritten));

		return std::make_shared<buffer_source>(buffer::from_copy(buf, nwritten));
	}
}

source_ptr response::serialize()
{
	buffer buffers;

	if (!status)
	{
		status = response::ok;
	}

	if (!headers.contains("Content-Type"))
	{
		headers.push_back("Content-Type", "text/plain"); //!< \todo pass "default" content-type instead!
	}

	if (!headers.contains("Content-Length") && !content_forbidden(status))
	{
		headers.set("Connection", "closed");
	}
	else if (!headers.contains("Connection"))
	{
		if (iequals(request_->header("Connection"), "keep-alive"))
			headers.push_back("Connection", "keep-alive");
		else
			headers.push_back("Connection", "closed");
	}

	// post-response hook
	connection_->server().post_process(const_cast<x0::request *>(request_), this);

	// enable chunked transfer encoding if required and possible
	if (!headers.contains("Content-Length"))
	{
		if (request_->supports_protocol(1, 1)
			&& !headers.contains("Transfer-Encoding"))
		{
			headers.push_back("Transfer-Encoding", "chunked");
			filter_chain.push_back(std::make_shared<chunked_filter>());
		}
		else
		{
			headers.set("Connection", "closed");
		}
	}

	if (request_->supports_protocol(1, 1))
		buffers.push_back("HTTP/1.1 ");
	else if (request_->supports_protocol(1, 0))
		buffers.push_back("HTTP/1.0 ");
	else
		buffers.push_back("HTTP/0.9 ");

	buffers.push_back(status_codes[status]);
	buffers.push_back(' ');
	buffers.push_back(status_cstr(status));
	buffers.push_back("\r\n");

	std::for_each(headers.begin(), headers.end(), [&](response_header& h) {
		buffers.push_back(h.name.data(), h.name.size());
		buffers.push_back(": ");
		buffers.push_back(h.value.data(), h.value.size());
		buffers.push_back("\r\n");
	});

	buffers.push_back("\r\n");

	return std::make_shared<buffer_source>(std::move(buffers));
}

response::response(connection_ptr connection, x0::request *request, int _status) :
	connection_(connection),
	request_(request),
	headers_sent_(false),
	status(_status),
	headers()
{
	//DEBUG("response(%p, conn=%p)", this, connection_.get());

	headers.push_back("Date", connection_->server().now().http_str().str());
	headers.push_back("Server", connection_->server().tag());
}

const char *response::status_cstr(int value)
{
	switch (value)
	{
		case 200: return "Ok";
		case 201: return "Created";
		case 202: return "Accepted";
		case 204: return "No Content";
		case 206: return "Partial Content";
		case 300: return "Multiple Choices";
		case 301: return "Moved Permanently";
		case 302: return "Moved Temporarily";
		case 304: return "Not Modified";
		case 400: return "Bad Request";
		case 401: return "Unauthorized";
		case 403: return "Forbidden";
		case 404: return "Not Found";
		case 416: return "Requested Range Not Satisfiable";
		case 500: return "Internal Server Error";
		case 501: return "Not_Implemented";
		case 502: return "Bad Gateway";
		case 503: return "Service Unavailable";
		default: return "";
	}
}

std::string response::status_str(int value)
{
	return std::string(status_cstr(value));
}

/** handler, being invoked when this response has been fully flushed and is considered done.
 */
void response::finished(const asio::error_code& ec)
{
//	DEBUG("response(%p).finished(%s)", this, ec.message().c_str());

	{
		server& srv = request_->connection.server();

		// log request/response
		srv.request_done(const_cast<x0::request *>(request_), this);
	}

	if (!ec)
	{
		if (strcasecmp(headers["Connection"].c_str(), "keep-alive") == 0)
		{
			connection_->resume();
		}
	}

	delete this;
}

void response::initialize()
{
	// pre-compute string representations of status codes for use in response serialization
	for (std::size_t i = 0; i < sizeof(status_codes) / sizeof(*status_codes); ++i)
		snprintf(status_codes[i], sizeof(*status_codes), "%03ld", i);
}

} // namespace x0
<|endoftext|>


<commit_before>/* <x0/strutils.tcc>
 *
 * This file is part of the x0 web server, released under GPLv3.
 * (c) 2009 Chrisitan Parpart <trapni@gentoo.org>
 */

#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>
#include <list>

namespace x0 {

template<typename T, typename U>
inline std::vector<T> split(const std::basic_string<U>& input, const std::basic_string<U>& sep)
{
	std::vector<T> result;
	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
	tokenizer tk(input, boost::char_separator<U>(sep.c_str()));

	for (tokenizer::iterator i = tk.begin(), e = tk.end(); i != e; ++i)
	{
		result.push_back(boost::lexical_cast<T>(*i));
	}

	return result;
}

template<typename T, typename U>
inline std::vector<T> split(const std::basic_string<U>& list, const U *sep)
{
	return split<T, U>(list, std::basic_string<U>(sep));
}

inline std::string http_date(std::time_t ts)
{
	if (struct tm *tm = localtime(&ts))
	{
		char buf[256];

		if (strftime(buf, sizeof(buf), "%a, %d-%b-%Y %T %z", tm) != 0)
		{
			return buf;
		}
	}
	return std::string();
}

} // namespace x0
<commit_msg>core: fix http date (changed from local to "GMT")<commit_after>/* <x0/strutils.tcc>
 *
 * This file is part of the x0 web server, released under GPLv3.
 * (c) 2009 Chrisitan Parpart <trapni@gentoo.org>
 */

#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>
#include <list>

namespace x0 {

template<typename T, typename U>
inline std::vector<T> split(const std::basic_string<U>& input, const std::basic_string<U>& sep)
{
	std::vector<T> result;
	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
	tokenizer tk(input, boost::char_separator<U>(sep.c_str()));

	for (tokenizer::iterator i = tk.begin(), e = tk.end(); i != e; ++i)
	{
		result.push_back(boost::lexical_cast<T>(*i));
	}

	return result;
}

template<typename T, typename U>
inline std::vector<T> split(const std::basic_string<U>& list, const U *sep)
{
	return split<T, U>(list, std::basic_string<U>(sep));
}

inline std::string http_date(std::time_t ts)
{
	if (struct tm *tm = gmtime(&ts))
	{
		char buf[256];

		if (strftime(buf, sizeof(buf), "%a, %d %b %Y %T GMT", tm) != 0)
		{
			return buf;
		}
	}
	return std::string();
}

} // namespace x0
<|endoftext|>


<commit_before>#include "Swiss.hpp"
#include "SingleElimination.hpp"
#include <map>
#include <cmath>

struct Record
{
  int win;
  int loss;
  int draw;
  Record() : win(0), loss(0), draw(0) {}
  int score() const { return win * 3 + draw; }
  int rounds() const { return win + loss + draw; };
};

bool operator<(Record const& a, Record const& b)
{
  if (a.rounds() == b.rounds()) {
    return a.score() < b.score();
  }
  return a.rounds() < b.rounds();
}

struct GameResult
{
  std::shared_ptr<lionheart::Player> winner;
  std::shared_ptr<lionheart::Player> loser;
  bool draw;
};

GameResult playGame(std::shared_ptr<lionheart::Player> p1,
                    std::shared_ptr<lionheart::Player> p2,
                    std::shared_ptr<const lionheart::Map> map,
                    std::shared_ptr<const lionheart::Paths> infantryPaths,
                    std::shared_ptr<const lionheart::Paths> mountedPaths,
                    std::shared_ptr<lionheart::Display> display)
{
  lionheart::Game game(p1, p2, map, infantryPaths, mountedPaths);
  game.start();
  std::cout << p1->getBlazon().name << " vs. " << p2->getBlazon().name << ": ";
  if (display) {
    display->show(game.getReport(), p1->getBlazon(), p2->getBlazon());
  }
  for (auto i = 0; i < 200; ++i)
  {
    game.doTurn(nullptr);
    if (display) {
      display->show(game.getReport(), p1->getBlazon(), p2->getBlazon());
    }
    if (!game.canContinue()) break;
  }
  GameResult result;
  result.winner = game.winner();
  result.loser = game.loser();
  if(result.winner)
  {
    std::cout << result.winner->getBlazon().name << " wins!" << std::endl;
    result.draw = false;
  }
  else
  {
    result.draw = true;
    std::cout << "draw" << std:: endl;
  }
  return result;
}

std::vector<std::shared_ptr<lionheart::Player>> lionheart::Swiss::run()
{
  std::map<Record,std::vector<std::shared_ptr<Player>>> oldBrackets;
  auto fortMap = lionheart::makeMap("forts.in");
  auto infantryPaths = std::make_shared<lionheart::Paths>(fortMap, 1);
  auto mountedPaths = std::make_shared<lionheart::Paths>(fortMap, 5);
  oldBrackets[Record()] = players;
  auto rounds = static_cast<int>(log2(players.size()) + 1);
  for(auto round=0;round<rounds;++round)
  {
    std::map<Record, std::vector<std::shared_ptr<Player>>> brackets;
    std::vector<std::pair<Record, std::shared_ptr<Player>>> byes;
    for(auto&& b:oldBrackets)
    {
      auto record = b.first;
      auto win = record;
      ++win.win;
      auto loss = record;
      ++loss.loss;
      auto draw = record;
      ++draw.draw;
      while (b.second.size() > 1)
      {
        auto p1 = b.second.back();
        b.second.pop_back();

        auto p2 = b.second.back();
        b.second.pop_back();

        if (display)
        {
          display->setOutput(std::string("round") + std::to_string(round) + "-" +
                             p1->getBlazon().name + "-" + p2->getBlazon().name);
        }
        auto result = playGame(p1,p2,fortMap,infantryPaths,mountedPaths,display);

        if (!result.draw) {
          brackets[win].push_back(result.winner);
          brackets[loss].push_back(result.loser);
        }
        else
        {
          brackets[draw].push_back(p1);
          brackets[draw].push_back(p2);
        }
      }
      while(!b.second.empty())
      {
        byes.push_back(std::make_pair(record,b.second.back()));
        b.second.pop_back();
      }
    }
    while(byes.size() > 1)
    {

        auto p1 = byes.back();
        byes.pop_back();

        auto p2 = byes.back();
        byes.pop_back();

        if (display)
        {
          display->setOutput(std::string("round") + std::to_string(round) + "-" +
                             p1.second->getBlazon().name + "-" +
                             p2.second->getBlazon().name);
        }
        auto result = playGame(p1.second,p2.second,fortMap,infantryPaths,mountedPaths,display);
        if(result.draw)
        {
          p1.first.draw+=1;
          brackets[p1.first].push_back(p1.second);
          p2.first.draw+=1;
          brackets[p2.first].push_back(p2.second);
        }
        else
        {
          if (result.winner == p1.second) {
            p1.first.win += 1;
            brackets[p1.first].push_back(p1.second);
            p2.first.loss += 1;
            brackets[p2.first].push_back(p2.second);
          }
          else
          {
            p1.first.loss += 1;
            brackets[p1.first].push_back(p1.second);
            p2.first.win += 1;
            brackets[p2.first].push_back(p2.second);
          }
        }
    }
    std::swap(brackets,oldBrackets);
    //Display round results

    std::cout << "Round " << round + 1 <<  " of " << rounds << std::endl;
    for(auto&& b:oldBrackets)
    {
      std::cout << "  Record (" << b.first.win << "-" << b.first.loss << "-" << b.first.draw << "):" << std::endl;
      for(auto&& p:b.second)
      {
        std::cout << "    " << p->getBlazon().name << std::endl;
      }
    }
  }
  std::cout << "Cut to top 8" << std::endl;
  auto best = oldBrackets.rbegin();
  std::vector<std::shared_ptr<lionheart::Player>> top8;
  while(top8.size() < 8)
  {
    top8.insert(top8.begin(),best->second.begin(),best->second.end());
    ++best;
  }
  for(auto&& p:top8)
    {
      std::cout << "     " << p->getBlazon().name << std::endl;
    }
  SingleElimination top8Tournament(top8,display);
  top8Tournament.run();

  return players;
}
<commit_msg>Update Swiss.cpp<commit_after>#include "Swiss.hpp"
#include "SingleElimination.hpp"
#include <map>
#include <cmath>

struct Record
{
  int win;
  int loss;
  int draw;
  Record() : win(0), loss(0), draw(0) {}
  int score() const { return win * 3 + draw; }
  int rounds() const { return win + loss + draw; };
};

bool operator<(Record const& a, Record const& b)
{
  if (a.rounds() == b.rounds()) {
    return a.score() < b.score();
  }
  return a.rounds() < b.rounds();
}

struct GameResult
{
  std::shared_ptr<lionheart::Player> winner;
  std::shared_ptr<lionheart::Player> loser;
  bool draw;
};

GameResult playGame(std::shared_ptr<lionheart::Player> p1,
                    std::shared_ptr<lionheart::Player> p2,
                    std::shared_ptr<const lionheart::Map> map,
                    std::shared_ptr<const lionheart::Paths> infantryPaths,
                    std::shared_ptr<const lionheart::Paths> mountedPaths,
                    std::shared_ptr<lionheart::Display> display)
{
  lionheart::Game game(p1, p2, map, infantryPaths, mountedPaths);
  game.start();
  std::cout << p1->getBlazon().name << " vs. " << p2->getBlazon().name << ": ";
  if (display) {
    display->show(game.getReport(), p1->getBlazon(), p2->getBlazon());
  }
  for (auto i = 0; i < 200; ++i)
  {
    game.doTurn(nullptr);
    if (display) {
      display->show(game.getReport(), p1->getBlazon(), p2->getBlazon());
    }
    if (!game.canContinue()) break;
  }
  GameResult result;
  result.winner = game.winner();
  result.loser = game.loser();
  if(result.winner)
  {
    std::cout << result.winner->getBlazon().name << " wins!" << std::endl;
    result.draw = false;
  }
  else
  {
    result.draw = true;
    std::cout << "draw" << std:: endl;
  }
  return result;
}

std::vector<std::shared_ptr<lionheart::Player>> lionheart::Swiss::run()
{
  std::map<Record,std::vector<std::shared_ptr<Player>>> oldBrackets;
  auto fortMap = lionheart::makeMap("forts.in");
  auto infantryPaths = std::make_shared<lionheart::Paths>(fortMap, 1);
  auto mountedPaths = std::make_shared<lionheart::Paths>(fortMap, 5);
  oldBrackets[Record()] = players;
  auto rounds = static_cast<int>(std::log2(players.size()) + 1);
  for(auto round=0;round<rounds;++round)
  {
    std::map<Record, std::vector<std::shared_ptr<Player>>> brackets;
    std::vector<std::pair<Record, std::shared_ptr<Player>>> byes;
    for(auto&& b:oldBrackets)
    {
      auto record = b.first;
      auto win = record;
      ++win.win;
      auto loss = record;
      ++loss.loss;
      auto draw = record;
      ++draw.draw;
      while (b.second.size() > 1)
      {
        auto p1 = b.second.back();
        b.second.pop_back();

        auto p2 = b.second.back();
        b.second.pop_back();

        if (display)
        {
          display->setOutput(std::string("round") + std::to_string(round) + "-" +
                             p1->getBlazon().name + "-" + p2->getBlazon().name);
        }
        auto result = playGame(p1,p2,fortMap,infantryPaths,mountedPaths,display);

        if (!result.draw) {
          brackets[win].push_back(result.winner);
          brackets[loss].push_back(result.loser);
        }
        else
        {
          brackets[draw].push_back(p1);
          brackets[draw].push_back(p2);
        }
      }
      while(!b.second.empty())
      {
        byes.push_back(std::make_pair(record,b.second.back()));
        b.second.pop_back();
      }
    }
    while(byes.size() > 1)
    {

        auto p1 = byes.back();
        byes.pop_back();

        auto p2 = byes.back();
        byes.pop_back();

        if (display)
        {
          display->setOutput(std::string("round") + std::to_string(round) + "-" +
                             p1.second->getBlazon().name + "-" +
                             p2.second->getBlazon().name);
        }
        auto result = playGame(p1.second,p2.second,fortMap,infantryPaths,mountedPaths,display);
        if(result.draw)
        {
          p1.first.draw+=1;
          brackets[p1.first].push_back(p1.second);
          p2.first.draw+=1;
          brackets[p2.first].push_back(p2.second);
        }
        else
        {
          if (result.winner == p1.second) {
            p1.first.win += 1;
            brackets[p1.first].push_back(p1.second);
            p2.first.loss += 1;
            brackets[p2.first].push_back(p2.second);
          }
          else
          {
            p1.first.loss += 1;
            brackets[p1.first].push_back(p1.second);
            p2.first.win += 1;
            brackets[p2.first].push_back(p2.second);
          }
        }
    }
    std::swap(brackets,oldBrackets);
    //Display round results

    std::cout << "Round " << round + 1 <<  " of " << rounds << std::endl;
    for(auto&& b:oldBrackets)
    {
      std::cout << "  Record (" << b.first.win << "-" << b.first.loss << "-" << b.first.draw << "):" << std::endl;
      for(auto&& p:b.second)
      {
        std::cout << "    " << p->getBlazon().name << std::endl;
      }
    }
  }
  std::cout << "Cut to top 8" << std::endl;
  auto best = oldBrackets.rbegin();
  std::vector<std::shared_ptr<lionheart::Player>> top8;
  while(top8.size() < 8)
  {
    top8.insert(top8.begin(),best->second.begin(),best->second.end());
    ++best;
  }
  for(auto&& p:top8)
    {
      std::cout << "     " << p->getBlazon().name << std::endl;
    }
  SingleElimination top8Tournament(top8,display);
  top8Tournament.run();

  return players;
}
<|endoftext|>


<commit_before>#include "clusterlib.h"
#include "testparams.h"
#include "MPITestFixture.h"

extern TestParams globalTestParams;

using namespace clusterlib;
using namespace std;
using namespace boost;

class ClusterlibProcessThreadService : public MPITestFixture
{
    CPPUNIT_TEST_SUITE(ClusterlibProcessThreadService);
    CPPUNIT_TEST(testProcessThreadService1);
    CPPUNIT_TEST(testProcessThreadService2);
    CPPUNIT_TEST(testProcessThreadService3);
    CPPUNIT_TEST_SUITE_END();

  public:

    ClusterlibProcessThreadService()
        : MPITestFixture(globalTestParams) {}

    /* Runs prior to each test */
    virtual void setUp()
    {
    }

    /* Runs after each test */
    virtual void tearDown()
    {
        cleanAndBarrierMPITest(NULL, false);
    }

    /**
     * Simple test to execute 'ls' when not capturing output.  Can't
     * do current path, since this can cause a deadlock since files
     * are being created in this path.
     */
    void testProcessThreadService1()
    {
        initializeAndBarrierMPITest(-1,
                                    true,
                                    NULL,
                                    false,
                                    "testProcessThreadService1");
        vector<string> addEnv;
        string path;
        string cmd = "/bin/ls -al / >& /dev/null";
        pid_t processId = -1;
        int32_t returnCode = -1;

        bool success = ProcessThreadService::forkExecWait(addEnv,
                                                          path,
                                                          cmd,
                                                          processId,
                                                          returnCode);
        MPI_CPPUNIT_ASSERT(success == true);
        MPI_CPPUNIT_ASSERT(processId != -1);
        MPI_CPPUNIT_ASSERT(returnCode == 0);
    }

    /**
     * Simple test to execute 'ls' when capturing output.  Can't
     * do current path, since this can cause a deadlock since files
     * are being created in this path.
     */
    void testProcessThreadService2()
    {
        initializeAndBarrierMPITest(-1,
                                    true,
                                    NULL,
                                    false,
                                    "testProcessThreadService2");
        vector<string> addEnv;
        string path;
        string cmd = "/bin/ls -al /";
        pid_t processId = -1;
        int32_t returnCode = -1;
        string stdoutOutput, stderrOutput;

        bool success = ProcessThreadService::forkExecWait(addEnv,
                                                          path,
                                                          cmd,
                                                          processId,
                                                          returnCode,
                                                          stdoutOutput,
                                                          stderrOutput);
        cerr << "stdout size=" << stdoutOutput.size() << ", data='"
             <<  stdoutOutput << "'" << endl;
        cerr << "stderr size=" << stderrOutput.size() << ", data='"
             <<  stderrOutput << "'" << endl;

        MPI_CPPUNIT_ASSERT(success == true);
        MPI_CPPUNIT_ASSERT(processId != -1);
        MPI_CPPUNIT_ASSERT(returnCode == 0);
        MPI_CPPUNIT_ASSERT(!stdoutOutput.empty());
        MPI_CPPUNIT_ASSERT(stderrOutput.empty());
    }

    /**
     * Exhaust the 4k buffer size.
     */
    void testProcessThreadService3()
    {
        initializeAndBarrierMPITest(-1,
                                    true,
                                    NULL,
                                    false,
                                    "testProcessThreadService3");
        vector<string> addEnv;
        string path;
        string cmd = "/bin/dd if=/dev/zero bs=1k count=64";
        pid_t processId = -1;
        int32_t returnCode = -1;
        string stdoutOutput, stderrOutput;

        bool success = ProcessThreadService::forkExecWait(addEnv,
                                                          path,
                                                          cmd,
                                                          processId,
                                                          returnCode,
                                                          stdoutOutput,
                                                          stderrOutput);
        cerr << "stdout size=" << stdoutOutput.size() << ", data='"
             <<  stdoutOutput << "'" << endl;
        cerr << "stderr size=" << stderrOutput.size() << ", data='"
             <<  stderrOutput << "'" << endl;

        MPI_CPPUNIT_ASSERT(success == true);
        MPI_CPPUNIT_ASSERT(processId != -1);
        MPI_CPPUNIT_ASSERT(returnCode == 0);
        MPI_CPPUNIT_ASSERT(stdoutOutput.size() == 64*1024);
        MPI_CPPUNIT_ASSERT(!stderrOutput.empty());
    }
};

/* Registers the fixture into the 'registry' */
CPPUNIT_TEST_SUITE_REGISTRATION(ClusterlibProcessThreadService);

<commit_msg>>& doesn't play nice on /bin/dash, hence >.<commit_after>#include "clusterlib.h"
#include "testparams.h"
#include "MPITestFixture.h"

extern TestParams globalTestParams;

using namespace clusterlib;
using namespace std;
using namespace boost;

class ClusterlibProcessThreadService : public MPITestFixture
{
    CPPUNIT_TEST_SUITE(ClusterlibProcessThreadService);
    CPPUNIT_TEST(testProcessThreadService1);
    CPPUNIT_TEST(testProcessThreadService2);
    CPPUNIT_TEST(testProcessThreadService3);
    CPPUNIT_TEST_SUITE_END();

  public:

    ClusterlibProcessThreadService()
        : MPITestFixture(globalTestParams) {}

    /* Runs prior to each test */
    virtual void setUp()
    {
    }

    /* Runs after each test */
    virtual void tearDown()
    {
        cleanAndBarrierMPITest(NULL, false);
    }

    /**
     * Simple test to execute 'ls' when not capturing output.  Can't
     * do current path, since this can cause a deadlock since files
     * are being created in this path.
     */
    void testProcessThreadService1()
    {
        initializeAndBarrierMPITest(-1,
                                    true,
                                    NULL,
                                    false,
                                    "testProcessThreadService1");
        vector<string> addEnv;
        string path;
        string cmd = "/bin/ls -al / > /dev/null";
        pid_t processId = -1;
        int32_t returnCode = -1;

        bool success = ProcessThreadService::forkExecWait(addEnv,
                                                          path,
                                                          cmd,
                                                          processId,
                                                          returnCode);
        MPI_CPPUNIT_ASSERT(success == true);
        MPI_CPPUNIT_ASSERT(processId != -1);
        MPI_CPPUNIT_ASSERT(returnCode == 0);
    }

    /**
     * Simple test to execute 'ls' when capturing output.  Can't
     * do current path, since this can cause a deadlock since files
     * are being created in this path.
     */
    void testProcessThreadService2()
    {
        initializeAndBarrierMPITest(-1,
                                    true,
                                    NULL,
                                    false,
                                    "testProcessThreadService2");
        vector<string> addEnv;
        string path;
        string cmd = "/bin/ls -al /";
        pid_t processId = -1;
        int32_t returnCode = -1;
        string stdoutOutput, stderrOutput;

        bool success = ProcessThreadService::forkExecWait(addEnv,
                                                          path,
                                                          cmd,
                                                          processId,
                                                          returnCode,
                                                          stdoutOutput,
                                                          stderrOutput);
        cerr << "stdout size=" << stdoutOutput.size() << ", data='"
             <<  stdoutOutput << "'" << endl;
        cerr << "stderr size=" << stderrOutput.size() << ", data='"
             <<  stderrOutput << "'" << endl;

        MPI_CPPUNIT_ASSERT(success == true);
        MPI_CPPUNIT_ASSERT(processId != -1);
        MPI_CPPUNIT_ASSERT(returnCode == 0);
        MPI_CPPUNIT_ASSERT(!stdoutOutput.empty());
        MPI_CPPUNIT_ASSERT(stderrOutput.empty());
    }

    /**
     * Exhaust the 4k buffer size.
     */
    void testProcessThreadService3()
    {
        initializeAndBarrierMPITest(-1,
                                    true,
                                    NULL,
                                    false,
                                    "testProcessThreadService3");
        vector<string> addEnv;
        string path;
        string cmd = "/bin/dd if=/dev/zero bs=1k count=64";
        pid_t processId = -1;
        int32_t returnCode = -1;
        string stdoutOutput, stderrOutput;

        bool success = ProcessThreadService::forkExecWait(addEnv,
                                                          path,
                                                          cmd,
                                                          processId,
                                                          returnCode,
                                                          stdoutOutput,
                                                          stderrOutput);
        cerr << "stdout size=" << stdoutOutput.size() << ", data='"
             <<  stdoutOutput << "'" << endl;
        cerr << "stderr size=" << stderrOutput.size() << ", data='"
             <<  stderrOutput << "'" << endl;

        MPI_CPPUNIT_ASSERT(success == true);
        MPI_CPPUNIT_ASSERT(processId != -1);
        MPI_CPPUNIT_ASSERT(returnCode == 0);
        MPI_CPPUNIT_ASSERT(stdoutOutput.size() == 64*1024);
        MPI_CPPUNIT_ASSERT(!stderrOutput.empty());
    }
};

/* Registers the fixture into the 'registry' */
CPPUNIT_TEST_SUITE_REGISTRATION(ClusterlibProcessThreadService);

<|endoftext|>


<commit_before>/*
 * This file is part of Clockwork.
 *
 * Copyright (c) 2014-2016 Jeremy Othieno.
 *
 * The MIT License (MIT)
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef CLOCKWORK_WEAK_VARIANT_HH
#define CLOCKWORK_WEAK_VARIANT_HH

#include <type_traits>

namespace clockwork {
/**
 * Checks whether Type is a valid reference type.
 * This generic implementation always returns true.
 */
template<class Type>
struct GenericWeakVariantValidator : std::integral_constant<bool, true> {};
/**
 * A WeakVariant is a data type, based on a variant, that contains a non-owning
 * reference to any kind of object.
 */
template<template<typename> class Validator = GenericWeakVariantValidator>
class WeakVariant {
public:
	/**
	 * Returns the referenced value.
	 */
	template<class Type> Type& get();
	/**
	 * Returns the referenced value.
	 */
	template<class Type> const Type& get() const;
	/**
	 * Instantiates a WeakVariant object that references the specified value.
	 * @param value the referenced value.
	 */
	template<class Type> static WeakVariant<Validator> create(Type& value);
private:
	/**
	 * Instantiates a WeakVariant object to the specified value.
	 * @param the referenced value.
	 */
	inline explicit WeakVariant(void* const value);
	/**
	 * Instantiates a WeakVariant object to the specified immutable value.
	 * @param the referenced value.
	 */
	inline explicit WeakVariant(const void* const value);
	/**
	 * The referenced value.
	 */
	union Reference {
		/**
		 * Instantiates a Reference object that references a specified object.
		 */
		inline explicit Reference(void* const value);
		/**
		 * Instantiates a Reference object that references a specified immutable object.
		 */
		inline explicit Reference(const void* const value);
		/**
		 * A pointer to a mutable value.
		 */
		void* value;
		/**
		 * A pointer to an immutable value.
		 */
		const void* const_value;
	} reference_;
};

template<template<typename> class Validator>
WeakVariant<Validator>::WeakVariant(void* const value) :
reference_(value) {}

template<template<typename> class Validator>
WeakVariant<Validator>::WeakVariant(const void* const value) :
reference_(value) {}

template<template<typename> class Validator>
WeakVariant<Validator>::Reference::Reference(void* const v) :
value(v) {}

template<template<typename> class Validator>
WeakVariant<Validator>::Reference::Reference(const void* const v) :
const_value(v) {}

template<template<typename> class Validator>
template<class T> T&
WeakVariant<Validator>::get() {
	static_assert(Validator<T>::value);
	return *static_cast<T*>(reference_.value);
}

template<template<typename> class Validator>
template<class T> const T&
WeakVariant<Validator>::get() const {
	static_assert(Validator<T>::value);
	return *static_cast<const T*>(reference_.const_value);
}

template<template<typename> class Validator>
template<class T> WeakVariant<Validator>
WeakVariant<Validator>::create(T& value) {
	static_assert(Validator<T>::value);
	return WeakVariant<Validator>(&value);
}
} // namespace clockwork

#endif // CLOCKWORK_WEAK_VARIANT_HH
<commit_msg>Change the WeakVariant's 'get' member function to 'as'<commit_after>/*
 * This file is part of Clockwork.
 *
 * Copyright (c) 2014-2016 Jeremy Othieno.
 *
 * The MIT License (MIT)
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef CLOCKWORK_WEAK_VARIANT_HH
#define CLOCKWORK_WEAK_VARIANT_HH

#include <type_traits>

namespace clockwork {
/**
 * Checks whether Type is a valid reference type.
 * This generic implementation always returns true.
 */
template<class Type>
struct GenericWeakVariantValidator : std::integral_constant<bool, true> {};
/**
 * A WeakVariant is a data type, based on a variant, that contains a non-owning
 * reference to any kind of object.
 */
template<template<typename> class Validator = GenericWeakVariantValidator>
class WeakVariant {
public:
	/**
	 * Returns the referenced value as a specified type.
	 */
	template<class Type> Type& as();
	/**
	 * Returns the referenced value as a specified type.
	 */
	template<class Type> const Type& as() const;
	/**
	 * Instantiates a WeakVariant object that references the specified value.
	 * @param value the referenced value.
	 */
	template<class Type> static WeakVariant<Validator> create(Type& value);
private:
	/**
	 * Instantiates a WeakVariant object to the specified value.
	 * @param the referenced value.
	 */
	inline explicit WeakVariant(void* const value);
	/**
	 * Instantiates a WeakVariant object to the specified immutable value.
	 * @param the referenced value.
	 */
	inline explicit WeakVariant(const void* const value);
	/**
	 * The referenced value.
	 */
	union Reference {
		/**
		 * Instantiates a Reference object that references a specified object.
		 */
		inline explicit Reference(void* const value);
		/**
		 * Instantiates a Reference object that references a specified immutable object.
		 */
		inline explicit Reference(const void* const value);
		/**
		 * A pointer to a mutable value.
		 */
		void* value;
		/**
		 * A pointer to an immutable value.
		 */
		const void* const_value;
	} reference_;
};

template<template<typename> class Validator>
WeakVariant<Validator>::WeakVariant(void* const value) :
reference_(value) {}

template<template<typename> class Validator>
WeakVariant<Validator>::WeakVariant(const void* const value) :
reference_(value) {}

template<template<typename> class Validator>
WeakVariant<Validator>::Reference::Reference(void* const v) :
value(v) {}

template<template<typename> class Validator>
WeakVariant<Validator>::Reference::Reference(const void* const v) :
const_value(v) {}

template<template<typename> class Validator>
template<class T> T&
WeakVariant<Validator>::as() {
	static_assert(Validator<T>::value);
	return *static_cast<T*>(reference_.value);
}

template<template<typename> class Validator>
template<class T> const T&
WeakVariant<Validator>::as() const {
	static_assert(Validator<T>::value);
	return *static_cast<const T*>(reference_.const_value);
}

template<template<typename> class Validator>
template<class T> WeakVariant<Validator>
WeakVariant<Validator>::create(T& value) {
	static_assert(Validator<T>::value);
	return WeakVariant<Validator>(&value);
}
} // namespace clockwork

#endif // CLOCKWORK_WEAK_VARIANT_HH
<|endoftext|>