use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LspRequest {
pub jsonrpc: String,
pub id: u64,
pub method: String,
pub params: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LspResponse {
pub jsonrpc: String,
pub id: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<LspError>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LspNotification {
pub jsonrpc: String,
pub method: String,
pub params: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LspError {
pub code: i32,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeParams {
pub process_id: u32,
pub root_uri: String,
pub capabilities: ClientCapabilities,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientCapabilities {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentSymbolParams {
pub text_document: TextDocumentIdentifier,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextDocumentIdentifier {
pub uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TextDocumentItem {
pub uri: String,
pub language_id: String,
pub version: i32,
pub text: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentSymbol {
pub name: String,
pub kind: SymbolKind,
pub range: LspRange,
pub selection_range: LspRange,
#[serde(skip_serializing_if = "Option::is_none")]
pub children: Option<Vec<DocumentSymbol>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LspRange {
pub start: LspPosition,
pub end: LspPosition,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LspPosition {
pub line: u32,
pub character: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum SymbolKind {
File = 1,
Module = 2,
Namespace = 3,
Package = 4,
Class = 5,
Method = 6,
Property = 7,
Field = 8,
Constructor = 9,
Enum = 10,
Interface = 11,
Function = 12,
Variable = 13,
Constant = 14,
String = 15,
Number = 16,
Boolean = 17,
Array = 18,
Object = 19,
Key = 20,
Null = 21,
EnumMember = 22,
Struct = 23,
Event = 24,
Operator = 25,
TypeParameter = 26,
}
impl Serialize for SymbolKind {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u8(*self as u8)
}
}
impl<'de> Deserialize<'de> for SymbolKind {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = u8::deserialize(deserializer)?;
match value {
1 => Ok(Self::File),
2 => Ok(Self::Module),
3 => Ok(Self::Namespace),
4 => Ok(Self::Package),
5 => Ok(Self::Class),
6 => Ok(Self::Method),
7 => Ok(Self::Property),
8 => Ok(Self::Field),
9 => Ok(Self::Constructor),
10 => Ok(Self::Enum),
11 => Ok(Self::Interface),
12 => Ok(Self::Function),
13 => Ok(Self::Variable),
14 => Ok(Self::Constant),
15 => Ok(Self::String),
16 => Ok(Self::Number),
17 => Ok(Self::Boolean),
18 => Ok(Self::Array),
19 => Ok(Self::Object),
20 => Ok(Self::Key),
21 => Ok(Self::Null),
22 => Ok(Self::EnumMember),
23 => Ok(Self::Struct),
24 => Ok(Self::Event),
25 => Ok(Self::Operator),
26 => Ok(Self::TypeParameter),
other => Err(serde::de::Error::custom(format!(
"unknown SymbolKind value: {other}"
))),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Diagnostic {
pub range: LspRange,
pub severity: Option<DiagnosticSeverity>,
pub code: Option<Value>,
pub source: Option<String>,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub related_information: Option<Vec<DiagnosticRelatedInformation>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[repr(u8)]
pub enum DiagnosticSeverity {
Error = 1,
Warning = 2,
Information = 3,
Hint = 4,
}
impl<'de> serde::Deserialize<'de> for DiagnosticSeverity {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let n = u8::deserialize(deserializer)?;
match n {
1 => Ok(Self::Error),
2 => Ok(Self::Warning),
3 => Ok(Self::Information),
4 => Ok(Self::Hint),
other => Err(serde::de::Error::custom(format!(
"unknown DiagnosticSeverity value: {other}"
))),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiagnosticRelatedInformation {
pub location: Location,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Location {
pub uri: String,
pub range: LspRange,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_serialize_initialize_request() {
let req = LspRequest {
jsonrpc: "2.0".into(),
id: 1,
method: "initialize".into(),
params: serde_json::to_value(InitializeParams {
process_id: 1234,
root_uri: "file:///tmp/project".into(),
capabilities: ClientCapabilities {},
})
.unwrap(),
};
let json = serde_json::to_value(&req).unwrap();
assert_eq!(json["jsonrpc"], "2.0");
assert_eq!(json["id"], 1);
assert_eq!(json["method"], "initialize");
assert_eq!(json["params"]["processId"], 1234);
assert_eq!(json["params"]["rootUri"], "file:///tmp/project");
}
#[test]
fn test_deserialize_document_symbol() {
let json = json!({
"name": "main",
"kind": 12,
"range": {
"start": { "line": 0, "character": 0 },
"end": { "line": 10, "character": 1 }
},
"selectionRange": {
"start": { "line": 0, "character": 3 },
"end": { "line": 0, "character": 7 }
},
"children": [
{
"name": "inner",
"kind": 12,
"range": {
"start": { "line": 2, "character": 4 },
"end": { "line": 5, "character": 5 }
},
"selectionRange": {
"start": { "line": 2, "character": 7 },
"end": { "line": 2, "character": 12 }
}
}
],
"detail": "fn main()"
});
let sym: DocumentSymbol = serde_json::from_value(json).unwrap();
assert_eq!(sym.name, "main");
assert_eq!(sym.kind, SymbolKind::Function);
assert_eq!(sym.range.start.line, 0);
assert_eq!(sym.range.end.line, 10);
assert_eq!(sym.detail.as_deref(), Some("fn main()"));
let children = sym.children.unwrap();
assert_eq!(children.len(), 1);
assert_eq!(children[0].name, "inner");
}
#[test]
fn test_symbol_kind_values() {
let kinds = [
(SymbolKind::File, 1u8),
(SymbolKind::Module, 2),
(SymbolKind::Class, 5),
(SymbolKind::Method, 6),
(SymbolKind::Function, 12),
(SymbolKind::Variable, 13),
(SymbolKind::Struct, 23),
(SymbolKind::TypeParameter, 26),
];
for (kind, expected_num) in kinds {
let serialized = serde_json::to_value(kind).unwrap();
assert_eq!(serialized, json!(expected_num), "SymbolKind::{kind:?}");
let deserialized: SymbolKind = serde_json::from_value(json!(expected_num)).unwrap();
assert_eq!(deserialized, kind);
}
}
#[test]
fn test_document_symbol_params_round_trip() {
let params = DocumentSymbolParams {
text_document: TextDocumentIdentifier {
uri: "file:///src/main.rs".into(),
},
};
let serialized = serde_json::to_value(¶ms).unwrap();
assert_eq!(serialized["textDocument"]["uri"], "file:///src/main.rs");
let rt: DocumentSymbolParams = serde_json::from_value(serialized).unwrap();
assert_eq!(rt.text_document.uri, "file:///src/main.rs");
}
#[test]
fn test_lsp_error_deserialize() {
let json = json!({
"code": -32601,
"message": "Method not found"
});
let err: LspError = serde_json::from_value(json).unwrap();
assert_eq!(err.code, -32601);
assert_eq!(err.message, "Method not found");
let serialized = serde_json::to_value(&err).unwrap();
assert_eq!(serialized["code"], -32601);
assert_eq!(serialized["message"], "Method not found");
}
}