Skip to main content

alien_bindings/providers/function/
grpc.rs

1use crate::error::{ErrorData, Result};
2use crate::traits::{Binding, Function, FunctionInvokeRequest, FunctionInvokeResponse};
3use alien_error::{Context as _, IntoAlienError as _};
4use async_trait::async_trait;
5use std::collections::BTreeMap;
6use std::fmt::{Debug, Formatter};
7use tonic::transport::Channel;
8
9// Import generated protobuf types
10pub mod proto {
11    tonic::include_proto!("alien_bindings.function");
12}
13
14use proto::{function_service_client::FunctionServiceClient, GetFunctionUrlRequest, InvokeRequest};
15
16/// gRPC-based Function implementation that forwards calls to a remote Function service
17pub struct GrpcFunction {
18    client: FunctionServiceClient<Channel>,
19    binding_name: String,
20}
21
22impl Debug for GrpcFunction {
23    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24        f.debug_struct("GrpcFunction")
25            .field("binding_name", &self.binding_name)
26            .finish()
27    }
28}
29
30impl GrpcFunction {
31    /// Create a new gRPC Function client
32    pub async fn new(binding_name: String, grpc_endpoint: String) -> Result<Self> {
33        let channel = crate::providers::grpc_provider::create_grpc_channel(grpc_endpoint).await?;
34        Self::new_from_channel(channel, binding_name).await
35    }
36
37    /// Create a new gRPC Function client from an existing channel
38    pub async fn new_from_channel(channel: Channel, binding_name: String) -> Result<Self> {
39        let client = FunctionServiceClient::new(channel);
40
41        Ok(Self {
42            client,
43            binding_name,
44        })
45    }
46}
47
48impl Binding for GrpcFunction {}
49
50#[async_trait]
51impl Function for GrpcFunction {
52    async fn invoke(&self, request: FunctionInvokeRequest) -> Result<FunctionInvokeResponse> {
53        let mut client = self.client.clone();
54
55        let grpc_request = tonic::Request::new(InvokeRequest {
56            binding_name: self.binding_name.clone(),
57            target_function: request.target_function,
58            method: request.method,
59            path: request.path,
60            headers: request.headers.into_iter().collect(),
61            body: request.body,
62            timeout_seconds: request.timeout.map(|t| t.as_secs()),
63        });
64
65        let response = client
66            .invoke(grpc_request)
67            .await
68            .into_alien_error()
69            .context(ErrorData::GrpcRequestFailed {
70                service: "FunctionService".to_string(),
71                method: "invoke".to_string(),
72                details: "Failed to invoke function".to_string(),
73            })?;
74
75        let response_inner = response.into_inner();
76        Ok(FunctionInvokeResponse {
77            status: response_inner.status as u16,
78            headers: response_inner
79                .headers
80                .into_iter()
81                .collect::<BTreeMap<_, _>>(),
82            body: response_inner.body,
83        })
84    }
85
86    async fn get_function_url(&self) -> Result<Option<String>> {
87        let mut client = self.client.clone();
88
89        let grpc_request = tonic::Request::new(GetFunctionUrlRequest {
90            binding_name: self.binding_name.clone(),
91        });
92
93        let response = client
94            .get_function_url(grpc_request)
95            .await
96            .into_alien_error()
97            .context(ErrorData::GrpcRequestFailed {
98                service: "FunctionService".to_string(),
99                method: "get_function_url".to_string(),
100                details: "Failed to get function URL".to_string(),
101            })?;
102
103        Ok(response.into_inner().url)
104    }
105
106    fn as_any(&self) -> &dyn std::any::Any {
107        self
108    }
109}