use std::{
error::Error,
fmt::{self, Display},
};
use rmpv::Value;
use tokio::sync::oneshot;
#[derive(Debug)]
pub struct NeovimError {
pub method: &'static str,
pub error_type: Option<i64>,
pub message: Value,
}
impl NeovimError {
pub fn new(method: &'static str, error: Value) -> Self {
let (error_type, message) = match error {
Value::Array(mut values) => (
values.first().and_then(Value::as_i64),
if values.len() >= 2 {
values.swap_remove(1)
} else {
Value::Nil
},
),
v => (None, v),
};
Self {
method,
error_type,
message,
}
}
}
impl Display for NeovimError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
fmt,
"Neovim returned error for request {:?}: {} (type={:?})",
self.method, self.message, self.error_type,
)
}
}
impl Error for NeovimError {}
#[derive(Debug)]
pub struct ResponseReceiveError {
pub method: &'static str,
pub error: oneshot::error::RecvError,
}
impl ResponseReceiveError {
pub fn new(method: &'static str, error: oneshot::error::RecvError) -> Self {
Self { method, error }
}
}
impl Display for ResponseReceiveError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
fmt,
"Could not receive response for request '{}': {}",
self.method, self.error
)
}
}
impl Error for ResponseReceiveError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.error)
}
}
#[derive(Debug)]
pub struct TypeError {
pub method: &'static str,
pub expected: &'static str,
pub actual: Value,
}
impl TypeError {
pub fn new(method: &'static str, expected: &'static str, actual: Value) -> Self {
Self {
method,
expected,
actual,
}
}
}
impl Display for TypeError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
fmt,
"Wrong type of return value for request '{}': expected {}, got {}",
self.method, self.expected, self.actual
)
}
}
impl Error for TypeError {}
#[derive(Debug)]
pub struct MessageIdNotFound {
pub msgid: u32,
}
impl MessageIdNotFound {
pub fn new(msgid: u32) -> Self {
Self { msgid }
}
}
impl Display for MessageIdNotFound {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "Unknown message ID {}", self.msgid)
}
}
impl Error for MessageIdNotFound {}
#[derive(Debug)]
pub struct ResponseSendError {
pub msgid: u32,
pub response: Result<Value, Value>,
}
impl ResponseSendError {
pub fn new(msgid: u32, response: Result<Value, Value>) -> Self {
Self { msgid, response }
}
}
impl Display for ResponseSendError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
fmt,
"Could not send response for message ID {}: {:?}",
self.msgid, self.response
)
}
}
impl Error for ResponseSendError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn neovim_error_extracts_type_and_message_from_error_array() {
let err = NeovimError::new(
"nvim_cmd",
Value::Array(vec![Value::from(1), Value::from("failed")]),
);
assert_eq!(err.method, "nvim_cmd");
assert_eq!(err.error_type, Some(1));
assert_eq!(err.message, Value::from("failed"));
}
#[test]
fn neovim_error_uses_string_value_as_message_without_error_type() {
let err = NeovimError::new("nvim_cmd", Value::from("failed"));
assert_eq!(err.method, "nvim_cmd");
assert_eq!(err.error_type, None);
assert_eq!(err.message, Value::from("failed"));
}
#[test]
fn neovim_error_uses_nil_message_when_message_is_missing() {
let err = NeovimError::new("nvim_cmd", Value::Array(vec![Value::from(1)]));
assert_eq!(err.method, "nvim_cmd");
assert_eq!(err.error_type, Some(1));
assert_eq!(err.message, Value::Nil);
}
#[tokio::test]
async fn response_receive_error_retains_method_and_source() {
let (sender, receiver) = oneshot::channel::<()>();
drop(sender);
let source = receiver.await.unwrap_err();
let err = ResponseReceiveError::new("nvim_input", source);
assert_eq!(err.method, "nvim_input");
assert!(err.source().is_some());
}
#[test]
fn type_error_retains_expected_type_and_actual_value() {
let err = TypeError::new("nvim_get_api_info", "array", Value::Nil);
assert_eq!(err.method, "nvim_get_api_info");
assert_eq!(err.expected, "array");
assert_eq!(err.actual, Value::Nil);
}
#[test]
fn response_send_error_retains_response() {
let err = ResponseSendError::new(7, Err(Value::from("failed")));
assert_eq!(err.msgid, 7);
assert_eq!(err.response, Err(Value::from("failed")));
}
}