Skip to content

Commit

Permalink
Generalize LspService over Error
Browse files Browse the repository at this point in the history
  • Loading branch information
oxalica committed Jul 22, 2023
1 parent 73f9d46 commit 2b36d52
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 61 deletions.
6 changes: 2 additions & 4 deletions src/client_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 24,7 @@ use lsp_types::request::{self, Request};
use tower_layer::Layer;
use tower_service::Service;

use crate::{
AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, LspService, ResponseError, Result,
};
use crate::{AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, LspService, Result};

struct ClientProcessExited;

Expand All @@ -40,7 38,7 @@ pub struct ClientProcessMonitor<S> {

impl<S: LspService> Service<AnyRequest> for ClientProcessMonitor<S> {
type Response = S::Response;
type Error = ResponseError;
type Error = S::Error;
type Future = S::Future;

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Expand Down
20 changes: 14 additions & 6 deletions src/concurrency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 39,12 @@ pub struct Concurrency<S> {

define_getters!(impl[S] Concurrency<S>, service: S);

impl<S: LspService> Service<AnyRequest> for Concurrency<S> {
impl<S: LspService> Service<AnyRequest> for Concurrency<S>
where
S::Error: From<ResponseError>,
{
type Response = S::Response;
type Error = ResponseError;
type Error = S::Error;
type Future = ResponseFuture<S::Future>;

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Expand Down Expand Up @@ -123,9 126,10 @@ pin_project! {
}
}

impl<Response, Fut> Future for ResponseFuture<Fut>
impl<Fut, Response, Error> Future for ResponseFuture<Fut>
where
Fut: Future<Output = Result<Response, ResponseError>>,
Fut: Future<Output = Result<Response, Error>>,
Error: From<ResponseError>,
{
type Output = Fut::Output;

Expand All @@ -137,12 141,16 @@ where
code: ErrorCode::REQUEST_CANCELLED,
message: "Client cancelled the request".into(),
data: None,
})),
}
.into())),
}
}
}

