use grex_core::Cancelled;
use rmcp::{
model::{CallToolResult, Content, ErrorCode},
ErrorData,
};
use serde_json::{json, Value};
pub const REQUEST_CANCELLED: i32 = -32800;
pub const INVALID_PARAMS: i32 = -32602;
pub const MANIFEST_ERROR: i32 = -32001;
pub const POLICY_ERROR: i32 = -32002;
pub const LOCK_ERROR: i32 = -32003;
pub const DRIFT_ERROR: i32 = -32004;
pub const PLUGIN_MISSING: i32 = -32005;
impl From<CancelledExt> for ErrorData {
fn from(_: CancelledExt) -> Self {
ErrorData::new(ErrorCode(REQUEST_CANCELLED), "request cancelled", None)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CancelledExt;
impl From<Cancelled> for CancelledExt {
fn from(_: Cancelled) -> Self {
CancelledExt
}
}
pub fn packop_error(message: &str) -> CallToolResult {
let body = json!({
"code": POLICY_ERROR,
"data": { "kind": "pack_op" },
"message": message,
});
CallToolResult::error(vec![Content::text(body.to_string())])
}
pub fn init_state_error(message: impl Into<String>) -> ErrorData {
let data: Value = json!({ "kind": "init_state" });
ErrorData::new(ErrorCode(POLICY_ERROR), message.into(), Some(data))
}
pub fn not_implemented_result(verb: &str) -> CallToolResult {
let body = json!({
"code": -32601,
"data": { "kind": "not_implemented" },
"message": format!("verb `{verb}` not yet implemented in M7-1; planned for M7-4"),
});
CallToolResult::error(vec![Content::text(body.to_string())])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cancelled_maps_to_minus_32800() {
let err: ErrorData = CancelledExt::from(Cancelled).into();
assert_eq!(err.code.0, REQUEST_CANCELLED);
assert!(err.message.contains("cancel"));
}
#[test]
fn packop_failure_maps_to_minus_32002_with_kind_pack_op() {
let r = packop_error("disk full");
assert_eq!(r.is_error, Some(true));
let text = r.content.first().expect("content").as_text().expect("text").text.clone();
let v: Value = serde_json::from_str(&text).expect("payload is JSON");
assert_eq!(v["code"], json!(POLICY_ERROR));
assert_eq!(v["data"]["kind"], json!("pack_op"));
assert!(v["message"].as_str().unwrap().contains("disk full"));
}
#[test]
fn init_state_failure_maps_to_minus_32002_with_kind_init_state() {
let err = init_state_error("not initialised");
assert_eq!(err.code.0, POLICY_ERROR);
let data = err.data.as_ref().expect("data attached");
assert_eq!(data["kind"], json!("init_state"));
}
#[test]
fn not_implemented_envelope_carries_minus_32601_and_kind() {
let r = not_implemented_result("ls");
assert_eq!(r.is_error, Some(true));
let text = r.content.first().expect("content").as_text().expect("text").text.clone();
let v: Value = serde_json::from_str(&text).expect("payload is JSON");
assert_eq!(v["code"], json!(-32601));
assert_eq!(v["data"]["kind"], json!("not_implemented"));
assert!(v["message"].as_str().unwrap().contains("ls"));
}
}