annotate utilities/vampipe-convert.cpp @ 41:10ac36b7198a

Exception handling in adapter code
author Chris Cannam <c.cannam@qmul.ac.uk>
date Tue, 23 Aug 2016 11:17:01 +0100
parents 0b48b10140bb
children 91f5c92d3bf7
rev   line source
c@23 1
c@23 2 #include "VampJson.h"
c@23 3 #include "VampnProto.h"
c@23 4
c@31 5 #include "bits/RequestOrResponse.h"
c@32 6 #include "bits/PreservingPluginHandleMapper.h"
c@31 7
c@23 8 #include <iostream>
c@23 9 #include <sstream>
c@23 10 #include <stdexcept>
c@23 11
c@23 12 using namespace std;
c@23 13 using namespace json11;
c@23 14 using namespace vampipe;
c@23 15
c@23 16 void usage()
c@23 17 {
c@23 18 string myname = "vampipe-convert";
c@23 19 cerr << "\n" << myname <<
c@24 20 ": Validate and convert Vamp request and response messages\n\n"
c@24 21 " Usage: " << myname << " [-i <informat>] [-o <outformat>] request\n"
c@24 22 " " << myname << " [-i <informat>] [-o <outformat>] response\n\n"
c@24 23 " where\n"
c@24 24 " <informat>: the format to read from stdin\n"
c@24 25 " (\"json\" or \"capnp\", default is \"json\")\n"
c@24 26 " <outformat>: the format to convert to and write to stdout\n"
c@24 27 " (\"json\" or \"capnp\", default is \"json\")\n"
c@24 28 " request|response: whether to expect Vamp request or response messages\n\n"
c@24 29 "If <informat> and <outformat> differ, convert from <informat> to <outformat>.\n"
c@24 30 "If <informat> and <outformat> are the same, just check validity of incoming\n"
c@24 31 "messages and pass them to output.\n\n";
c@24 32
c@23 33 exit(2);
c@23 34 }
c@23 35
c@24 36 Json
c@24 37 convertRequestJson(string input)
c@24 38 {
c@24 39 string err;
c@24 40 Json j = Json::parse(input, err);
c@24 41 if (err != "") {
c@24 42 throw VampJson::Failure("invalid json: " + err);
c@24 43 }
c@24 44 if (!j.is_object()) {
c@24 45 throw VampJson::Failure("object expected at top level");
c@24 46 }
c@24 47 if (!j["type"].is_string()) {
c@24 48 throw VampJson::Failure("string expected for type field");
c@24 49 }
c@24 50 if (!j["content"].is_object()) {
c@24 51 throw VampJson::Failure("object expected for content field");
c@24 52 }
c@24 53 return j;
c@24 54 }
c@24 55
c@24 56 Json
c@24 57 convertResponseJson(string input)
c@24 58 {
c@24 59 string err;
c@24 60 Json j = Json::parse(input, err);
c@24 61 if (err != "") {
c@24 62 throw VampJson::Failure("invalid json: " + err);
c@24 63 }
c@24 64 if (!j.is_object()) {
c@24 65 throw VampJson::Failure("object expected at top level");
c@24 66 }
c@24 67 if (!j["success"].is_bool()) {
c@24 68 throw VampJson::Failure("bool expected for success field");
c@24 69 }
c@24 70 if (!j["content"].is_object()) {
c@24 71 throw VampJson::Failure("object expected for content field");
c@24 72 }
c@24 73 return j;
c@24 74 }
c@24 75
c@30 76 //!!! Lots of potential for refactoring the conversion classes based
c@30 77 //!!! on the common matter in the following eight functions...
c@30 78
c@32 79 PreservingPluginHandleMapper mapper;
c@32 80
c@23 81 RequestOrResponse
c@24 82 readRequestJson()
c@23 83 {
c@23 84 RequestOrResponse rr;
c@24 85 rr.direction = RequestOrResponse::Request;
c@24 86
c@24 87 string input;
c@24 88 if (!getline(cin, input)) {
c@25 89 rr.type = RRType::NotValid;
c@24 90 return rr;
c@24 91 }
c@24 92
c@24 93 Json j = convertRequestJson(input);
c@27 94
c@25 95 rr.type = VampJson::getRequestResponseType(j);
c@24 96
c@27 97 switch (rr.type) {
c@27 98
c@27 99 case RRType::List:
c@27 100 VampJson::toVampRequest_List(j); // type check only
c@27 101 break;
c@27 102 case RRType::Load:
c@24 103 rr.loadRequest = VampJson::toVampRequest_Load(j);
c@27 104 break;
c@27 105 case RRType::Configure:
c@32 106 rr.configurationRequest = VampJson::toVampRequest_Configure(j, mapper);
c@27 107 break;
c@27 108 case RRType::Process:
c@32 109 rr.processRequest = VampJson::toVampRequest_Process(j, mapper);
c@27 110 break;
c@27 111 case RRType::Finish:
c@32 112 rr.finishPlugin = VampJson::toVampRequest_Finish(j, mapper);
c@27 113 break;
c@27 114 case RRType::NotValid:
c@27 115 break;
c@24 116 }
c@24 117
c@24 118 return rr;
c@24 119 }
c@24 120
c@24 121 void
c@24 122 writeRequestJson(RequestOrResponse &rr)
c@24 123 {
c@24 124 Json j;
c@24 125
c@27 126 switch (rr.type) {
c@27 127
c@27 128 case RRType::List:
c@24 129 j = VampJson::fromVampRequest_List();
c@27 130 break;
c@27 131 case RRType::Load:
c@24 132 j = VampJson::fromVampRequest_Load(rr.loadRequest);
c@27 133 break;
c@27 134 case RRType::Configure:
c@32 135 j = VampJson::fromVampRequest_Configure(rr.configurationRequest, mapper);
c@27 136 break;
c@27 137 case RRType::Process:
c@32 138 j = VampJson::fromVampRequest_Process(rr.processRequest, mapper);
c@27 139 break;
c@27 140 case RRType::Finish:
c@32 141 j = VampJson::fromVampRequest_Finish(rr.finishPlugin, mapper);
c@27 142 break;
c@27 143 case RRType::NotValid:
c@27 144 break;
c@24 145 }
c@24 146
c@24 147 cout << j.dump() << endl;
c@24 148 }
c@24 149
c@24 150 RequestOrResponse
c@24 151 readResponseJson()
c@24 152 {
c@24 153 RequestOrResponse rr;
c@24 154 rr.direction = RequestOrResponse::Response;
c@24 155
c@23 156 string input;
c@23 157 if (!getline(cin, input)) {
c@25 158 rr.type = RRType::NotValid;
c@23 159 return rr;
c@23 160 }
c@23 161
c@24 162 Json j = convertResponseJson(input);
c@27 163
c@25 164 rr.type = VampJson::getRequestResponseType(j);
c@23 165
c@27 166 switch (rr.type) {
c@27 167
c@27 168 case RRType::List:
c@24 169 rr.listResponse = VampJson::toVampResponse_List(j);
c@27 170 break;
c@27 171 case RRType::Load:
c@32 172 rr.loadResponse = VampJson::toVampResponse_Load(j, mapper);
c@27 173 break;
c@27 174 case RRType::Configure:
c@24 175 rr.configurationResponse = VampJson::toVampResponse_Configure(j);
c@27 176 break;
c@27 177 case RRType::Process:
c@24 178 rr.processResponse = VampJson::toVampResponse_Process(j);
c@27 179 break;
c@27 180 case RRType::Finish:
c@24 181 rr.finishResponse = VampJson::toVampResponse_Finish(j);
c@27 182 break;
c@27 183 case RRType::NotValid:
c@27 184 break;
c@23 185 }
c@23 186
c@23 187 return rr;
c@23 188 }
c@23 189
c@24 190 void
c@24 191 writeResponseJson(RequestOrResponse &rr)
c@24 192 {
c@24 193 Json j;
c@24 194
c@27 195 switch (rr.type) {
c@27 196
c@27 197 case RRType::List:
c@24 198 j = VampJson::fromVampResponse_List("", rr.listResponse);
c@27 199 break;
c@27 200 case RRType::Load:
c@32 201 j = VampJson::fromVampResponse_Load(rr.loadResponse, mapper);
c@27 202 break;
c@27 203 case RRType::Configure:
c@27 204 j = VampJson::fromVampResponse_Configure(rr.configurationResponse);
c@27 205 break;
c@27 206 case RRType::Process:
c@27 207 j = VampJson::fromVampResponse_Process(rr.processResponse);
c@27 208 break;
c@27 209 case RRType::Finish:
c@27 210 j = VampJson::fromVampResponse_Finish(rr.finishResponse);
c@27 211 break;
c@27 212 case RRType::NotValid:
c@27 213 break;
c@24 214 }
c@24 215
c@27 216 cout << j.dump() << endl;
c@27 217 }
c@25 218
c@27 219 RequestOrResponse
c@33 220 readRequestCapnp(kj::BufferedInputStreamWrapper &buffered)
c@27 221 {
c@27 222 RequestOrResponse rr;
c@27 223 rr.direction = RequestOrResponse::Request;
c@27 224
c@33 225 ::capnp::InputStreamMessageReader message(buffered);
c@27 226 VampRequest::Reader reader = message.getRoot<VampRequest>();
c@27 227
c@27 228 rr.type = VampnProto::getRequestResponseType(reader);
c@27 229
c@27 230 switch (rr.type) {
c@27 231
c@27 232 case RRType::List:
c@27 233 VampnProto::readVampRequest_List(reader); // type check only
c@27 234 break;
c@27 235 case RRType::Load:
c@27 236 VampnProto::readVampRequest_Load(rr.loadRequest, reader);
c@27 237 break;
c@27 238 case RRType::Configure:
c@32 239 VampnProto::readVampRequest_Configure(rr.configurationRequest,
c@32 240 reader, mapper);
c@27 241 break;
c@27 242 case RRType::Process:
c@32 243 VampnProto::readVampRequest_Process(rr.processRequest, reader, mapper);
c@27 244 break;
c@27 245 case RRType::Finish:
c@32 246 VampnProto::readVampRequest_Finish(rr.finishPlugin, reader, mapper);
c@27 247 break;
c@27 248 case RRType::NotValid:
c@27 249 break;
c@27 250 }
c@27 251
c@27 252 return rr;
c@27 253 }
c@27 254
c@29 255 void
c@29 256 writeRequestCapnp(RequestOrResponse &rr)
c@29 257 {
c@29 258 ::capnp::MallocMessageBuilder message;
c@29 259 VampRequest::Builder builder = message.initRoot<VampRequest>();
c@29 260
c@29 261 switch (rr.type) {
c@29 262
c@29 263 case RRType::List:
c@29 264 VampnProto::buildVampRequest_List(builder);
c@29 265 break;
c@29 266 case RRType::Load:
c@29 267 VampnProto::buildVampRequest_Load(builder, rr.loadRequest);
c@29 268 break;
c@29 269 case RRType::Configure:
c@29 270 VampnProto::buildVampRequest_Configure(builder,
c@32 271 rr.configurationRequest, mapper);
c@29 272 break;
c@29 273 case RRType::Process:
c@32 274 VampnProto::buildVampRequest_Process(builder, rr.processRequest, mapper);
c@29 275 break;
c@29 276 case RRType::Finish:
c@32 277 VampnProto::buildVampRequest_Finish(builder, rr.finishPlugin, mapper);
c@29 278 break;
c@29 279 case RRType::NotValid:
c@29 280 break;
c@29 281 }
c@29 282
c@33 283 writeMessageToFd(1, message);
c@29 284 }
c@29 285
c@27 286 RequestOrResponse
c@33 287 readResponseCapnp(kj::BufferedInputStreamWrapper &buffered)
c@27 288 {
c@27 289 RequestOrResponse rr;
c@27 290 rr.direction = RequestOrResponse::Response;
c@27 291
c@33 292 ::capnp::InputStreamMessageReader message(buffered);
c@27 293 VampResponse::Reader reader = message.getRoot<VampResponse>();
c@27 294
c@27 295 rr.type = VampnProto::getRequestResponseType(reader);
c@27 296
c@27 297 switch (rr.type) {
c@27 298
c@27 299 case RRType::List:
c@27 300 VampnProto::readVampResponse_List(rr.listResponse, reader);
c@27 301 break;
c@27 302 case RRType::Load:
c@32 303 VampnProto::readVampResponse_Load(rr.loadResponse, reader, mapper);
c@27 304 break;
c@27 305 case RRType::Configure:
c@27 306 VampnProto::readVampResponse_Configure(rr.configurationResponse,
c@27 307 reader);
c@27 308 break;
c@27 309 case RRType::Process:
c@27 310 VampnProto::readVampResponse_Process(rr.processResponse, reader);
c@27 311 break;
c@27 312 case RRType::Finish:
c@27 313 VampnProto::readVampResponse_Finish(rr.finishResponse, reader);
c@27 314 break;
c@27 315 case RRType::NotValid:
c@27 316 break;
c@27 317 }
c@27 318
c@27 319 return rr;
c@24 320 }
c@24 321
c@29 322 void
c@29 323 writeResponseCapnp(RequestOrResponse &rr)
c@29 324 {
c@29 325 ::capnp::MallocMessageBuilder message;
c@29 326 VampResponse::Builder builder = message.initRoot<VampResponse>();
c@29 327
c@29 328 switch (rr.type) {
c@29 329
c@29 330 case RRType::List:
c@29 331 VampnProto::buildVampResponse_List(builder, "", rr.listResponse);
c@29 332 break;
c@29 333 case RRType::Load:
c@32 334 VampnProto::buildVampResponse_Load(builder, rr.loadResponse, mapper);
c@29 335 break;
c@29 336 case RRType::Configure:
c@29 337 VampnProto::buildVampResponse_Configure(builder, rr.configurationResponse);
c@29 338 break;
c@29 339 case RRType::Process:
c@29 340 VampnProto::buildVampResponse_Process(builder, rr.processResponse);
c@29 341 break;
c@29 342 case RRType::Finish:
c@29 343 VampnProto::buildVampResponse_Finish(builder, rr.finishResponse);
c@29 344 break;
c@29 345 case RRType::NotValid:
c@29 346 break;
c@29 347 }
c@29 348
c@33 349 writeMessageToFd(1, message);
c@33 350 }
c@33 351
c@33 352 RequestOrResponse
c@33 353 readInputJson(RequestOrResponse::Direction direction)
c@33 354 {
c@33 355 if (direction == RequestOrResponse::Request) {
c@33 356 return readRequestJson();
c@33 357 } else {
c@33 358 return readResponseJson();
c@33 359 }
c@33 360 }
c@33 361
c@33 362 RequestOrResponse
c@33 363 readInputCapnp(RequestOrResponse::Direction direction)
c@33 364 {
c@33 365 static kj::FdInputStream stream(0); // stdin
c@33 366 static kj::BufferedInputStreamWrapper buffered(stream);
c@33 367
c@33 368 if (buffered.tryGetReadBuffer() == nullptr) {
c@33 369 return {};
c@33 370 }
c@33 371
c@33 372 if (direction == RequestOrResponse::Request) {
c@33 373 return readRequestCapnp(buffered);
c@33 374 } else {
c@33 375 return readResponseCapnp(buffered);
c@33 376 }
c@29 377 }
c@29 378
c@23 379 RequestOrResponse
c@24 380 readInput(string format, RequestOrResponse::Direction direction)
c@23 381 {
c@23 382 if (format == "json") {
c@33 383 return readInputJson(direction);
c@27 384 } else if (format == "capnp") {
c@33 385 return readInputCapnp(direction);
c@23 386 } else {
c@27 387 throw runtime_error("unknown input format \"" + format + "\"");
c@23 388 }
c@23 389 }
c@23 390
c@23 391 void
c@24 392 writeOutput(string format, RequestOrResponse &rr)
c@23 393 {
c@24 394 if (format == "json") {
c@24 395 if (rr.direction == RequestOrResponse::Request) {
c@24 396 writeRequestJson(rr);
c@24 397 } else {
c@24 398 writeResponseJson(rr);
c@24 399 }
c@29 400 } else if (format == "capnp") {
c@29 401 if (rr.direction == RequestOrResponse::Request) {
c@29 402 writeRequestCapnp(rr);
c@29 403 } else {
c@29 404 writeResponseCapnp(rr);
c@29 405 }
c@24 406 } else {
c@27 407 throw runtime_error("unknown output format \"" + format + "\"");
c@24 408 }
c@23 409 }
c@23 410
c@23 411 int main(int argc, char **argv)
c@23 412 {
c@24 413 if (argc < 2) {
c@23 414 usage();
c@23 415 }
c@23 416
c@24 417 string informat = "json", outformat = "json";
c@27 418 RequestOrResponse::Direction direction = RequestOrResponse::Request;
c@24 419 bool haveDirection = false;
c@23 420
c@24 421 for (int i = 1; i < argc; ++i) {
c@23 422
c@23 423 string arg = argv[i];
c@24 424 bool final = (i + 1 == argc);
c@23 425
c@23 426 if (arg == "-i") {
c@27 427 if (final) usage();
c@23 428 else informat = argv[++i];
c@23 429
c@23 430 } else if (arg == "-o") {
c@27 431 if (final) usage();
c@23 432 else outformat = argv[++i];
c@23 433
c@24 434 } else if (arg == "request") {
c@24 435 direction = RequestOrResponse::Request;
c@24 436 haveDirection = true;
c@24 437
c@24 438 } else if (arg == "response") {
c@24 439 direction = RequestOrResponse::Response;
c@24 440 haveDirection = true;
c@24 441
c@23 442 } else {
c@23 443 usage();
c@23 444 }
c@23 445 }
c@23 446
c@24 447 if (informat == "" || outformat == "" || !haveDirection) {
c@23 448 usage();
c@23 449 }
c@23 450
c@23 451 while (true) {
c@23 452
c@23 453 try {
c@23 454
c@24 455 RequestOrResponse rr = readInput(informat, direction);
c@29 456
c@29 457 // NotValid without an exception indicates EOF:
c@25 458 if (rr.type == RRType::NotValid) break;
c@29 459
c@23 460 writeOutput(outformat, rr);
c@23 461
c@23 462 } catch (std::exception &e) {
c@23 463 cerr << "Error: " << e.what() << endl;
c@23 464 exit(1);
c@23 465 }
c@23 466 }
c@23 467
c@23 468 exit(0);
c@23 469 }
c@23 470
c@23 471