1use super::BindingValue;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(tag = "service", rename_all = "lowercase")]
14pub enum FunctionBinding {
15 Lambda(LambdaFunctionBinding),
17 CloudRun(CloudRunFunctionBinding),
19 ContainerApp(ContainerAppFunctionBinding),
21 Kubernetes(KubernetesFunctionBinding),
23 Local(LocalFunctionBinding),
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct LambdaFunctionBinding {
31 pub function_name: BindingValue<String>,
33 pub region: BindingValue<String>,
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub url: Option<BindingValue<String>>,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42#[serde(rename_all = "camelCase")]
43pub struct CloudRunFunctionBinding {
44 pub project_id: BindingValue<String>,
46 pub service_name: BindingValue<String>,
48 pub location: BindingValue<String>,
50 pub private_url: BindingValue<String>,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub public_url: Option<BindingValue<String>>,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct ContainerAppFunctionBinding {
61 pub subscription_id: BindingValue<String>,
63 pub resource_group_name: BindingValue<String>,
65 pub container_app_name: BindingValue<String>,
67 pub private_url: BindingValue<String>,
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub public_url: Option<BindingValue<String>>,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76#[serde(rename_all = "camelCase")]
77pub struct KubernetesFunctionBinding {
78 pub name: BindingValue<String>,
80 pub namespace: BindingValue<String>,
82 pub service_name: BindingValue<String>,
84 pub service_port: BindingValue<u16>,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub public_url: Option<BindingValue<String>>,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93#[serde(rename_all = "camelCase")]
94pub struct LocalFunctionBinding {
95 pub function_url: BindingValue<String>,
97}
98
99impl FunctionBinding {
100 pub fn lambda(
102 function_name: impl Into<BindingValue<String>>,
103 region: impl Into<BindingValue<String>>,
104 ) -> Self {
105 Self::Lambda(LambdaFunctionBinding {
106 function_name: function_name.into(),
107 region: region.into(),
108 url: None,
109 })
110 }
111
112 pub fn lambda_with_url(
114 function_name: impl Into<BindingValue<String>>,
115 region: impl Into<BindingValue<String>>,
116 url: impl Into<BindingValue<String>>,
117 ) -> Self {
118 Self::Lambda(LambdaFunctionBinding {
119 function_name: function_name.into(),
120 region: region.into(),
121 url: Some(url.into()),
122 })
123 }
124
125 pub fn cloud_run(
127 project_id: impl Into<BindingValue<String>>,
128 service_name: impl Into<BindingValue<String>>,
129 location: impl Into<BindingValue<String>>,
130 private_url: impl Into<BindingValue<String>>,
131 ) -> Self {
132 Self::CloudRun(CloudRunFunctionBinding {
133 project_id: project_id.into(),
134 service_name: service_name.into(),
135 location: location.into(),
136 private_url: private_url.into(),
137 public_url: None,
138 })
139 }
140
141 pub fn cloud_run_with_public_url(
143 project_id: impl Into<BindingValue<String>>,
144 service_name: impl Into<BindingValue<String>>,
145 location: impl Into<BindingValue<String>>,
146 private_url: impl Into<BindingValue<String>>,
147 public_url: impl Into<BindingValue<String>>,
148 ) -> Self {
149 Self::CloudRun(CloudRunFunctionBinding {
150 project_id: project_id.into(),
151 service_name: service_name.into(),
152 location: location.into(),
153 private_url: private_url.into(),
154 public_url: Some(public_url.into()),
155 })
156 }
157
158 pub fn container_app(
160 subscription_id: impl Into<BindingValue<String>>,
161 resource_group_name: impl Into<BindingValue<String>>,
162 container_app_name: impl Into<BindingValue<String>>,
163 private_url: impl Into<BindingValue<String>>,
164 ) -> Self {
165 Self::ContainerApp(ContainerAppFunctionBinding {
166 subscription_id: subscription_id.into(),
167 resource_group_name: resource_group_name.into(),
168 container_app_name: container_app_name.into(),
169 private_url: private_url.into(),
170 public_url: None,
171 })
172 }
173
174 pub fn container_app_with_public_url(
176 subscription_id: impl Into<BindingValue<String>>,
177 resource_group_name: impl Into<BindingValue<String>>,
178 container_app_name: impl Into<BindingValue<String>>,
179 private_url: impl Into<BindingValue<String>>,
180 public_url: impl Into<BindingValue<String>>,
181 ) -> Self {
182 Self::ContainerApp(ContainerAppFunctionBinding {
183 subscription_id: subscription_id.into(),
184 resource_group_name: resource_group_name.into(),
185 container_app_name: container_app_name.into(),
186 private_url: private_url.into(),
187 public_url: Some(public_url.into()),
188 })
189 }
190
191 pub fn local(function_url: impl Into<BindingValue<String>>) -> Self {
193 Self::Local(LocalFunctionBinding {
194 function_url: function_url.into(),
195 })
196 }
197
198 pub fn kubernetes(
200 name: impl Into<BindingValue<String>>,
201 namespace: impl Into<BindingValue<String>>,
202 service_name: impl Into<BindingValue<String>>,
203 service_port: impl Into<BindingValue<u16>>,
204 ) -> Self {
205 Self::Kubernetes(KubernetesFunctionBinding {
206 name: name.into(),
207 namespace: namespace.into(),
208 service_name: service_name.into(),
209 service_port: service_port.into(),
210 public_url: None,
211 })
212 }
213
214 pub fn kubernetes_with_public_url(
216 name: impl Into<BindingValue<String>>,
217 namespace: impl Into<BindingValue<String>>,
218 service_name: impl Into<BindingValue<String>>,
219 service_port: impl Into<BindingValue<u16>>,
220 public_url: impl Into<BindingValue<String>>,
221 ) -> Self {
222 Self::Kubernetes(KubernetesFunctionBinding {
223 name: name.into(),
224 namespace: namespace.into(),
225 service_name: service_name.into(),
226 service_port: service_port.into(),
227 public_url: Some(public_url.into()),
228 })
229 }
230
231 pub fn get_public_url(&self) -> Option<&BindingValue<String>> {
233 match self {
234 FunctionBinding::Lambda(binding) => binding.url.as_ref(),
235 FunctionBinding::CloudRun(binding) => binding.public_url.as_ref(),
236 FunctionBinding::ContainerApp(binding) => binding.public_url.as_ref(),
237 FunctionBinding::Kubernetes(binding) => binding.public_url.as_ref(),
238 FunctionBinding::Local(binding) => Some(&binding.function_url),
239 }
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_lambda_binding() {
249 let binding = FunctionBinding::lambda("my-function", "us-east-1");
250
251 if let FunctionBinding::Lambda(lambda_binding) = binding {
252 assert_eq!(
253 lambda_binding.function_name,
254 BindingValue::Value("my-function".to_string())
255 );
256 assert_eq!(
257 lambda_binding.region,
258 BindingValue::Value("us-east-1".to_string())
259 );
260 assert!(lambda_binding.url.is_none());
261 } else {
262 panic!("Expected Lambda binding");
263 }
264 }
265
266 #[test]
267 fn test_lambda_binding_with_url() {
268 let binding = FunctionBinding::lambda_with_url(
269 "my-function",
270 "us-east-1",
271 "https://abc123.lambda-url.us-east-1.on.aws/",
272 );
273
274 if let FunctionBinding::Lambda(lambda_binding) = binding {
275 assert_eq!(
276 lambda_binding.function_name,
277 BindingValue::Value("my-function".to_string())
278 );
279 assert_eq!(
280 lambda_binding.region,
281 BindingValue::Value("us-east-1".to_string())
282 );
283 assert_eq!(
284 lambda_binding.url,
285 Some(BindingValue::Value(
286 "https://abc123.lambda-url.us-east-1.on.aws/".to_string()
287 ))
288 );
289 } else {
290 panic!("Expected Lambda binding");
291 }
292 }
293
294 #[test]
295 fn test_cloud_run_binding() {
296 let binding = FunctionBinding::cloud_run(
297 "my-project",
298 "my-service",
299 "us-central1",
300 "https://my-service-abc123.a.run.app",
301 );
302
303 if let FunctionBinding::CloudRun(cloudrun_binding) = binding {
304 assert_eq!(
305 cloudrun_binding.project_id,
306 BindingValue::Value("my-project".to_string())
307 );
308 assert_eq!(
309 cloudrun_binding.service_name,
310 BindingValue::Value("my-service".to_string())
311 );
312 assert_eq!(
313 cloudrun_binding.location,
314 BindingValue::Value("us-central1".to_string())
315 );
316 assert_eq!(
317 cloudrun_binding.private_url,
318 BindingValue::Value("https://my-service-abc123.a.run.app".to_string())
319 );
320 assert!(cloudrun_binding.public_url.is_none());
321 } else {
322 panic!("Expected CloudRun binding");
323 }
324 }
325
326 #[test]
327 fn test_container_app_binding() {
328 let binding = FunctionBinding::container_app(
329 "sub-123",
330 "my-rg",
331 "my-app",
332 "https://my-app.internal.env.region.azurecontainerapps.io",
333 );
334
335 if let FunctionBinding::ContainerApp(container_app_binding) = binding {
336 assert_eq!(
337 container_app_binding.subscription_id,
338 BindingValue::Value("sub-123".to_string())
339 );
340 assert_eq!(
341 container_app_binding.resource_group_name,
342 BindingValue::Value("my-rg".to_string())
343 );
344 assert_eq!(
345 container_app_binding.container_app_name,
346 BindingValue::Value("my-app".to_string())
347 );
348 assert_eq!(
349 container_app_binding.private_url,
350 BindingValue::Value(
351 "https://my-app.internal.env.region.azurecontainerapps.io".to_string()
352 )
353 );
354 assert!(container_app_binding.public_url.is_none());
355 } else {
356 panic!("Expected ContainerApp binding");
357 }
358 }
359
360 #[test]
361 fn test_binding_value_expressions() {
362 use serde_json::json;
363
364 let binding = FunctionBinding::Lambda(LambdaFunctionBinding {
365 function_name: BindingValue::Expression(json!({"Fn::Ref": "MyFunction"})),
366 region: BindingValue::Value("us-east-1".to_string()),
367 url: Some(BindingValue::Expression(
368 json!({"Fn::GetAtt": ["MyFunction", "FunctionUrl"]}),
369 )),
370 });
371
372 let serialized = serde_json::to_string(&binding).unwrap();
373 let deserialized: FunctionBinding = serde_json::from_str(&serialized).unwrap();
374 assert_eq!(binding, deserialized);
375 }
376
377 #[test]
378 fn test_get_public_url() {
379 let lambda_binding = FunctionBinding::lambda_with_url(
380 "my-function",
381 "us-east-1",
382 "https://abc123.lambda-url.us-east-1.on.aws/",
383 );
384 assert!(lambda_binding.get_public_url().is_some());
385
386 let lambda_binding_no_url = FunctionBinding::lambda("my-function", "us-east-1");
387 assert!(lambda_binding_no_url.get_public_url().is_none());
388 }
389
390 #[test]
391 fn test_local_binding() {
392 let binding = FunctionBinding::local("http://localhost:3000");
393
394 if let FunctionBinding::Local(local_binding) = binding {
395 assert_eq!(
396 local_binding.function_url,
397 BindingValue::Value("http://localhost:3000".to_string())
398 );
399 } else {
400 panic!("Expected Local binding");
401 }
402 }
403
404 #[test]
405 fn test_local_binding_public_url() {
406 let binding = FunctionBinding::local("http://localhost:3000");
407 let url = binding.get_public_url();
408 assert!(url.is_some());
409 assert_eq!(
410 url.unwrap(),
411 &BindingValue::Value("http://localhost:3000".to_string())
412 );
413 }
414}