annotate utilities/vampipe-convert.cpp @ 67:db17657ac875

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