fix(client): add error enum while deal client info (#76)
1. wrap the error type for more standardized 2. add more information in error for debug trace 3. wrap helper func for more user-friendly code Signed-off-by: jokemanfire <hu.dingyang@zte.com.cn>
This commit is contained in:
parent
2c0cafdde3
commit
4ca00c2052
1 changed files with 80 additions and 28 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, Stream, StreamExt};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
|
|
@ -6,15 +7,67 @@ use crate::model::{
|
||||||
CancelledNotificationParam, ClientInfo, ClientMessage, ClientNotification, ClientRequest,
|
CancelledNotificationParam, ClientInfo, ClientMessage, ClientNotification, ClientRequest,
|
||||||
ClientResult, CompleteRequest, CompleteRequestParam, CompleteResult, GetPromptRequest,
|
ClientResult, CompleteRequest, CompleteRequestParam, CompleteResult, GetPromptRequest,
|
||||||
GetPromptRequestParam, GetPromptResult, InitializeRequest, InitializedNotification,
|
GetPromptRequestParam, GetPromptResult, InitializeRequest, InitializedNotification,
|
||||||
ListPromptsRequest, ListPromptsResult, ListResourceTemplatesRequest,
|
JsonRpcResponse, ListPromptsRequest, ListPromptsResult, ListResourceTemplatesRequest,
|
||||||
ListResourceTemplatesResult, ListResourcesRequest, ListResourcesResult, ListToolsRequest,
|
ListResourceTemplatesResult, ListResourcesRequest, ListResourcesResult, ListToolsRequest,
|
||||||
ListToolsResult, PaginatedRequestParam, PaginatedRequestParamInner, ProgressNotification,
|
ListToolsResult, PaginatedRequestParam, PaginatedRequestParamInner, ProgressNotification,
|
||||||
ProgressNotificationParam, ReadResourceRequest, ReadResourceRequestParam, ReadResourceResult,
|
ProgressNotificationParam, ReadResourceRequest, ReadResourceRequestParam, ReadResourceResult,
|
||||||
RootsListChangedNotification, ServerInfo, ServerNotification, ServerRequest, ServerResult,
|
RequestId, RootsListChangedNotification, ServerInfo, ServerJsonRpcMessage, ServerNotification,
|
||||||
SetLevelRequest, SetLevelRequestParam, SubscribeRequest, SubscribeRequestParam,
|
ServerRequest, ServerResult, SetLevelRequest, SetLevelRequestParam, SubscribeRequest,
|
||||||
UnsubscribeRequest, UnsubscribeRequestParam,
|
SubscribeRequestParam, UnsubscribeRequest, UnsubscribeRequestParam,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// It represents the error that may occur when serving the client.
|
||||||
|
///
|
||||||
|
/// if you want to handle the error, you can use `serve_client_with_ct` or `serve_client` with `Result<RunningService<RoleClient, S>, ClientError>`
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ClientError {
|
||||||
|
#[error("expect initialized response, but received: {0:?}")]
|
||||||
|
ExpectedInitResponse(Option<ServerJsonRpcMessage>),
|
||||||
|
|
||||||
|
#[error("expect initialized result, but received: {0:?}")]
|
||||||
|
ExpectedInitResult(Option<ServerResult>),
|
||||||
|
|
||||||
|
#[error("conflict initialized response id: expected {0}, got {1}")]
|
||||||
|
ConflictInitResponseId(RequestId, RequestId),
|
||||||
|
|
||||||
|
#[error("connection closed: {0}")]
|
||||||
|
ConnectionClosed(String),
|
||||||
|
|
||||||
|
#[error("IO error: {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to get the next message from the stream
|
||||||
|
async fn expect_next_message<S>(
|
||||||
|
stream: &mut S,
|
||||||
|
context: &str,
|
||||||
|
) -> Result<ServerJsonRpcMessage, ClientError>
|
||||||
|
where
|
||||||
|
S: Stream<Item = ServerJsonRpcMessage> + Unpin,
|
||||||
|
{
|
||||||
|
stream
|
||||||
|
.next()
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| ClientError::ConnectionClosed(context.to_string()))
|
||||||
|
.map_err(|e| ClientError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to expect a response from the stream
|
||||||
|
async fn expect_response<S>(
|
||||||
|
stream: &mut S,
|
||||||
|
context: &str,
|
||||||
|
) -> Result<(ServerResult, RequestId), ClientError>
|
||||||
|
where
|
||||||
|
S: Stream<Item = ServerJsonRpcMessage> + Unpin,
|
||||||
|
{
|
||||||
|
let msg = expect_next_message(stream, context).await?;
|
||||||
|
|
||||||
|
match msg {
|
||||||
|
ServerJsonRpcMessage::Response(JsonRpcResponse { id, result, .. }) => Ok((result, id)),
|
||||||
|
_ => Err(ClientError::ExpectedInitResponse(Some(msg))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
pub struct RoleClient;
|
pub struct RoleClient;
|
||||||
|
|
||||||
|
|
@ -74,6 +127,15 @@ where
|
||||||
let mut sink = Box::pin(sink);
|
let mut sink = Box::pin(sink);
|
||||||
let mut stream = Box::pin(stream);
|
let mut stream = Box::pin(stream);
|
||||||
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
|
let id_provider = <Arc<AtomicU32RequestIdProvider>>::default();
|
||||||
|
|
||||||
|
// Convert ClientError to std::io::Error, then to E
|
||||||
|
let handle_client_error = |e: ClientError| -> E {
|
||||||
|
match e {
|
||||||
|
ClientError::Io(io_err) => io_err.into(),
|
||||||
|
other => std::io::Error::new(std::io::ErrorKind::Other, format!("{}", other)).into(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// service
|
// service
|
||||||
let id = id_provider.next_request_id();
|
let id = id_provider.next_request_id();
|
||||||
let init_request = InitializeRequest {
|
let init_request = InitializeRequest {
|
||||||
|
|
@ -85,34 +147,24 @@ where
|
||||||
.into_json_rpc_message(),
|
.into_json_rpc_message(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (response, response_id) = stream
|
|
||||||
.next()
|
let (response, response_id) = expect_response(&mut stream, "initialize response")
|
||||||
.await
|
.await
|
||||||
.ok_or(std::io::Error::new(
|
.map_err(handle_client_error)?;
|
||||||
std::io::ErrorKind::UnexpectedEof,
|
|
||||||
"expect initialize response",
|
|
||||||
))?
|
|
||||||
.into_message()
|
|
||||||
.into_result()
|
|
||||||
.ok_or(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::InvalidData,
|
|
||||||
"expect initialize result",
|
|
||||||
))?;
|
|
||||||
if id != response_id {
|
if id != response_id {
|
||||||
return Err(std::io::Error::new(
|
return Err(handle_client_error(ClientError::ConflictInitResponseId(
|
||||||
std::io::ErrorKind::InvalidData,
|
id,
|
||||||
"conflict initialize response id",
|
response_id,
|
||||||
)
|
)));
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
let response = response.map_err(std::io::Error::other)?;
|
|
||||||
let ServerResult::InitializeResult(initialize_result) = response else {
|
let ServerResult::InitializeResult(initialize_result) = response else {
|
||||||
return Err(std::io::Error::new(
|
return Err(handle_client_error(ClientError::ExpectedInitResult(Some(
|
||||||
std::io::ErrorKind::InvalidData,
|
response,
|
||||||
"expect initialize result",
|
))));
|
||||||
)
|
|
||||||
.into());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// send notification
|
// send notification
|
||||||
let notification = ClientMessage::Notification(ClientNotification::InitializedNotification(
|
let notification = ClientMessage::Notification(ClientNotification::InitializedNotification(
|
||||||
InitializedNotification {
|
InitializedNotification {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue