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@32
|
116 rr.finishPlugin = 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@27
|
130 switch (rr.type) {
|
c@27
|
131
|
c@27
|
132 case RRType::List:
|
c@24
|
133 j = VampJson::fromVampRequest_List();
|
c@27
|
134 break;
|
c@27
|
135 case RRType::Load:
|
c@24
|
136 j = VampJson::fromVampRequest_Load(rr.loadRequest);
|
c@27
|
137 break;
|
c@27
|
138 case RRType::Configure:
|
c@32
|
139 j = VampJson::fromVampRequest_Configure(rr.configurationRequest, mapper);
|
c@27
|
140 break;
|
c@27
|
141 case RRType::Process:
|
c@44
|
142 j = VampJson::fromVampRequest_Process
|
c@44
|
143 (rr.processRequest, mapper,
|
c@44
|
144 useBase64 ?
|
c@44
|
145 VampJson::BufferSerialisation::Base64 :
|
c@44
|
146 VampJson::BufferSerialisation::Text);
|
c@27
|
147 break;
|
c@27
|
148 case RRType::Finish:
|
c@32
|
149 j = VampJson::fromVampRequest_Finish(rr.finishPlugin, mapper);
|
c@27
|
150 break;
|
c@27
|
151 case RRType::NotValid:
|
c@27
|
152 break;
|
c@24
|
153 }
|
c@24
|
154
|
c@24
|
155 cout << j.dump() << endl;
|
c@24
|
156 }
|
c@24
|
157
|
c@24
|
158 RequestOrResponse
|
c@24
|
159 readResponseJson()
|
c@24
|
160 {
|
c@24
|
161 RequestOrResponse rr;
|
c@24
|
162 rr.direction = RequestOrResponse::Response;
|
c@24
|
163
|
c@23
|
164 string input;
|
c@23
|
165 if (!getline(cin, input)) {
|
c@25
|
166 rr.type = RRType::NotValid;
|
c@23
|
167 return rr;
|
c@23
|
168 }
|
c@23
|
169
|
c@24
|
170 Json j = convertResponseJson(input);
|
c@27
|
171
|
c@25
|
172 rr.type = VampJson::getRequestResponseType(j);
|
c@44
|
173 VampJson::BufferSerialisation serialisation = VampJson::BufferSerialisation::Text;
|
c@23
|
174
|
c@27
|
175 switch (rr.type) {
|
c@27
|
176
|
c@27
|
177 case RRType::List:
|
c@24
|
178 rr.listResponse = VampJson::toVampResponse_List(j);
|
c@27
|
179 break;
|
c@27
|
180 case RRType::Load:
|
c@32
|
181 rr.loadResponse = VampJson::toVampResponse_Load(j, mapper);
|
c@27
|
182 break;
|
c@27
|
183 case RRType::Configure:
|
c@24
|
184 rr.configurationResponse = VampJson::toVampResponse_Configure(j);
|
c@27
|
185 break;
|
c@27
|
186 case RRType::Process:
|
c@51
|
187 rr.processResponse = VampJson::toVampResponse_Process(j, mapper, serialisation);
|
c@27
|
188 break;
|
c@27
|
189 case RRType::Finish:
|
c@51
|
190 rr.finishResponse = VampJson::toVampResponse_Finish(j, mapper, serialisation);
|
c@27
|
191 break;
|
c@27
|
192 case RRType::NotValid:
|
c@27
|
193 break;
|
c@23
|
194 }
|
c@23
|
195
|
c@23
|
196 return rr;
|
c@23
|
197 }
|
c@23
|
198
|
c@24
|
199 void
|
c@44
|
200 writeResponseJson(RequestOrResponse &rr, bool useBase64)
|
c@24
|
201 {
|
c@24
|
202 Json j;
|
c@24
|
203
|
c@27
|
204 switch (rr.type) {
|
c@27
|
205
|
c@27
|
206 case RRType::List:
|
c@24
|
207 j = VampJson::fromVampResponse_List("", rr.listResponse);
|
c@27
|
208 break;
|
c@27
|
209 case RRType::Load:
|
c@32
|
210 j = VampJson::fromVampResponse_Load(rr.loadResponse, mapper);
|
c@27
|
211 break;
|
c@27
|
212 case RRType::Configure:
|
c@27
|
213 j = VampJson::fromVampResponse_Configure(rr.configurationResponse);
|
c@27
|
214 break;
|
c@27
|
215 case RRType::Process:
|
c@44
|
216 j = VampJson::fromVampResponse_Process
|
c@44
|
217 (rr.processResponse,
|
c@51
|
218 mapper,
|
c@44
|
219 useBase64 ?
|
c@44
|
220 VampJson::BufferSerialisation::Base64 :
|
c@44
|
221 VampJson::BufferSerialisation::Text);
|
c@27
|
222 break;
|
c@27
|
223 case RRType::Finish:
|
c@44
|
224 j = VampJson::fromVampResponse_Finish
|
c@44
|
225 (rr.finishResponse,
|
c@51
|
226 mapper,
|
c@44
|
227 useBase64 ?
|
c@44
|
228 VampJson::BufferSerialisation::Base64 :
|
c@44
|
229 VampJson::BufferSerialisation::Text);
|
c@27
|
230 break;
|
c@27
|
231 case RRType::NotValid:
|
c@27
|
232 break;
|
c@24
|
233 }
|
c@24
|
234
|
c@27
|
235 cout << j.dump() << endl;
|
c@27
|
236 }
|
c@25
|
237
|
c@27
|
238 RequestOrResponse
|
c@33
|
239 readRequestCapnp(kj::BufferedInputStreamWrapper &buffered)
|
c@27
|
240 {
|
c@27
|
241 RequestOrResponse rr;
|
c@27
|
242 rr.direction = RequestOrResponse::Request;
|
c@27
|
243
|
c@33
|
244 ::capnp::InputStreamMessageReader message(buffered);
|
c@27
|
245 VampRequest::Reader reader = message.getRoot<VampRequest>();
|
c@27
|
246
|
c@27
|
247 rr.type = VampnProto::getRequestResponseType(reader);
|
c@27
|
248
|
c@27
|
249 switch (rr.type) {
|
c@27
|
250
|
c@27
|
251 case RRType::List:
|
c@27
|
252 VampnProto::readVampRequest_List(reader); // type check only
|
c@27
|
253 break;
|
c@27
|
254 case RRType::Load:
|
c@27
|
255 VampnProto::readVampRequest_Load(rr.loadRequest, reader);
|
c@27
|
256 break;
|
c@27
|
257 case RRType::Configure:
|
c@32
|
258 VampnProto::readVampRequest_Configure(rr.configurationRequest,
|
c@32
|
259 reader, mapper);
|
c@27
|
260 break;
|
c@27
|
261 case RRType::Process:
|
c@32
|
262 VampnProto::readVampRequest_Process(rr.processRequest, reader, mapper);
|
c@27
|
263 break;
|
c@27
|
264 case RRType::Finish:
|
c@32
|
265 VampnProto::readVampRequest_Finish(rr.finishPlugin, reader, mapper);
|
c@27
|
266 break;
|
c@27
|
267 case RRType::NotValid:
|
c@27
|
268 break;
|
c@27
|
269 }
|
c@27
|
270
|
c@27
|
271 return rr;
|
c@27
|
272 }
|
c@27
|
273
|
c@29
|
274 void
|
c@29
|
275 writeRequestCapnp(RequestOrResponse &rr)
|
c@29
|
276 {
|
c@29
|
277 ::capnp::MallocMessageBuilder message;
|
c@29
|
278 VampRequest::Builder builder = message.initRoot<VampRequest>();
|
c@29
|
279
|
c@29
|
280 switch (rr.type) {
|
c@29
|
281
|
c@29
|
282 case RRType::List:
|
c@29
|
283 VampnProto::buildVampRequest_List(builder);
|
c@29
|
284 break;
|
c@29
|
285 case RRType::Load:
|
c@29
|
286 VampnProto::buildVampRequest_Load(builder, rr.loadRequest);
|
c@29
|
287 break;
|
c@29
|
288 case RRType::Configure:
|
c@29
|
289 VampnProto::buildVampRequest_Configure(builder,
|
c@32
|
290 rr.configurationRequest, mapper);
|
c@29
|
291 break;
|
c@29
|
292 case RRType::Process:
|
c@32
|
293 VampnProto::buildVampRequest_Process(builder, rr.processRequest, mapper);
|
c@29
|
294 break;
|
c@29
|
295 case RRType::Finish:
|
c@32
|
296 VampnProto::buildVampRequest_Finish(builder, rr.finishPlugin, mapper);
|
c@29
|
297 break;
|
c@29
|
298 case RRType::NotValid:
|
c@29
|
299 break;
|
c@29
|
300 }
|
c@29
|
301
|
c@33
|
302 writeMessageToFd(1, message);
|
c@29
|
303 }
|
c@29
|
304
|
c@27
|
305 RequestOrResponse
|
c@33
|
306 readResponseCapnp(kj::BufferedInputStreamWrapper &buffered)
|
c@27
|
307 {
|
c@27
|
308 RequestOrResponse rr;
|
c@27
|
309 rr.direction = RequestOrResponse::Response;
|
c@27
|
310
|
c@33
|
311 ::capnp::InputStreamMessageReader message(buffered);
|
c@27
|
312 VampResponse::Reader reader = message.getRoot<VampResponse>();
|
c@27
|
313
|
c@27
|
314 rr.type = VampnProto::getRequestResponseType(reader);
|
c@27
|
315
|
c@27
|
316 switch (rr.type) {
|
c@27
|
317
|
c@27
|
318 case RRType::List:
|
c@27
|
319 VampnProto::readVampResponse_List(rr.listResponse, reader);
|
c@27
|
320 break;
|
c@27
|
321 case RRType::Load:
|
c@32
|
322 VampnProto::readVampResponse_Load(rr.loadResponse, reader, mapper);
|
c@27
|
323 break;
|
c@27
|
324 case RRType::Configure:
|
c@27
|
325 VampnProto::readVampResponse_Configure(rr.configurationResponse,
|
c@27
|
326 reader);
|
c@27
|
327 break;
|
c@27
|
328 case RRType::Process:
|
c@51
|
329 VampnProto::readVampResponse_Process(rr.processResponse, reader, mapper);
|
c@27
|
330 break;
|
c@27
|
331 case RRType::Finish:
|
c@51
|
332 VampnProto::readVampResponse_Finish(rr.finishResponse, reader, mapper);
|
c@27
|
333 break;
|
c@27
|
334 case RRType::NotValid:
|
c@27
|
335 break;
|
c@27
|
336 }
|
c@27
|
337
|
c@27
|
338 return rr;
|
c@24
|
339 }
|
c@24
|
340
|
c@29
|
341 void
|
c@29
|
342 writeResponseCapnp(RequestOrResponse &rr)
|
c@29
|
343 {
|
c@29
|
344 ::capnp::MallocMessageBuilder message;
|
c@29
|
345 VampResponse::Builder builder = message.initRoot<VampResponse>();
|
c@29
|
346
|
c@29
|
347 switch (rr.type) {
|
c@29
|
348
|
c@29
|
349 case RRType::List:
|
c@29
|
350 VampnProto::buildVampResponse_List(builder, "", rr.listResponse);
|
c@29
|
351 break;
|
c@29
|
352 case RRType::Load:
|
c@32
|
353 VampnProto::buildVampResponse_Load(builder, rr.loadResponse, mapper);
|
c@29
|
354 break;
|
c@29
|
355 case RRType::Configure:
|
c@29
|
356 VampnProto::buildVampResponse_Configure(builder, rr.configurationResponse);
|
c@29
|
357 break;
|
c@29
|
358 case RRType::Process:
|
c@51
|
359 VampnProto::buildVampResponse_Process(builder, rr.processResponse, mapper);
|
c@29
|
360 break;
|
c@29
|
361 case RRType::Finish:
|
c@51
|
362 VampnProto::buildVampResponse_Finish(builder, rr.finishResponse, mapper);
|
c@29
|
363 break;
|
c@29
|
364 case RRType::NotValid:
|
c@29
|
365 break;
|
c@29
|
366 }
|
c@29
|
367
|
c@33
|
368 writeMessageToFd(1, message);
|
c@33
|
369 }
|
c@33
|
370
|
c@33
|
371 RequestOrResponse
|
c@33
|
372 readInputJson(RequestOrResponse::Direction direction)
|
c@33
|
373 {
|
c@33
|
374 if (direction == RequestOrResponse::Request) {
|
c@33
|
375 return readRequestJson();
|
c@33
|
376 } else {
|
c@33
|
377 return readResponseJson();
|
c@33
|
378 }
|
c@33
|
379 }
|
c@33
|
380
|
c@33
|
381 RequestOrResponse
|
c@33
|
382 readInputCapnp(RequestOrResponse::Direction direction)
|
c@33
|
383 {
|
c@33
|
384 static kj::FdInputStream stream(0); // stdin
|
c@33
|
385 static kj::BufferedInputStreamWrapper buffered(stream);
|
c@33
|
386
|
c@33
|
387 if (buffered.tryGetReadBuffer() == nullptr) {
|
c@33
|
388 return {};
|
c@33
|
389 }
|
c@33
|
390
|
c@33
|
391 if (direction == RequestOrResponse::Request) {
|
c@33
|
392 return readRequestCapnp(buffered);
|
c@33
|
393 } else {
|
c@33
|
394 return readResponseCapnp(buffered);
|
c@33
|
395 }
|
c@29
|
396 }
|
c@29
|
397
|
c@23
|
398 RequestOrResponse
|
c@24
|
399 readInput(string format, RequestOrResponse::Direction direction)
|
c@23
|
400 {
|
c@23
|
401 if (format == "json") {
|
c@33
|
402 return readInputJson(direction);
|
c@27
|
403 } else if (format == "capnp") {
|
c@33
|
404 return readInputCapnp(direction);
|
c@23
|
405 } else {
|
c@27
|
406 throw runtime_error("unknown input format \"" + format + "\"");
|
c@23
|
407 }
|
c@23
|
408 }
|
c@23
|
409
|
c@23
|
410 void
|
c@24
|
411 writeOutput(string format, RequestOrResponse &rr)
|
c@23
|
412 {
|
c@24
|
413 if (format == "json") {
|
c@24
|
414 if (rr.direction == RequestOrResponse::Request) {
|
c@44
|
415 writeRequestJson(rr, false);
|
c@24
|
416 } else {
|
c@44
|
417 writeResponseJson(rr, false);
|
c@44
|
418 }
|
c@44
|
419 } else if (format == "json-b64") {
|
c@44
|
420 if (rr.direction == RequestOrResponse::Request) {
|
c@44
|
421 writeRequestJson(rr, true);
|
c@44
|
422 } else {
|
c@44
|
423 writeResponseJson(rr, true);
|
c@24
|
424 }
|
c@29
|
425 } else if (format == "capnp") {
|
c@29
|
426 if (rr.direction == RequestOrResponse::Request) {
|
c@29
|
427 writeRequestCapnp(rr);
|
c@29
|
428 } else {
|
c@29
|
429 writeResponseCapnp(rr);
|
c@29
|
430 }
|
c@24
|
431 } else {
|
c@27
|
432 throw runtime_error("unknown output format \"" + format + "\"");
|
c@24
|
433 }
|
c@23
|
434 }
|
c@23
|
435
|
c@23
|
436 int main(int argc, char **argv)
|
c@23
|
437 {
|
c@24
|
438 if (argc < 2) {
|
c@23
|
439 usage();
|
c@23
|
440 }
|
c@23
|
441
|
c@24
|
442 string informat = "json", outformat = "json";
|
c@27
|
443 RequestOrResponse::Direction direction = RequestOrResponse::Request;
|
c@24
|
444 bool haveDirection = false;
|
c@23
|
445
|
c@24
|
446 for (int i = 1; i < argc; ++i) {
|
c@23
|
447
|
c@23
|
448 string arg = argv[i];
|
c@24
|
449 bool final = (i + 1 == argc);
|
c@23
|
450
|
c@23
|
451 if (arg == "-i") {
|
c@27
|
452 if (final) usage();
|
c@23
|
453 else informat = argv[++i];
|
c@23
|
454
|
c@23
|
455 } else if (arg == "-o") {
|
c@27
|
456 if (final) usage();
|
c@23
|
457 else outformat = argv[++i];
|
c@23
|
458
|
c@24
|
459 } else if (arg == "request") {
|
c@24
|
460 direction = RequestOrResponse::Request;
|
c@24
|
461 haveDirection = true;
|
c@24
|
462
|
c@24
|
463 } else if (arg == "response") {
|
c@24
|
464 direction = RequestOrResponse::Response;
|
c@24
|
465 haveDirection = true;
|
c@24
|
466
|
c@23
|
467 } else {
|
c@23
|
468 usage();
|
c@23
|
469 }
|
c@23
|
470 }
|
c@23
|
471
|
c@24
|
472 if (informat == "" || outformat == "" || !haveDirection) {
|
c@23
|
473 usage();
|
c@23
|
474 }
|
c@23
|
475
|
c@23
|
476 while (true) {
|
c@23
|
477
|
c@23
|
478 try {
|
c@23
|
479
|
c@24
|
480 RequestOrResponse rr = readInput(informat, direction);
|
c@29
|
481
|
c@29
|
482 // NotValid without an exception indicates EOF:
|
c@25
|
483 if (rr.type == RRType::NotValid) break;
|
c@29
|
484
|
c@23
|
485 writeOutput(outformat, rr);
|
c@23
|
486
|
c@23
|
487 } catch (std::exception &e) {
|
c@23
|
488 cerr << "Error: " << e.what() << endl;
|
c@23
|
489 exit(1);
|
c@23
|
490 }
|
c@23
|
491 }
|
c@23
|
492
|
c@23
|
493 exit(0);
|
c@23
|
494 }
|
c@23
|
495
|
c@23
|
496
|