Loading...
Searching...
No Matches
PuppetJson.hpp
1#pragma once
2
3// Shared helpers for the out-of-process plug-in scanners ("puppets"):
4// JSON string escaping and command-line parsing. Header-only, no Qt —
5// the puppets only link ossia + fmt.
6//
7// Scanner protocol: a puppet is spawned as
8// ossia-score-xxxpuppet <plugin-path> [request-id] [port] [token]
9// and replies on ws://127.0.0.1:<port> with a single JSON object that
10// echoes back "Request": <id> and "Token": "<token>". The host drops
11// any reply whose token does not match the scan session, so replies
12// from another score instance's puppets (or stale puppets from a
13// previous scan) can never pollute the plug-in database.
14// [port]/[token] are optional so that a puppet can still be run by hand
15// against the legacy fixed port for debugging.
16
17#include <charconv>
18#include <string>
19#include <string_view>
20
21namespace score::puppet
22{
26inline std::string json_escape(std::string_view s)
27{
28 static constexpr char hex[] = "0123456789abcdef";
29 std::string out;
30 out.reserve(s.size() + 8);
31 for(unsigned char c : s)
32 {
33 switch(c)
34 {
35 case '"':
36 out += "\\\"";
37 break;
38 case '\\':
39 out += "\\\\";
40 break;
41 case '\b':
42 out += "\\b";
43 break;
44 case '\f':
45 out += "\\f";
46 break;
47 case '\n':
48 out += "\\n";
49 break;
50 case '\r':
51 out += "\\r";
52 break;
53 case '\t':
54 out += "\\t";
55 break;
56 default:
57 if(c < 0x20)
58 {
59 out += "\\u00";
60 out += hex[(c >> 4) & 0xf];
61 out += hex[c & 0xf];
62 }
63 else
64 {
65 out += (char)c;
66 }
67 break;
68 }
69 }
70 return out;
71}
72
73inline std::string json_escape(const char* s)
74{
75 return s ? json_escape(std::string_view{s}) : std::string{};
76}
77
79{
80 std::string path;
81 int request_id{0};
82 int port{0};
83 std::string token;
84
85 bool valid{false};
86};
87
92parse_arguments(int argc, char** argv, int default_port) noexcept
93{
95 res.port = default_port;
96 if(argc <= 1)
97 return res;
98
99 res.path = argv[1];
100 if(res.path.empty())
101 return res;
102
103 auto to_int = [](const char* str, int fallback) {
104 int value{};
105 std::string_view sv{str};
106 auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), value);
107 if(ec != std::errc{} || ptr != sv.data() + sv.size())
108 return fallback;
109 return value;
110 };
111
112 // -1 fallback: a malformed id must not masquerade as request 0 (the host
113 // drops replies for unknown ids)
114 if(argc > 2)
115 res.request_id = to_int(argv[2], -1);
116 if(argc > 3)
117 {
118 if(int p = to_int(argv[3], 0); p > 0 && p <= 65535)
119 res.port = p;
120 }
121 if(argc > 4)
122 res.token = argv[4];
123
124 res.valid = true;
125 return res;
126}
127}
STL namespace.
Definition PuppetJson.hpp:79