Radix Relay
Hybrid mesh communications with Signal Protocol encryption
Loading...
Searching...
No Matches
command_parser.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <core/events.hpp>
5#include <functional>
6#include <memory>
7#include <optional>
8#include <string>
9#include <string_view>
10#include <variant>
11#include <vector>
12
13namespace radix_relay::core {
14
25template<concepts::signal_bridge Bridge> struct command_parser
26{
27 using command_variant_t = std::variant<events::help,
46
47 using parse_result_t = std::optional<command_variant_t>;
48 using handler_t = std::function<parse_result_t(const std::string &)>;
49
50 explicit command_parser(std::shared_ptr<Bridge> bridge) : bridge_(std::move(bridge)) { build_handler_chain(); }
51
61 [[nodiscard]] auto parse(const std::string &input) const -> command_variant_t
62 {
63 auto effective_input = input;
64
65 if (active_chat_rdx_.has_value() and not input.starts_with("/")) {
66 effective_input = "/send " + active_chat_rdx_.value() + " " + input;
67 }
68
69 for (const auto &handler : handlers_) {
70 // cppcheck-suppress useStlAlgorithm
71 if (auto result = handler(effective_input)) { return *result; }
72 }
73
74 return events::unknown_command{ .input = effective_input };
75 }
76
82 auto enter_chat_mode(const std::string &rdx_fingerprint) const -> void { active_chat_rdx_ = rdx_fingerprint; }
83
87 auto exit_chat_mode() const -> void { active_chat_rdx_.reset(); }
88
94 [[nodiscard]] auto in_chat_mode() const -> bool { return active_chat_rdx_.has_value(); }
95
96private:
97 std::shared_ptr<Bridge> bridge_;
98 mutable std::optional<std::string> active_chat_rdx_;
99 std::vector<handler_t> handlers_;
100
104 template<typename Event> static auto exact_match(std::string_view command) -> handler_t
105 {
106 return [cmd = std::string(command)](const std::string &input) -> parse_result_t {
107 if (input == cmd) { return Event{}; }
108 return std::nullopt;
109 };
110 }
111
115 template<typename Event, typename Extractor>
116 static auto prefix_match(std::string_view prefix, Extractor extractor) -> handler_t
117 {
118 return [p = std::string(prefix), ext = std::move(extractor)](const std::string &input) -> parse_result_t {
119 if (input.starts_with(p)) { return ext(input.substr(p.length())); }
120 return std::nullopt;
121 };
122 }
123
124 auto build_handler_chain() -> void
125 {
126 // Handlers ordered by expected frequency of use (most common first)
127
128 // /send <peer> <message> - most frequent in chat mode
129 handlers_.push_back(prefix_match<events::send>("/send ", [](const std::string &args) {
130 const auto first_space = args.find(' ');
131 if (first_space != std::string::npos and not args.empty()) {
132 return events::send{ .peer = args.substr(0, first_space), .message = args.substr(first_space + 1) };
133 }
134 return events::send{ .peer = "", .message = "" };
135 }));
136
137 // /chat <contact> - entering conversations
138 handlers_.push_back([this](const std::string &input) -> parse_result_t {
139 constexpr auto chat_cmd = "/chat ";
140 if (not input.starts_with(chat_cmd)) { return std::nullopt; }
141
142 const auto contact_name = input.substr(std::string_view(chat_cmd).length());
143 if (not contact_name.empty()) {
144 try {
145 const auto contact = bridge_->lookup_contact(contact_name);
146 active_chat_rdx_ = contact.rdx_fingerprint;
147 } catch (const std::exception & /*e*/) {
148 // Contact lookup failed, don't enter chat mode
149 }
150 }
151 return events::chat{ .contact = contact_name };
152 });
153
154 // /leave - exiting conversations
155 handlers_.push_back([this](const std::string &input) -> parse_result_t {
156 if (input == "/leave") {
157 active_chat_rdx_.reset();
158 return events::leave{};
159 }
160 return std::nullopt;
161 });
162
163 // Common informational commands
164 handlers_.push_back(exact_match<events::help>("/help"));
165 handlers_.push_back(exact_match<events::status>("/status"));
166 handlers_.push_back(exact_match<events::peers>("/peers"));
167 handlers_.push_back(exact_match<events::sessions>("/sessions"));
168
169 // Connection management
170 handlers_.push_back(prefix_match<events::connect>(
171 "/connect ", [](const std::string &args) { return events::connect{ .relay = args }; }));
172 handlers_.push_back(exact_match<events::disconnect>("/disconnect"));
173
174 // Contact/identity management
175 handlers_.push_back(exact_match<events::identities>("/identities"));
176 handlers_.push_back(prefix_match<events::trust>("/trust ", [](const std::string &args) {
177 const auto first_space = args.find(' ');
178 if (first_space != std::string::npos and not args.empty()) {
179 return events::trust{ .peer = args.substr(0, first_space), .alias = args.substr(first_space + 1) };
180 }
181 return events::trust{ .peer = args, .alias = "" };
182 }));
183 handlers_.push_back(
184 prefix_match<events::verify>("/verify ", [](const std::string &args) { return events::verify{ .peer = args }; }));
185
186 // Less frequent commands
187 handlers_.push_back(prefix_match<events::broadcast>(
188 "/broadcast ", [](const std::string &args) { return events::broadcast{ .message = args }; }));
189 handlers_.push_back(
190 prefix_match<events::mode>("/mode ", [](const std::string &args) { return events::mode{ .new_mode = args }; }));
191 handlers_.push_back(exact_match<events::scan>("/scan"));
192 handlers_.push_back(exact_match<events::version>("/version"));
193 handlers_.push_back(exact_match<events::publish_identity>("/publish"));
194 handlers_.push_back(exact_match<events::unpublish_identity>("/unpublish"));
195 }
196};
197
198}// namespace radix_relay::core
Chain of Responsibility command parser.
auto exit_chat_mode() const -> void
Exits chat mode.
command_parser(std::shared_ptr< Bridge > bridge)
auto enter_chat_mode(const std::string &rdx_fingerprint) const -> void
Enters chat mode with the specified contact.
std::variant< events::help, events::peers, events::status, events::sessions, events::identities, events::scan, events::version, events::mode, events::send, events::broadcast, events::connect, events::disconnect, events::publish_identity, events::unpublish_identity, events::trust, events::verify, events::chat, events::leave, events::unknown_command > command_variant_t
std::optional< command_variant_t > parse_result_t
auto parse(const std::string &input) const -> command_variant_t
Parses a raw command string into a typed command event.
std::function< parse_result_t(const std::string &)> handler_t
auto in_chat_mode() const -> bool
Checks if currently in chat mode.
Broadcast message to all peers.
Definition events.hpp:63
Enter chat mode with a specific contact.
Definition events.hpp:103
Disconnect from current relay.
Definition events.hpp:75
Request display of available commands.
Definition events.hpp:15
Request list of discovered identities.
Definition events.hpp:35
Change operational mode.
Definition events.hpp:50
Request list of connected peers.
Definition events.hpp:20
Publish identity bundle to the network.
Definition events.hpp:80
Request scan for nearby peers.
Definition events.hpp:40
Send encrypted message to a specific peer.
Definition events.hpp:56
Request list of active sessions.
Definition events.hpp:30
Request current system status.
Definition events.hpp:25
Establish trust with a peer and assign an alias.
Definition events.hpp:90
Unknown or unrecognized command.
Definition events.hpp:114
std::string input
The original unrecognized input.
Definition events.hpp:115
Remove identity bundle from the network.
Definition events.hpp:85
Verify identity fingerprint of a peer.
Definition events.hpp:97
Request application version information.
Definition events.hpp:45