Mercurial > hg > piper-cpp
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 |