Completely rework and rewrite command line argument parsing.
Also fix some things: To actually set a variable to the value of the argument, & clipp::value must be used, not .set().
This commit is contained in:
parent
c360cbe5fe
commit
0e7993c194
92
include/optional.h
Normal file
92
include/optional.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// Copyright 2018 Christoph Heiss <me@christoph-heiss.me>
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
//
|
||||
// See accompanying file LICENSE in the project root directory
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
/*! \brief Very simple re-implementation of std::{experimental::}optional.
|
||||
* \param T Type of the value the optional should hold.
|
||||
*
|
||||
* Until std::{experimental::}optional is standardized, this can be used as a
|
||||
* very simple and dumb replacement.
|
||||
*/
|
||||
template <typename T>
|
||||
class Optional {
|
||||
public:
|
||||
/*! \brief Type of the value the optional holds. */
|
||||
typedef T value_type;
|
||||
|
||||
/*! \brief Constructs a new, empty optional.
|
||||
*
|
||||
* The default value will be default-constructed.
|
||||
*/
|
||||
Optional() : has_value_{}, default_value_{} { }
|
||||
|
||||
/*! \brief Constructos a new optional with an default value.
|
||||
* \param default_value The default value the optional will hold.
|
||||
*/
|
||||
Optional(T default_value) : has_value_{}, default_value_{default_value} { }
|
||||
|
||||
/*! \brief Indicates if the optional holds a value.
|
||||
* \return If the optional holds a value.
|
||||
*/
|
||||
bool has_value() const
|
||||
{
|
||||
return has_value_;
|
||||
}
|
||||
|
||||
/*! \brief Gets the value.
|
||||
* \return The value the optional holds.
|
||||
*
|
||||
* If the optional has no value, a default-constructed object is returned.
|
||||
*/
|
||||
const T& value() const&
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
/*! \brief Returns the value if the optional has one, otherwise the default value.
|
||||
* \return The value of the optional if it has one, otherwise the default value.
|
||||
*/
|
||||
const T& value_or_default() const&
|
||||
{
|
||||
return has_value() ? value() : default_value();
|
||||
}
|
||||
|
||||
/*! \brief Sets the value of the optional.
|
||||
* \param val The new value of the optional.
|
||||
* \return The new value of the optional.
|
||||
*/
|
||||
const T& set_value(T&& val)
|
||||
{
|
||||
has_value_ = true;
|
||||
value_ = std::move(val);
|
||||
return value();
|
||||
}
|
||||
|
||||
/*! \brief Returns the default value of the optional.
|
||||
* \return The default value of the optional.
|
||||
*/
|
||||
const T& default_value() const&
|
||||
{
|
||||
return default_value_;
|
||||
}
|
||||
|
||||
private:
|
||||
/*! \brief Indicates if this optional has a value set. */
|
||||
bool has_value_;
|
||||
|
||||
/*! \brief The value that may or may not be present.
|
||||
*
|
||||
* Its present is indicated by #has_value_
|
||||
*/
|
||||
T value_;
|
||||
|
||||
/*! \brief The default value of this optional. */
|
||||
const T default_value_;
|
||||
};
|
|
@ -24,15 +24,14 @@
|
|||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct Options {
|
||||
Options() : host{"localhost:6544"} { }
|
||||
|
||||
std::string host;
|
||||
};
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
std::ostream& operator<<(std::ostream& ostream, const rslp::Command& command)
|
||||
{
|
||||
using Type = rslp::Command_Data::DataCase;
|
||||
|
@ -80,8 +79,8 @@ Options parse_commandline(int argc, char** argv)
|
|||
bool show_help{};
|
||||
|
||||
auto cli = (
|
||||
clipp::option("-h", "--host").set(options.host)
|
||||
.doc("Set the host (and port, optional) to connect to [default: localhost:6544]"),
|
||||
clipp::option("-h", "--host") & clipp::value("host", options.host)
|
||||
.doc("Sets the host and port to connect to [default: localhost:6544]"),
|
||||
clipp::option("--help").set(show_help).doc("Show help and exit.")
|
||||
);
|
||||
|
||||
|
@ -95,7 +94,7 @@ Options parse_commandline(int argc, char** argv)
|
|||
|
||||
class GrpcResplyClient {
|
||||
public:
|
||||
GrpcResplyClient(std::shared_ptr<grpc::Channel> channel) :
|
||||
explicit GrpcResplyClient(std::shared_ptr<grpc::Channel> channel) :
|
||||
stub_{rslp::ProtoAdapter::NewStub(channel)}
|
||||
{ }
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <arpa/inet.h>
|
||||
#include <functional>
|
||||
|
||||
|
@ -20,26 +21,22 @@
|
|||
|
||||
using namespace google;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Options {
|
||||
Options() : host{"localhost"}, port{"6543"} { }
|
||||
Options() : host{"localhost:6543"} { }
|
||||
|
||||
std::string host;
|
||||
std::string port;
|
||||
};
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
Options parse_commandline(int argc, char** argv)
|
||||
{
|
||||
Options options;
|
||||
bool show_help{};
|
||||
|
||||
auto cli = (
|
||||
clipp::option("-h", "--host").set(options.host)
|
||||
.doc("Set the host to connect to [default: localhost]"),
|
||||
clipp::option("-p", "--port").set(options.port)
|
||||
.doc("Set the port to connect to [default: 6543]"),
|
||||
clipp::option("-h", "--host") & clipp::value("host", options.host)
|
||||
.doc("Sets the host and port to connect to [default: localhost:6543]"),
|
||||
clipp::option("--help").set(show_help).doc("Show help and exit.")
|
||||
);
|
||||
|
||||
|
@ -94,15 +91,21 @@ std::ostream& operator<<(std::ostream& ostream, const rslp::Command& command)
|
|||
|
||||
class ProtobufResplyClient {
|
||||
public:
|
||||
ProtobufResplyClient(const std::string& host, const std::string& port) :
|
||||
host_{host}, port_{port}, socket_{io_context_}
|
||||
{ }
|
||||
explicit ProtobufResplyClient(const std::string& host) :
|
||||
socket_{io_context_}
|
||||
{
|
||||
std::stringstream stream{host};
|
||||
|
||||
std::getline(stream, host_, ':');
|
||||
std::getline(stream, port_);
|
||||
}
|
||||
|
||||
bool connect()
|
||||
{
|
||||
asio::error_code error_code;
|
||||
asio::ip::tcp::resolver resolver{io_context_};
|
||||
|
||||
std::cout << "host=" << host_ << "; port=" << port_ << std::endl;
|
||||
auto results = resolver.resolve(host_, port_, error_code);
|
||||
if (error_code) {
|
||||
std::cerr << "Error while connecting: " << error_code.message() << std::endl;
|
||||
|
@ -182,8 +185,8 @@ private:
|
|||
}
|
||||
|
||||
private:
|
||||
const std::string& host_;
|
||||
const std::string& port_;
|
||||
std::string host_;
|
||||
std::string port_;
|
||||
|
||||
asio::io_context io_context_;
|
||||
asio::ip::tcp::socket socket_;
|
||||
|
@ -197,13 +200,13 @@ int main(int argc, char* argv[])
|
|||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
auto options{parse_commandline(argc, argv)};
|
||||
|
||||
ProtobufResplyClient client{options.host, options.port};
|
||||
ProtobufResplyClient client{options.host};
|
||||
if (!client.connect()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (std::cin) {
|
||||
std::cout << options.host << ':' << options.port << "> ";
|
||||
std::cout << options.host << "> ";
|
||||
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
|
|
124
src/proxy.cc
124
src/proxy.cc
|
@ -18,6 +18,7 @@
|
|||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "asio.hpp"
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include "spdlog/spdlog.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "resply.h"
|
||||
#include "optional.h"
|
||||
#include "rslp.pb.h"
|
||||
#include "grpc++/grpc++.h"
|
||||
#include "grpc/support/log.h"
|
||||
|
@ -38,46 +40,60 @@ using json = nlohmann::json;
|
|||
using namespace google;
|
||||
|
||||
|
||||
struct Options {
|
||||
Options() :
|
||||
config_path{".proxy-conf.json"}, daemonize{}, log_path{"proxy.log"},
|
||||
protobuf_port{6543}, grpc_port{"6544"}, remote_host{"localhost:6379"}, verbose{} { }
|
||||
|
||||
std::string config_path;
|
||||
bool daemonize;
|
||||
std::string log_path;
|
||||
unsigned short protobuf_port;
|
||||
std::string grpc_port;
|
||||
std::string remote_host;
|
||||
bool verbose;
|
||||
};
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string GLOBAL_LOGGER_NAME{"Proxy"};
|
||||
|
||||
void resply_result_to_rslp_data(rslp::Command_Data* data, const resply::Result& result);
|
||||
|
||||
struct Options {
|
||||
bool daemonize;
|
||||
std::string log_path;
|
||||
unsigned short protobuf_port;
|
||||
unsigned short grpc_port;
|
||||
std::string redis_host;
|
||||
bool verbose;
|
||||
};
|
||||
|
||||
Options parse_commandline(int argc, char** argv)
|
||||
{
|
||||
Options options;
|
||||
bool show_help{}, show_version{};
|
||||
|
||||
Optional<bool> daemonize{}, verbose{};
|
||||
Optional<unsigned short> protobuf_port{6543}, grpc_port{6544};
|
||||
Optional<std::string> config_path{".proxy-conf.json"};
|
||||
Optional<std::string> log_path{"proxy.log"};
|
||||
Optional<std::string> redis_host{"localhost:6379"};
|
||||
|
||||
auto cli = (
|
||||
clipp::option("-c", "--conf-path") & clipp::value("path", options.config_path)
|
||||
clipp::option("-c", "--conf-path") & clipp::value("path")
|
||||
.call([&](auto p) { config_path.set_value(p); })
|
||||
.doc("Path to the configuration file [default: $CWD/.proxy-conf.json]"),
|
||||
clipp::option("-d", "--daemonize").set(options.daemonize).doc("Fork to background."),
|
||||
clipp::option("-l", "--log-path") & clipp::value("path", options.log_path)
|
||||
|
||||
clipp::option("-d", "--daemonize")
|
||||
.call([&](auto d) { daemonize.set_value(d); })
|
||||
.doc("Fork to background."),
|
||||
|
||||
clipp::option("-l", "--log-path") & clipp::value("path")
|
||||
.call([&](auto p) { log_path.set_value(p); })
|
||||
.doc("Path to the log file [default: $CWD/proxy.log] (Only applies when daemonized.)"),
|
||||
clipp::option("--protobuf-port") & clipp::value("port", options.protobuf_port)
|
||||
|
||||
clipp::option("--protobuf-port") & clipp::integer("port")
|
||||
.call([&](auto p) { protobuf_port.set_value(static_cast<unsigned short>(std::stoi(p))); })
|
||||
.doc("Port the protobuf server should listen on [default: 6543]"),
|
||||
clipp::option("--grpc-port") & clipp::value("port", options.grpc_port)
|
||||
|
||||
clipp::option("--grpc-port") & clipp::integer("port")
|
||||
.call([&](auto p) { grpc_port.set_value(static_cast<unsigned short>(std::stoi(p))); })
|
||||
.doc("Port the gRPC server should listen on [default: 6544]"),
|
||||
clipp::option("-r", "--remote-host") & clipp::value("host", options.remote_host)
|
||||
.doc("Host (redis-server) to connect to [default: localhost:6379]"),
|
||||
clipp::option("-v", "--verbose").set(options.verbose).doc("Enable verbose logging."),
|
||||
|
||||
clipp::option("-r", "--redis-host") & clipp::value("host")
|
||||
.call([&](auto h) { redis_host.set_value(h); })
|
||||
.doc("Host (redis server) to connect to [default: localhost:6379]"),
|
||||
|
||||
clipp::option("-v", "--verbose")
|
||||
.call([&](auto v) { verbose.set_value(v); })
|
||||
.doc("Enable verbose logging."),
|
||||
|
||||
clipp::option("--help").set(show_help).doc("Show help and exit."),
|
||||
clipp::option("--version").set(show_version).doc("Show version and exit.")
|
||||
);
|
||||
|
@ -96,6 +112,34 @@ Options parse_commandline(int argc, char** argv)
|
|||
std::exit(0);
|
||||
}
|
||||
|
||||
json config;
|
||||
if (config_path.value_or_default().length()) {
|
||||
std::ifstream file{config_path.value_or_default()};
|
||||
if (file) {
|
||||
file >> config;
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
auto choose_opt = [&](const std::string& name, const auto& opt)
|
||||
-> typename std::remove_reference<decltype(opt)>::type::value_type {
|
||||
if (opt.has_value()) {
|
||||
return opt.value();
|
||||
} else if (config.count(name)) {
|
||||
return config[name];
|
||||
}
|
||||
|
||||
return opt.default_value();
|
||||
};
|
||||
|
||||
Options options;
|
||||
options.daemonize = choose_opt("daemonize", daemonize);
|
||||
options.log_path = choose_opt("log-path", log_path);
|
||||
options.protobuf_port = choose_opt("protobuf-port", protobuf_port);
|
||||
options.grpc_port = choose_opt("grpc-port", grpc_port);
|
||||
options.redis_host = choose_opt("redis-host", redis_host);
|
||||
options.verbose = choose_opt("verbose", verbose);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
|
@ -363,7 +407,7 @@ public:
|
|||
|
||||
for (;;) {
|
||||
auto server{std::make_shared<ProtobufAdapter>(
|
||||
options.remote_host, io_context
|
||||
options.redis_host, io_context
|
||||
)};
|
||||
acceptor.accept(server->socket());
|
||||
|
||||
|
@ -378,7 +422,7 @@ private:
|
|||
|
||||
class GrpcAdapter final : public rslp::ProtoAdapter::Service {
|
||||
public:
|
||||
GrpcAdapter(const std::string& redis_host) :
|
||||
explicit GrpcAdapter(const std::string& redis_host) :
|
||||
client_{redis_host}, logger_{create_logger(LOGGER_NAME)}
|
||||
{ }
|
||||
|
||||
|
@ -473,9 +517,12 @@ public:
|
|||
setup_grpc_logging();
|
||||
|
||||
grpc::ServerBuilder builder;
|
||||
builder.AddListeningPort("0.0.0.0:" + options.grpc_port, grpc::InsecureServerCredentials());
|
||||
builder.AddListeningPort(
|
||||
"0.0.0.0:" + std::to_string(options.grpc_port),
|
||||
grpc::InsecureServerCredentials()
|
||||
);
|
||||
|
||||
GrpcAdapter adapter{options.remote_host};
|
||||
GrpcAdapter adapter{options.redis_host};
|
||||
adapter.initialize();
|
||||
builder.RegisterService(&adapter);
|
||||
|
||||
|
@ -515,13 +562,11 @@ private:
|
|||
|
||||
class Proxy {
|
||||
public:
|
||||
Proxy(const Options& options) :
|
||||
explicit Proxy(const Options& options) :
|
||||
options_{options}, logger_{spdlog::stdout_color_mt(LOGGER_NAME)} { }
|
||||
|
||||
void run()
|
||||
{
|
||||
read_config_file();
|
||||
|
||||
if (options_.daemonize) {
|
||||
daemonize();
|
||||
}
|
||||
|
@ -541,22 +586,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
void read_config_file()
|
||||
{
|
||||
if (!options_.config_path.length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream file{options_.config_path};
|
||||
|
||||
if (!file) {
|
||||
logger_->warn("Configuration file not found! Using compiled-in defaults ..");
|
||||
} else {
|
||||
file >> config_;
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void daemonize()
|
||||
{
|
||||
logger_->info("Daemonizing server, logfile: {}", options_.log_path);
|
||||
|
@ -576,7 +605,6 @@ private:
|
|||
|
||||
const Options& options_;
|
||||
std::shared_ptr<spdlog::logger> logger_;
|
||||
json config_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -16,22 +16,21 @@
|
|||
#include "resply.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct Options {
|
||||
Options() : host{"localhost:6379"} { }
|
||||
|
||||
std::string host;
|
||||
};
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
Options parse_commandline(int argc, char** argv)
|
||||
{
|
||||
Options options;
|
||||
bool show_help{}, show_version{};
|
||||
|
||||
auto cli = (
|
||||
clipp::option("-h", "--host").set(options.host)
|
||||
clipp::option("-h", "--host") & clipp::value("host", options.host)
|
||||
.doc("Set the host (and port, optional) to connect to [default: localhost:6379]"),
|
||||
clipp::option("--help").set(show_help).doc("Show help and exit."),
|
||||
clipp::option("--version").set(show_version).doc("Show version and exit.")
|
||||
|
|
Reference in a new issue