comparison vamp-server/convert.cpp @ 75:81e1c48e97f9

Rearrange and rename to Piper C++ structure
author Chris Cannam <c.cannam@qmul.ac.uk>
date Mon, 10 Oct 2016 16:31:09 +0100
parents
children c897c9a8daf1
comparison
equal deleted inserted replaced
74:d45cfa25aaad 75:81e1c48e97f9
1
2 #include "vamp-json/VampJson.h"
3 #include "vamp-capnp/VampnProto.h"
4 #include "vamp-support/RequestOrResponse.h"
5 #include "vamp-support/PreservingPluginHandleMapper.h"
6
7 #include <iostream>
8 #include <sstream>
9 #include <stdexcept>
10
11 using namespace std;
12 using namespace json11;
13 using namespace piper;
14
15 void usage()
16 {
17 string myname = "piper-convert";
18 cerr << "\n" << myname <<
19 ": Validate and convert Piper request and response messages\n\n"
20 " Usage: " << myname << " [-i <informat>] [-o <outformat>] request\n"
21 " " << myname << " [-i <informat>] [-o <outformat>] response\n\n"
22 " where\n"
23 " <informat>: the format to read from stdin\n"
24 " (\"json\" or \"capnp\", default is \"json\")\n"
25 " <outformat>: the format to convert to and write to stdout\n"
26 " (\"json\", \"json-b64\" or \"capnp\", default is \"json\")\n"
27 " request|response: whether messages are Vamp request or response type\n\n"
28 "If <informat> and <outformat> differ, convert from <informat> to <outformat>.\n"
29 "If <informat> and <outformat> are the same, just check validity of incoming\n"
30 "messages and pass them to output.\n\n"
31 "Specifying \"json-b64\" as output format forces base64 encoding for process and\n"
32 "feature blocks, unlike the \"json\" output format which uses text encoding.\n"
33 "The \"json\" input format accepts either.\n\n";
34
35 exit(2);
36 }
37
38 Json
39 convertRequestJson(string input, string &err)
40 {
41 Json j = Json::parse(input, err);
42 if (err != "") {
43 err = "invalid json: " + err;
44 return {};
45 }
46 if (!j.is_object()) {
47 err = "object expected at top level";
48 } else if (!j["method"].is_string()) {
49 err = "string expected for method field";
50 } else if (!j["params"].is_null() && !j["params"].is_object()) {
51 err = "object expected for params field";
52 }
53 return j;
54 }
55
56 Json
57 convertResponseJson(string input, string &err)
58 {
59 Json j = Json::parse(input, err);
60 if (err != "") {
61 err = "invalid json: " + err;
62 return {};
63 }
64 if (!j.is_object()) {
65 err = "object expected at top level";
66 } else {
67 if (!j["result"].is_object()) {
68 if (!j["error"].is_object()) {
69 err = "expected either result or error object";
70 }
71 }
72 }
73 return j;
74 }
75
76 //!!! Lots of potential for refactoring the conversion classes based
77 //!!! on the common matter in the following eight functions...
78
79 PreservingPluginHandleMapper mapper;
80
81 static RequestOrResponse::RpcId
82 readJsonId(const Json &j)
83 {
84 RequestOrResponse::RpcId id;
85
86 if (j["id"].is_number()) {
87 id.type = RequestOrResponse::RpcId::Number;
88 id.number = j["id"].number_value();
89 } else if (j["id"].is_string()) {
90 id.type = RequestOrResponse::RpcId::Tag;
91 id.tag = j["id"].string_value();
92 } else {
93 id.type = RequestOrResponse::RpcId::Absent;
94 }
95
96 return id;
97 }
98
99 static Json
100 writeJsonId(const RequestOrResponse::RpcId &id)
101 {
102 if (id.type == RequestOrResponse::RpcId::Number) {
103 return id.number;
104 } else if (id.type == RequestOrResponse::RpcId::Tag) {
105 return id.tag;
106 } else {
107 return Json();
108 }
109 }
110
111 template <typename Reader>
112 static RequestOrResponse::RpcId
113 readCapnpId(const Reader &r)
114 {
115 int number;
116 string tag;
117 switch (r.getId().which()) {
118 case RpcRequest::Id::Which::NUMBER:
119 number = r.getId().getNumber();
120 return { RequestOrResponse::RpcId::Number, number, "" };
121 case RpcRequest::Id::Which::TAG:
122 tag = r.getId().getTag();
123 return { RequestOrResponse::RpcId::Tag, 0, tag };
124 case RpcRequest::Id::Which::NONE:
125 return { RequestOrResponse::RpcId::Absent, 0, "" };
126 }
127 return {};
128 }
129
130 template <typename Builder>
131 static void
132 buildCapnpId(Builder &b, const RequestOrResponse::RpcId &id)
133 {
134 switch (id.type) {
135 case RequestOrResponse::RpcId::Number:
136 b.getId().setNumber(id.number);
137 break;
138 case RequestOrResponse::RpcId::Tag:
139 b.getId().setTag(id.tag);
140 break;
141 case RequestOrResponse::RpcId::Absent:
142 b.getId().setNone();
143 break;
144 }
145 }
146
147 RequestOrResponse
148 readRequestJson(string &err)
149 {
150 RequestOrResponse rr;
151 rr.direction = RequestOrResponse::Request;
152
153 string input;
154 if (!getline(cin, input)) {
155 // the EOF case, not actually an error
156 rr.type = RRType::NotValid;
157 return rr;
158 }
159
160 Json j = convertRequestJson(input, err);
161 if (err != "") return {};
162
163 rr.type = VampJson::getRequestResponseType(j, err);
164 if (err != "") return {};
165
166 rr.id = readJsonId(j);
167
168 VampJson::BufferSerialisation serialisation =
169 VampJson::BufferSerialisation::Array;
170
171 switch (rr.type) {
172
173 case RRType::List:
174 VampJson::toRpcRequest_List(j, err); // type check only
175 break;
176 case RRType::Load:
177 rr.loadRequest = VampJson::toRpcRequest_Load(j, err);
178 break;
179 case RRType::Configure:
180 rr.configurationRequest = VampJson::toRpcRequest_Configure(j, mapper, err);
181 break;
182 case RRType::Process:
183 rr.processRequest = VampJson::toRpcRequest_Process(j, mapper, serialisation, err);
184 break;
185 case RRType::Finish:
186 rr.finishRequest = VampJson::toRpcRequest_Finish(j, mapper, err);
187 break;
188 case RRType::NotValid:
189 break;
190 }
191
192 return rr;
193 }
194
195 void
196 writeRequestJson(RequestOrResponse &rr, bool useBase64)
197 {
198 Json j;
199
200 VampJson::BufferSerialisation serialisation =
201 (useBase64 ?
202 VampJson::BufferSerialisation::Base64 :
203 VampJson::BufferSerialisation::Array);
204
205 Json id = writeJsonId(rr.id);
206
207 switch (rr.type) {
208
209 case RRType::List:
210 j = VampJson::fromRpcRequest_List(id);
211 break;
212 case RRType::Load:
213 j = VampJson::fromRpcRequest_Load(rr.loadRequest, id);
214 break;
215 case RRType::Configure:
216 j = VampJson::fromRpcRequest_Configure(rr.configurationRequest, mapper, id);
217 break;
218 case RRType::Process:
219 j = VampJson::fromRpcRequest_Process
220 (rr.processRequest, mapper, serialisation, id);
221 break;
222 case RRType::Finish:
223 j = VampJson::fromRpcRequest_Finish(rr.finishRequest, mapper, id);
224 break;
225 case RRType::NotValid:
226 break;
227 }
228
229 cout << j.dump() << endl;
230 }
231
232 RequestOrResponse
233 readResponseJson(string &err)
234 {
235 RequestOrResponse rr;
236 rr.direction = RequestOrResponse::Response;
237
238 string input;
239 if (!getline(cin, input)) {
240 // the EOF case, not actually an error
241 rr.type = RRType::NotValid;
242 return rr;
243 }
244
245 Json j = convertResponseJson(input, err);
246 if (err != "") return {};
247
248 rr.type = VampJson::getRequestResponseType(j, err);
249 if (err != "") return {};
250
251 rr.id = readJsonId(j);
252
253 VampJson::BufferSerialisation serialisation =
254 VampJson::BufferSerialisation::Array;
255
256 rr.success = j["success"].bool_value();
257 rr.errorText = j["errorText"].string_value();
258
259 switch (rr.type) {
260
261 case RRType::List:
262 rr.listResponse = VampJson::toRpcResponse_List(j, err);
263 break;
264 case RRType::Load:
265 rr.loadResponse = VampJson::toRpcResponse_Load(j, mapper, err);
266 break;
267 case RRType::Configure:
268 rr.configurationResponse = VampJson::toRpcResponse_Configure(j, mapper, err);
269 break;
270 case RRType::Process:
271 rr.processResponse = VampJson::toRpcResponse_Process(j, mapper, serialisation, err);
272 break;
273 case RRType::Finish:
274 rr.finishResponse = VampJson::toRpcResponse_Finish(j, mapper, serialisation, err);
275 break;
276 case RRType::NotValid:
277 break;
278 }
279
280 return rr;
281 }
282
283 void
284 writeResponseJson(RequestOrResponse &rr, bool useBase64)
285 {
286 Json j;
287
288 VampJson::BufferSerialisation serialisation =
289 (useBase64 ?
290 VampJson::BufferSerialisation::Base64 :
291 VampJson::BufferSerialisation::Array);
292
293 Json id = writeJsonId(rr.id);
294
295 if (!rr.success) {
296
297 j = VampJson::fromError(rr.errorText, rr.type, id);
298
299 } else {
300
301 switch (rr.type) {
302
303 case RRType::List:
304 j = VampJson::fromRpcResponse_List(rr.listResponse, id);
305 break;
306 case RRType::Load:
307 j = VampJson::fromRpcResponse_Load(rr.loadResponse, mapper, id);
308 break;
309 case RRType::Configure:
310 j = VampJson::fromRpcResponse_Configure(rr.configurationResponse,
311 mapper, id);
312 break;
313 case RRType::Process:
314 j = VampJson::fromRpcResponse_Process
315 (rr.processResponse, mapper, serialisation, id);
316 break;
317 case RRType::Finish:
318 j = VampJson::fromRpcResponse_Finish
319 (rr.finishResponse, mapper, serialisation, id);
320 break;
321 case RRType::NotValid:
322 break;
323 }
324 }
325
326 cout << j.dump() << endl;
327 }
328
329 RequestOrResponse
330 readRequestCapnp(kj::BufferedInputStreamWrapper &buffered)
331 {
332 RequestOrResponse rr;
333 rr.direction = RequestOrResponse::Request;
334
335 ::capnp::InputStreamMessageReader message(buffered);
336 RpcRequest::Reader reader = message.getRoot<RpcRequest>();
337
338 rr.type = VampnProto::getRequestResponseType(reader);
339 rr.id = readCapnpId(reader);
340
341 switch (rr.type) {
342
343 case RRType::List:
344 VampnProto::readRpcRequest_List(reader); // type check only
345 break;
346 case RRType::Load:
347 VampnProto::readRpcRequest_Load(rr.loadRequest, reader);
348 break;
349 case RRType::Configure:
350 VampnProto::readRpcRequest_Configure(rr.configurationRequest,
351 reader, mapper);
352 break;
353 case RRType::Process:
354 VampnProto::readRpcRequest_Process(rr.processRequest, reader, mapper);
355 break;
356 case RRType::Finish:
357 VampnProto::readRpcRequest_Finish(rr.finishRequest, reader, mapper);
358 break;
359 case RRType::NotValid:
360 break;
361 }
362
363 return rr;
364 }
365
366 void
367 writeRequestCapnp(RequestOrResponse &rr)
368 {
369 ::capnp::MallocMessageBuilder message;
370 RpcRequest::Builder builder = message.initRoot<RpcRequest>();
371
372 buildCapnpId(builder, rr.id);
373
374 switch (rr.type) {
375
376 case RRType::List:
377 VampnProto::buildRpcRequest_List(builder);
378 break;
379 case RRType::Load:
380 VampnProto::buildRpcRequest_Load(builder, rr.loadRequest);
381 break;
382 case RRType::Configure:
383 VampnProto::buildRpcRequest_Configure(builder,
384 rr.configurationRequest, mapper);
385 break;
386 case RRType::Process:
387 VampnProto::buildRpcRequest_Process(builder, rr.processRequest, mapper);
388 break;
389 case RRType::Finish:
390 VampnProto::buildRpcRequest_Finish(builder, rr.finishRequest, mapper);
391 break;
392 case RRType::NotValid:
393 break;
394 }
395
396 writeMessageToFd(1, message);
397 }
398
399 RequestOrResponse
400 readResponseCapnp(kj::BufferedInputStreamWrapper &buffered)
401 {
402 RequestOrResponse rr;
403 rr.direction = RequestOrResponse::Response;
404
405 ::capnp::InputStreamMessageReader message(buffered);
406 RpcResponse::Reader reader = message.getRoot<RpcResponse>();
407
408 rr.type = VampnProto::getRequestResponseType(reader);
409 rr.success = true;
410 rr.errorText = "";
411 rr.id = readCapnpId(reader);
412 int errorCode = 0;
413
414 switch (rr.type) {
415
416 case RRType::List:
417 VampnProto::readRpcResponse_List(rr.listResponse, reader);
418 break;
419 case RRType::Load:
420 VampnProto::readRpcResponse_Load(rr.loadResponse, reader, mapper);
421 break;
422 case RRType::Configure:
423 VampnProto::readRpcResponse_Configure(rr.configurationResponse,
424 reader, mapper);
425 break;
426 case RRType::Process:
427 VampnProto::readRpcResponse_Process(rr.processResponse, reader, mapper);
428 break;
429 case RRType::Finish:
430 VampnProto::readRpcResponse_Finish(rr.finishResponse, reader, mapper);
431 break;
432 case RRType::NotValid:
433 // error
434 rr.success = false;
435 VampnProto::readRpcResponse_Error(errorCode, rr.errorText, reader);
436 break;
437 }
438
439 return rr;
440 }
441
442 void
443 writeResponseCapnp(RequestOrResponse &rr)
444 {
445 ::capnp::MallocMessageBuilder message;
446 RpcResponse::Builder builder = message.initRoot<RpcResponse>();
447
448 buildCapnpId(builder, rr.id);
449
450 if (!rr.success) {
451
452 VampnProto::buildRpcResponse_Error(builder, rr.errorText, rr.type);
453
454 } else {
455
456 switch (rr.type) {
457
458 case RRType::List:
459 VampnProto::buildRpcResponse_List(builder, rr.listResponse);
460 break;
461 case RRType::Load:
462 VampnProto::buildRpcResponse_Load(builder, rr.loadResponse, mapper);
463 break;
464 case RRType::Configure:
465 VampnProto::buildRpcResponse_Configure(builder, rr.configurationResponse, mapper);
466 break;
467 case RRType::Process:
468 VampnProto::buildRpcResponse_Process(builder, rr.processResponse, mapper);
469 break;
470 case RRType::Finish:
471 VampnProto::buildRpcResponse_Finish(builder, rr.finishResponse, mapper);
472 break;
473 case RRType::NotValid:
474 break;
475 }
476 }
477
478 writeMessageToFd(1, message);
479 }
480
481 RequestOrResponse
482 readInputJson(RequestOrResponse::Direction direction, string &err)
483 {
484 if (direction == RequestOrResponse::Request) {
485 return readRequestJson(err);
486 } else {
487 return readResponseJson(err);
488 }
489 }
490
491 RequestOrResponse
492 readInputCapnp(RequestOrResponse::Direction direction)
493 {
494 static kj::FdInputStream stream(0); // stdin
495 static kj::BufferedInputStreamWrapper buffered(stream);
496
497 if (buffered.tryGetReadBuffer() == nullptr) {
498 return {};
499 }
500
501 if (direction == RequestOrResponse::Request) {
502 return readRequestCapnp(buffered);
503 } else {
504 return readResponseCapnp(buffered);
505 }
506 }
507
508 RequestOrResponse
509 readInput(string format, RequestOrResponse::Direction direction)
510 {
511 if (format == "json") {
512 string err;
513 auto result = readInputJson(direction, err);
514 if (err != "") throw runtime_error(err);
515 else return result;
516 } else if (format == "capnp") {
517 return readInputCapnp(direction);
518 } else {
519 throw runtime_error("unknown input format \"" + format + "\"");
520 }
521 }
522
523 void
524 writeOutput(string format, RequestOrResponse &rr)
525 {
526 if (format == "json") {
527 if (rr.direction == RequestOrResponse::Request) {
528 writeRequestJson(rr, false);
529 } else {
530 writeResponseJson(rr, false);
531 }
532 } else if (format == "json-b64") {
533 if (rr.direction == RequestOrResponse::Request) {
534 writeRequestJson(rr, true);
535 } else {
536 writeResponseJson(rr, true);
537 }
538 } else if (format == "capnp") {
539 if (rr.direction == RequestOrResponse::Request) {
540 writeRequestCapnp(rr);
541 } else {
542 writeResponseCapnp(rr);
543 }
544 } else {
545 throw runtime_error("unknown output format \"" + format + "\"");
546 }
547 }
548
549 int main(int argc, char **argv)
550 {
551 if (argc < 2) {
552 usage();
553 }
554
555 string informat = "json", outformat = "json";
556 RequestOrResponse::Direction direction = RequestOrResponse::Request;
557 bool haveDirection = false;
558
559 for (int i = 1; i < argc; ++i) {
560
561 string arg = argv[i];
562 bool final = (i + 1 == argc);
563
564 if (arg == "-i") {
565 if (final) usage();
566 else informat = argv[++i];
567
568 } else if (arg == "-o") {
569 if (final) usage();
570 else outformat = argv[++i];
571
572 } else if (arg == "request") {
573 direction = RequestOrResponse::Request;
574 haveDirection = true;
575
576 } else if (arg == "response") {
577 direction = RequestOrResponse::Response;
578 haveDirection = true;
579
580 } else {
581 usage();
582 }
583 }
584
585 if (informat == "" || outformat == "" || !haveDirection) {
586 usage();
587 }
588
589 while (true) {
590
591 try {
592
593 RequestOrResponse rr = readInput(informat, direction);
594
595 // NotValid without an exception indicates EOF:
596 if (rr.type == RRType::NotValid) break;
597
598 writeOutput(outformat, rr);
599
600 } catch (std::exception &e) {
601
602 cerr << "Error: " << e.what() << endl;
603 exit(1);
604 }
605 }
606
607 exit(0);
608 }
609
610