c@116
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@118
|
2 /*
|
c@118
|
3 Piper C++
|
c@118
|
4
|
c@118
|
5 An API for audio analysis and feature extraction plugins.
|
c@118
|
6
|
c@118
|
7 Centre for Digital Music, Queen Mary, University of London.
|
c@118
|
8 Copyright 2006-2016 Chris Cannam and QMUL.
|
c@118
|
9
|
c@118
|
10 Permission is hereby granted, free of charge, to any person
|
c@118
|
11 obtaining a copy of this software and associated documentation
|
c@118
|
12 files (the "Software"), to deal in the Software without
|
c@118
|
13 restriction, including without limitation the rights to use, copy,
|
c@118
|
14 modify, merge, publish, distribute, sublicense, and/or sell copies
|
c@118
|
15 of the Software, and to permit persons to whom the Software is
|
c@118
|
16 furnished to do so, subject to the following conditions:
|
c@118
|
17
|
c@118
|
18 The above copyright notice and this permission notice shall be
|
c@118
|
19 included in all copies or substantial portions of the Software.
|
c@118
|
20
|
c@118
|
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
c@118
|
22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
c@118
|
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
c@118
|
24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
c@118
|
25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
c@118
|
26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
c@118
|
27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
c@118
|
28
|
c@118
|
29 Except as contained in this notice, the names of the Centre for
|
c@118
|
30 Digital Music; Queen Mary, University of London; and Chris Cannam
|
c@118
|
31 shall not be used in advertising or otherwise to promote the sale,
|
c@118
|
32 use or other dealings in this Software without prior written
|
c@118
|
33 authorization.
|
c@118
|
34 */
|
c@75
|
35
|
c@75
|
36 #include "vamp-json/VampJson.h"
|
c@75
|
37 #include "vamp-capnp/VampnProto.h"
|
c@75
|
38 #include "vamp-support/RequestOrResponse.h"
|
c@75
|
39 #include "vamp-support/PreservingPluginHandleMapper.h"
|
c@75
|
40
|
c@75
|
41 #include <iostream>
|
c@75
|
42 #include <sstream>
|
c@75
|
43 #include <stdexcept>
|
c@75
|
44
|
c@91
|
45 #include <capnp/serialize.h>
|
c@91
|
46
|
c@123
|
47 // for _setmode stuff
|
c@123
|
48 #ifdef _WIN32
|
c@123
|
49 #include <io.h>
|
c@123
|
50 #include <fcntl.h>
|
c@123
|
51 #endif
|
c@123
|
52
|
c@75
|
53 using namespace std;
|
c@75
|
54 using namespace json11;
|
c@97
|
55 using namespace piper_vamp;
|
c@75
|
56
|
c@75
|
57 void usage()
|
c@75
|
58 {
|
c@75
|
59 string myname = "piper-convert";
|
c@75
|
60 cerr << "\n" << myname <<
|
c@116
|
61 ": Validate and convert Piper request and response messages\n\n"
|
c@116
|
62 " Usage: " << myname << " [-i <informat>] [-o <outformat>] request\n"
|
c@116
|
63 " " << myname << " [-i <informat>] [-o <outformat>] response\n\n"
|
c@116
|
64 " where\n"
|
c@116
|
65 " <informat>: the format to read from stdin\n"
|
c@116
|
66 " (\"json\" or \"capnp\", default is \"json\")\n"
|
c@116
|
67 " <outformat>: the format to convert to and write to stdout\n"
|
c@116
|
68 " (\"json\", \"json-b64\" or \"capnp\", default is \"json\")\n"
|
c@116
|
69 " request|response: whether messages are Vamp request or response type\n\n"
|
c@116
|
70 "If <informat> and <outformat> differ, convert from <informat> to <outformat>.\n"
|
c@116
|
71 "If <informat> and <outformat> are the same, just check validity of incoming\n"
|
c@116
|
72 "messages and pass them to output.\n\n"
|
c@116
|
73 "Specifying \"json-b64\" as output format forces base64 encoding for process and\n"
|
c@116
|
74 "feature blocks, unlike the \"json\" output format which uses text encoding.\n"
|
c@116
|
75 "The \"json\" input format accepts either.\n\n";
|
c@75
|
76
|
c@75
|
77 exit(2);
|
c@75
|
78 }
|
c@75
|
79
|
c@75
|
80 Json
|
c@75
|
81 convertRequestJson(string input, string &err)
|
c@75
|
82 {
|
c@75
|
83 Json j = Json::parse(input, err);
|
c@75
|
84 if (err != "") {
|
c@116
|
85 err = "invalid json: " + err;
|
c@116
|
86 return {};
|
c@75
|
87 }
|
c@75
|
88 if (!j.is_object()) {
|
c@116
|
89 err = "object expected at top level";
|
c@75
|
90 } else if (!j["method"].is_string()) {
|
c@116
|
91 err = "string expected for method field";
|
c@75
|
92 } else if (!j["params"].is_null() && !j["params"].is_object()) {
|
c@116
|
93 err = "object expected for params field";
|
c@75
|
94 }
|
c@75
|
95 return j;
|
c@75
|
96 }
|
c@75
|
97
|
c@75
|
98 Json
|
c@75
|
99 convertResponseJson(string input, string &err)
|
c@75
|
100 {
|
c@75
|
101 Json j = Json::parse(input, err);
|
c@75
|
102 if (err != "") {
|
c@116
|
103 err = "invalid json: " + err;
|
c@116
|
104 return {};
|
c@75
|
105 }
|
c@75
|
106 if (!j.is_object()) {
|
c@116
|
107 err = "object expected at top level";
|
c@75
|
108 } else {
|
c@75
|
109 if (!j["result"].is_object()) {
|
c@75
|
110 if (!j["error"].is_object()) {
|
c@75
|
111 err = "expected either result or error object";
|
c@75
|
112 }
|
c@75
|
113 }
|
c@75
|
114 }
|
c@75
|
115 return j;
|
c@75
|
116 }
|
c@75
|
117
|
c@75
|
118 //!!! Lots of potential for refactoring the conversion classes based
|
c@75
|
119 //!!! on the common matter in the following eight functions...
|
c@75
|
120
|
c@75
|
121 PreservingPluginHandleMapper mapper;
|
c@75
|
122
|
c@75
|
123 static RequestOrResponse::RpcId
|
c@75
|
124 readJsonId(const Json &j)
|
c@75
|
125 {
|
c@75
|
126 RequestOrResponse::RpcId id;
|
c@75
|
127
|
c@75
|
128 if (j["id"].is_number()) {
|
c@75
|
129 id.type = RequestOrResponse::RpcId::Number;
|
c@75
|
130 id.number = j["id"].number_value();
|
c@75
|
131 } else if (j["id"].is_string()) {
|
c@75
|
132 id.type = RequestOrResponse::RpcId::Tag;
|
c@75
|
133 id.tag = j["id"].string_value();
|
c@75
|
134 } else {
|
c@75
|
135 id.type = RequestOrResponse::RpcId::Absent;
|
c@75
|
136 }
|
c@75
|
137
|
c@75
|
138 return id;
|
c@75
|
139 }
|
c@75
|
140
|
c@75
|
141 static Json
|
c@75
|
142 writeJsonId(const RequestOrResponse::RpcId &id)
|
c@75
|
143 {
|
c@75
|
144 if (id.type == RequestOrResponse::RpcId::Number) {
|
c@75
|
145 return id.number;
|
c@75
|
146 } else if (id.type == RequestOrResponse::RpcId::Tag) {
|
c@75
|
147 return id.tag;
|
c@75
|
148 } else {
|
c@75
|
149 return Json();
|
c@75
|
150 }
|
c@75
|
151 }
|
c@75
|
152
|
cannam@294
|
153 template <typename Reader, typename Id>
|
c@75
|
154 static RequestOrResponse::RpcId
|
c@75
|
155 readCapnpId(const Reader &r)
|
c@75
|
156 {
|
c@75
|
157 int number;
|
c@75
|
158 string tag;
|
c@75
|
159 switch (r.getId().which()) {
|
cannam@294
|
160 case Id::Which::NUMBER:
|
c@75
|
161 number = r.getId().getNumber();
|
c@75
|
162 return { RequestOrResponse::RpcId::Number, number, "" };
|
cannam@294
|
163 case Id::Which::TAG:
|
c@75
|
164 tag = r.getId().getTag();
|
c@75
|
165 return { RequestOrResponse::RpcId::Tag, 0, tag };
|
cannam@294
|
166 case Id::Which::NONE:
|
c@75
|
167 return { RequestOrResponse::RpcId::Absent, 0, "" };
|
c@75
|
168 }
|
cannam@153
|
169 return { RequestOrResponse::RpcId::Absent, 0, "" };
|
c@75
|
170 }
|
c@75
|
171
|
c@75
|
172 template <typename Builder>
|
c@75
|
173 static void
|
c@75
|
174 buildCapnpId(Builder &b, const RequestOrResponse::RpcId &id)
|
c@75
|
175 {
|
c@75
|
176 switch (id.type) {
|
c@75
|
177 case RequestOrResponse::RpcId::Number:
|
c@75
|
178 b.getId().setNumber(id.number);
|
c@75
|
179 break;
|
c@75
|
180 case RequestOrResponse::RpcId::Tag:
|
c@75
|
181 b.getId().setTag(id.tag);
|
c@75
|
182 break;
|
c@75
|
183 case RequestOrResponse::RpcId::Absent:
|
c@75
|
184 b.getId().setNone();
|
c@75
|
185 break;
|
c@75
|
186 }
|
c@75
|
187 }
|
c@75
|
188
|
c@75
|
189 RequestOrResponse
|
cannam@158
|
190 readRequestJson(string &err, bool &eof)
|
c@75
|
191 {
|
c@75
|
192 RequestOrResponse rr;
|
c@75
|
193 rr.direction = RequestOrResponse::Request;
|
c@75
|
194
|
c@75
|
195 string input;
|
c@75
|
196 if (!getline(cin, input)) {
|
c@116
|
197 // the EOF case, not actually an error
|
cannam@158
|
198 eof = true;
|
c@116
|
199 return rr;
|
c@75
|
200 }
|
c@75
|
201
|
c@75
|
202 Json j = convertRequestJson(input, err);
|
c@75
|
203 if (err != "") return {};
|
c@75
|
204
|
c@75
|
205 rr.type = VampJson::getRequestResponseType(j, err);
|
c@75
|
206 if (err != "") return {};
|
c@75
|
207
|
c@75
|
208 rr.id = readJsonId(j);
|
c@75
|
209
|
c@75
|
210 VampJson::BufferSerialisation serialisation =
|
c@75
|
211 VampJson::BufferSerialisation::Array;
|
c@75
|
212
|
c@75
|
213 switch (rr.type) {
|
c@75
|
214
|
c@75
|
215 case RRType::List:
|
c@130
|
216 rr.listRequest = VampJson::toRpcRequest_List(j, err);
|
c@116
|
217 break;
|
c@75
|
218 case RRType::Load:
|
c@116
|
219 rr.loadRequest = VampJson::toRpcRequest_Load(j, err);
|
c@116
|
220 break;
|
c@75
|
221 case RRType::Configure:
|
c@116
|
222 rr.configurationRequest = VampJson::toRpcRequest_Configure(j, mapper, err);
|
c@116
|
223 break;
|
c@75
|
224 case RRType::Process:
|
c@116
|
225 rr.processRequest = VampJson::toRpcRequest_Process(j, mapper, serialisation, err);
|
c@116
|
226 break;
|
c@75
|
227 case RRType::Finish:
|
c@116
|
228 rr.finishRequest = VampJson::toRpcRequest_Finish(j, mapper, err);
|
c@116
|
229 break;
|
c@75
|
230 case RRType::NotValid:
|
c@116
|
231 break;
|
c@75
|
232 }
|
c@75
|
233
|
c@75
|
234 return rr;
|
c@75
|
235 }
|
c@75
|
236
|
c@75
|
237 void
|
c@75
|
238 writeRequestJson(RequestOrResponse &rr, bool useBase64)
|
c@75
|
239 {
|
c@75
|
240 Json j;
|
c@75
|
241
|
c@75
|
242 VampJson::BufferSerialisation serialisation =
|
c@75
|
243 (useBase64 ?
|
c@75
|
244 VampJson::BufferSerialisation::Base64 :
|
c@75
|
245 VampJson::BufferSerialisation::Array);
|
c@75
|
246
|
c@75
|
247 Json id = writeJsonId(rr.id);
|
c@75
|
248
|
c@75
|
249 switch (rr.type) {
|
c@75
|
250
|
c@75
|
251 case RRType::List:
|
c@127
|
252 j = VampJson::fromRpcRequest_List(rr.listRequest, id);
|
c@116
|
253 break;
|
c@75
|
254 case RRType::Load:
|
c@116
|
255 j = VampJson::fromRpcRequest_Load(rr.loadRequest, id);
|
c@116
|
256 break;
|
c@75
|
257 case RRType::Configure:
|
c@116
|
258 j = VampJson::fromRpcRequest_Configure(rr.configurationRequest, mapper, id);
|
c@116
|
259 break;
|
c@75
|
260 case RRType::Process:
|
c@116
|
261 j = VampJson::fromRpcRequest_Process
|
c@116
|
262 (rr.processRequest, mapper, serialisation, id);
|
c@116
|
263 break;
|
c@75
|
264 case RRType::Finish:
|
c@116
|
265 j = VampJson::fromRpcRequest_Finish(rr.finishRequest, mapper, id);
|
c@116
|
266 break;
|
c@75
|
267 case RRType::NotValid:
|
c@116
|
268 break;
|
c@75
|
269 }
|
c@75
|
270
|
c@75
|
271 cout << j.dump() << endl;
|
c@75
|
272 }
|
c@75
|
273
|
c@75
|
274 RequestOrResponse
|
cannam@158
|
275 readResponseJson(string &err, bool &eof)
|
c@75
|
276 {
|
c@75
|
277 RequestOrResponse rr;
|
c@75
|
278 rr.direction = RequestOrResponse::Response;
|
c@75
|
279
|
c@75
|
280 string input;
|
c@75
|
281 if (!getline(cin, input)) {
|
c@116
|
282 // the EOF case, not actually an error
|
cannam@158
|
283 eof = true;
|
c@116
|
284 return rr;
|
c@75
|
285 }
|
c@75
|
286
|
c@75
|
287 Json j = convertResponseJson(input, err);
|
c@75
|
288 if (err != "") return {};
|
c@75
|
289
|
c@75
|
290 rr.type = VampJson::getRequestResponseType(j, err);
|
c@75
|
291 if (err != "") return {};
|
c@75
|
292
|
c@75
|
293 rr.id = readJsonId(j);
|
c@75
|
294
|
c@75
|
295 VampJson::BufferSerialisation serialisation =
|
c@75
|
296 VampJson::BufferSerialisation::Array;
|
c@75
|
297
|
dev@174
|
298 const bool isSuccess = j["result"].is_object();
|
dev@174
|
299 const bool isError = j["error"].is_object();
|
dev@174
|
300 rr.success = isSuccess;
|
dev@174
|
301 rr.errorText = isError ? j["error"]["message"].string_value() : "";
|
c@75
|
302
|
c@75
|
303 switch (rr.type) {
|
c@75
|
304
|
c@75
|
305 case RRType::List:
|
c@116
|
306 rr.listResponse = VampJson::toRpcResponse_List(j, err);
|
c@116
|
307 break;
|
c@75
|
308 case RRType::Load:
|
c@116
|
309 rr.loadResponse = VampJson::toRpcResponse_Load(j, mapper, err);
|
c@116
|
310 break;
|
c@75
|
311 case RRType::Configure:
|
c@116
|
312 rr.configurationResponse = VampJson::toRpcResponse_Configure(j, mapper, err);
|
c@116
|
313 break;
|
c@75
|
314 case RRType::Process:
|
c@116
|
315 rr.processResponse = VampJson::toRpcResponse_Process(j, mapper, serialisation, err);
|
c@116
|
316 break;
|
c@75
|
317 case RRType::Finish:
|
c@116
|
318 rr.finishResponse = VampJson::toRpcResponse_Finish(j, mapper, serialisation, err);
|
c@116
|
319 break;
|
c@75
|
320 case RRType::NotValid:
|
c@116
|
321 break;
|
c@75
|
322 }
|
c@75
|
323
|
c@75
|
324 return rr;
|
c@75
|
325 }
|
c@75
|
326
|
c@75
|
327 void
|
c@75
|
328 writeResponseJson(RequestOrResponse &rr, bool useBase64)
|
c@75
|
329 {
|
c@75
|
330 Json j;
|
c@75
|
331
|
c@75
|
332 VampJson::BufferSerialisation serialisation =
|
c@75
|
333 (useBase64 ?
|
c@75
|
334 VampJson::BufferSerialisation::Base64 :
|
c@75
|
335 VampJson::BufferSerialisation::Array);
|
c@75
|
336
|
c@75
|
337 Json id = writeJsonId(rr.id);
|
c@75
|
338
|
c@75
|
339 if (!rr.success) {
|
dev@176
|
340 // errorText here likely contains a full message produced by simple-server
|
dev@176
|
341 // setting writeVerbatimError to true avoids doubling error descriptions
|
dev@176
|
342 j = VampJson::fromError(rr.errorText, rr.type, id, true);
|
c@75
|
343
|
c@75
|
344 } else {
|
c@116
|
345 switch (rr.type) {
|
c@75
|
346
|
c@116
|
347 case RRType::List:
|
c@116
|
348 j = VampJson::fromRpcResponse_List(rr.listResponse, id);
|
c@116
|
349 break;
|
c@116
|
350 case RRType::Load:
|
c@116
|
351 j = VampJson::fromRpcResponse_Load(rr.loadResponse, mapper, id);
|
c@116
|
352 break;
|
c@116
|
353 case RRType::Configure:
|
c@116
|
354 j = VampJson::fromRpcResponse_Configure(rr.configurationResponse,
|
c@75
|
355 mapper, id);
|
c@116
|
356 break;
|
c@116
|
357 case RRType::Process:
|
c@116
|
358 j = VampJson::fromRpcResponse_Process
|
c@116
|
359 (rr.processResponse, mapper, serialisation, id);
|
c@116
|
360 break;
|
c@116
|
361 case RRType::Finish:
|
c@116
|
362 j = VampJson::fromRpcResponse_Finish
|
c@116
|
363 (rr.finishResponse, mapper, serialisation, id);
|
c@116
|
364 break;
|
c@116
|
365 case RRType::NotValid:
|
cannam@158
|
366 j = VampJson::fromError(rr.errorText, rr.type, id);
|
c@116
|
367 break;
|
c@116
|
368 }
|
c@75
|
369 }
|
c@75
|
370
|
c@75
|
371 cout << j.dump() << endl;
|
c@75
|
372 }
|
c@75
|
373
|
c@75
|
374 RequestOrResponse
|
c@75
|
375 readRequestCapnp(kj::BufferedInputStreamWrapper &buffered)
|
c@75
|
376 {
|
c@75
|
377 RequestOrResponse rr;
|
c@75
|
378 rr.direction = RequestOrResponse::Request;
|
c@75
|
379
|
c@97
|
380 capnp::InputStreamMessageReader message(buffered);
|
c@97
|
381 piper::RpcRequest::Reader reader = message.getRoot<piper::RpcRequest>();
|
c@75
|
382
|
c@75
|
383 rr.type = VampnProto::getRequestResponseType(reader);
|
cannam@294
|
384 rr.id = readCapnpId<piper::RpcRequest::Reader, piper::RpcRequest::Id>
|
cannam@294
|
385 (reader);
|
c@75
|
386
|
c@75
|
387 switch (rr.type) {
|
c@75
|
388
|
c@75
|
389 case RRType::List:
|
c@127
|
390 VampnProto::readRpcRequest_List(rr.listRequest, reader);
|
c@116
|
391 break;
|
c@75
|
392 case RRType::Load:
|
c@116
|
393 VampnProto::readRpcRequest_Load(rr.loadRequest, reader);
|
c@116
|
394 break;
|
c@75
|
395 case RRType::Configure:
|
c@116
|
396 VampnProto::readRpcRequest_Configure(rr.configurationRequest,
|
c@116
|
397 reader, mapper);
|
c@116
|
398 break;
|
c@75
|
399 case RRType::Process:
|
c@116
|
400 VampnProto::readRpcRequest_Process(rr.processRequest, reader, mapper);
|
c@116
|
401 break;
|
c@75
|
402 case RRType::Finish:
|
c@116
|
403 VampnProto::readRpcRequest_Finish(rr.finishRequest, reader, mapper);
|
c@116
|
404 break;
|
c@75
|
405 case RRType::NotValid:
|
c@116
|
406 break;
|
c@75
|
407 }
|
c@75
|
408
|
c@75
|
409 return rr;
|
c@75
|
410 }
|
c@75
|
411
|
c@75
|
412 void
|
c@75
|
413 writeRequestCapnp(RequestOrResponse &rr)
|
c@75
|
414 {
|
c@97
|
415 capnp::MallocMessageBuilder message;
|
c@97
|
416 piper::RpcRequest::Builder builder = message.initRoot<piper::RpcRequest>();
|
c@75
|
417
|
c@75
|
418 buildCapnpId(builder, rr.id);
|
c@75
|
419
|
c@75
|
420 switch (rr.type) {
|
c@75
|
421
|
c@75
|
422 case RRType::List:
|
c@127
|
423 VampnProto::buildRpcRequest_List(builder, rr.listRequest);
|
c@116
|
424 break;
|
c@75
|
425 case RRType::Load:
|
c@116
|
426 VampnProto::buildRpcRequest_Load(builder, rr.loadRequest);
|
c@116
|
427 break;
|
c@75
|
428 case RRType::Configure:
|
c@116
|
429 VampnProto::buildRpcRequest_Configure(builder,
|
c@75
|
430 rr.configurationRequest, mapper);
|
c@116
|
431 break;
|
c@75
|
432 case RRType::Process:
|
c@116
|
433 VampnProto::buildRpcRequest_Process(builder, rr.processRequest, mapper);
|
c@116
|
434 break;
|
c@75
|
435 case RRType::Finish:
|
c@116
|
436 VampnProto::buildRpcRequest_Finish(builder, rr.finishRequest, mapper);
|
c@116
|
437 break;
|
c@75
|
438 case RRType::NotValid:
|
c@116
|
439 break;
|
c@75
|
440 }
|
c@75
|
441
|
c@75
|
442 writeMessageToFd(1, message);
|
c@75
|
443 }
|
c@75
|
444
|
c@75
|
445 RequestOrResponse
|
c@75
|
446 readResponseCapnp(kj::BufferedInputStreamWrapper &buffered)
|
c@75
|
447 {
|
c@75
|
448 RequestOrResponse rr;
|
c@75
|
449 rr.direction = RequestOrResponse::Response;
|
c@75
|
450
|
c@97
|
451 capnp::InputStreamMessageReader message(buffered);
|
c@97
|
452 piper::RpcResponse::Reader reader = message.getRoot<piper::RpcResponse>();
|
c@75
|
453
|
c@75
|
454 rr.type = VampnProto::getRequestResponseType(reader);
|
c@75
|
455 rr.success = true;
|
c@75
|
456 rr.errorText = "";
|
cannam@294
|
457 rr.id = readCapnpId<piper::RpcResponse::Reader, piper::RpcResponse::Id>
|
cannam@294
|
458 (reader);
|
c@75
|
459 int errorCode = 0;
|
c@75
|
460
|
c@75
|
461 switch (rr.type) {
|
c@75
|
462
|
c@75
|
463 case RRType::List:
|
c@116
|
464 VampnProto::readRpcResponse_List(rr.listResponse, reader);
|
c@116
|
465 break;
|
c@75
|
466 case RRType::Load:
|
c@116
|
467 VampnProto::readRpcResponse_Load(rr.loadResponse, reader, mapper);
|
c@116
|
468 break;
|
c@75
|
469 case RRType::Configure:
|
c@116
|
470 VampnProto::readRpcResponse_Configure(rr.configurationResponse,
|
c@116
|
471 reader, mapper);
|
c@116
|
472 break;
|
c@75
|
473 case RRType::Process:
|
c@116
|
474 VampnProto::readRpcResponse_Process(rr.processResponse, reader, mapper);
|
c@116
|
475 break;
|
c@75
|
476 case RRType::Finish:
|
c@116
|
477 VampnProto::readRpcResponse_Finish(rr.finishResponse, reader, mapper);
|
c@116
|
478 break;
|
c@75
|
479 case RRType::NotValid:
|
c@75
|
480 VampnProto::readRpcResponse_Error(errorCode, rr.errorText, reader);
|
c@116
|
481 break;
|
c@75
|
482 }
|
c@75
|
483
|
c@75
|
484 return rr;
|
c@75
|
485 }
|
c@75
|
486
|
c@75
|
487 void
|
c@75
|
488 writeResponseCapnp(RequestOrResponse &rr)
|
c@75
|
489 {
|
c@97
|
490 capnp::MallocMessageBuilder message;
|
c@97
|
491 piper::RpcResponse::Builder builder = message.initRoot<piper::RpcResponse>();
|
c@75
|
492
|
c@75
|
493 buildCapnpId(builder, rr.id);
|
c@75
|
494
|
c@75
|
495 if (!rr.success) {
|
c@75
|
496
|
c@116
|
497 VampnProto::buildRpcResponse_Error(builder, rr.errorText, rr.type);
|
c@75
|
498
|
c@75
|
499 } else {
|
c@116
|
500
|
c@116
|
501 switch (rr.type) {
|
c@75
|
502
|
c@116
|
503 case RRType::List:
|
c@116
|
504 VampnProto::buildRpcResponse_List(builder, rr.listResponse);
|
c@116
|
505 break;
|
c@116
|
506 case RRType::Load:
|
c@116
|
507 VampnProto::buildRpcResponse_Load(builder, rr.loadResponse, mapper);
|
c@116
|
508 break;
|
c@116
|
509 case RRType::Configure:
|
c@116
|
510 VampnProto::buildRpcResponse_Configure(builder, rr.configurationResponse, mapper);
|
c@116
|
511 break;
|
c@116
|
512 case RRType::Process:
|
c@116
|
513 VampnProto::buildRpcResponse_Process(builder, rr.processResponse, mapper);
|
c@116
|
514 break;
|
c@116
|
515 case RRType::Finish:
|
c@116
|
516 VampnProto::buildRpcResponse_Finish(builder, rr.finishResponse, mapper);
|
c@116
|
517 break;
|
c@116
|
518 case RRType::NotValid:
|
cannam@158
|
519 VampnProto::buildRpcResponse_Error(builder, rr.errorText, rr.type);
|
c@116
|
520 break;
|
c@116
|
521 }
|
c@75
|
522 }
|
c@75
|
523
|
c@75
|
524 writeMessageToFd(1, message);
|
c@75
|
525 }
|
c@75
|
526
|
c@75
|
527 RequestOrResponse
|
cannam@158
|
528 readInputJson(RequestOrResponse::Direction direction, string &err, bool &eof)
|
c@75
|
529 {
|
c@75
|
530 if (direction == RequestOrResponse::Request) {
|
cannam@158
|
531 return readRequestJson(err, eof);
|
c@75
|
532 } else {
|
cannam@158
|
533 return readResponseJson(err, eof);
|
c@75
|
534 }
|
c@75
|
535 }
|
c@75
|
536
|
c@75
|
537 RequestOrResponse
|
cannam@158
|
538 readInputCapnp(RequestOrResponse::Direction direction, bool &eof)
|
c@75
|
539 {
|
c@75
|
540 static kj::FdInputStream stream(0); // stdin
|
c@75
|
541 static kj::BufferedInputStreamWrapper buffered(stream);
|
c@75
|
542
|
c@75
|
543 if (buffered.tryGetReadBuffer() == nullptr) {
|
cannam@158
|
544 eof = true;
|
c@116
|
545 return {};
|
c@75
|
546 }
|
c@75
|
547
|
c@75
|
548 if (direction == RequestOrResponse::Request) {
|
c@116
|
549 return readRequestCapnp(buffered);
|
c@75
|
550 } else {
|
c@116
|
551 return readResponseCapnp(buffered);
|
c@75
|
552 }
|
c@75
|
553 }
|
c@75
|
554
|
c@75
|
555 RequestOrResponse
|
cannam@158
|
556 readInput(string format, RequestOrResponse::Direction direction, bool &eof)
|
c@75
|
557 {
|
cannam@158
|
558 eof = false;
|
cannam@158
|
559
|
c@75
|
560 if (format == "json") {
|
c@116
|
561 string err;
|
cannam@158
|
562 auto result = readInputJson(direction, err, eof);
|
dev@180
|
563 if (err != "") throw runtime_error(err);
|
dev@180
|
564 else return result;
|
c@75
|
565 } else if (format == "capnp") {
|
cannam@158
|
566 return readInputCapnp(direction, eof);
|
c@75
|
567 } else {
|
c@116
|
568 throw runtime_error("unknown input format \"" + format + "\"");
|
c@75
|
569 }
|
c@75
|
570 }
|
c@75
|
571
|
c@75
|
572 void
|
c@75
|
573 writeOutput(string format, RequestOrResponse &rr)
|
c@75
|
574 {
|
c@75
|
575 if (format == "json") {
|
c@116
|
576 if (rr.direction == RequestOrResponse::Request) {
|
c@116
|
577 writeRequestJson(rr, false);
|
c@116
|
578 } else {
|
c@116
|
579 writeResponseJson(rr, false);
|
c@116
|
580 }
|
c@75
|
581 } else if (format == "json-b64") {
|
c@116
|
582 if (rr.direction == RequestOrResponse::Request) {
|
c@116
|
583 writeRequestJson(rr, true);
|
c@116
|
584 } else {
|
c@116
|
585 writeResponseJson(rr, true);
|
c@116
|
586 }
|
c@75
|
587 } else if (format == "capnp") {
|
c@116
|
588 if (rr.direction == RequestOrResponse::Request) {
|
c@116
|
589 writeRequestCapnp(rr);
|
c@116
|
590 } else {
|
c@116
|
591 writeResponseCapnp(rr);
|
c@116
|
592 }
|
c@75
|
593 } else {
|
c@116
|
594 throw runtime_error("unknown output format \"" + format + "\"");
|
c@75
|
595 }
|
c@75
|
596 }
|
c@75
|
597
|
c@75
|
598 int main(int argc, char **argv)
|
c@75
|
599 {
|
c@75
|
600 if (argc < 2) {
|
c@116
|
601 usage();
|
c@75
|
602 }
|
c@75
|
603
|
c@75
|
604 string informat = "json", outformat = "json";
|
c@75
|
605 RequestOrResponse::Direction direction = RequestOrResponse::Request;
|
c@75
|
606 bool haveDirection = false;
|
c@75
|
607
|
c@75
|
608 for (int i = 1; i < argc; ++i) {
|
c@75
|
609
|
c@116
|
610 string arg = argv[i];
|
c@116
|
611 bool final = (i + 1 == argc);
|
c@116
|
612
|
c@116
|
613 if (arg == "-i") {
|
c@116
|
614 if (final) usage();
|
c@116
|
615 else informat = argv[++i];
|
c@75
|
616
|
c@116
|
617 } else if (arg == "-o") {
|
c@116
|
618 if (final) usage();
|
c@116
|
619 else outformat = argv[++i];
|
c@75
|
620
|
c@116
|
621 } else if (arg == "request") {
|
c@116
|
622 direction = RequestOrResponse::Request;
|
c@116
|
623 haveDirection = true;
|
c@75
|
624
|
c@116
|
625 } else if (arg == "response") {
|
c@116
|
626 direction = RequestOrResponse::Response;
|
c@116
|
627 haveDirection = true;
|
c@116
|
628
|
c@116
|
629 } else {
|
c@116
|
630 usage();
|
c@116
|
631 }
|
c@75
|
632 }
|
c@75
|
633
|
c@75
|
634 if (informat == "" || outformat == "" || !haveDirection) {
|
c@116
|
635 usage();
|
c@75
|
636 }
|
c@75
|
637
|
c@123
|
638 #ifdef _WIN32
|
c@123
|
639 if (informat == "capnp") {
|
c@123
|
640 int result = _setmode(_fileno(stdin), _O_BINARY);
|
c@123
|
641 if (result == -1) {
|
c@123
|
642 cerr << "Failed to set binary mode on stdin, necessary for capnp format" << endl;
|
c@123
|
643 exit(1);
|
c@123
|
644 }
|
c@123
|
645 }
|
c@123
|
646 if (outformat == "capnp") {
|
c@123
|
647 int result = _setmode(_fileno(stdout), _O_BINARY);
|
c@123
|
648 if (result == -1) {
|
c@123
|
649 cerr << "Failed to set binary mode on stdout, necessary for capnp format" << endl;
|
c@123
|
650 exit(1);
|
c@123
|
651 }
|
c@123
|
652 }
|
c@123
|
653 #endif
|
c@123
|
654
|
c@75
|
655 while (true) {
|
c@75
|
656
|
c@116
|
657 try {
|
c@75
|
658
|
cannam@158
|
659 bool eof = false;
|
cannam@158
|
660 RequestOrResponse rr = readInput(informat, direction, eof);
|
cannam@158
|
661 if (eof) break;
|
c@75
|
662
|
c@116
|
663 writeOutput(outformat, rr);
|
cannam@158
|
664
|
c@116
|
665 } catch (std::exception &e) {
|
c@116
|
666 cerr << "Error: " << e.what() << endl;
|
c@116
|
667 exit(1);
|
c@116
|
668 }
|
c@75
|
669 }
|
c@75
|
670
|
c@75
|
671 exit(0);
|
c@75
|
672 }
|
c@75
|
673
|
c@75
|
674
|