Skip to main content

volt_client_grpc/
grpc_utils.rs

1//! gRPC utility functions for creating clients and servers.
2
3use crate::credential::VoltCredential;
4use crate::error::{Result, VoltError};
5use tonic::transport::{Certificate, Channel, ClientTlsConfig, Identity};
6
7/// Create a gRPC channel with TLS
8pub async fn create_channel(
9    address: &str,
10    credential: &VoltCredential,
11    relaying: bool,
12) -> Result<Channel> {
13    let endpoint = format!("https://{}", address);
14
15    // Get CA certificate
16    let ca_pem = if relaying {
17        credential
18            .config()
19            .volt
20            .relay
21            .as_ref()
22            .and_then(|r| r.ca_pem.as_deref())
23    } else {
24        credential
25            .cache()
26            .and_then(|c| c.ca.as_deref())
27            .or_else(|| credential.config().volt.ca_pem.as_deref())
28    };
29
30    let ca_pem =
31        ca_pem.ok_or_else(|| VoltError::CertificateError("No CA certificate available".into()))?;
32
33    let ca = Certificate::from_pem(ca_pem);
34
35    // Build TLS config
36    let mut tls_config = ClientTlsConfig::new().ca_certificate(ca);
37
38    // Add client certificate if available (not relaying)
39    if !relaying {
40        if let Some(cache) = credential.cache() {
41            if let (Some(cert), Some(key)) = (cache.cert.as_ref(), cache.key.as_ref()) {
42                let identity = Identity::from_pem(cert, key);
43                tls_config = tls_config.identity(identity);
44            }
45        }
46    }
47
48    // Create channel
49    Channel::from_shared(endpoint)
50        .map_err(|e| VoltError::ConnectionError(e.to_string()))?
51        .tls_config(tls_config)
52        .map_err(VoltError::TransportError)?
53        .connect()
54        .await
55        .map_err(VoltError::from)
56}
57
58/// gRPC call options
59#[derive(Debug, Clone, Default)]
60pub struct CallOptions {
61    /// Timeout in milliseconds
62    pub timeout_ms: Option<u64>,
63    /// Whether to wait for ready
64    pub wait_for_ready: bool,
65}
66
67impl CallOptions {
68    pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
69        self.timeout_ms = Some(timeout_ms);
70        self
71    }
72
73    pub fn with_wait_for_ready(mut self) -> Self {
74        self.wait_for_ready = true;
75        self
76    }
77}