Skip to main content

alien_bindings/providers/container/
grpc.rs

1//! gRPC container binding implementation
2//!
3//! For accessing container bindings via gRPC from TypeScript/Python SDKs.
4
5use crate::error::{ErrorData, Result};
6use crate::traits::{Binding, Container};
7use alien_error::{Context as _, IntoAlienError as _};
8use async_trait::async_trait;
9use std::fmt::{Debug, Formatter};
10use tonic::transport::Channel;
11
12// Import generated protobuf types
13pub mod proto {
14    tonic::include_proto!("alien_bindings.container");
15}
16
17use proto::{
18    container_service_client::ContainerServiceClient, GetContainerNameRequest,
19    GetInternalUrlRequest, GetPublicUrlRequest,
20};
21
22/// gRPC-based Container implementation that forwards calls to a remote Container service
23pub struct GrpcContainer {
24    binding_name: String,
25    // Cached values for synchronous getters
26    internal_url: String,
27    public_url: Option<String>,
28    container_name: String,
29}
30
31impl Debug for GrpcContainer {
32    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
33        f.debug_struct("GrpcContainer")
34            .field("binding_name", &self.binding_name)
35            .finish()
36    }
37}
38
39impl GrpcContainer {
40    /// Create a new gRPC Container client
41    pub async fn new(binding_name: String, grpc_endpoint: String) -> Result<Self> {
42        let channel = crate::providers::grpc_provider::create_grpc_channel(grpc_endpoint).await?;
43        Self::new_from_channel(channel, binding_name).await
44    }
45
46    /// Create a new gRPC Container client from an existing channel
47    pub async fn new_from_channel(channel: Channel, binding_name: String) -> Result<Self> {
48        let mut client = ContainerServiceClient::new(channel);
49
50        // Fetch and cache all values since the Container trait has sync getters
51        let internal_url = {
52            let request = tonic::Request::new(GetInternalUrlRequest {
53                binding_name: binding_name.clone(),
54            });
55            client
56                .get_internal_url(request)
57                .await
58                .into_alien_error()
59                .context(ErrorData::GrpcRequestFailed {
60                    service: "ContainerService".to_string(),
61                    method: "get_internal_url".to_string(),
62                    details: "Failed to get internal URL".to_string(),
63                })?
64                .into_inner()
65                .url
66        };
67
68        let public_url = {
69            let request = tonic::Request::new(GetPublicUrlRequest {
70                binding_name: binding_name.clone(),
71            });
72            client
73                .get_public_url(request)
74                .await
75                .into_alien_error()
76                .context(ErrorData::GrpcRequestFailed {
77                    service: "ContainerService".to_string(),
78                    method: "get_public_url".to_string(),
79                    details: "Failed to get public URL".to_string(),
80                })?
81                .into_inner()
82                .url
83        };
84
85        let container_name = {
86            let request = tonic::Request::new(GetContainerNameRequest {
87                binding_name: binding_name.clone(),
88            });
89            client
90                .get_container_name(request)
91                .await
92                .into_alien_error()
93                .context(ErrorData::GrpcRequestFailed {
94                    service: "ContainerService".to_string(),
95                    method: "get_container_name".to_string(),
96                    details: "Failed to get container name".to_string(),
97                })?
98                .into_inner()
99                .name
100        };
101
102        Ok(Self {
103            binding_name,
104            internal_url,
105            public_url,
106            container_name,
107        })
108    }
109}
110
111impl Binding for GrpcContainer {}
112
113#[async_trait]
114impl Container for GrpcContainer {
115    fn get_internal_url(&self) -> &str {
116        &self.internal_url
117    }
118
119    fn get_public_url(&self) -> Option<&str> {
120        self.public_url.as_deref()
121    }
122
123    fn get_container_name(&self) -> &str {
124        &self.container_name
125    }
126
127    fn as_any(&self) -> &dyn std::any::Any {
128        self
129    }
130}