openvcs_core/
plugin_protocol.rs

1use crate::models::VcsEvent;
2use serde::{Deserialize, Serialize};
3
4#[cfg(feature = "plugin-protocol")]
5use serde_json::Value;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct RpcRequest {
9    pub id: u64,
10    pub method: String,
11    #[serde(default)]
12    pub params: Value,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct RpcResponse {
17    pub id: u64,
18    pub ok: bool,
19    #[serde(default)]
20    pub result: Value,
21    #[serde(default)]
22    pub error: Option<String>,
23    #[serde(default, skip_serializing_if = "Option::is_none")]
24    pub error_code: Option<String>,
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub error_data: Option<Value>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30#[serde(untagged)]
31pub enum PluginMessage {
32    Request(RpcRequest),
33    Response(RpcResponse),
34    Event { event: VcsEvent },
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn rpc_request_params_defaults_to_null() {
43        let req: RpcRequest =
44            serde_json::from_str(r#"{"id":1,"method":"ping"}"#).expect("valid request");
45        assert_eq!(req.id, 1);
46        assert_eq!(req.method, "ping");
47        assert_eq!(req.params, serde_json::Value::Null);
48    }
49
50    #[test]
51    fn rpc_response_fields_default_when_missing() {
52        let resp: RpcResponse =
53            serde_json::from_str(r#"{"id":9,"ok":true}"#).expect("valid response");
54        assert_eq!(resp.id, 9);
55        assert!(resp.ok);
56        assert_eq!(resp.result, serde_json::Value::Null);
57        assert!(resp.error.is_none());
58        assert!(resp.error_code.is_none());
59        assert!(resp.error_data.is_none());
60    }
61
62    #[test]
63    fn rpc_response_skips_optional_error_fields_when_none() {
64        let resp = RpcResponse {
65            id: 1,
66            ok: false,
67            result: serde_json::Value::Null,
68            error: Some("boom".into()),
69            error_code: None,
70            error_data: None,
71        };
72
73        let value = serde_json::to_value(&resp).expect("serializes");
74        assert!(value.get("error").is_some());
75        assert!(value.get("error_code").is_none());
76        assert!(value.get("error_data").is_none());
77    }
78
79    #[test]
80    fn plugin_message_deserializes_event_variant() {
81        let msg: PluginMessage = serde_json::from_str(r#"{"event":{"type":"info","msg":"hello"}}"#)
82            .expect("valid event message");
83
84        match msg {
85            PluginMessage::Event { event } => match event {
86                VcsEvent::Info { msg } => assert_eq!(msg, "hello"),
87                other => panic!("unexpected event: {other:?}"),
88            },
89            other => panic!("unexpected message: {other:?}"),
90        }
91    }
92}