annotate utilities/vampipe-convert.cpp @ 66:6f160dee1192

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