impl<S: LspService> LspService for Concurrency<S> {
impl<S: LspService> LspService for Concurrency<S>
where
S::Error: From<ResponseError>,
{
fn notify(&mut self, notif: AnyNotification) -> ControlFlow<Result<()>> {
if notif.method == notification::Cancel::METHOD {
if let Ok(params) = serde_json::from_value::<lsp_types::CancelParams>(notif.params) {
Expand Down
20 changes: 15 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 164,7 @@ pub enum Error {
}

/// The core service abstraction, representing either a Language Server or Language Client.
pub trait LspService: Service<AnyRequest, Error = ResponseError> {
pub trait LspService: Service<AnyRequest> {
/// The handler of [LSP notifications](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#notificationMessage).
///
/// Notifications are delivered in order and synchronously. This is mandatory since they can
Expand Down Expand Up @@ -477,7 477,11 @@ enum MainLoopEvent {

define_getters!(impl[S: LspService] MainLoop<S>, service: S);

impl<S: LspService<Response = JsonValue>> MainLoop<S> {
impl<S> MainLoop<S>
where
S: LspService<Response = JsonValue>,
ResponseError: From<S::Error>,
{
/// Create a Language Server main loop.
#[must_use]
pub fn new_server(builder: impl FnOnce(ClientSocket) -> S) -> (Self, ClientSocket) {
Expand Down Expand Up @@ -572,7 576,7 @@ impl<S: LspService<Response = JsonValue>> MainLoop<S> {
let resp = AnyResponse {
id: req.id,
result: None,
error: Some(err),
error: Some(err.into()),
};
return ControlFlow::Continue(Some(Message::Response(resp)));
}
Expand Down Expand Up @@ -618,15 622,19 @@ pin_project! {
}
}

impl<Fut: Future<Output = Result<JsonValue, ResponseError>>> Future for RequestFuture<Fut> {
impl<Fut, Error> Future for RequestFuture<Fut>
where
Fut: Future<Output = Result<JsonValue, Error>>,
ResponseError: From<Error>,
{
type Output = AnyResponse;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let (mut result, mut error) = (None, None);
match ready!(this.fut.poll(cx)) {
Ok(v) => result = Some(v),
Err(err) => error = Some(err),
Err(err) => error = Some(err.into()),
}
Poll::Ready(AnyResponse {
id: this.id.take().expect("Future is consumed"),
Expand Down Expand Up @@ -847,6 855,8 @@ mod tests {
where
S: LspService<Response = JsonValue> Send,
S::Future: Send,
S::Error: From<Error> Send,
ResponseError: From<S::Error>,
{
f.run(input, output)
}
Expand Down
40 changes: 20 additions & 20 deletions src/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 17,14 @@ use crate::{AnyEvent, AnyNotification, AnyRequest, ErrorCode, LspService, Respon
/// The middleware catching panics of underlying handlers and turn them into error responses.
///
/// See [module level documentations](self) for details.
pub struct CatchUnwind<S> {
pub struct CatchUnwind<S: LspService> {
service: S,
handler: Handler,
handler: Handler<S::Error>,
}

define_getters!(impl[S] CatchUnwind<S>, service: S);
define_getters!(impl[S: LspService] CatchUnwind<S>, service: S);

type Handler = fn(method: &str, payload: Box<dyn Any Send>) -> ResponseError;
type Handler<E> = fn(method: &str, payload: Box<dyn Any Send>) -> E;

fn default_handler(method: &str, payload: Box<dyn Any Send>) -> ResponseError {
let msg = match payload.downcast::<String>() {
Expand All @@ -43,8 43,8 @@ fn default_handler(method: &str, payload: Box<dyn Any Send>) -> ResponseError

impl<S: LspService> Service<AnyRequest> for CatchUnwind<S> {
type Response = S::Response;
type Error = ResponseError;
type Future = ResponseFuture<S::Future>;
type Error = S::Error;
type Future = ResponseFuture<S::Future, S::Error>;

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
Expand Down Expand Up @@ -72,30 72,30 @@ impl<S: LspService> Service<AnyRequest> for CatchUnwind<S> {

pin_project! {
/// The [`Future`] type used by the [`CatchUnwind`] middleware.
pub struct ResponseFuture<Fut> {
pub struct ResponseFuture<Fut, Error> {
#[pin]
inner: ResponseFutureInner<Fut>,
inner: ResponseFutureInner<Fut, Error>,
}
}

pin_project! {
#[project = ResponseFutureProj]
enum ResponseFutureInner<Fut> {
enum ResponseFutureInner<Fut, Error> {
Future {
#[pin]
fut: Fut,
method: String,
handler: Handler,
handler: Handler<Error>,
},
Ready {
err: Option<ResponseError>,
err: Option<Error>,
},
}
}

impl<Response, Fut> Future for ResponseFuture<Fut>
impl<Response, Fut, Error> Future for ResponseFuture<Fut, Error>
where
Fut: Future<Output = Result<Response, ResponseError>>,
Fut: Future<Output = Result<Response, Error>>,
{
type Output = Fut::Output;

Expand Down Expand Up @@ -134,28 134,28 @@ impl<S: LspService> LspService for CatchUnwind<S> {
/// The error code is set to [`ErrorCode::INTERNAL_ERROR`].
#[derive(Clone)]
#[must_use]
pub struct CatchUnwindBuilder {
handler: Handler,
pub struct CatchUnwindBuilder<Error = ResponseError> {
handler: Handler<Error>,
}

impl Default for CatchUnwindBuilder {
impl Default for CatchUnwindBuilder<ResponseError> {
fn default() -> Self {
Self::new_with_handler(default_handler)
}
}

impl CatchUnwindBuilder {
impl<Error> CatchUnwindBuilder<Error> {
/// Create the builder of [`CatchUnwind`] middleware with a custom handler converting panic
/// payloads into [`ResponseError`].
pub fn new_with_handler(handler: Handler) -> Self {
pub fn new_with_handler(handler: Handler<Error>) -> Self {
Self { handler }
}
}

/// A type alias of [`CatchUnwindBuilder`] conforming to the naming convention of [`tower_layer`].
pub type CatchUnwindLayer = CatchUnwindBuilder;
pub type CatchUnwindLayer<Error = ResponseError> = CatchUnwindBuilder<Error>;

impl<S> Layer<S> for CatchUnwindBuilder {
impl<S: LspService> Layer<S> for CatchUnwindBuilder<S::Error> {
type Service = CatchUnwind<S>;

fn layer(&self, inner: S) -> Self::Service {
Expand Down
47 changes: 29 additions & 18 deletions src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 11,40 @@ use lsp_types::request::Request;
use tower_service::Service;

use crate::{
AnyEvent, AnyNotification, AnyRequest, Error, ErrorCode, JsonValue, LspService, ResponseError,
Result,
AnyEvent, AnyNotification, AnyRequest, ErrorCode, JsonValue, LspService, ResponseError, Result,
};

/// A router dispatching requests and notifications to individual handlers.
pub struct Router<St> {
pub struct Router<St, Error = ResponseError> {
state: St,
req_handlers: HashMap<&'static str, BoxReqHandler<St>>,
req_handlers: HashMap<&'static str, BoxReqHandler<St, Error>>,
notif_handlers: HashMap<&'static str, BoxNotifHandler<St>>,
event_handlers: HashMap<TypeId, BoxEventHandler<St>>,
unhandled_req: BoxReqHandler<St>,
unhandled_req: BoxReqHandler<St, Error>,
unhandled_notif: BoxNotifHandler<St>,
unhandled_event: BoxEventHandler<St>,
}

type BoxReqFuture = Pin<Box<dyn Future<Output = Result<JsonValue, ResponseError>> Send>>;
type BoxReqHandler<St> = Box<dyn Fn(&mut St, AnyRequest) -> BoxReqFuture Send>;
type BoxReqFuture<Error> = Pin<Box<dyn Future<Output = Result<JsonValue, Error>> Send>>;
type BoxReqHandler<St, Error> = Box<dyn Fn(&mut St, AnyRequest) -> BoxReqFuture<Error> Send>;
type BoxNotifHandler<St> = Box<dyn Fn(&mut St, AnyNotification) -> ControlFlow<Result<()>> Send>;
type BoxEventHandler<St> = Box<dyn Fn(&mut St, AnyEvent) -> ControlFlow<Result<()>> Send>;

impl<St: Default> Default for Router<St> {
impl<St, Error> Default for Router<St, Error>
where
St: Default,
Error: From<ResponseError> Send 'static,
{
fn default() -> Self {
Self::new(St::default())
}
}

impl<St> Router<St> {
// TODO: Make it possible to construct with arbitrary `Error`, with no default handlers.
impl<St, Error> Router<St, Error>
where
Error: From<ResponseError> Send 'static,
{
/// Create a empty `Router`.
#[must_use]
pub fn new(state: St) -> Self {
Expand All @@ -51,20 58,23 @@ impl<St> Router<St> {
code: ErrorCode::METHOD_NOT_FOUND,
message: format!("No such method {}", req.method),
data: None,
})))
}
.into())))
}),
unhandled_notif: Box::new(|_, notif| {
if notif.method.starts_with("$/") {
ControlFlow::Continue(())
} else {
ControlFlow::Break(Err(Error::Routing(format!(
ControlFlow::Break(Err(crate::Error::Routing(format!(
"Unhandled notification: {}",
notif.method,
))))
}
}),
unhandled_event: Box::new(|_, event| {
ControlFlow::Break(Err(Error::Routing(format!("Unhandled event: {event:?}"))))
ControlFlow::Break(Err(crate::Error::Routing(format!(
"Unhandled event: {event:?}"
))))
}),
}
}
Expand All @@ -77,7 87,7 @@ impl<St> Router<St> {
handler: impl Fn(&mut St, R::Params) -> Fut Send 'static,
) -> &mut Self
where
Fut: Future<Output = Result<R::Result, ResponseError>> Send 'static,
Fut: Future<Output = Result<R::Result, Error>> Send 'static,
{
self.req_handlers.insert(
R::METHOD,
Expand All @@ -93,7 103,8 @@ impl<St> Router<St> {
code: ErrorCode::INVALID_PARAMS,
message: format!("Failed to deserialize parameters: {err}"),
data: None,
}))),
}
.into()))),
},
),
);
Expand Down Expand Up @@ -148,7 159,7 @@ impl<St> Router<St> {
handler: impl Fn(&mut St, AnyRequest) -> Fut Send 'static,
) -> &mut Self
where
Fut: Future<Output = Result<JsonValue, ResponseError>> Send 'static,
Fut: Future<Output = Result<JsonValue, Error>> Send 'static,
{
self.unhandled_req = Box::new(move |state, req| Box::pin(handler(state, req)));
self
Expand Down Expand Up @@ -187,10 198,10 @@ impl<St> Router<St> {
}
}

impl<St> Service<AnyRequest> for Router<St> {
impl<St, Error> Service<AnyRequest> for Router<St, Error> {
type Response = JsonValue;
type Error = ResponseError;
type Future = BoxReqFuture;
type Error = Error;
type Future = BoxReqFuture<Error>;

fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
Expand Down
Loading

0 comments on commit 2b36d52

Please sign in to comment.