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}