use super::jsonrpc::{JsonRpcError, JsonRpcResponse};
use serde_json::{Value, json};
pub const PARSE_ERROR: i32 = -32700;
pub const INVALID_REQUEST: i32 = -32600;
pub const METHOD_NOT_FOUND: i32 = -32601;
pub const INVALID_PARAMS: i32 = -32602;
pub const INTERNAL_ERROR: i32 = -32603;
pub const SERVER_ERROR_START: i32 = -32099;
pub const SERVER_ERROR_END: i32 = -32000;
pub const UNKNOWN_ERROR_CODE: i32 = -32001;
pub const CONNECTION_CLOSED: i32 = -32050;
pub const TRANSPORT_ERROR: i32 = -32051;
pub const SERVER_CANCELLED: i32 = -32802;
pub const CONTENT_MODIFIED: i32 = -32801;
pub const REQUEST_CANCELLED: i32 = -32800;
pub const REQUEST_FAILED: i32 = -32803;
pub const SERVER_NOT_INITIALIZED: i32 = -32002;
pub fn cancelled_response(id: &Value) -> JsonRpcResponse {
JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: Some(id.clone()),
result: None,
error: Some(JsonRpcError {
code: REQUEST_CANCELLED,
message: "Request cancelled".into(),
data: None,
}),
}
}
pub fn cancelled_response_with_method(id: &Value, method: &str) -> JsonRpcResponse {
let provider_name = method.split('/').next_back().unwrap_or(method);
let message = format!("Request cancelled - {} provider", provider_name);
let data = json!({
"provider": method,
"request_id": id.clone(),
"timestamp": std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
});
JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: Some(id.clone()),
result: None,
error: Some(JsonRpcError { code: REQUEST_CANCELLED, message, data: Some(data) }),
}
}
pub fn request_cancelled_error() -> JsonRpcError {
JsonRpcError { code: REQUEST_CANCELLED, message: "Request cancelled".to_string(), data: None }
}
pub fn server_cancelled_error() -> JsonRpcError {
JsonRpcError {
code: SERVER_CANCELLED,
message: "Server cancelled the request".to_string(),
data: None,
}
}
pub fn enhanced_error(
code: i32,
message: &str,
error_type: &str,
method: Option<&str>,
) -> JsonRpcError {
let mut data = json!({
"error_type": error_type,
"context": "Enhanced LSP error response with comprehensive context",
"server_info": {
"name": "perl-lsp",
"version": env!("CARGO_PKG_VERSION"),
"capabilities": "Enhanced error handling and concurrent request management"
},
"timestamp": std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
});
if let Some(method_name) = method {
data["method"] = json!(method_name);
}
JsonRpcError { code, message: message.to_string(), data: Some(data) }
}
pub fn method_not_found(method: &str) -> JsonRpcError {
JsonRpcError {
code: METHOD_NOT_FOUND,
message: format!("Method not found: {}", method),
data: None,
}
}
pub fn method_not_advertised() -> JsonRpcError {
JsonRpcError {
code: METHOD_NOT_FOUND,
message: "Method not advertised in server capabilities".to_string(),
data: None,
}
}
pub fn invalid_params(message: &str) -> JsonRpcError {
JsonRpcError { code: INVALID_PARAMS, message: message.to_string(), data: None }
}
pub fn server_not_initialized() -> JsonRpcError {
JsonRpcError {
code: SERVER_NOT_INITIALIZED,
message: "Server not initialized".to_string(),
data: None,
}
}
pub fn document_not_found_error() -> Value {
json!({
"status": "error",
"message": "Document not found"
})
}
pub fn internal_error(message: &str) -> JsonRpcError {
JsonRpcError { code: INTERNAL_ERROR, message: message.to_string(), data: None }
}
pub fn connection_closed_error() -> JsonRpcError {
JsonRpcError { code: CONNECTION_CLOSED, message: "Connection closed".to_string(), data: None }
}
pub fn transport_error(message: &str) -> JsonRpcError {
JsonRpcError { code: TRANSPORT_ERROR, message: message.to_string(), data: None }
}
pub fn req_uri(params: &Value) -> Result<&str, JsonRpcError> {
params
.pointer("/textDocument/uri")
.and_then(|v| v.as_str())
.ok_or_else(|| invalid_params("Missing required parameter: textDocument.uri"))
}
pub fn req_position(params: &Value) -> Result<(u32, u32), JsonRpcError> {
let line_u64 = params
.pointer("/position/line")
.and_then(|v| v.as_u64())
.ok_or_else(|| invalid_params("Missing required parameter: position.line"))?;
let line =
u32::try_from(line_u64).map_err(|_| invalid_params("position.line exceeds u32::MAX"))?;
let character_u64 = params
.pointer("/position/character")
.and_then(|v| v.as_u64())
.ok_or_else(|| invalid_params("Missing required parameter: position.character"))?;
let character = u32::try_from(character_u64)
.map_err(|_| invalid_params("position.character exceeds u32::MAX"))?;
Ok((line, character))
}
pub fn req_range(params: &Value) -> Result<((u32, u32), (u32, u32)), JsonRpcError> {
let start_line_u64 = params
.pointer("/range/start/line")
.and_then(|v| v.as_u64())
.ok_or_else(|| invalid_params("Missing required parameter: range.start.line"))?;
let start_line = u32::try_from(start_line_u64)
.map_err(|_| invalid_params("range.start.line exceeds u32::MAX"))?;
let start_char_u64 = params
.pointer("/range/start/character")
.and_then(|v| v.as_u64())
.ok_or_else(|| invalid_params("Missing required parameter: range.start.character"))?;
let start_char = u32::try_from(start_char_u64)
.map_err(|_| invalid_params("range.start.character exceeds u32::MAX"))?;
let end_line_u64 = params
.pointer("/range/end/line")
.and_then(|v| v.as_u64())
.ok_or_else(|| invalid_params("Missing required parameter: range.end.line"))?;
let end_line = u32::try_from(end_line_u64)
.map_err(|_| invalid_params("range.end.line exceeds u32::MAX"))?;
let end_char_u64 = params
.pointer("/range/end/character")
.and_then(|v| v.as_u64())
.ok_or_else(|| invalid_params("Missing required parameter: range.end.character"))?;
let end_char = u32::try_from(end_char_u64)
.map_err(|_| invalid_params("range.end.character exceeds u32::MAX"))?;
Ok(((start_line, start_char), (end_line, end_char)))
}