Skip to main content

turul_rpc_server/
handler.rs

1use async_trait::async_trait;
2use serde_json::Value;
3
4use turul_rpc_core::error::JsonRpcErrorObject;
5use turul_rpc_core::request::RequestParams;
6
7use crate::session::SessionContext;
8
9/// Trait for handling JSON-RPC method calls.
10///
11/// Handlers return their own domain error type. The dispatcher converts
12/// it to a JSON-RPC error response via [`ToJsonRpcError`].
13#[async_trait]
14pub trait JsonRpcHandler: Send + Sync {
15    /// The error type returned by this handler.
16    type Error: std::error::Error + Send + Sync + 'static;
17
18    /// Handle a JSON-RPC method call with optional session context.
19    /// Returns domain errors only — the dispatcher converts them to
20    /// JSON-RPC errors.
21    async fn handle(
22        &self,
23        method: &str,
24        params: Option<RequestParams>,
25        session_context: Option<SessionContext>,
26    ) -> Result<Value, Self::Error>;
27
28    /// Handle a JSON-RPC notification with optional session context.
29    /// Default implementation does nothing.
30    async fn handle_notification(
31        &self,
32        method: &str,
33        params: Option<RequestParams>,
34        session_context: Option<SessionContext>,
35    ) -> Result<(), Self::Error> {
36        let _ = (method, params, session_context);
37        Ok(())
38    }
39
40    /// List supported methods (optional — used for introspection).
41    fn supported_methods(&self) -> Vec<String> {
42        vec![]
43    }
44}
45
46/// A simple function-based handler.
47pub struct FunctionHandler<F, N, E>
48where
49    E: std::error::Error + Send + Sync + 'static,
50    F: Fn(
51            &str,
52            Option<RequestParams>,
53            Option<SessionContext>,
54        ) -> futures::future::BoxFuture<'static, Result<Value, E>>
55        + Send
56        + Sync,
57    N: Fn(
58            &str,
59            Option<RequestParams>,
60            Option<SessionContext>,
61        ) -> futures::future::BoxFuture<'static, Result<(), E>>
62        + Send
63        + Sync,
64{
65    handler_fn: F,
66    notification_fn: Option<N>,
67    methods: Vec<String>,
68}
69
70impl<F, N, E> FunctionHandler<F, N, E>
71where
72    E: std::error::Error + Send + Sync + 'static,
73    F: Fn(
74            &str,
75            Option<RequestParams>,
76            Option<SessionContext>,
77        ) -> futures::future::BoxFuture<'static, Result<Value, E>>
78        + Send
79        + Sync,
80    N: Fn(
81            &str,
82            Option<RequestParams>,
83            Option<SessionContext>,
84        ) -> futures::future::BoxFuture<'static, Result<(), E>>
85        + Send
86        + Sync,
87{
88    pub fn new(handler_fn: F) -> Self {
89        Self {
90            handler_fn,
91            notification_fn: None,
92            methods: vec![],
93        }
94    }
95
96    pub fn with_notification_handler(mut self, notification_fn: N) -> Self {
97        self.notification_fn = Some(notification_fn);
98        self
99    }
100
101    pub fn with_methods(mut self, methods: Vec<String>) -> Self {
102        self.methods = methods;
103        self
104    }
105}
106
107#[async_trait]
108impl<F, N, E> JsonRpcHandler for FunctionHandler<F, N, E>
109where
110    E: std::error::Error + Send + Sync + 'static,
111    F: Fn(
112            &str,
113            Option<RequestParams>,
114            Option<SessionContext>,
115        ) -> futures::future::BoxFuture<'static, Result<Value, E>>
116        + Send
117        + Sync,
118    N: Fn(
119            &str,
120            Option<RequestParams>,
121            Option<SessionContext>,
122        ) -> futures::future::BoxFuture<'static, Result<(), E>>
123        + Send
124        + Sync,
125{
126    type Error = E;
127
128    async fn handle(
129        &self,
130        method: &str,
131        params: Option<RequestParams>,
132        session_context: Option<SessionContext>,
133    ) -> Result<Value, Self::Error> {
134        (self.handler_fn)(method, params, session_context).await
135    }
136
137    async fn handle_notification(
138        &self,
139        method: &str,
140        params: Option<RequestParams>,
141        session_context: Option<SessionContext>,
142    ) -> Result<(), Self::Error> {
143        if let Some(ref notification_fn) = self.notification_fn {
144            (notification_fn)(method, params, session_context).await
145        } else {
146            Ok(())
147        }
148    }
149
150    fn supported_methods(&self) -> Vec<String> {
151        self.methods.clone()
152    }
153}
154
155/// Trait for errors that can be converted to JSON-RPC error objects.
156///
157/// Implement this on your domain error type so the dispatcher can map
158/// handler failures to spec-compliant JSON-RPC error responses.
159pub trait ToJsonRpcError: std::error::Error + Send + Sync + 'static {
160    /// Convert this error to a JSON-RPC error object.
161    fn to_error_object(&self) -> JsonRpcErrorObject;
162}