1 file changed
+53
-15
crates/xcrs/src/xcrs.rs
+53
-15
@@ -134,21 +134,34 @@ impl ControlKit {
134
let body = self.send(method, params).await?;
135
136
if let Some(error) = body.get("error") {
137
- let runner_info = if error.get("code").and_then(serde_json::Value::as_i64)
138
- == Some(CONTROLKIT_METHOD_NOT_FOUND)
139
- && method != "device.info"
140
- {
141
- self.send("device.info", serde_json::json!({}))
142
- .await
143
- .ok()
144
- .and_then(|body| body.get("result").cloned())
145
- } else {
146
- None
147
- };
137
+ let (runner_info, runner_info_error) =
138
+ if error.get("code").and_then(serde_json::Value::as_i64)
139
+ == Some(CONTROLKIT_METHOD_NOT_FOUND)
140
+ && method != "device.info"
141
+ {
142
+ match self.send("device.info", serde_json::json!({})).await {
143
+ Ok(body) => match body.get("result") {
144
+ Some(result) => (Some(result.clone()), None),
145
+ None => {
146
+ let reason = body
147
+ .get("error")
148
+ .map(|error| format!("device.info returned {error}"))
149
+ .unwrap_or_else(|| {
150
+ "device.info response did not contain a result".to_string()
151
+ });
152
+ (None, Some(reason))
153
+ }
154
+ },
155
+ Err(error) => (None, Some(error.to_string())),
156
+ }
157
+ } else {
158
+ (None, None)
159
+ };
160
return Err(anyhow!(controlkit_rpc_error(
161
method,
162
error,
151
- runner_info.as_ref()
163
+ runner_info.as_ref(),
164
+ runner_info_error.as_deref()
165
)));
166
}
167
@@ -190,6 +203,7 @@ fn controlkit_rpc_error(
203
method: &str,
204
error: &serde_json::Value,
205
runner_info: Option<&serde_json::Value>,
206
+ runner_info_error: Option<&str>,
207
) -> String {
208
let code = error
209
.get("code")
@@ -207,15 +221,19 @@ fn controlkit_rpc_error(
221
let runner = runner_info
222
.and_then(|info| info.get("runner"))
223
.and_then(serde_json::Value::as_str)
210
- .unwrap_or("unknown ControlKit runner");
224
+ .unwrap_or("The connected ControlKit runner");
225
let protocol = runner_info
226
.and_then(|info| info.get("protocolVersion"))
227
.and_then(serde_json::Value::as_u64)
228
.map(|version| format!(" protocol {version}"))
229
.unwrap_or_default();
230
231
+ let runner_info_note = runner_info_error
232
+ .map(|error| format!(" Runner metadata could not be read: {error}."))
233
+ .unwrap_or_default();
234
+
235
format!(
218
- "{runner}{protocol} does not implement ControlKit method '{method}'. Rebuild or upgrade xcrs-controlkit, restart its runner, and retry."
236
+ "{runner}{protocol} does not implement ControlKit method '{method}'.{runner_info_note} Rebuild or upgrade xcrs-controlkit, restart its runner, and retry."
237
)
238
}
239
@@ -1343,13 +1361,33 @@ mod tests {
1361
"protocolVersion": 1
1362
});
1363
1346
- let message = controlkit_rpc_error("device.dump.ui", &error, Some(&runner_info));
1364
+ let message = controlkit_rpc_error("device.dump.ui", &error, Some(&runner_info), None);
1365
1366
assert!(message.contains("XCRSControlKit protocol 1"));
1367
assert!(message.contains("device.dump.ui"));
1368
assert!(message.contains("Rebuild or upgrade"));
1369
}
1370
1371
+ #[test]
1372
+ fn reports_when_controlkit_runner_metadata_is_unavailable() {
1373
+ let error = serde_json::json!({
1374
+ "code": -32601,
1375
+ "message": "Method not found"
1376
+ });
1377
+
1378
+ let message = controlkit_rpc_error(
1379
+ "device.dump.ui",
1380
+ &error,
1381
+ None,
1382
+ Some("device.info returned a JSON-RPC error"),
1383
+ );
1384
+
1385
+ assert!(message.contains("The connected ControlKit runner"));
1386
+ assert!(message.contains("Runner metadata could not be read"));
1387
+ assert!(message.contains("device.info returned a JSON-RPC error"));
1388
+ assert!(!message.contains("unknown ControlKit runner"));
1389
+ }
1390
+
1391
#[test]
1392
fn extracts_identified_visible_controlkit_elements() {
1393
let hierarchy = serde_json::json!({