annotate utilities/vampipe-convert.cpp @ 58:c38e12d4bbdd

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