37
37
%requires Util
38
38
%requires Logger
39
39
40
+ %try-module linenoise
41
+ %define NoLinenoise
42
+ %endtry
43
+
40
44
%exec-class QdpCmd
41
45
42
46
class QdpCmd {
43
47
public {
48
+ static bool ix;
49
+
44
50
#! program options
45
51
const Opts = {
46
52
"listconnections": "c,list-connections",
@@ -65,12 +71,18 @@ class QdpCmd {
65
71
"field-update": \QdpCmd::fieldUpdate(),
66
72
"fupdate": \QdpCmd::fieldUpdate(),
67
73
"info": \QdpCmd::getInfo(),
74
+ "interactive": \QdpCmd::interactive(),
75
+ "ix": \QdpCmd::interactive(),
68
76
"ldetails": \QdpCmd::listChildDetails(),
69
77
"levents": \QdpCmd::listEvents(),
70
78
"list": \QdpCmd::listChildren(),
71
79
"list-details": \QdpCmd::listChildDetails(),
72
80
"list-events": \QdpCmd::listEvents(),
73
81
"listen": \QdpCmd::listen(),
82
+ "lmessages": \QdpCmd::listMessages(),
83
+ "lmsgs": \QdpCmd::listMessages(),
84
+ "message": \QdpCmd::message(),
85
+ "msg": \QdpCmd::message(),
74
86
"pcreate": \QdpCmd::providerCreate(),
75
87
"pdelete": \QdpCmd::providerDelete(),
76
88
"provider-create": \QdpCmd::providerCreate(),
@@ -81,6 +93,9 @@ class QdpCmd {
81
93
"response": \QdpCmd::response(),
82
94
"rsearch": \QdpCmd::doRequestSearch(),
83
95
"search": \QdpCmd::search(),
96
+ "send-message": \QdpCmd::sendMessage(),
97
+ "smessage": \QdpCmd::sendMessage(),
98
+ "smsg": \QdpCmd::sendMessage(),
84
99
"update": \QdpCmd::update(),
85
100
"upsert": \QdpCmd::upsert(),
86
101
};
@@ -284,6 +299,59 @@ class QdpCmd {
284
299
}
285
300
}
286
301
302
+ static interactive(AbstractDataProvider provider) {
303
+ provider.checkObservable();
304
+ provider.checkMessages();
305
+ MyObserver observer();
306
+ Observable observable = cast<Observable>(provider);
307
+
308
+ ix = True;
309
+
310
+ # print a message
311
+ stdout.printf("listening to events from %y; enter <message id>: <message body> to send a message, 'help' for "
312
+ "help\n", provider.getName());
313
+ observable.registerObserver(observer);
314
+
315
+ InputHelper input_helper(provider);
316
+ while (True) {
317
+ string input = input_helper.get();
318
+ #printf("input: %y\n", input);
319
+ bool quit;
320
+ switch (input) {
321
+ case "exit":
322
+ case "quit":
323
+ quit = True;
324
+ break;
325
+ }
326
+ if (quit) {
327
+ break;
328
+ }
329
+ # check for msg ID
330
+ (*string msgid, *string msg) = (input =~ x/([^:]+):(.*)/);
331
+ if (!msgid) {
332
+ stdout.printf("cannot parse input %y; enter <message id>: <message body> to send a message, "
333
+ "'help' for help\n", input);
334
+ continue;
335
+ }
336
+
337
+ auto v = parse_to_qore_value(msg);
338
+ printf("> sending %y -> %y\n", msgid, v);
339
+ provider.sendMessage(msgid, v);
340
+ }
341
+ ix = False;
342
+ printf("stopped\n");
343
+ }
344
+
345
+ static listMessages(AbstractDataProvider provider) {
346
+ hash<string, AbstractDataProviderType> messages = provider.getMessageTypes();
347
+ map printf(" - %s\n", $1), keys messages;
348
+ }
349
+
350
+ static message(AbstractDataProvider provider) {
351
+ string message = QdpCmd::getString("message", True);
352
+ QdpCmd::showType(provider.getMessageType(message));
353
+ }
354
+
287
355
static listen(AbstractDataProvider provider) {
288
356
provider.checkObservable();
289
357
MyObserver observer();
@@ -363,6 +431,13 @@ class QdpCmd {
363
431
map printf("%y\n", $1), i;
364
432
}
365
433
434
+ static sendMessage(AbstractDataProvider provider) {
435
+ string message_id = QdpCmd::getString("message_id", True);
436
+ auto val = QdpCmd::getAny("data");
437
+ *hash<auto> send_options = QdpCmd::getHash("send options");
438
+ provider.sendMessage(message_id, val, send_options);
439
+ }
440
+
366
441
static update(AbstractDataProvider provider) {
367
442
hash<auto> set = QdpCmd::getHash("update 'set'", True);
368
443
*hash<auto> where_cond = QdpCmd::getHash("update 'where'", True);
@@ -481,6 +556,14 @@ class QdpCmd {
481
556
}
482
557
}
483
558
559
+ static auto getAny(string action) {
560
+ auto arg = shift ARGV;
561
+ if (exists arg) {
562
+ arg = parse_to_qore_value(arg);
563
+ }
564
+ return arg;
565
+ }
566
+
484
567
private AbstractDataProvider getDataProvider(string name) {
485
568
if (name =~ /{([^}]*)}/) {
486
569
try {
@@ -572,17 +655,25 @@ class QdpCmd {
572
655
errors [<code>]
573
656
lists all error replies
574
657
event <event>
575
- shows the event type (if supported)
658
+ shows the event type
576
659
events
577
660
list all supported event types
578
661
info
579
662
show information about the data provider
663
+ interactive|ix
664
+ listen to events generated by an observable data provider and send response messages
580
665
list
581
666
list child data provider names
582
667
list-details|ldetails
583
668
list child data providers with details
669
+ list-events|levents
670
+ list event types
671
+ list-messages|lmessages|lmsgs
672
+ list message types
584
673
listen
585
674
listen to events generated by an observable data provider
675
+ message|msg <message>
676
+ shows the message type
586
677
provider-create|pcreate <name> <desc hash> [<option hash>]
587
678
create a new data provider
588
679
provider-delete|pdelete <name>
@@ -597,6 +688,8 @@ class QdpCmd {
597
688
executes a request and then a search on the results (if supported)
598
689
search [search criteria] (ex: search name=\"my name\")
599
690
search for record(s) matching the given criteria
691
+ send-message|send-msg|smsg <message id> [<message payload>] [<send options>]
692
+ sends a message
600
693
update <set criteria> <match criteria> (ex update name=other id=123)
601
694
update the given records with the given information
602
695
upsert <record> (ex update id=123,name=\"my name\")
@@ -671,7 +764,18 @@ class Term {
671
764
class MyObserver inherits Observer {
672
765
update(string event_id, hash<auto> data_) {
673
766
string eventstr = sprintf("%N", data_);
674
- stdout.printf("< %s received event %y:\n< %s\n", now_us().format("YYYY-MM-DD HH:mm:SS.xx"), event_id, eventstr);
767
+ string msg = sprintf("< %s received event %y:\n< %s\n", now_us().format("YYYY-MM-DD HH:mm:SS.xx"), event_id,
768
+ eventstr);
769
+ %ifndef NoLinenoise
770
+ msg =~ s/\n/\015\012/g;
771
+ %endif
772
+ if (QdpCmd::ix) {
773
+ stdout.print("\r");
774
+ }
775
+ stdout.print(msg);
776
+ if (QdpCmd::ix) {
777
+ stdout.print("> ");
778
+ }
675
779
stdout.sync();
676
780
}
677
781
}
@@ -684,8 +788,94 @@ class ConsoleAppender inherits LoggerAppenderWithLayout {
684
788
processEventImpl(int type, auto params) {
685
789
switch (type) {
686
790
case EVENT_LOG:
791
+ %ifndef NoLinenoise
792
+ params =~ s/\n/\r\n/g;
793
+ %endif
687
794
print(params);
688
795
break;
689
796
}
690
797
}
691
798
}
799
+
800
+ class InputHelper {
801
+ public {
802
+ # message map
803
+ hash<string, AbstractDataProviderType> mmap;
804
+
805
+ const Prompt = "> ";
806
+ const HistoryFile = ".qdp";
807
+
808
+ const Commands = {
809
+ "help": True,
810
+ "history": True,
811
+ "exit": True,
812
+ "quit": True,
813
+ };
814
+ }
815
+
816
+ constructor(AbstractDataProvider provider) {
817
+ mmap = provider.getMessageTypes();
818
+
819
+ %ifndef NoLinenoise
820
+ Linenoise::history_set_max_len(100);
821
+ Linenoise::set_callback(\completion());
822
+
823
+ try {
824
+ Linenoise::history_load(HistoryFile);
825
+ } catch (hash<ExceptionInfo> ex) {
826
+ printf("cannot load history: %s: %s\n", ex.err, ex.desc);
827
+ }
828
+ %endif
829
+ }
830
+
831
+ destructor() {
832
+ %ifndef NoLinenoise
833
+ Linenoise::history_save(HistoryFile);
834
+ %endif
835
+ }
836
+
837
+ string get() {
838
+ string line;
839
+ while (True) {
840
+ %ifndef NoLinenoise
841
+ *string line0 = Linenoise::line(Prompt);
842
+ if (!exists line0) {
843
+ line = "exit";
844
+ break;
845
+ }
846
+ line = line0;
847
+ Linenoise::history_add(line);
848
+ %else
849
+ stdout.print(Prompt);
850
+ stdout.sync();
851
+ line = trim(stdin.readLine());
852
+ %endif
853
+
854
+ if (line == 'help' || line == '?') {
855
+ printf("commands:\n help\n quit\n history\n");
856
+ continue;
857
+ }
858
+ %ifndef NoLinenoise
859
+ if (line == 'history') {
860
+ map printf("%s\n", $1), Linenoise::history();
861
+ continue;
862
+ }
863
+ %endif
864
+
865
+ if (!line.val()) {
866
+ continue;
867
+ }
868
+
869
+ break;
870
+ }
871
+
872
+ return line;
873
+ }
874
+
875
+ softlist<string> completion(string str) {
876
+ list<string> rv = ();
877
+ rv += map $1, keys Commands, $1.equalPartial(str);
878
+ rv += map $1 + ": ", keys mmap, $1.equalPartial(str);
879
+ return rv;
880
+ }
881
+ }
0 commit comments