comparison irccdctl/cli.cpp @ 804:d55a64c6586b

irccdctl: unify CLI output, closes #928 @1h
author David Demelier <markand@malikania.fr>
date Wed, 14 Nov 2018 14:07:05 +0100
parents f26bb089232d
children 5a421b20a4f4
comparison
equal deleted inserted replaced
803:14f9e3b03779 804:d55a64c6586b
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */ 17 */
18 18
19 #include <iomanip>
20 #include <ios>
19 #include <iostream> 21 #include <iostream>
20 22
21 #include <irccd/json_util.hpp> 23 #include <irccd/json_util.hpp>
22 #include <irccd/options.hpp> 24 #include <irccd/options.hpp>
23 #include <irccd/string_util.hpp> 25 #include <irccd/string_util.hpp>
27 #include <irccd/daemon/rule_service.hpp> 29 #include <irccd/daemon/rule_service.hpp>
28 30
29 #include "cli.hpp" 31 #include "cli.hpp"
30 32
31 using irccd::json_util::deserializer; 33 using irccd::json_util::deserializer;
34 using irccd::json_util::pretty;
32 35
33 namespace irccd::ctl { 36 namespace irccd::ctl {
34 37
35 // {{{ helpers 38 // {{{ helpers
36 39
37 namespace { 40 namespace {
41
42 template <typename T>
43 auto operator<<(std::ostream& out, const std::optional<T>& value) -> std::ostream&
44 {
45 if (value)
46 out << pretty(*value);
47
48 return out;
49 }
38 50
39 template <typename T> 51 template <typename T>
40 auto bind() noexcept -> cli::constructor 52 auto bind() noexcept -> cli::constructor
41 { 53 {
42 return [] () noexcept { 54 return [] () noexcept {
57 return result.find("--format")->second; 69 return result.find("--format")->second;
58 70
59 return "native"; 71 return "native";
60 } 72 }
61 73
62 void onConnect(const nlohmann::json &v) 74 auto align(std::string_view topic) -> std::ostream&
63 { 75 {
64 std::cout << "event: onConnect\n"; 76 assert(topic.size() <= 16);
65 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 77
66 } 78 return std::cout << std::setw(16) << std::left << topic;
67 79 }
68 void onInvite(const nlohmann::json &v) 80
69 { 81 void onConnect(const deserializer& v)
70 std::cout << "event: onInvite\n"; 82 {
71 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 83 align("event:") << "onConnect\n";
72 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 84 align("server:") << v.get<std::string>("server") << "\n";
73 std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; 85 }
74 } 86
75 87 void onInvite(const deserializer& v)
76 void onJoin(const nlohmann::json &v) 88 {
77 { 89 align("event:") << "onInvite\n";
78 std::cout << "event: onJoin\n"; 90 align("server:") << v.get<std::string>("server") << "\n";
79 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 91 align("origin:") << v.get<std::string>("origin") << "\n";
80 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 92 align("channel:") << v.get<std::string>("channel") << "\n";
81 std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; 93 }
82 } 94
83 95 void onJoin(const deserializer& v)
84 void onKick(const nlohmann::json &v) 96 {
85 { 97 align("event:") << "onJoin\n";
86 std::cout << "event: onKick\n"; 98 align("server:") << v.get<std::string>("server") << "\n";
87 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 99 align("origin:") << v.get<std::string>("origin") << "\n";
88 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 100 align("channel:") << v.get<std::string>("channel") << "\n";
89 std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; 101 }
90 std::cout << "target: " << json_util::pretty(v.value("target", "(unknown)")) << "\n"; 102
91 std::cout << "reason: " << json_util::pretty(v.value("reason", "(unknown)")) << "\n"; 103 void onKick(const deserializer& v)
92 } 104 {
93 105 align("event:") << "onKick\n";
94 void onMessage(const nlohmann::json &v) 106 align("server:") << v.get<std::string>("server") << "\n";
95 { 107 align("origin:") << v.get<std::string>("origin") << "\n";
96 std::cout << "event: onMessage\n"; 108 align("channel:") << v.get<std::string>("channel") << "\n";
97 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 109 align("target:") << v.get<std::string>("target") << "\n";
98 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 110 align("reason:") << v.get<std::string>("reason") << "\n";
99 std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; 111 }
100 std::cout << "message: " << json_util::pretty(v.value("message", "(unknown)")) << "\n"; 112
101 } 113 void onMessage(const deserializer& v)
102 114 {
103 void onMe(const nlohmann::json &v) 115 align("event:") << "onMessage\n";
104 { 116 align("server:") << v.get<std::string>("server") << "\n";
105 std::cout << "event: onMe\n"; 117 align("origin:") << v.get<std::string>("origin") << "\n";
106 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 118 align("channel:") << v.get<std::string>("channel") << "\n";
107 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 119 align("message:") << v.get<std::string>("message") << "\n";
108 std::cout << "target: " << json_util::pretty(v.value("target", "(unknown)")) << "\n"; 120 }
109 std::cout << "message: " << json_util::pretty(v.value("message", "(unknown)")) << "\n"; 121
110 } 122 void onMe(const deserializer& v)
111 123 {
112 void onMode(const nlohmann::json &v) 124 align("event:") << "onMe\n";
113 { 125 align("server:") << v.get<std::string>("server") << "\n";
114 std::cout << "event: onMode\n"; 126 align("origin:") << v.get<std::string>("origin") << "\n";
115 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 127 align("target:") << v.get<std::string>("target") << "\n";
116 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 128 align("message:") << v.get<std::string>("message") << "\n";
117 std::cout << "mode: " << json_util::pretty(v.value("mode", "(unknown)")) << "\n"; 129 }
118 } 130
119 131 void onMode(const deserializer& v)
120 void onNames(const nlohmann::json &v) 132 {
121 { 133 align("event:") << "onMode\n";
122 std::cout << "event: onNames\n"; 134 align("server:") << v.get<std::string>("server") << "\n";
123 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 135 align("origin:") << v.get<std::string>("origin") << "\n";
124 std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; 136 align("mode:") << v.get<std::string>("mode") << "\n";
125 std::cout << "names: " << json_util::pretty(v.value("names", "(unknown)")) << "\n"; 137 }
126 } 138
127 139 void onNames(const deserializer& v)
128 void onNick(const nlohmann::json &v) 140 {
129 { 141 align("event:") << "onNames\n";
130 std::cout << "event: onNick\n"; 142 align("server:") << v.get<std::string>("server") << "\n";
131 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 143 align("channel:") << v.get<std::string>("channel") << "\n";
132 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 144 align("names:") << v.get<std::string>("names") << "\n";
133 std::cout << "nickname: " << json_util::pretty(v.value("nickname", "(unknown)")) << "\n"; 145 }
134 } 146
135 147 void onNick(const deserializer& v)
136 void onNotice(const nlohmann::json &v) 148 {
137 { 149 align("event:") << "onNick\n";
138 std::cout << "event: onNotice\n"; 150 align("server:") << v.get<std::string>("server") << "\n";
139 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 151 align("origin:") << v.get<std::string>("origin") << "\n";
140 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 152 align("nickname:") << v.get<std::string>("nickname") << "\n";
141 std::cout << "message: " << json_util::pretty(v.value("message", "(unknown)")) << "\n"; 153 }
142 } 154
143 155 void onNotice(const deserializer& v)
144 void onPart(const nlohmann::json &v) 156 {
145 { 157 align("event:") << "onNotice\n";
146 std::cout << "event: onPart\n"; 158 align("server:") << v.get<std::string>("server") << "\n";
147 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 159 align("origin:") << v.get<std::string>("origin") << "\n";
148 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 160 align("message:") << v.get<std::string>("message") << "\n";
149 std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; 161 }
150 std::cout << "reason: " << json_util::pretty(v.value("reason", "(unknown)")) << "\n"; 162
151 } 163 void onPart(const deserializer& v)
152 164 {
153 void onTopic(const nlohmann::json &v) 165 align("event:") << "onPart\n";
154 { 166 align("server:") << v.get<std::string>("server") << "\n";
155 std::cout << "event: onTopic\n"; 167 align("origin:") << v.get<std::string>("origin") << "\n";
156 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 168 align("channel:") << v.get<std::string>("channel") << "\n";
157 std::cout << "origin: " << json_util::pretty(v.value("origin", "(unknown)")) << "\n"; 169 align("reason:") << v.get<std::string>("reason") << "\n";
158 std::cout << "channel: " << json_util::pretty(v.value("channel", "(unknown)")) << "\n"; 170 }
159 std::cout << "topic: " << json_util::pretty(v.value("topic", "(unknown)")) << "\n"; 171
160 } 172 void onTopic(const deserializer& v)
161 173 {
162 void onWhois(const nlohmann::json &v) 174 align("event:") << "onTopic\n";
163 { 175 align("server:") << v.get<std::string>("server") << "\n";
164 std::cout << "event: onWhois\n"; 176 align("origin:") << v.get<std::string>("origin") << "\n";
165 std::cout << "server: " << json_util::pretty(v.value("server", "(unknown)")) << "\n"; 177 align("channel:") << v.get<std::string>("channel") << "\n";
166 std::cout << "nickname: " << json_util::pretty(v.value("nickname", "(unknown)")) << "\n"; 178 align("topic:") << v.get<std::string>("topic") << "\n";
167 std::cout << "username: " << json_util::pretty(v.value("username", "(unknown)")) << "\n"; 179 }
168 std::cout << "hostname: " << json_util::pretty(v.value("hostname", "(unknown)")) << "\n"; 180
169 std::cout << "realname: " << json_util::pretty(v.value("realname", "(unknown)")) << "\n"; 181 void onWhois(const deserializer& v)
182 {
183 align("event:") << "onWhois\n";
184 align("server:") << v.get<std::string>("server") << "\n";
185 align("nickname:") << v.get<std::string>("nickname") << "\n";
186 align("username:") << v.get<std::string>("username") << "\n";
187 align("hostname:") << v.get<std::string>("hostname") << "\n";
188 align("realname:") << v.get<std::string>("realname") << "\n";
170 } 189 }
171 190
172 const std::unordered_map<std::string_view, std::function<void (const nlohmann::json&)>> events{ 191 const std::unordered_map<std::string_view, std::function<void (const nlohmann::json&)>> events{
173 { "onConnect", onConnect }, 192 { "onConnect", onConnect },
174 { "onInvite", onInvite }, 193 { "onInvite", onInvite },
186 }; 205 };
187 206
188 void get_event(ctl::controller& ctl, std::string fmt) 207 void get_event(ctl::controller& ctl, std::string fmt)
189 { 208 {
190 ctl.recv([&ctl, fmt] (auto code, auto message) { 209 ctl.recv([&ctl, fmt] (auto code, auto message) {
210 const deserializer doc(message);
211
191 if (code) 212 if (code)
192 throw std::system_error(code); 213 throw std::system_error(code);
193 214
194 const auto event = deserializer(message).get<std::string>("event"); 215 const auto event = doc.get<std::string>("event");
195 const auto it = events.find(event ? *event : ""); 216 const auto it = events.find(event ? *event : "");
196 217
197 if (it != events.end()) { 218 if (it != events.end()) {
198 if (fmt == "json") 219 if (fmt == "json")
199 std::cout << message.dump(4) << std::endl; 220 std::cout << message.dump(4) << std::endl;
200 else { 221 else {
201 it->second(message); 222 it->second(doc);
202 std::cout << std::endl; 223 std::cout << std::endl;
203 } 224 }
204 } 225 }
205 226
206 get_event(ctl, std::move(fmt)); 227 get_event(ctl, std::move(fmt));
313 { "variable", args[1] } 334 { "variable", args[1] }
314 }); 335 });
315 336
316 request(ctl, std::move(json), [args] (auto result) { 337 request(ctl, std::move(json), [args] (auto result) {
317 if (result["variables"].is_object()) 338 if (result["variables"].is_object())
318 std::cout << json_util::pretty(result["variables"][args[1]]) << std::endl; 339 std::cout << pretty(result["variables"][args[1]]) << std::endl;
319 }); 340 });
320 } 341 }
321 342
322 void plugin_config_cli::getall(ctl::controller& ctl, const std::vector<std::string> &args) 343 void plugin_config_cli::getall(ctl::controller& ctl, const std::vector<std::string> &args)
323 { 344 {
328 349
329 request(ctl, json, [] (auto result) { 350 request(ctl, json, [] (auto result) {
330 const auto variables = result["variables"]; 351 const auto variables = result["variables"];
331 352
332 for (auto v = variables.begin(); v != variables.end(); ++v) 353 for (auto v = variables.begin(); v != variables.end(); ++v)
333 std::cout << std::setw(16) << std::left << v.key() << " : " << json_util::pretty(v.value()) << std::endl; 354 std::cout << std::setw(16) << std::left << v.key() << " : " << pretty(v.value()) << std::endl;
334 }); 355 });
335 } 356 }
336 357
337 auto plugin_config_cli::get_name() const noexcept -> std::string_view 358 auto plugin_config_cli::get_name() const noexcept -> std::string_view
338 { 359 {
376 }); 397 });
377 398
378 request(ctl, json, [] (auto result) { 399 request(ctl, json, [] (auto result) {
379 const deserializer doc(result); 400 const deserializer doc(result);
380 401
381 std::cout << std::boolalpha; 402 align("author:") << doc.get<std::string>("author") << std::endl;
382 std::cout << "Author : " 403 align("license:") << doc.get<std::string>("license") << std::endl;
383 << doc.get<std::string>("author").value_or("(unknown)") << std::endl; 404 align("summary:") << doc.get<std::string>("summary") << std::endl;
384 std::cout << "License : " 405 align("version:") << doc.get<std::string>("version") << std::endl;
385 << doc.get<std::string>("license").value_or("(unknown)") << std::endl;
386 std::cout << "Summary : "
387 << doc.get<std::string>("summary").value_or("(unknown)") << std::endl;
388 std::cout << "Version : "
389 << doc.get<std::string>("version").value_or("(unknown)") << std::endl;
390 }); 406 });
391 } 407 }
392 408
393 // }}} 409 // }}}
394 410
642 return "accept"; 658 return "accept";
643 else 659 else
644 return "drop"; 660 return "drop";
645 }; 661 };
646 662
647 std::cout << "rule: " << index << std::endl; 663 align("rule:") << index << std::endl;
648 std::cout << "servers: " << unjoin(json["servers"]) << std::endl; 664 align("servers:") << unjoin(json["servers"]) << std::endl;
649 std::cout << "channels: " << unjoin(json["channels"]) << std::endl; 665 align("channels:") << unjoin(json["channels"]) << std::endl;
650 std::cout << "plugins: " << unjoin(json["plugins"]) << std::endl; 666 align("plugins:") << unjoin(json["plugins"]) << std::endl;
651 std::cout << "events: " << unjoin(json["events"]) << std::endl; 667 align("events:") << unjoin(json["events"]) << std::endl;
652 std::cout << "action: " << unstr(json["action"]) << std::endl; 668 align("action:") << unstr(json["action"]) << std::endl;
653 std::cout << std::endl;
654 } 669 }
655 670
656 auto rule_info_cli::get_name() const noexcept -> std::string_view 671 auto rule_info_cli::get_name() const noexcept -> std::string_view
657 { 672 {
658 return "rule-info"; 673 return "rule-info";
689 704
690 void rule_list_cli::exec(ctl::controller& ctl, const std::vector<std::string>&) 705 void rule_list_cli::exec(ctl::controller& ctl, const std::vector<std::string>&)
691 { 706 {
692 request(ctl, {{ "command", "rule-list" }}, [] (auto result) { 707 request(ctl, {{ "command", "rule-list" }}, [] (auto result) {
693 auto pos = 0; 708 auto pos = 0;
694 709 auto length = result["list"].size();
695 for (const auto& obj : result["list"]) 710
696 if (obj.is_object()) 711 for (const auto& obj : result["list"]) {
697 rule_info_cli::print(obj, pos++); 712 if (!obj.is_object())
713 continue;
714
715 rule_info_cli::print(obj, pos++);
716
717 if (pos < length)
718 std::cout << std::endl;
719 }
698 }); 720 });
699 } 721 }
700 722
701 // }}} 723 // }}}
702 724
845 { "command", "server-info" }, 867 { "command", "server-info" },
846 { "server", args[0] } 868 { "server", args[0] }
847 }); 869 });
848 870
849 request(ctl, std::move(json), [] (auto result) { 871 request(ctl, std::move(json), [] (auto result) {
850 std::cout << std::boolalpha; 872 const deserializer doc(result);
851 std::cout << "Name : " << json_util::pretty(result["name"]) << std::endl; 873
852 std::cout << "Hostname : " << json_util::pretty(result["hostname"]) << std::endl; 874 align("name:") << doc.get<std::string>("name") << std::endl;
853 std::cout << "Port : " << json_util::pretty(result["port"]) << std::endl; 875 align("hostname:") << doc.get<std::string>("hostname") << std::endl;
854 std::cout << "Ipv6 : " << json_util::pretty(result["ipv6"]) << std::endl; 876 align("port:") << doc.get<std::uint64_t>("port") << std::endl;
855 std::cout << "SSL : " << json_util::pretty(result["ssl"]) << std::endl; 877 align("nickname:") << doc.get<std::string>("nickname") << std::endl;
856 std::cout << "SSL verified : " << json_util::pretty(result["sslVerify"]) << std::endl; 878 align("username:") << doc.get<std::string>("username") << std::endl;
857 std::cout << "Channels : "; 879 align("realname:") << doc.get<std::string>("realname") << std::endl;
880 align("ipv4:") << doc.get<bool>("ipv4") << std::endl;
881 align("ipv6:") << doc.get<bool>("ipv6") << std::endl;
882 align("ssl:") << doc.get<bool>("ssl") << std::endl;
883 align("channels:");
858 884
859 for (const auto& v : result["channels"]) 885 for (const auto& v : result["channels"])
860 if (v.is_string()) 886 if (v.is_string())
861 std::cout << v.template get<std::string>() << " "; 887 std::cout << v.template get<std::string>() << " ";
862 888
863 std::cout << std::endl; 889 std::cout << std::endl;
864
865 std::cout << "Nickname : " << json_util::pretty(result["nickname"]) << std::endl;
866 std::cout << "User name : " << json_util::pretty(result["username"]) << std::endl;
867 std::cout << "Real name : " << json_util::pretty(result["realname"]) << std::endl;
868 }); 890 });
869 } 891 }
870 892
871 // }}} 893 // }}}
872 894