Skip to main content

cgraph/ipc/
protocol.rs

1use serde::{Deserialize, Serialize};
2
3use crate::state::{HierarchyKind, SourceLocation};
4
5pub const PROTOCOL_VERSION: u16 = 1;
6
7/// Versioned wrapper independent of the eventual socket framing strategy.
8#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
9pub struct Envelope<T> {
10    pub version: u16,
11    pub request_id: Option<u64>,
12    pub payload: T,
13}
14
15impl<T> Envelope<T> {
16    pub fn new(request_id: Option<u64>, payload: T) -> Self {
17        Self {
18            version: PROTOCOL_VERSION,
19            request_id,
20            payload,
21        }
22    }
23}
24
25#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
26#[serde(tag = "type", rename_all = "snake_case")]
27pub enum IpcRequest {
28    FocusSymbol {
29        hierarchy: HierarchyKind,
30        symbol: String,
31        location: Option<SourceLocation>,
32    },
33}
34
35#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
36#[serde(tag = "type", rename_all = "snake_case")]
37pub enum IpcResponse {
38    Accepted,
39    Error { message: String },
40}
41
42#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
43#[serde(tag = "type", rename_all = "snake_case")]
44pub enum IpcEvent {
45    /// Opens an LSP source position. Line and UTF-16 character coordinates are
46    /// zero-based; editor adapters convert them to their UI coordinate system.
47    OpenLocation {
48        uri: String,
49        line: u32,
50        character: u32,
51    },
52}
53
54#[cfg(test)]
55mod tests {
56    use super::{Envelope, IpcEvent, IpcRequest, PROTOCOL_VERSION};
57    use crate::state::HierarchyKind;
58
59    #[test]
60    fn serializes_a_versioned_tagged_request() {
61        let message = Envelope::new(
62            Some(7),
63            IpcRequest::FocusSymbol {
64                hierarchy: HierarchyKind::Call,
65                symbol: "run".to_owned(),
66                location: None,
67            },
68        );
69
70        let value = serde_json::to_value(message).unwrap();
71        assert_eq!(value["version"], PROTOCOL_VERSION);
72        assert_eq!(value["request_id"], 7);
73        assert_eq!(value["payload"]["type"], "focus_symbol");
74    }
75
76    #[test]
77    fn serializes_open_location_with_zero_based_coordinates() {
78        let message = Envelope::new(
79            None,
80            IpcEvent::OpenLocation {
81                uri: "file:///workspace/main.py".to_owned(),
82                line: 4,
83                character: 2,
84            },
85        );
86
87        let encoded = serde_json::to_string(&message).unwrap();
88        assert!(!encoded.contains('\n'));
89        let value: serde_json::Value = serde_json::from_str(&encoded).unwrap();
90        assert_eq!(value["version"], PROTOCOL_VERSION);
91        assert!(value["request_id"].is_null());
92        assert_eq!(value["payload"]["type"], "open_location");
93        assert_eq!(value["payload"]["line"], 4);
94        assert_eq!(value["payload"]["character"], 2);
95    }
96}