Skip to main content

ash_rpc/
builders.rs

1//! Builder patterns for JSON-RPC types.
2
3use crate::types::*;
4
5/// Builder for JSON-RPC requests
6pub struct RequestBuilder {
7    method: String,
8    params: Option<serde_json::Value>,
9    id: Option<RequestId>,
10    correlation_id: Option<String>,
11}
12
13impl RequestBuilder {
14    /// Create a new request builder
15    pub fn new(method: impl Into<String>) -> Self {
16        Self {
17            method: method.into(),
18            params: None,
19            id: None,
20            correlation_id: Some(uuid::Uuid::new_v4().to_string()),
21        }
22    }
23
24    /// Set request parameters
25    pub fn params(mut self, params: serde_json::Value) -> Self {
26        self.params = Some(params);
27        self
28    }
29
30    /// Set request ID
31    pub fn id(mut self, id: RequestId) -> Self {
32        self.id = Some(id);
33        self
34    }
35
36    /// Set correlation ID
37    pub fn correlation_id(mut self, correlation_id: String) -> Self {
38        self.correlation_id = Some(correlation_id);
39        self
40    }
41
42    /// Build the request
43    pub fn build(self) -> Request {
44        Request {
45            jsonrpc: "2.0".to_string(),
46            method: self.method,
47            params: self.params,
48            id: self.id,
49            correlation_id: self.correlation_id,
50        }
51    }
52}
53
54/// Builder for JSON-RPC responses
55pub struct ResponseBuilder {
56    result: Option<serde_json::Value>,
57    error: Option<Error>,
58    id: Option<RequestId>,
59    correlation_id: Option<String>,
60}
61
62impl ResponseBuilder {
63    /// Create a new response builder
64    pub fn new() -> Self {
65        Self {
66            result: None,
67            error: None,
68            id: None,
69            correlation_id: None,
70        }
71    }
72
73    /// Set successful result
74    pub fn success(mut self, result: serde_json::Value) -> Self {
75        self.result = Some(result);
76        self
77    }
78
79    /// Set error
80    pub fn error(mut self, error: Error) -> Self {
81        self.error = Some(error);
82        self
83    }
84
85    /// Set response ID
86    pub fn id(mut self, id: Option<RequestId>) -> Self {
87        self.id = id;
88        self
89    }
90    /// Set correlation ID
91    pub fn correlation_id(mut self, correlation_id: Option<String>) -> Self {
92        self.correlation_id = correlation_id;
93        self
94    }
95    /// Build the response
96    pub fn build(self) -> Response {
97        Response {
98            jsonrpc: "2.0".to_string(),
99            result: self.result,
100            error: self.error,
101            id: self.id,
102            correlation_id: self.correlation_id,
103        }
104    }
105}
106
107/// Builder for JSON-RPC notifications
108pub struct NotificationBuilder {
109    method: String,
110    params: Option<serde_json::Value>,
111}
112
113impl NotificationBuilder {
114    /// Create a new notification builder
115    pub fn new(method: impl Into<String>) -> Self {
116        Self {
117            method: method.into(),
118            params: None,
119        }
120    }
121
122    /// Set notification parameters
123    pub fn params(mut self, params: serde_json::Value) -> Self {
124        self.params = Some(params);
125        self
126    }
127
128    /// Build the notification
129    pub fn build(self) -> Notification {
130        Notification {
131            jsonrpc: "2.0".to_string(),
132            method: self.method,
133            params: self.params,
134        }
135    }
136}
137
138/// Builder for JSON-RPC errors
139pub struct ErrorBuilder {
140    code: i32,
141    message: String,
142    data: Option<serde_json::Value>,
143}
144
145impl ErrorBuilder {
146    /// Create a new error builder
147    pub fn new(code: i32, message: impl Into<String>) -> Self {
148        Self {
149            code,
150            message: message.into(),
151            data: None,
152        }
153    }
154
155    /// Add additional error data
156    pub fn data(mut self, data: serde_json::Value) -> Self {
157        self.data = Some(data);
158        self
159    }
160
161    /// Build the error
162    pub fn build(self) -> Error {
163        Error {
164            code: self.code,
165            message: self.message,
166            data: self.data,
167        }
168    }
169}
170
171impl Default for ResponseBuilder {
172    fn default() -> Self {
173        Self::new()
174    }
175}
176
177/// Builder for security configuration with validation
178#[cfg(any(feature = "tcp", feature = "tcp-stream", feature = "tcp-stream-tls"))]
179pub struct SecurityConfigBuilder {
180    max_connections: usize,
181    max_request_size: usize,
182    request_timeout: std::time::Duration,
183    idle_timeout: std::time::Duration,
184}
185
186#[cfg(any(feature = "tcp", feature = "tcp-stream", feature = "tcp-stream-tls"))]
187impl SecurityConfigBuilder {
188    /// Create a new security config builder with secure defaults
189    pub fn new() -> Self {
190        Self {
191            max_connections: 1000,
192            max_request_size: 1024 * 1024, // 1 MB
193            request_timeout: std::time::Duration::from_secs(30),
194            idle_timeout: std::time::Duration::from_secs(300), // 5 minutes
195        }
196    }
197
198    /// Set maximum concurrent connections
199    ///
200    /// # Arguments
201    /// * `max` - Maximum number of connections (1-100000)
202    ///
203    /// # Panics
204    /// Panics if max is 0 or greater than 100000
205    pub fn max_connections(mut self, max: usize) -> Self {
206        assert!(
207            max > 0 && max <= 100_000,
208            "max_connections must be between 1 and 100000"
209        );
210        self.max_connections = max;
211        self
212    }
213
214    /// Set maximum request size in bytes
215    ///
216    /// # Arguments
217    /// * `size` - Maximum size in bytes (1024 to 100MB)
218    ///
219    /// # Panics
220    /// Panics if size is less than 1024 bytes or greater than 100MB
221    pub fn max_request_size(mut self, size: usize) -> Self {
222        assert!(
223            (1024..=100 * 1024 * 1024).contains(&size),
224            "max_request_size must be between 1KB and 100MB"
225        );
226        self.max_request_size = size;
227        self
228    }
229
230    /// Set request timeout
231    ///
232    /// # Arguments
233    /// * `timeout` - Timeout duration (1 second to 5 minutes)
234    ///
235    /// # Panics
236    /// Panics if timeout is less than 1 second or greater than 5 minutes
237    pub fn request_timeout(mut self, timeout: std::time::Duration) -> Self {
238        assert!(
239            (1..=300).contains(&timeout.as_secs()),
240            "request_timeout must be between 1 second and 5 minutes"
241        );
242        self.request_timeout = timeout;
243        self
244    }
245
246    /// Set idle connection timeout
247    ///
248    /// # Arguments
249    /// * `timeout` - Timeout duration (10 seconds to 1 hour)
250    ///
251    /// # Panics
252    /// Panics if timeout is less than 10 seconds or greater than 1 hour
253    pub fn idle_timeout(mut self, timeout: std::time::Duration) -> Self {
254        assert!(
255            (10..=3600).contains(&timeout.as_secs()),
256            "idle_timeout must be between 10 seconds and 1 hour"
257        );
258        self.idle_timeout = timeout;
259        self
260    }
261
262    /// Build the security configuration with validation
263    pub fn build(self) -> crate::transports::SecurityConfig {
264        tracing::info!(
265            max_connections = self.max_connections,
266            max_request_size = self.max_request_size,
267            request_timeout_secs = self.request_timeout.as_secs(),
268            idle_timeout_secs = self.idle_timeout.as_secs(),
269            "creating security configuration"
270        );
271
272        crate::transports::SecurityConfig {
273            max_connections: self.max_connections,
274            max_request_size: self.max_request_size,
275            request_timeout: self.request_timeout,
276            idle_timeout: self.idle_timeout,
277        }
278    }
279}
280
281#[cfg(any(feature = "tcp", feature = "tcp-stream", feature = "tcp-stream-tls"))]
282impl Default for SecurityConfigBuilder {
283    fn default() -> Self {
284        Self::new()
285    }
286}
287
288#[cfg(all(
289    test,
290    any(feature = "tcp", feature = "tcp-stream", feature = "tcp-stream-tls")
291))]
292mod tests {
293    use super::*;
294
295    #[test]
296    #[should_panic(expected = "max_connections must be between 1 and 100000")]
297    fn test_max_connections_zero_panics() {
298        SecurityConfigBuilder::new().max_connections(0).build();
299    }
300
301    #[test]
302    fn test_valid_security_config() {
303        let config = SecurityConfigBuilder::new()
304            .max_connections(500)
305            .max_request_size(2 * 1024 * 1024)
306            .request_timeout(std::time::Duration::from_secs(60))
307            .idle_timeout(std::time::Duration::from_secs(600))
308            .build();
309
310        assert_eq!(config.max_connections, 500);
311        assert_eq!(config.max_request_size, 2 * 1024 * 1024);
312    }
313
314    // RequestBuilder tests
315    #[test]
316    fn test_request_builder_basic() {
317        let request = RequestBuilder::new("test_method").build();
318        assert_eq!(request.method, "test_method");
319        assert_eq!(request.jsonrpc, "2.0");
320        assert!(request.correlation_id.is_some());
321    }
322
323    #[test]
324    fn test_request_builder_with_params() {
325        let params = serde_json::json!({"key": "value"});
326        let request = RequestBuilder::new("method").params(params.clone()).build();
327        assert_eq!(request.params, Some(params));
328    }
329
330    #[test]
331    fn test_request_builder_with_id() {
332        let id = serde_json::json!(123);
333        let request = RequestBuilder::new("method").id(id.clone()).build();
334        assert_eq!(request.id, Some(id));
335    }
336
337    #[test]
338    fn test_request_builder_with_correlation_id() {
339        let correlation_id = "custom-correlation-id".to_string();
340        let request = RequestBuilder::new("method")
341            .correlation_id(correlation_id.clone())
342            .build();
343        assert_eq!(request.correlation_id, Some(correlation_id));
344    }
345
346    #[test]
347    fn test_request_builder_complete() {
348        let params = serde_json::json!([1, 2, 3]);
349        let id = serde_json::json!(456);
350        let correlation_id = "test-corr-id".to_string();
351
352        let request = RequestBuilder::new("complete_method")
353            .params(params.clone())
354            .id(id.clone())
355            .correlation_id(correlation_id.clone())
356            .build();
357
358        assert_eq!(request.method, "complete_method");
359        assert_eq!(request.params, Some(params));
360        assert_eq!(request.id, Some(id));
361        assert_eq!(request.correlation_id, Some(correlation_id));
362    }
363
364    // ResponseBuilder tests
365    #[test]
366    fn test_response_builder_success() {
367        let result = serde_json::json!({"status": "ok"});
368        let id = serde_json::json!(1);
369
370        let response = ResponseBuilder::new()
371            .success(result.clone())
372            .id(Some(id.clone()))
373            .build();
374
375        assert_eq!(response.result, Some(result));
376        assert!(response.error.is_none());
377        assert_eq!(response.id, Some(id));
378    }
379
380    #[test]
381    fn test_response_builder_error() {
382        let error =
383            crate::ErrorBuilder::new(crate::error_codes::INVALID_REQUEST, "Invalid request")
384                .build();
385        let id = serde_json::json!(2);
386
387        let response = ResponseBuilder::new()
388            .error(error.clone())
389            .id(Some(id.clone()))
390            .build();
391
392        assert!(response.result.is_none());
393        assert_eq!(
394            response.error.unwrap().code,
395            crate::error_codes::INVALID_REQUEST
396        );
397        assert_eq!(response.id, Some(id));
398    }
399
400    #[test]
401    fn test_response_builder_with_correlation_id_basic() {
402        let correlation_id = "resp-corr-id".to_string();
403        let response = ResponseBuilder::new()
404            .success(serde_json::json!("ok"))
405            .correlation_id(Some(correlation_id.clone()))
406            .build();
407
408        assert_eq!(response.correlation_id, Some(correlation_id));
409    }
410
411    #[test]
412    fn test_response_builder_jsonrpc_version() {
413        let response = ResponseBuilder::new()
414            .success(serde_json::json!("test"))
415            .build();
416        assert_eq!(response.jsonrpc, "2.0");
417    }
418
419    // NotificationBuilder tests
420    #[test]
421    fn test_notification_builder_basic() {
422        let notification = NotificationBuilder::new("event").build();
423        assert_eq!(notification.method, "event");
424        assert_eq!(notification.jsonrpc, "2.0");
425        assert!(notification.params.is_none());
426    }
427
428    #[test]
429    fn test_notification_builder_with_params() {
430        let params = serde_json::json!({"event": "update"});
431        let notification = NotificationBuilder::new("notify")
432            .params(params.clone())
433            .build();
434        assert_eq!(notification.params, Some(params));
435    }
436
437    // ErrorBuilder tests
438    #[test]
439    fn test_error_builder_basic() {
440        let error = ErrorBuilder::new(crate::error_codes::INVALID_REQUEST, "Test error").build();
441        assert_eq!(error.code, crate::error_codes::INVALID_REQUEST);
442        assert_eq!(error.message, "Test error");
443        assert!(error.data.is_none());
444    }
445
446    #[test]
447    fn test_error_builder_with_data() {
448        let data = serde_json::json!({"detail": "more info"});
449        let error = ErrorBuilder::new(crate::error_codes::INTERNAL_ERROR, "Error")
450            .data(data.clone())
451            .build();
452        assert_eq!(error.data, Some(data));
453    }
454
455    #[test]
456    fn test_error_builder_string_conversion() {
457        let error = ErrorBuilder::new(
458            crate::error_codes::INTERNAL_ERROR,
459            String::from("Dynamic error"),
460        )
461        .build();
462        assert_eq!(error.message, "Dynamic error");
463    }
464
465    // SecurityConfigBuilder validation tests
466    #[test]
467    #[should_panic(expected = "max_request_size must be between")]
468    fn test_security_config_request_size_too_small() {
469        SecurityConfigBuilder::new()
470            .max_request_size(512) // Less than 1KB
471            .build();
472    }
473
474    #[test]
475    #[should_panic(expected = "max_request_size must be between")]
476    fn test_security_config_request_size_too_large() {
477        SecurityConfigBuilder::new()
478            .max_request_size(200 * 1024 * 1024) // More than 100MB
479            .build();
480    }
481
482    #[test]
483    #[should_panic(expected = "max_connections must be between")]
484    fn test_security_config_connections_too_large() {
485        SecurityConfigBuilder::new()
486            .max_connections(150_000)
487            .build();
488    }
489
490    #[test]
491    fn test_security_config_boundary_values() {
492        // Test minimum valid values
493        let config_min = SecurityConfigBuilder::new()
494            .max_connections(1)
495            .max_request_size(1024)
496            .build();
497        assert_eq!(config_min.max_connections, 1);
498        assert_eq!(config_min.max_request_size, 1024);
499
500        // Test maximum valid values
501        let config_max = SecurityConfigBuilder::new()
502            .max_connections(100_000)
503            .max_request_size(100 * 1024 * 1024)
504            .build();
505        assert_eq!(config_max.max_connections, 100_000);
506        assert_eq!(config_max.max_request_size, 100 * 1024 * 1024);
507    }
508
509    #[test]
510    fn test_security_config_timeouts() {
511        let request_timeout = std::time::Duration::from_secs(45);
512        let idle_timeout = std::time::Duration::from_secs(900);
513
514        let config = SecurityConfigBuilder::new()
515            .request_timeout(request_timeout)
516            .idle_timeout(idle_timeout)
517            .build();
518
519        assert_eq!(config.request_timeout, request_timeout);
520        assert_eq!(config.idle_timeout, idle_timeout);
521    }
522
523    #[test]
524    fn test_security_config_defaults() {
525        let config = SecurityConfigBuilder::new().build();
526        assert_eq!(config.max_connections, 1000);
527        assert_eq!(config.max_request_size, 1024 * 1024);
528        assert_eq!(config.request_timeout, std::time::Duration::from_secs(30));
529        assert_eq!(config.idle_timeout, std::time::Duration::from_secs(300));
530    }
531
532    #[test]
533    fn test_security_config_builder_default() {
534        let builder = SecurityConfigBuilder::default();
535        let config = builder.build();
536        assert_eq!(config.max_connections, 1000);
537    }
538
539    #[test]
540    #[should_panic(expected = "request_timeout must be between")]
541    fn test_security_config_timeout_too_short() {
542        SecurityConfigBuilder::new()
543            .request_timeout(std::time::Duration::from_millis(500))
544            .build();
545    }
546
547    #[test]
548    #[should_panic(expected = "request_timeout must be between")]
549    fn test_security_config_timeout_too_long() {
550        SecurityConfigBuilder::new()
551            .request_timeout(std::time::Duration::from_secs(400))
552            .build();
553    }
554
555    #[test]
556    #[should_panic(expected = "idle_timeout must be between")]
557    fn test_security_config_idle_timeout_too_short() {
558        SecurityConfigBuilder::new()
559            .idle_timeout(std::time::Duration::from_secs(5))
560            .build();
561    }
562
563    #[test]
564    #[should_panic(expected = "idle_timeout must be between")]
565    fn test_security_config_idle_timeout_too_long() {
566        SecurityConfigBuilder::new()
567            .idle_timeout(std::time::Duration::from_secs(4000))
568            .build();
569    }
570
571    #[test]
572    fn test_request_builder_method_set() {
573        let request = RequestBuilder::new("test_method").build();
574        assert_eq!(request.method, "test_method");
575    }
576
577    #[test]
578    fn test_request_builder_auto_correlation_id() {
579        let request = RequestBuilder::new("test").build();
580        assert!(request.correlation_id.is_some());
581    }
582
583    #[test]
584    fn test_request_builder_custom_correlation_id() {
585        let custom_id = "custom-correlation-123".to_string();
586        let request = RequestBuilder::new("test")
587            .correlation_id(custom_id.clone())
588            .build();
589        assert_eq!(request.correlation_id, Some(custom_id));
590    }
591
592    #[test]
593    fn test_request_builder_full_chain() {
594        let request = RequestBuilder::new("full_test")
595            .params(serde_json::json!({"key": "value"}))
596            .id(serde_json::json!(123))
597            .correlation_id("corr-123".to_string())
598            .build();
599
600        assert_eq!(request.method, "full_test");
601        assert!(request.params.is_some());
602        assert_eq!(request.id, Some(serde_json::json!(123)));
603        assert_eq!(request.correlation_id, Some("corr-123".to_string()));
604    }
605
606    #[test]
607    fn test_response_builder_default_trait() {
608        let builder = ResponseBuilder::default();
609        let response = builder.build();
610        assert_eq!(response.jsonrpc, "2.0");
611    }
612
613    #[test]
614    fn test_response_builder_success_with_null() {
615        let response = ResponseBuilder::new()
616            .success(serde_json::json!(null))
617            .build();
618        assert_eq!(response.result, Some(serde_json::json!(null)));
619    }
620
621    #[test]
622    fn test_response_builder_with_correlation_id() {
623        let corr_id = "test-correlation".to_string();
624        let response = ResponseBuilder::new()
625            .success(serde_json::json!(42))
626            .correlation_id(Some(corr_id.clone()))
627            .build();
628        assert_eq!(response.correlation_id, Some(corr_id));
629    }
630
631    #[test]
632    fn test_response_builder_full_error() {
633        let error = ErrorBuilder::new(crate::error_codes::INTERNAL_ERROR, "Custom error")
634            .data(serde_json::json!({"field": "value"}))
635            .build();
636
637        let response = ResponseBuilder::new()
638            .error(error.clone())
639            .id(Some(serde_json::json!(1)))
640            .correlation_id(Some("err-corr".to_string()))
641            .build();
642
643        assert!(response.result.is_none());
644        assert_eq!(response.error, Some(error));
645        assert_eq!(response.id, Some(serde_json::json!(1)));
646        assert_eq!(response.correlation_id, Some("err-corr".to_string()));
647    }
648
649    #[test]
650    fn test_notification_builder_string_method() {
651        let notification = NotificationBuilder::new(String::from("dynamic_method")).build();
652        assert_eq!(notification.method, "dynamic_method");
653    }
654
655    #[test]
656    fn test_error_builder_multiple_data() {
657        let error = ErrorBuilder::new(crate::error_codes::INTERNAL_ERROR, "Error")
658            .data(serde_json::json!({"key1": "value1"}))
659            .build();
660
661        assert!(error.data.is_some());
662        let data = error.data.unwrap();
663        assert_eq!(data["key1"], "value1");
664    }
665}
666
667/// Builder for streaming response
668#[cfg(feature = "streaming")]
669pub struct StreamResponseBuilder {
670    result: Option<serde_json::Value>,
671    error: Option<Error>,
672    id: RequestId,
673    stream_id: String,
674    stream_status: Option<crate::streaming::StreamStatus>,
675}
676
677#[cfg(feature = "streaming")]
678impl StreamResponseBuilder {
679    /// Create a new stream response builder
680    pub fn new(stream_id: impl Into<String>, id: RequestId) -> Self {
681        Self {
682            result: None,
683            error: None,
684            id,
685            stream_id: stream_id.into(),
686            stream_status: None,
687        }
688    }
689
690    /// Set successful result
691    pub fn success(mut self, result: serde_json::Value) -> Self {
692        self.result = Some(result);
693        self.stream_status = Some(crate::streaming::StreamStatus::Active);
694        self
695    }
696
697    /// Set error
698    pub fn error(mut self, error: Error) -> Self {
699        self.error = Some(error);
700        self.stream_status = Some(crate::streaming::StreamStatus::Error);
701        self
702    }
703
704    /// Set stream status
705    pub fn status(mut self, status: crate::streaming::StreamStatus) -> Self {
706        self.stream_status = Some(status);
707        self
708    }
709
710    /// Build the stream response
711    pub fn build(self) -> crate::streaming::StreamResponse {
712        crate::streaming::StreamResponse {
713            jsonrpc: "2.0".to_string(),
714            result: self.result,
715            error: self.error,
716            id: self.id,
717            stream_id: self.stream_id,
718            stream_status: self.stream_status,
719        }
720    }
721}