ev_client/
client.rs

1use crate::error::{ClientError, Result};
2use std::time::Duration;
3use tonic::transport::{Channel, ClientTlsConfig, Endpoint};
4
5#[derive(Clone, Debug)]
6pub struct Client {
7    channel: Channel,
8    endpoint: String,
9}
10
11/// Builder for configuring a Client
12#[derive(Debug)]
13pub struct ClientBuilder {
14    endpoint: String,
15    timeout: Option<Duration>,
16    connect_timeout: Option<Duration>,
17    tls_config: Option<ClientTlsConfig>,
18}
19
20impl Client {
21    /// Create a new Client with the given endpoint
22    pub async fn connect(endpoint: impl Into<String>) -> Result<Self> {
23        let endpoint = endpoint.into();
24        let channel = Self::create_channel(&endpoint).await?;
25
26        Ok(Self { channel, endpoint })
27    }
28
29    /// Create a new Client builder
30    pub fn builder() -> ClientBuilder {
31        ClientBuilder {
32            endpoint: String::new(),
33            timeout: None,
34            connect_timeout: None,
35            tls_config: None,
36        }
37    }
38
39    /// Create a new Client with custom channel configuration
40    pub async fn connect_with_config<F>(endpoint: impl Into<String>, config: F) -> Result<Self>
41    where
42        F: FnOnce(Endpoint) -> Endpoint,
43    {
44        let endpoint_str = endpoint.into();
45        let endpoint = Endpoint::from_shared(endpoint_str.clone())
46            .map_err(|e| ClientError::InvalidEndpoint(e.to_string()))?;
47
48        let endpoint = config(endpoint);
49        let channel = endpoint.connect().await.map_err(ClientError::Transport)?;
50
51        Ok(Self {
52            channel,
53            endpoint: endpoint_str,
54        })
55    }
56
57    /// Get the underlying channel
58    pub fn channel(&self) -> &Channel {
59        &self.channel
60    }
61
62    /// Get the endpoint URL
63    pub fn endpoint(&self) -> &str {
64        &self.endpoint
65    }
66
67    async fn create_channel(endpoint: &str) -> Result<Channel> {
68        let endpoint = Endpoint::from_shared(endpoint.to_string())
69            .map_err(|e| ClientError::InvalidEndpoint(e.to_string()))?
70            .timeout(Duration::from_secs(10))
71            .connect_timeout(Duration::from_secs(5));
72
73        let channel = endpoint.connect().await.map_err(ClientError::Transport)?;
74
75        Ok(channel)
76    }
77}
78
79impl ClientBuilder {
80    /// Set the endpoint URL
81    pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
82        self.endpoint = endpoint.into();
83        self
84    }
85
86    /// Set the request timeout
87    pub fn timeout(mut self, timeout: Duration) -> Self {
88        self.timeout = Some(timeout);
89        self
90    }
91
92    /// Set the connection timeout
93    pub fn connect_timeout(mut self, timeout: Duration) -> Self {
94        self.connect_timeout = Some(timeout);
95        self
96    }
97
98    /// Enable TLS with default configuration
99    pub fn tls(mut self) -> Self {
100        self.tls_config = Some(ClientTlsConfig::new());
101        self
102    }
103
104    /// Set custom TLS configuration
105    pub fn tls_config(mut self, config: ClientTlsConfig) -> Self {
106        self.tls_config = Some(config);
107        self
108    }
109
110    /// Build the Client
111    pub async fn build(self) -> Result<Client> {
112        if self.endpoint.is_empty() {
113            return Err(ClientError::InvalidEndpoint(
114                "Endpoint cannot be empty".to_string(),
115            ));
116        }
117
118        let endpoint = Endpoint::from_shared(self.endpoint.clone())
119            .map_err(|e| ClientError::InvalidEndpoint(e.to_string()))?;
120
121        // Apply timeout configurations
122        let endpoint = if let Some(timeout) = self.timeout {
123            endpoint.timeout(timeout)
124        } else {
125            endpoint.timeout(Duration::from_secs(10))
126        };
127
128        let endpoint = if let Some(connect_timeout) = self.connect_timeout {
129            endpoint.connect_timeout(connect_timeout)
130        } else {
131            endpoint.connect_timeout(Duration::from_secs(5))
132        };
133
134        // Apply TLS configuration if provided
135        let endpoint = if let Some(tls_config) = self.tls_config {
136            endpoint.tls_config(tls_config)?
137        } else {
138            endpoint
139        };
140
141        let channel = endpoint.connect().await.map_err(ClientError::Transport)?;
142
143        Ok(Client {
144            channel,
145            endpoint: self.endpoint,
146        })
147    }
148}