Skip to content

Commit

Permalink
[EN-7490] Implement Tools
Browse files Browse the repository at this point in the history
  • Loading branch information
ttldtor committed Sep 18, 2023
1 parent 87aa39c commit 3980dc4
Show file tree
Hide file tree
Showing 12 changed files with 389 additions and 145 deletions.
9 changes: 9 additions & 0 deletions tools/Tools/src/Args/Args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@
namespace dxfcpp::tools {

const std::string AddressArg::NAME{"address"};
const std::size_t AddressArg::POSITION{0};
const std::string AddressArg::HELP_TEXT{R"(
The address(es) to connect to retrieve data (see "Help address").
For Token-Based Authorization, use the following format: "<address>:<port>[login=entitle:<token>]".
)"};

const std::string TypesArg::NAME{"types"};
const std::size_t TypesArg::POSITION{1};
const std::string TypesArg::HELP_TEXT{R"(
Comma-separated list of dxfeed event types (e.g. Quote, TimeAndSale).
Use "all" for all available event types.
)"};

const std::string SymbolsArg::NAME{"symbols"};
const std::size_t SymbolsArg::POSITION{2};
const std::string SymbolsArg::HELP_TEXT{R"(
Comma-separated list of symbol names to get events for (e.g. "IBM, AAPL, MSFT").
Use "all" for wildcard subscription.
Expand Down Expand Up @@ -94,4 +97,10 @@ const std::string HelpArg::HELP_TEXT{R"(
Display this help screen.
)"};

const std::string ArticleArgRequired::NAME{"article"};
const std::size_t ArticleArgRequired::POSITION{0};
const std::string ArticleArgRequired::HELP_TEXT{R"(
Help article to show.
)"};

}
141 changes: 98 additions & 43 deletions tools/Tools/src/Args/Args.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,27 @@ inline auto splitAndTrim = [](const std::string &s, char sep = ',') noexcept {
};

template <typename R> struct ParseResult {
static const std::size_t LAST_INDEX{static_cast<std::size_t>(-1)};
R result{};
std::string errorString{};
bool isError{};
bool isHelp{};
std::size_t nextIndex{LAST_INDEX};

static ParseResult ok(R &&r) noexcept {
return {r, "", false, false};
static ParseResult ok(R &&r, std::size_t nextIndex = LAST_INDEX) noexcept {
return {r, "", false, false, nextIndex};
}

static ParseResult ok(const R &r) noexcept {
return {r, "", false, false};
static ParseResult ok(const R &r, std::size_t nextIndex = LAST_INDEX) noexcept {
return {r, "", false, false, nextIndex};
}

static ParseResult error(const std::string &errorString) {
return {{}, errorString, true, false};
static ParseResult error(const std::string &errorString) noexcept {
return {{}, errorString, true, false, LAST_INDEX};
}

static ParseResult help() {
return {{}, {}, false, true};
static ParseResult help() noexcept {
return {{}, {}, false, true, LAST_INDEX};
}
};

Expand Down Expand Up @@ -100,23 +102,46 @@ struct Arg {
}
};

struct PositionalArg : Arg {};
struct PositionalArg : Arg {
template <typename A>
static ParseResult<std::optional<std::string>> parse(const std::vector<std::string> &args) noexcept {
return ParseResult<std::optional<std::string>>::ok(args.size() > A::POSITION ? std::optional{args[A::POSITION]}
: std::nullopt);
}
};

struct RequiredMixin {
template <typename A>
static ParseResult<std::string> defaultParseImpl(const std::vector<std::string> &args) noexcept {
return ParseResult<std::string>::ok(args[A::POSITION]);
}

template <typename A, auto parseImpl = defaultParseImpl<A>>
static ParseResult<std::string> parse(const std::vector<std::string> &args) noexcept {
if (args.size() > A::POSITION) {
return parseImpl(args);
}

return ParseResult<std::string>::error("\"" + A::NAME + "\" argument parsing error. Insufficient parameters.");
}
};

struct NamedArg : Arg {
template <typename A> static bool canParse(const std::vector<std::string> &args, std::size_t index) {
template <typename A> static bool canParse(const std::vector<std::string> &args, std::size_t index) noexcept {
return args.size() > index + 1 && ((!A::SHORT_NAME.empty() && args[index] == "-" + A::SHORT_NAME) ||
(!A::LONG_NAME.empty() && args[index] == "--" + A::LONG_NAME));
}

template <typename A>
static ParseResult<std::optional<std::string>> parse(const std::vector<std::string> &args, std::size_t index) {
static ParseResult<std::optional<std::string>> parse(const std::vector<std::string> &args,
std::size_t index) noexcept {
if (args.size() <= index + 1) {
return ParseResult<std::optional<std::string>>::ok(std::nullopt);
}

if ((!A::SHORT_NAME.empty() && args[index] == "-" + A::SHORT_NAME) ||
(!A::LONG_NAME.empty() && args[index] == "--" + A::LONG_NAME)) {
return ParseResult<std::optional<std::string>>::ok(args[index + 1]);
return ParseResult<std::optional<std::string>>::ok(args[index + 1], index + 1);
}

return ParseResult<std::optional<std::string>>::ok(std::nullopt);
Expand Down Expand Up @@ -148,7 +173,7 @@ struct NamedUnsignedIntArg : NamedArg {
if ((!A::SHORT_NAME.empty() && args[index] == "-" + A::SHORT_NAME) ||
(!A::LONG_NAME.empty() && args[index] == "--" + A::LONG_NAME)) {
try {
return ParseResult<std::optional<std::size_t>>::ok(std::stoull(args[index + 1]));
return ParseResult<std::optional<std::size_t>>::ok(std::stoull(args[index + 1]), index + 1);
} catch (...) {
return ParseResult<std::optional<std::size_t>>::ok(std::nullopt);
}
Expand All @@ -159,16 +184,33 @@ struct NamedUnsignedIntArg : NamedArg {
};

struct FlagArg : NamedArg {
template <typename A> static ParseResult<bool> parse(const std::vector<std::string> &args, std::size_t index) {
return ParseResult<bool>::ok(args.size() > index &&
((!A::SHORT_NAME.empty() && args[index] == "-" + A::SHORT_NAME) ||
(!A::LONG_NAME.empty() && args[index] == "--" + A::LONG_NAME)));
template <typename A>
static ParseResult<bool> parse(const std::vector<std::string> &args, std::size_t index) noexcept {
auto r = ParseResult<bool>::ok(args.size() > index &&
((!A::SHORT_NAME.empty() && args[index] == "-" + A::SHORT_NAME) ||
(!A::LONG_NAME.empty() && args[index] == "--" + A::LONG_NAME)));

if (r.result) {
r.nextIndex = index + 1;
}

return r;
}
};

struct TailArg : PositionalArg, RequiredMixin {
template <typename A> static ParseResult<std::string> parse(const std::vector<std::string> &args) noexcept {
return RequiredMixin::parse<A, [](const std::vector<std::string> &args) noexcept {
return ParseResult<std::string>::ok(args | ranges::views::drop(A::POSITION + 1) |
ranges::views::join(std::string(" ")) | ranges::to<std::string>,
args.size() - 1);
}>(args);
}
};

struct AddressArg : PositionalArg {
const static std::string NAME;
const static std::size_t POSITION{0};
const static std::size_t POSITION;
const static std::string HELP_TEXT;

[[nodiscard]] static std::string prepareHelp(std::size_t namePadding,
Expand All @@ -186,12 +228,11 @@ struct AddressArg : PositionalArg {
}

static ParseResult<std::optional<std::string>> parse(const std::vector<std::string> &args) {
return ParseResult<std::optional<std::string>>::ok(args.size() > POSITION ? std::optional{args[POSITION]}
: std::nullopt);
return PositionalArg::parse<AddressArg>(args);
}
};

struct AddressArgRequired : AddressArg {
struct AddressArgRequired : AddressArg, RequiredMixin {
[[nodiscard]] static std::string prepareHelp(std::size_t namePadding,
std::size_t nameFieldSize /* padding + name + padding */,
std::size_t windowSize) noexcept {
Expand All @@ -203,17 +244,13 @@ struct AddressArgRequired : AddressArg {
}

static ParseResult<std::string> parse(const std::vector<std::string> &args) {
if (args.size() > POSITION) {
return ParseResult<std::string>::ok(args[POSITION]);
}

return ParseResult<std::string>::error("Address parsing error. Insufficient parameters.");
return RequiredMixin::parse<AddressArgRequired>(args);
}
};

struct TypesArg : PositionalArg {
const static std::string NAME;
const static std::size_t POSITION{1};
const static std::size_t POSITION;
const static std::string HELP_TEXT;

[[nodiscard]] static std::string prepareHelp(std::size_t namePadding,
Expand All @@ -231,12 +268,11 @@ struct TypesArg : PositionalArg {
}

static ParseResult<std::optional<std::string>> parse(const std::vector<std::string> &args) {
return ParseResult<std::optional<std::string>>::ok(args.size() > POSITION ? std::optional{args[POSITION]}
: std::nullopt);
return PositionalArg::parse<TypesArg>(args);
}
};

struct TypesArgRequired : TypesArg {
struct TypesArgRequired : TypesArg, RequiredMixin {
[[nodiscard]] static std::string prepareHelp(std::size_t namePadding,
std::size_t nameFieldSize /* padding + name + padding */,
std::size_t windowSize) noexcept {
Expand All @@ -248,17 +284,13 @@ struct TypesArgRequired : TypesArg {
}

static ParseResult<std::string> parse(const std::vector<std::string> &args) {
if (args.size() > POSITION) {
return ParseResult<std::string>::ok(args[POSITION]);
}

return ParseResult<std::string>::error("Types parsing error. Insufficient parameters.");
return RequiredMixin::parse<TypesArgRequired>(args);
}
};

struct SymbolsArg : PositionalArg {
const static std::string NAME;
const static std::size_t POSITION{2};
const static std::size_t POSITION;
const static std::string HELP_TEXT;

[[nodiscard]] static std::string prepareHelp(std::size_t namePadding,
Expand All @@ -276,12 +308,11 @@ struct SymbolsArg : PositionalArg {
}

static ParseResult<std::optional<std::string>> parse(const std::vector<std::string> &args) {
return ParseResult<std::optional<std::string>>::ok(args.size() > POSITION ? std::optional{args[POSITION]}
: std::nullopt);
return PositionalArg::parse<SymbolsArg>(args);
}
};

struct SymbolsArgRequired : SymbolsArg {
struct SymbolsArgRequired : SymbolsArg, RequiredMixin {
[[nodiscard]] static std::string prepareHelp(std::size_t namePadding,
std::size_t nameFieldSize /* padding + name + padding */,
std::size_t windowSize) noexcept {
Expand Down Expand Up @@ -571,10 +602,34 @@ struct HelpArg : FlagArg {
}
};

struct ArticleArgRequired : TailArg {
const static std::string NAME;
const static std::size_t POSITION;
const static std::string HELP_TEXT;

[[nodiscard]] static std::string prepareHelp(std::size_t namePadding,
std::size_t nameFieldSize /* padding + name + padding */,
std::size_t windowSize) noexcept {
return Arg::prepareHelp<ArticleArgRequired>(namePadding, nameFieldSize, windowSize);
}

[[nodiscard]] static std::string getFullName() noexcept {
return fmt::format("{} (pos. {})", NAME, POSITION);
}

[[nodiscard]] static std::string getFullHelpText() noexcept {
return fmt::format("Required. {}", trimStr(HELP_TEXT));
}

static ParseResult<std::string> parse(const std::vector<std::string> &args) {
return TailArg::parse<ArticleArgRequired>(args);
}
};

using ArgType = std::variant<tools::AddressArg, tools::AddressArgRequired, tools::TypesArg, tools::TypesArgRequired,
tools::SymbolsArg, tools::SymbolsArgRequired, tools::PropertiesArg, tools::FromTimeArg,
tools::SourceArg, tools::TapeArg, tools::QuiteArg, tools::ForceStreamArg,
tools::CPUUsageByCoreArg, tools::DetachListenerArg, tools::IntervalArg, tools::HelpArg>;
using ArgType =
std::variant<tools::AddressArg, tools::AddressArgRequired, tools::TypesArg, tools::TypesArgRequired,
tools::SymbolsArg, tools::SymbolsArgRequired, tools::PropertiesArg, tools::FromTimeArg,
tools::SourceArg, tools::TapeArg, tools::QuiteArg, tools::ForceStreamArg, tools::CPUUsageByCoreArg,
tools::DetachListenerArg, tools::IntervalArg, tools::HelpArg, tools::ArticleArgRequired>;

} // namespace dxfcpp::tools
3 changes: 1 addition & 2 deletions tools/Tools/src/Connect/ConnectTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
namespace dxfcpp::tools {
const std::string ConnectTool::NAME{"Connect"};
const std::string ConnectTool::SHORT_DESCRIPTION{"Connects to specified address(es)."};
const std::string ConnectTool::DESCRIPTION{
R"(
const std::string ConnectTool::DESCRIPTION{R"(
Connects to the specified address(es) and subscribes to the specified symbols.
)"};
const std::vector<std::string> ConnectTool::USAGE{
Expand Down
15 changes: 15 additions & 0 deletions tools/Tools/src/Connect/ConnectTool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ struct ConnectTool {
static const std::vector<std::string> ADDITIONAL_INFO;
static const std::vector<ArgType> ARGS;

[[nodiscard]] static std::string getName() noexcept {
return NAME;
}

[[nodiscard]] static std::string getFullName() noexcept {
return NAME;
}

[[nodiscard]] static std::string prepareHelp(std::size_t namePadding,
std::size_t nameFieldSize /* padding + name + padding */,
std::size_t) noexcept {
return fmt::format("{:{}}{:<{}}{:{}}{}\n", "", namePadding, getFullName(),
nameFieldSize - 2 * namePadding, "", namePadding, SHORT_DESCRIPTION);
}

struct Args {
std::string address;
std::string types;
Expand Down
15 changes: 15 additions & 0 deletions tools/Tools/src/Dump/DumpTool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ struct DumpTool {
static const std::vector<std::string> ADDITIONAL_INFO;
static const std::vector<ArgType> ARGS;

[[nodiscard]] static std::string getName() noexcept {
return NAME;
}

[[nodiscard]] static std::string getFullName() noexcept {
return NAME;
}

[[nodiscard]] static std::string prepareHelp(std::size_t namePadding,
std::size_t nameFieldSize /* padding + name + padding */,
std::size_t) noexcept {
return fmt::format("{:{}}{:<{}}{:{}}{}\n", "", namePadding, getFullName(),
nameFieldSize - 2 * namePadding, "", namePadding, SHORT_DESCRIPTION);
}

struct Args {
std::string address;
std::optional<std::string> types;
Expand Down
30 changes: 29 additions & 1 deletion tools/Tools/src/Help/HelpTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@

#include "HelpTool.hpp"

#include "../Connect/ConnectTool.hpp"
#include "../Dump/DumpTool.hpp"
#include "../LatencyTest/LatencyTestTool.hpp"
#include "../PerfTest/PerfTestTool.hpp"

#include <fmt/chrono.h>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <fmt/std.h>

#include <range/v3/all.hpp>

namespace dxfcpp::tools {
const std::unordered_map<std::string, std::string> HelpTool::EMBEDDED_ARTICLES{
{"Connect",
Expand Down Expand Up @@ -287,11 +299,27 @@ Displays documentation pages.
)"};
const std::vector<std::string> HelpTool::USAGE{
NAME + " <article>",
NAME + " <tool>",
NAME + " contents",
NAME + " all",
};
const std::vector<std::string> HelpTool::ADDITIONAL_INFO{
R"(To see help on some topic type "Help <topic>".)",
R"(To see list of all articles type "Help contents".)",
R"(Use "Help all" to generate all existing help articles.)",
};

const std::vector<ArgType> HelpTool::ARGS{ArticleArgRequired{}, HelpArg{}};

const std::unordered_map<std::string, HelpTool::Tool> HelpTool::ALL_TOOLS{{ConnectTool::getName(), ConnectTool{}},
{DumpTool::getName(), DumpTool{}},
{HelpTool::getName(), HelpTool{}},
{LatencyTest::getName(), LatencyTest{}},
{PerfTestTool::getName(), PerfTestTool{}}};

const std::vector<std::string> HelpTool::ALL_TOOL_NAMES =
ALL_TOOLS | ranges::views::keys | ranges::to<std::vector<std::string>>();

const std::vector<std::string> HelpTool::ALL_ARTICLE_NAMES =
EMBEDDED_ARTICLES | ranges::views::keys | ranges::to<std::vector<std::string>>();

} // namespace dxfcpp::tools
Loading

0 comments on commit 3980dc4

Please sign in to comment.