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    client: ContainerServiceClient<Channel>,
25    binding_name: String,
26    // Cached values for synchronous getters
27    internal_url: String,
28    public_url: Option<String>,
29    container_name: String,
30}
31
32impl Debug for GrpcContainer {
33    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34        f.debug_struct("GrpcContainer")
35            .field("binding_name", &self.binding_name)
36            .finish()
37    }
38}
39
40impl GrpcContainer {
41    /// Create a new gRPC Container client
42    pub async fn new(binding_name: String, grpc_endpoint: String) -> Result<Self> {
43        let channel = crate::providers::grpc_provider::create_grpc_channel(grpc_endpoint).await?;
44        Self::new_from_channel(channel, binding_name).await
45    }
46
47    /// Create a new gRPC Container client from an existing channel
48    pub async fn new_from_channel(channel: Channel, binding_name: String) -> Result<Self> {
49        let mut client = ContainerServiceClient::new(channel);
50
51        // Fetch and cache all values since the Container trait has sync getters
52        let internal_url = {
53            let request = tonic::Request::new(GetInternalUrlRequest {
54                binding_name: binding_name.clone(),
55            });
56            client
57                .get_internal_url(request)
58                .await
59                .into_alien_error()
60                .context(ErrorData::GrpcRequestFailed {
61                    service: "ContainerService".to_string(),
62                    method: "get_internal_url".to_string(),
63                    details: "Failed to get internal URL".to_string(),
64                })?
65                .into_inner()
66                .url
67        };
68
69        let public_url = {
70            let request = tonic::Request::new(GetPublicUrlRequest {
71                binding_name: binding_name.clone(),
72            });
73            client
74                .get_public_url(request)
75                .await
76                .into_alien_error()
77                .context(ErrorData::GrpcRequestFailed {
78                    service: "ContainerService".to_string(),
79                    method: "get_public_url".to_string(),
80                    details: "Failed to get public URL".to_string(),
81                })?
82                .into_inner()
83                .url
84        };
85
86        let container_name = {
87            let request = tonic::Request::new(GetContainerNameRequest {
88                binding_name: binding_name.clone(),
89            });
90            client
91                .get_container_name(request)
92                .await
93                .into_alien_error()
94                .context(ErrorData::GrpcRequestFailed {
95                    service: "ContainerService".to_string(),
96                    method: "get_container_name".to_string(),
97                    details: "Failed to get container name".to_string(),
98                })?
99                .into_inner()
100                .name
101        };
102
103        Ok(Self {
104            client,
105            binding_name,
106            internal_url,
107            public_url,
108            container_name,
109        })
110    }
111}
112
113impl Binding for GrpcContainer {}
114
115#[async_trait]
116impl Container for GrpcContainer {
117    fn get_internal_url(&self) -> &str {
118        &self.internal_url
119    }
120
121    fn get_public_url(&self) -> Option<&str> {
122        self.public_url.as_deref()
123    }
124
125    fn get_container_name(&self) -> &str {
126        &self.container_name
127    }
128
129    fn as_any(&self) -> &dyn std::any::Any {
130        self
131    }
132}