magi-code 0.96.2

Repository-aware CLI coding agent for terminal work
Documentation
use serde::{Deserialize, Deserializer, Serialize, de};
use serde_json::Value;

pub(crate) const METHOD_NOT_FOUND: i64 = -32601;

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum RequestId {
    Number(i64),
    String(String),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct JsonRpcRequest {
    pub(crate) jsonrpc: String,
    pub(crate) id: RequestId,
    pub(crate) method: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub(crate) params: Option<Value>,
}

impl JsonRpcRequest {
    pub(crate) fn new(id: RequestId, method: impl Into<String>, params: Option<Value>) -> Self {
        Self {
            jsonrpc: "2.0".to_string(),
            id,
            method: method.into(),
            params,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct JsonRpcResponse {
    pub(crate) jsonrpc: String,
    pub(crate) id: RequestId,
    pub(crate) result: Value,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct JsonRpcError {
    pub(crate) jsonrpc: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub(crate) id: Option<RequestId>,
    pub(crate) error: ErrorData,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct JsonRpcNotification {
    pub(crate) jsonrpc: String,
    pub(crate) method: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub(crate) params: Option<Value>,
}

impl JsonRpcNotification {
    pub(crate) fn new(method: impl Into<String>, params: Option<Value>) -> Self {
        Self {
            jsonrpc: "2.0".to_string(),
            method: method.into(),
            params,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct ErrorData {
    pub(crate) code: i64,
    pub(crate) message: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub(crate) data: Option<Value>,
}

#[derive(Debug, Clone, PartialEq)]
pub(crate) enum JsonRpcMessage {
    Response(JsonRpcResponse),
    Error(JsonRpcError),
    Notification(JsonRpcNotification),
    Request(JsonRpcRequest),
}

impl<'de> Deserialize<'de> for JsonRpcMessage {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = Value::deserialize(deserializer)?;
        if value.get("jsonrpc").and_then(Value::as_str) != Some("2.0") {
            return Err(de::Error::custom(
                "JSON-RPC message missing jsonrpc \"2.0\"",
            ));
        }
        #[derive(Deserialize)]
        #[serde(untagged)]
        enum UntaggedMessage {
            Response(JsonRpcResponse),
            Error(JsonRpcError),
            Notification(JsonRpcNotification),
            Request(JsonRpcRequest),
        }
        match UntaggedMessage::deserialize(value).map_err(de::Error::custom)? {
            UntaggedMessage::Response(message) => Ok(JsonRpcMessage::Response(message)),
            UntaggedMessage::Error(message) => Ok(JsonRpcMessage::Error(message)),
            UntaggedMessage::Notification(message) => Ok(JsonRpcMessage::Notification(message)),
            UntaggedMessage::Request(message) => Ok(JsonRpcMessage::Request(message)),
        }
    }
}