ash_rpc_stateful/
lib.rs

1//! # ash-rpc-stateful
2//!
3//! Stateful JSON-RPC handlers with shared context support.
4//!
5//! This crate extends ash-rpc-core with stateful method handlers that can access
6//! shared application state through a service context.
7//!
8//! ## Features
9//!
10//! - **Shared context** - Pass application state to method handlers
11//! - **Error handling** - Proper error propagation through the context system
12//! - **Method registry** - Organize stateful methods in a registry
13//! - **Type safety** - Generic over context types for compile-time guarantees
14
15use ash_rpc_core::{Message, MessageProcessor, Request, Response};
16use std::sync::Arc;
17
18/// Trait for service context shared across stateful handlers
19pub trait ServiceContext: Send + Sync + 'static {
20    type Error: std::error::Error + Send + Sync + 'static;
21}
22
23/// Trait for stateful JSON-RPC handlers
24pub trait StatefulHandler<C: ServiceContext>: Send + Sync {
25    /// Handle a JSON-RPC request with access to shared context
26    fn handle_request(&self, context: &C, request: Request) -> Result<Response, C::Error>;
27
28    /// Handle a JSON-RPC notification with access to shared context
29    fn handle_notification(
30        &self,
31        context: &C,
32        notification: ash_rpc_core::Notification,
33    ) -> Result<(), C::Error> {
34        let _ = context;
35        let _ = notification;
36        Ok(())
37    }
38}
39
40/// Trait for stateful method handlers
41pub trait StatefulMethodHandler<C: ServiceContext>: Send + Sync {
42    /// Call the method handler with context and parameters
43    fn call(
44        &self,
45        context: &C,
46        params: Option<serde_json::Value>,
47        id: Option<ash_rpc_core::RequestId>,
48    ) -> Result<Response, C::Error>;
49}
50
51impl<C, F> StatefulMethodHandler<C> for F
52where
53    C: ServiceContext,
54    F: Fn(
55            &C,
56            Option<serde_json::Value>,
57            Option<ash_rpc_core::RequestId>,
58        ) -> Result<Response, C::Error>
59        + Send
60        + Sync,
61{
62    fn call(
63        &self,
64        context: &C,
65        params: Option<serde_json::Value>,
66        id: Option<ash_rpc_core::RequestId>,
67    ) -> Result<Response, C::Error> {
68        self(context, params, id)
69    }
70}
71
72/// Registry for organizing stateful JSON-RPC methods
73pub struct StatefulMethodRegistry<C: ServiceContext> {
74    methods: std::collections::HashMap<String, Box<dyn StatefulMethodHandler<C>>>,
75}
76
77impl<C: ServiceContext> StatefulMethodRegistry<C> {
78    /// Create a new empty registry
79    pub fn new() -> Self {
80        Self {
81            methods: std::collections::HashMap::new(),
82        }
83    }
84
85    /// Register a method handler
86    pub fn register<H>(mut self, method: impl Into<String>, handler: H) -> Self
87    where
88        H: StatefulMethodHandler<C> + 'static,
89    {
90        self.methods.insert(method.into(), Box::new(handler));
91        self
92    }
93
94    /// Register a method using a closure
95    pub fn register_fn<F>(mut self, method: impl Into<String>, handler: F) -> Self
96    where
97        F: Fn(
98                &C,
99                Option<serde_json::Value>,
100                Option<ash_rpc_core::RequestId>,
101            ) -> Result<Response, C::Error>
102            + Send
103            + Sync
104            + 'static,
105    {
106        self.methods.insert(method.into(), Box::new(handler));
107        self
108    }
109
110    /// Call a registered method with context
111    pub fn call(
112        &self,
113        context: &C,
114        method: &str,
115        params: Option<serde_json::Value>,
116        id: Option<ash_rpc_core::RequestId>,
117    ) -> Result<Response, C::Error> {
118        if let Some(handler) = self.methods.get(method) {
119            handler.call(context, params, id)
120        } else {
121            Ok(ash_rpc_core::ResponseBuilder::new()
122                .error(
123                    ash_rpc_core::ErrorBuilder::new(
124                        ash_rpc_core::error_codes::METHOD_NOT_FOUND,
125                        "Method not found",
126                    )
127                    .build(),
128                )
129                .id(id)
130                .build())
131        }
132    }
133}
134
135impl<C: ServiceContext> Default for StatefulMethodRegistry<C> {
136    fn default() -> Self {
137        Self::new()
138    }
139}
140
141impl<C: ServiceContext> StatefulHandler<C> for StatefulMethodRegistry<C> {
142    fn handle_request(&self, context: &C, request: Request) -> Result<Response, C::Error> {
143        self.call(context, &request.method, request.params, request.id)
144    }
145
146    fn handle_notification(
147        &self,
148        context: &C,
149        notification: ash_rpc_core::Notification,
150    ) -> Result<(), C::Error> {
151        let _ = self.call(context, &notification.method, notification.params, None)?;
152        Ok(())
153    }
154}
155
156pub struct StatefulProcessor<C: ServiceContext> {
157    context: Arc<C>,
158    handler: Arc<dyn StatefulHandler<C>>,
159}
160
161impl<C: ServiceContext> StatefulProcessor<C> {
162    pub fn new<H>(context: C, handler: H) -> Self
163    where
164        H: StatefulHandler<C> + 'static,
165    {
166        Self {
167            context: Arc::new(context),
168            handler: Arc::new(handler),
169        }
170    }
171
172    pub fn builder(context: C) -> StatefulProcessorBuilder<C> {
173        StatefulProcessorBuilder::new(context)
174    }
175}
176
177impl<C: ServiceContext> MessageProcessor for StatefulProcessor<C> {
178    fn process_message(&self, message: Message) -> Option<Response> {
179        match message {
180            Message::Request(request) => {
181                match self.handler.handle_request(&self.context, request) {
182                    Ok(response) => Some(response),
183                    Err(_) => Some(
184                        ash_rpc_core::ResponseBuilder::new()
185                            .error(
186                                ash_rpc_core::ErrorBuilder::new(
187                                    ash_rpc_core::error_codes::INTERNAL_ERROR,
188                                    "Internal server error",
189                                )
190                                .build(),
191                            )
192                            .id(None)
193                            .build(),
194                    ),
195                }
196            }
197            Message::Notification(notification) => {
198                let _ = self
199                    .handler
200                    .handle_notification(&self.context, notification);
201                None
202            }
203            Message::Response(_) => None,
204        }
205    }
206}
207
208pub struct StatefulProcessorBuilder<C: ServiceContext> {
209    context: C,
210    handler: Option<Arc<dyn StatefulHandler<C>>>,
211}
212
213impl<C: ServiceContext> StatefulProcessorBuilder<C> {
214    pub fn new(context: C) -> Self {
215        Self {
216            context,
217            handler: None,
218        }
219    }
220
221    pub fn handler<H>(mut self, handler: H) -> Self
222    where
223        H: StatefulHandler<C> + 'static,
224    {
225        self.handler = Some(Arc::new(handler));
226        self
227    }
228
229    pub fn registry(mut self, registry: StatefulMethodRegistry<C>) -> Self {
230        self.handler = Some(Arc::new(registry));
231        self
232    }
233
234    pub fn build(self) -> Result<StatefulProcessor<C>, Box<dyn std::error::Error>> {
235        let handler = self.handler.ok_or("Handler not set")?;
236        Ok(StatefulProcessor {
237            context: Arc::new(self.context),
238            handler,
239        })
240    }
241}