zoe_relay/router.rs
1//! Service Router Abstractions
2//!
3//! This module provides the core abstractions for routing incoming connections
4//! to different services based on a service identifier sent by the client.
5//!
6//! ## Core Traits
7//!
8//! - [`ServiceRouter`]: Routes connections to services based on service IDs
9//! - [`Service`]: Handles individual service connections
10//!
11//! ## Service Routing Flow
12//!
13//! 1. Client connects and sends a `u8` service identifier
14//! 2. [`ServiceRouter::parse_service_id`] converts the `u8` to a typed service ID
15//! 3. [`ServiceRouter::create_service`] creates the appropriate service instance
16//! 4. The service's [`Service::run`] method handles the connection
17//!
18//! ## Example
19//!
20//! ```rust
21//! use zoe_relay::{ServiceRouter, Service, ConnectionInfo};
22//! use zoe_wire_protocol::StreamPair;
23//! use async_trait::async_trait;
24//!
25//! #[derive(Debug, Clone, PartialEq)]
26//! enum ServiceType {
27//! MessageService,
28//! BlobService,
29//! }
30//!
31//! impl TryFrom<u8> for ServiceType {
32//! type Error = MyError;
33//!
34//! fn try_from(value: u8) -> Result<Self, Self::Error> {
35//! match value {
36//! 1 => Ok(ServiceType::MessageService),
37//! 2 => Ok(ServiceType::BlobService),
38//! _ => Err(MyError::UnknownService(value)),
39//! }
40//! }
41//! }
42//!
43//! impl std::fmt::Display for ServiceType {
44//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45//! match self {
46//! ServiceType::MessageService => write!(f, "MessageService"),
47//! ServiceType::BlobService => write!(f, "BlobService"),
48//! }
49//! }
50//! }
51//!
52//! #[derive(Debug, thiserror::Error)]
53//! enum MyError {
54//! #[error("Unknown service ID: {0}")]
55//! UnknownService(u8),
56//! }
57//!
58//! struct MyRouter;
59//! struct MyService { /* service fields */ }
60//!
61//! #[async_trait]
62//! impl Service for MyService {
63//! type Error = MyError;
64//! async fn run(self) -> Result<(), Self::Error> {
65//! // Service implementation
66//! Ok(())
67//! }
68//! }
69//!
70//! #[async_trait]
71//! impl ServiceRouter for MyRouter {
72//! type Error = MyError;
73//! type ServiceId = ServiceType;
74//! type Service = MyService;
75//!
76//! async fn parse_service_id(&self, service_id: u8) -> Result<Self::ServiceId, Self::Error> {
77//! ServiceType::try_from(service_id)
78//! }
79//!
80//! async fn create_service(
81//! &self,
82//! service_id: &Self::ServiceId,
83//! connection_info: &ConnectionInfo,
84//! streams: StreamPair,
85//! ) -> Result<Self::Service, Self::Error> {
86//! Ok(MyService { /* initialize service */ })
87//! }
88//! }
89//! ```
90
91use async_trait::async_trait;
92
93use zoe_wire_protocol::ConnectionInfo;
94use zoe_wire_protocol::StreamPair;
95
96/// A service that handles a specific type of connection
97///
98/// Services are created by a [`ServiceRouter`] and run independently
99/// to handle bi-directional streams from authenticated clients.
100#[async_trait]
101pub trait Service: Send + Sync {
102 /// The error type returned by this service
103 type Error: std::error::Error + Send + Sync + 'static;
104
105 /// Run the service with the provided streams
106 ///
107 /// This method will be called in its own task and should handle
108 /// the bi-directional communication with the client.
109 async fn run(self) -> Result<(), Self::Error>;
110}
111
112/// Routes incoming connections to appropriate services
113///
114/// The router is responsible for parsing service identifiers and
115/// creating service instances to handle connections.
116#[async_trait]
117pub trait ServiceRouter: Send + Sync {
118 /// The error type returned by routing operations
119 type Error: std::error::Error + Send + Sync + 'static;
120
121 /// The typed service identifier (e.g., an enum)
122 type ServiceId: std::fmt::Debug + Send + Sync;
123
124 /// The service type that handles connections
125 type Service: Service;
126
127 /// Parse a raw service ID byte into a typed service identifier
128 ///
129 /// # Arguments
130 /// * `service_id` - The raw service ID byte sent by the client
131 ///
132 /// # Returns
133 /// A typed service identifier or an error if the ID is invalid
134 async fn parse_service_id(&self, service_id: u8) -> Result<Self::ServiceId, Self::Error>;
135
136 /// Create a service instance for the given service ID
137 ///
138 /// # Arguments
139 /// * `service_id` - The parsed service identifier
140 /// * `connection_info` - Information about the authenticated client
141 /// * `streams` - The bi-directional streams for communication
142 ///
143 /// # Returns
144 /// A service instance ready to handle the connection
145 async fn create_service(
146 &self,
147 service_id: &Self::ServiceId,
148 connection_info: &ConnectionInfo,
149 streams: StreamPair,
150 ) -> Result<Self::Service, Self::Error>;
151}