1use anyhow::Result;
2use depyler_annotations::LambdaEventType;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone)]
8pub struct LambdaTypeMapper {
9 event_mappings: HashMap<LambdaEventType, EventTypeMapping>,
10 response_mappings: HashMap<LambdaEventType, ResponseTypeMapping>,
11}
12
13#[derive(Debug, Clone)]
14pub struct EventTypeMapping {
15 pub rust_type: String,
16 pub aws_events_module: String,
17 pub imports: Vec<String>,
18 pub serde_attributes: Vec<String>,
19}
20
21#[derive(Debug, Clone)]
22pub struct ResponseTypeMapping {
23 pub rust_type: String,
24 pub conversion_impl: Option<String>,
25 pub imports: Vec<String>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct TypeConversionRule {
30 pub python_pattern: String,
31 pub rust_type: String,
32 pub lambda_context: LambdaContext,
33 pub serde_attribute: Option<String>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub enum LambdaContext {
38 EventRoot,
39 HeaderValue,
40 StatusCode,
41 Timestamp,
42 Records,
43 RequestContext,
44 PathParameters,
45 QueryStringParameters,
46 Body,
47}
48
49impl Default for LambdaTypeMapper {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55impl LambdaTypeMapper {
56 pub fn new() -> Self {
57 let mut event_mappings = HashMap::new();
58 let mut response_mappings = HashMap::new();
59
60 event_mappings.insert(
62 LambdaEventType::S3Event,
63 EventTypeMapping {
64 rust_type: "S3Event".to_string(),
65 aws_events_module: "s3".to_string(),
66 imports: vec!["use aws_lambda_events::s3::S3Event;".to_string()],
67 serde_attributes: vec![],
68 },
69 );
70
71 response_mappings.insert(
72 LambdaEventType::S3Event,
73 ResponseTypeMapping {
74 rust_type: "serde_json::Value".to_string(),
75 conversion_impl: None,
76 imports: vec!["use serde_json;".to_string()],
77 },
78 );
79
80 event_mappings.insert(
82 LambdaEventType::ApiGatewayProxyRequest,
83 EventTypeMapping {
84 rust_type: "ApiGatewayProxyRequest".to_string(),
85 aws_events_module: "apigw".to_string(),
86 imports: vec![
87 "use aws_lambda_events::apigw::{ApiGatewayProxyRequest, ApiGatewayProxyResponse};".to_string(),
88 "use std::collections::HashMap;".to_string(),
89 ],
90 serde_attributes: vec![],
91 },
92 );
93
94 response_mappings.insert(
95 LambdaEventType::ApiGatewayProxyRequest,
96 ResponseTypeMapping {
97 rust_type: "ApiGatewayProxyResponse".to_string(),
98 conversion_impl: Some(APIGW_RESPONSE_IMPL.to_string()),
99 imports: vec![
100 "use aws_lambda_events::apigw::ApiGatewayProxyResponse;".to_string(),
101 "use std::collections::HashMap;".to_string(),
102 ],
103 },
104 );
105
106 event_mappings.insert(
108 LambdaEventType::ApiGatewayV2HttpRequest,
109 EventTypeMapping {
110 rust_type: "ApiGatewayV2httpRequest".to_string(),
111 aws_events_module: "apigw".to_string(),
112 imports: vec![
113 "use aws_lambda_events::apigw::{ApiGatewayV2httpRequest, ApiGatewayV2httpResponse};".to_string(),
114 "use std::collections::HashMap;".to_string(),
115 ],
116 serde_attributes: vec![],
117 },
118 );
119
120 response_mappings.insert(
121 LambdaEventType::ApiGatewayV2HttpRequest,
122 ResponseTypeMapping {
123 rust_type: "ApiGatewayV2httpResponse".to_string(),
124 conversion_impl: Some(APIGW_V2_RESPONSE_IMPL.to_string()),
125 imports: vec![
126 "use aws_lambda_events::apigw::ApiGatewayV2httpResponse;".to_string(),
127 "use std::collections::HashMap;".to_string(),
128 ],
129 },
130 );
131
132 event_mappings.insert(
134 LambdaEventType::SqsEvent,
135 EventTypeMapping {
136 rust_type: "SqsEvent".to_string(),
137 aws_events_module: "sqs".to_string(),
138 imports: vec![
139 "use aws_lambda_events::sqs::{SqsEvent, SqsBatchResponse, SqsBatchItemFailure};".to_string(),
140 ],
141 serde_attributes: vec![],
142 },
143 );
144
145 response_mappings.insert(
146 LambdaEventType::SqsEvent,
147 ResponseTypeMapping {
148 rust_type: "SqsBatchResponse".to_string(),
149 conversion_impl: None,
150 imports: vec!["use aws_lambda_events::sqs::SqsBatchResponse;".to_string()],
151 },
152 );
153
154 event_mappings.insert(
156 LambdaEventType::SnsEvent,
157 EventTypeMapping {
158 rust_type: "SnsEvent".to_string(),
159 aws_events_module: "sns".to_string(),
160 imports: vec!["use aws_lambda_events::sns::SnsEvent;".to_string()],
161 serde_attributes: vec![],
162 },
163 );
164
165 response_mappings.insert(
166 LambdaEventType::SnsEvent,
167 ResponseTypeMapping {
168 rust_type: "serde_json::Value".to_string(),
169 conversion_impl: None,
170 imports: vec!["use serde_json;".to_string()],
171 },
172 );
173
174 event_mappings.insert(
176 LambdaEventType::DynamodbEvent,
177 EventTypeMapping {
178 rust_type: "DynamodbEvent".to_string(),
179 aws_events_module: "dynamodb".to_string(),
180 imports: vec!["use aws_lambda_events::dynamodb::DynamodbEvent;".to_string()],
181 serde_attributes: vec![],
182 },
183 );
184
185 response_mappings.insert(
186 LambdaEventType::DynamodbEvent,
187 ResponseTypeMapping {
188 rust_type: "serde_json::Value".to_string(),
189 conversion_impl: None,
190 imports: vec!["use serde_json;".to_string()],
191 },
192 );
193
194 event_mappings.insert(
196 LambdaEventType::EventBridgeEvent(None),
197 EventTypeMapping {
198 rust_type: "EventBridgeEvent<serde_json::Value>".to_string(),
199 aws_events_module: "eventbridge".to_string(),
200 imports: vec![
201 "use aws_lambda_events::eventbridge::EventBridgeEvent;".to_string(),
202 "use serde_json;".to_string(),
203 ],
204 serde_attributes: vec![],
205 },
206 );
207
208 response_mappings.insert(
209 LambdaEventType::EventBridgeEvent(None),
210 ResponseTypeMapping {
211 rust_type: "()".to_string(),
212 conversion_impl: None,
213 imports: vec![],
214 },
215 );
216
217 Self {
218 event_mappings,
219 response_mappings,
220 }
221 }
222
223 pub fn get_event_mapping(&self, event_type: &LambdaEventType) -> Option<&EventTypeMapping> {
225 if let LambdaEventType::EventBridgeEvent(Some(_)) = event_type {
227 return self
228 .event_mappings
229 .get(&LambdaEventType::EventBridgeEvent(None));
230 }
231
232 self.event_mappings.get(event_type)
233 }
234
235 pub fn get_response_mapping(
237 &self,
238 event_type: &LambdaEventType,
239 ) -> Option<&ResponseTypeMapping> {
240 if let LambdaEventType::EventBridgeEvent(Some(_)) = event_type {
242 return self
243 .response_mappings
244 .get(&LambdaEventType::EventBridgeEvent(None));
245 }
246
247 self.response_mappings.get(event_type)
248 }
249
250 pub fn get_type_conversion_rules(&self) -> Vec<TypeConversionRule> {
252 vec![
253 TypeConversionRule {
254 python_pattern: "dict".to_string(),
255 rust_type: "T: Deserialize".to_string(),
256 lambda_context: LambdaContext::EventRoot,
257 serde_attribute: Some("#[serde(rename_all = \"camelCase\")]".to_string()),
258 },
259 TypeConversionRule {
260 python_pattern: "str".to_string(),
261 rust_type: "HeaderValue".to_string(),
262 lambda_context: LambdaContext::HeaderValue,
263 serde_attribute: Some("#[serde(with = \"http_serde::header_value\")]".to_string()),
264 },
265 TypeConversionRule {
266 python_pattern: "int".to_string(),
267 rust_type: "u16".to_string(),
268 lambda_context: LambdaContext::StatusCode,
269 serde_attribute: None,
270 },
271 TypeConversionRule {
272 python_pattern: "float".to_string(),
273 rust_type: "f64".to_string(),
274 lambda_context: LambdaContext::Timestamp,
275 serde_attribute: Some(
276 "#[serde(with = \"aws_lambda_events::time::float_unix_epoch\")]".to_string(),
277 ),
278 },
279 TypeConversionRule {
280 python_pattern: "List[dict]".to_string(),
281 rust_type: "Vec<Record>".to_string(),
282 lambda_context: LambdaContext::Records,
283 serde_attribute: None,
284 },
285 TypeConversionRule {
286 python_pattern: "Dict[str, str]".to_string(),
287 rust_type: "HashMap<String, String>".to_string(),
288 lambda_context: LambdaContext::PathParameters,
289 serde_attribute: Some("#[serde(default)]".to_string()),
290 },
291 TypeConversionRule {
292 python_pattern: "Dict[str, List[str]]".to_string(),
293 rust_type: "HashMap<String, Vec<String>>".to_string(),
294 lambda_context: LambdaContext::QueryStringParameters,
295 serde_attribute: Some("#[serde(default)]".to_string()),
296 },
297 TypeConversionRule {
298 python_pattern: "str".to_string(),
299 rust_type: "Option<String>".to_string(),
300 lambda_context: LambdaContext::Body,
301 serde_attribute: None,
302 },
303 ]
304 }
305
306 pub fn generate_custom_eventbridge_types(&self, custom_type: &str) -> Result<String> {
308 Ok(format!(
309 r#"#[derive(Debug, Deserialize, Serialize)]
310#[serde(rename_all = "camelCase")]
311pub struct {custom_type} {{
312 // Define your custom event fields here
313 // Example:
314 // pub event_id: String,
315 // pub timestamp: String,
316 // pub data: serde_json::Value,
317}}
318
319#[derive(Debug, Deserialize)]
320#[serde(tag = "detail-type", content = "detail")]
321pub enum EventType {{
322 #[serde(rename = "Custom Event")]
323 CustomEvent({custom_type}),
324 // Add more event variants as needed
325}}
326"#
327 ))
328 }
329
330 pub fn generate_response_builders(&self, event_type: &LambdaEventType) -> Result<String> {
332 match event_type {
333 LambdaEventType::ApiGatewayProxyRequest => Ok(APIGW_RESPONSE_BUILDER.to_string()),
334 LambdaEventType::ApiGatewayV2HttpRequest => Ok(APIGW_V2_RESPONSE_BUILDER.to_string()),
335 LambdaEventType::SqsEvent => Ok(SQS_RESPONSE_BUILDER.to_string()),
336 _ => Ok(String::new()),
337 }
338 }
339
340 pub fn add_custom_event_mapping(
342 &mut self,
343 event_type: LambdaEventType,
344 mapping: EventTypeMapping,
345 ) {
346 self.event_mappings.insert(event_type, mapping);
347 }
348
349 pub fn add_custom_response_mapping(
351 &mut self,
352 event_type: LambdaEventType,
353 mapping: ResponseTypeMapping,
354 ) {
355 self.response_mappings.insert(event_type, mapping);
356 }
357
358 pub fn get_required_imports(&self, event_type: &LambdaEventType) -> Vec<String> {
360 let mut imports = Vec::new();
361
362 if let Some(event_mapping) = self.get_event_mapping(event_type) {
363 imports.extend(event_mapping.imports.clone());
364 }
365
366 if let Some(response_mapping) = self.get_response_mapping(event_type) {
367 imports.extend(response_mapping.imports.clone());
368 }
369
370 imports.sort();
372 imports.dedup();
373 imports
374 }
375
376 pub fn generate_error_conversions(&self) -> String {
378 LAMBDA_ERROR_CONVERSIONS.to_string()
379 }
380}
381
382const APIGW_RESPONSE_IMPL: &str = r#"impl From<HandlerOutput> for ApiGatewayProxyResponse {
384 fn from(output: HandlerOutput) -> Self {
385 ApiGatewayProxyResponse {
386 status_code: output.status_code,
387 body: Some(serde_json::to_string(&output.body).unwrap()),
388 headers: output.headers.into_iter().collect(),
389 multi_value_headers: Default::default(),
390 is_base64_encoded: false,
391 }
392 }
393}"#;
394
395const APIGW_V2_RESPONSE_IMPL: &str = r#"impl From<HandlerOutput> for ApiGatewayV2httpResponse {
396 fn from(output: HandlerOutput) -> Self {
397 ApiGatewayV2httpResponse {
398 status_code: output.status_code,
399 body: Some(serde_json::to_string(&output.body).unwrap()),
400 headers: output.headers.into_iter().collect(),
401 is_base64_encoded: Some(false),
402 cookies: vec![],
403 }
404 }
405}"#;
406
407const APIGW_RESPONSE_BUILDER: &str = r#"pub struct ResponseBuilder {
408 status_code: u16,
409 headers: HashMap<String, String>,
410 body: Option<String>,
411}
412
413impl ResponseBuilder {
414 pub fn new() -> Self {
415 Self {
416 status_code: 200,
417 headers: HashMap::new(),
418 body: None,
419 }
420 }
421
422 pub fn status(mut self, status: u16) -> Self {
423 self.status_code = status;
424 self
425 }
426
427 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
428 self.headers.insert(key.into(), value.into());
429 self
430 }
431
432 pub fn json<T: serde::Serialize>(mut self, data: &T) -> Result<Self, serde_json::Error> {
433 self.body = Some(serde_json::to_string(data)?);
434 self.headers.insert("Content-Type".to_string(), "application/json".to_string());
435 Ok(self)
436 }
437
438 pub fn build(self) -> ApiGatewayProxyResponse {
439 ApiGatewayProxyResponse {
440 status_code: self.status_code,
441 headers: self.headers,
442 body: self.body,
443 multi_value_headers: Default::default(),
444 is_base64_encoded: false,
445 }
446 }
447}"#;
448
449const APIGW_V2_RESPONSE_BUILDER: &str = r#"pub struct ResponseBuilderV2 {
450 status_code: u16,
451 headers: HashMap<String, String>,
452 body: Option<String>,
453}
454
455impl ResponseBuilderV2 {
456 pub fn new() -> Self {
457 Self {
458 status_code: 200,
459 headers: HashMap::new(),
460 body: None,
461 }
462 }
463
464 pub fn status(mut self, status: u16) -> Self {
465 self.status_code = status;
466 self
467 }
468
469 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
470 self.headers.insert(key.into(), value.into());
471 self
472 }
473
474 pub fn json<T: serde::Serialize>(mut self, data: &T) -> Result<Self, serde_json::Error> {
475 self.body = Some(serde_json::to_string(data)?);
476 self.headers.insert("Content-Type".to_string(), "application/json".to_string());
477 Ok(self)
478 }
479
480 pub fn build(self) -> ApiGatewayV2httpResponse {
481 ApiGatewayV2httpResponse {
482 status_code: self.status_code,
483 headers: self.headers,
484 body: self.body,
485 is_base64_encoded: Some(false),
486 cookies: vec![],
487 }
488 }
489}"#;
490
491const SQS_RESPONSE_BUILDER: &str = r#"pub struct SqsResponseBuilder {
492 batch_item_failures: Vec<SqsBatchItemFailure>,
493}
494
495impl SqsResponseBuilder {
496 pub fn new() -> Self {
497 Self {
498 batch_item_failures: Vec::new(),
499 }
500 }
501
502 pub fn add_failure(mut self, item_identifier: String) -> Self {
503 self.batch_item_failures.push(SqsBatchItemFailure {
504 item_identifier,
505 });
506 self
507 }
508
509 pub fn build(self) -> SqsBatchResponse {
510 SqsBatchResponse {
511 batch_item_failures: self.batch_item_failures,
512 }
513 }
514}"#;
515
516const LAMBDA_ERROR_CONVERSIONS: &str = r#"#[derive(Debug, thiserror::Error)]
517pub enum LambdaError {
518 #[error("Serialization failed: {0}")]
519 Serialization(#[from] serde_json::Error),
520
521 #[error("Handler error: {0}")]
522 Handler(String),
523
524 #[error("Runtime error: {0}")]
525 Runtime(#[from] lambda_runtime::Error),
526
527 #[error("HTTP error: {0}")]
528 Http(String),
529
530 #[error("Missing parameter: {0}")]
531 MissingParameter(String),
532}
533
534// Note: lambda_runtime::Error conversion would be available when using the actual lambda_runtime crate
535// impl From<LambdaError> for lambda_runtime::Error {
536// fn from(err: LambdaError) -> Self {
537// lambda_runtime::Error::from(err.to_string())
538// }
539// }
540
541// Automatic error chain generation for common Python patterns
542impl From<&str> for LambdaError {
543 fn from(msg: &str) -> Self {
544 if msg.contains("KeyError") {
545 LambdaError::MissingParameter(msg.to_string())
546 } else if msg.contains("ValueError") {
547 LambdaError::Handler(msg.to_string())
548 } else {
549 LambdaError::Handler(msg.to_string())
550 }
551 }
552}"#;
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557
558 #[test]
559 fn test_event_mapping_retrieval() {
560 let mapper = LambdaTypeMapper::new();
561
562 let s3_mapping = mapper.get_event_mapping(&LambdaEventType::S3Event).unwrap();
563 assert_eq!(s3_mapping.rust_type, "S3Event");
564 assert_eq!(s3_mapping.aws_events_module, "s3");
565 }
566
567 #[test]
568 fn test_response_mapping_retrieval() {
569 let mapper = LambdaTypeMapper::new();
570
571 let apigw_response = mapper
572 .get_response_mapping(&LambdaEventType::ApiGatewayProxyRequest)
573 .unwrap();
574 assert_eq!(apigw_response.rust_type, "ApiGatewayProxyResponse");
575 assert!(apigw_response.conversion_impl.is_some());
576 }
577
578 #[test]
579 fn test_eventbridge_custom_type() {
580 let mapper = LambdaTypeMapper::new();
581
582 let custom_event = LambdaEventType::EventBridgeEvent(Some("OrderEvent".to_string()));
583 let mapping = mapper.get_event_mapping(&custom_event).unwrap();
584
585 assert_eq!(mapping.rust_type, "EventBridgeEvent<serde_json::Value>");
586 }
587
588 #[test]
589 fn test_type_conversion_rules() {
590 let mapper = LambdaTypeMapper::new();
591 let rules = mapper.get_type_conversion_rules();
592
593 assert!(!rules.is_empty());
594
595 let dict_rule = rules.iter().find(|r| r.python_pattern == "dict").unwrap();
596 assert_eq!(dict_rule.rust_type, "T: Deserialize");
597 assert!(dict_rule.serde_attribute.is_some());
598 }
599
600 #[test]
601 fn test_custom_eventbridge_types_generation() {
602 let mapper = LambdaTypeMapper::new();
603 let generated = mapper
604 .generate_custom_eventbridge_types("OrderEvent")
605 .unwrap();
606
607 assert!(generated.contains("struct OrderEvent"));
608 assert!(generated.contains("enum EventType"));
609 }
610
611 #[test]
612 fn test_required_imports() {
613 let mapper = LambdaTypeMapper::new();
614 let imports = mapper.get_required_imports(&LambdaEventType::ApiGatewayProxyRequest);
615
616 assert!(imports.iter().any(|i| i.contains("ApiGatewayProxyRequest")));
617 assert!(imports.iter().any(|i| i.contains("HashMap")));
618 }
619
620 #[test]
621 fn test_response_builders() {
622 let mapper = LambdaTypeMapper::new();
623 let builder = mapper
624 .generate_response_builders(&LambdaEventType::ApiGatewayProxyRequest)
625 .unwrap();
626
627 assert!(builder.contains("ResponseBuilder"));
628 assert!(builder.contains("fn status"));
629 assert!(builder.contains("fn json"));
630 }
631
632 #[test]
633 fn test_error_conversions() {
634 let mapper = LambdaTypeMapper::new();
635 let errors = mapper.generate_error_conversions();
636
637 assert!(errors.contains("enum LambdaError"));
638 assert!(errors.contains("MissingParameter"));
639 assert!(errors.contains("thiserror::Error"));
640 }
641
642 #[test]
643 fn test_custom_mapping_addition() {
644 let mut mapper = LambdaTypeMapper::new();
645
646 let custom_event = LambdaEventType::Custom("MyEvent".to_string());
647 let custom_mapping = EventTypeMapping {
648 rust_type: "MyCustomEvent".to_string(),
649 aws_events_module: "custom".to_string(),
650 imports: vec!["use my_crate::MyCustomEvent;".to_string()],
651 serde_attributes: vec![],
652 };
653
654 mapper.add_custom_event_mapping(custom_event.clone(), custom_mapping);
655
656 let retrieved = mapper.get_event_mapping(&custom_event).unwrap();
657 assert_eq!(retrieved.rust_type, "MyCustomEvent");
658 }
659
660 #[test]
665 fn test_lambda_type_mapper_default() {
666 let mapper = LambdaTypeMapper::default();
667 assert!(mapper
669 .get_event_mapping(&LambdaEventType::S3Event)
670 .is_some());
671 }
672
673 #[test]
674 fn test_sqs_event_mapping() {
675 let mapper = LambdaTypeMapper::new();
676 let mapping = mapper
677 .get_event_mapping(&LambdaEventType::SqsEvent)
678 .unwrap();
679 assert_eq!(mapping.rust_type, "SqsEvent");
680 assert_eq!(mapping.aws_events_module, "sqs");
681 }
682
683 #[test]
684 fn test_sqs_response_mapping() {
685 let mapper = LambdaTypeMapper::new();
686 let mapping = mapper
687 .get_response_mapping(&LambdaEventType::SqsEvent)
688 .unwrap();
689 assert_eq!(mapping.rust_type, "SqsBatchResponse");
690 }
691
692 #[test]
693 fn test_sns_event_mapping() {
694 let mapper = LambdaTypeMapper::new();
695 let mapping = mapper
696 .get_event_mapping(&LambdaEventType::SnsEvent)
697 .unwrap();
698 assert_eq!(mapping.rust_type, "SnsEvent");
699 assert_eq!(mapping.aws_events_module, "sns");
700 }
701
702 #[test]
703 fn test_sns_response_mapping() {
704 let mapper = LambdaTypeMapper::new();
705 let mapping = mapper
706 .get_response_mapping(&LambdaEventType::SnsEvent)
707 .unwrap();
708 assert_eq!(mapping.rust_type, "serde_json::Value");
709 }
710
711 #[test]
712 fn test_dynamodb_event_mapping() {
713 let mapper = LambdaTypeMapper::new();
714 let mapping = mapper
715 .get_event_mapping(&LambdaEventType::DynamodbEvent)
716 .unwrap();
717 assert_eq!(mapping.rust_type, "DynamodbEvent");
718 assert_eq!(mapping.aws_events_module, "dynamodb");
719 }
720
721 #[test]
722 fn test_dynamodb_response_mapping() {
723 let mapper = LambdaTypeMapper::new();
724 let mapping = mapper
725 .get_response_mapping(&LambdaEventType::DynamodbEvent)
726 .unwrap();
727 assert_eq!(mapping.rust_type, "serde_json::Value");
728 }
729
730 #[test]
731 fn test_eventbridge_base_event_mapping() {
732 let mapper = LambdaTypeMapper::new();
733 let mapping = mapper
734 .get_event_mapping(&LambdaEventType::EventBridgeEvent(None))
735 .unwrap();
736 assert_eq!(mapping.rust_type, "EventBridgeEvent<serde_json::Value>");
737 assert_eq!(mapping.aws_events_module, "eventbridge");
738 }
739
740 #[test]
741 fn test_eventbridge_base_response_mapping() {
742 let mapper = LambdaTypeMapper::new();
743 let mapping = mapper
744 .get_response_mapping(&LambdaEventType::EventBridgeEvent(None))
745 .unwrap();
746 assert_eq!(mapping.rust_type, "()");
747 }
748
749 #[test]
750 fn test_eventbridge_custom_response_mapping() {
751 let mapper = LambdaTypeMapper::new();
752 let custom_event = LambdaEventType::EventBridgeEvent(Some("MyEvent".to_string()));
753 let mapping = mapper.get_response_mapping(&custom_event).unwrap();
754 assert_eq!(mapping.rust_type, "()");
755 }
756
757 #[test]
758 fn test_s3_response_mapping() {
759 let mapper = LambdaTypeMapper::new();
760 let mapping = mapper
761 .get_response_mapping(&LambdaEventType::S3Event)
762 .unwrap();
763 assert_eq!(mapping.rust_type, "serde_json::Value");
764 }
765
766 #[test]
767 fn test_apigw_v1_event_mapping() {
768 let mapper = LambdaTypeMapper::new();
769 let mapping = mapper
770 .get_event_mapping(&LambdaEventType::ApiGatewayProxyRequest)
771 .unwrap();
772 assert_eq!(mapping.rust_type, "ApiGatewayProxyRequest");
773 assert_eq!(mapping.aws_events_module, "apigw");
774 assert!(!mapping.imports.is_empty());
775 }
776
777 #[test]
778 fn test_apigw_v2_event_mapping() {
779 let mapper = LambdaTypeMapper::new();
780 let mapping = mapper
781 .get_event_mapping(&LambdaEventType::ApiGatewayV2HttpRequest)
782 .unwrap();
783 assert_eq!(mapping.rust_type, "ApiGatewayV2httpRequest");
784 assert_eq!(mapping.aws_events_module, "apigw");
785 }
786
787 #[test]
788 fn test_apigw_v2_response_mapping() {
789 let mapper = LambdaTypeMapper::new();
790 let mapping = mapper
791 .get_response_mapping(&LambdaEventType::ApiGatewayV2HttpRequest)
792 .unwrap();
793 assert_eq!(mapping.rust_type, "ApiGatewayV2httpResponse");
794 assert!(mapping.conversion_impl.is_some());
795 }
796
797 #[test]
798 fn test_get_event_mapping_unknown() {
799 let mapper = LambdaTypeMapper::new();
800 let mapping = mapper.get_event_mapping(&LambdaEventType::Custom("Unknown".to_string()));
801 assert!(mapping.is_none());
802 }
803
804 #[test]
805 fn test_get_response_mapping_unknown() {
806 let mapper = LambdaTypeMapper::new();
807 let mapping = mapper.get_response_mapping(&LambdaEventType::Custom("Unknown".to_string()));
808 assert!(mapping.is_none());
809 }
810
811 #[test]
812 fn test_type_conversion_rules_count() {
813 let mapper = LambdaTypeMapper::new();
814 let rules = mapper.get_type_conversion_rules();
815 assert_eq!(rules.len(), 8);
817 }
818
819 #[test]
820 fn test_type_conversion_rules_status_code() {
821 let mapper = LambdaTypeMapper::new();
822 let rules = mapper.get_type_conversion_rules();
823 let status_rule = rules
824 .iter()
825 .find(|r| matches!(r.lambda_context, LambdaContext::StatusCode))
826 .unwrap();
827 assert_eq!(status_rule.python_pattern, "int");
828 assert_eq!(status_rule.rust_type, "u16");
829 }
830
831 #[test]
832 fn test_type_conversion_rules_timestamp() {
833 let mapper = LambdaTypeMapper::new();
834 let rules = mapper.get_type_conversion_rules();
835 let timestamp_rule = rules
836 .iter()
837 .find(|r| matches!(r.lambda_context, LambdaContext::Timestamp))
838 .unwrap();
839 assert_eq!(timestamp_rule.python_pattern, "float");
840 assert_eq!(timestamp_rule.rust_type, "f64");
841 assert!(timestamp_rule.serde_attribute.is_some());
842 }
843
844 #[test]
845 fn test_type_conversion_rules_records() {
846 let mapper = LambdaTypeMapper::new();
847 let rules = mapper.get_type_conversion_rules();
848 let records_rule = rules
849 .iter()
850 .find(|r| matches!(r.lambda_context, LambdaContext::Records))
851 .unwrap();
852 assert_eq!(records_rule.python_pattern, "List[dict]");
853 assert_eq!(records_rule.rust_type, "Vec<Record>");
854 }
855
856 #[test]
857 fn test_type_conversion_rules_path_params() {
858 let mapper = LambdaTypeMapper::new();
859 let rules = mapper.get_type_conversion_rules();
860 let path_rule = rules
861 .iter()
862 .find(|r| matches!(r.lambda_context, LambdaContext::PathParameters))
863 .unwrap();
864 assert_eq!(path_rule.rust_type, "HashMap<String, String>");
865 }
866
867 #[test]
868 fn test_type_conversion_rules_query_params() {
869 let mapper = LambdaTypeMapper::new();
870 let rules = mapper.get_type_conversion_rules();
871 let query_rule = rules
872 .iter()
873 .find(|r| matches!(r.lambda_context, LambdaContext::QueryStringParameters))
874 .unwrap();
875 assert_eq!(query_rule.rust_type, "HashMap<String, Vec<String>>");
876 }
877
878 #[test]
879 fn test_type_conversion_rules_body() {
880 let mapper = LambdaTypeMapper::new();
881 let rules = mapper.get_type_conversion_rules();
882 let body_rule = rules
883 .iter()
884 .find(|r| matches!(r.lambda_context, LambdaContext::Body))
885 .unwrap();
886 assert_eq!(body_rule.rust_type, "Option<String>");
887 }
888
889 #[test]
890 fn test_type_conversion_rules_header_value() {
891 let mapper = LambdaTypeMapper::new();
892 let rules = mapper.get_type_conversion_rules();
893 let header_rule = rules
894 .iter()
895 .find(|r| matches!(r.lambda_context, LambdaContext::HeaderValue))
896 .unwrap();
897 assert_eq!(header_rule.rust_type, "HeaderValue");
898 }
899
900 #[test]
901 fn test_generate_response_builders_apigw_v2() {
902 let mapper = LambdaTypeMapper::new();
903 let builder = mapper
904 .generate_response_builders(&LambdaEventType::ApiGatewayV2HttpRequest)
905 .unwrap();
906 assert!(builder.contains("ResponseBuilderV2"));
907 assert!(builder.contains("fn status"));
908 assert!(builder.contains("fn json"));
909 }
910
911 #[test]
912 fn test_generate_response_builders_sqs() {
913 let mapper = LambdaTypeMapper::new();
914 let builder = mapper
915 .generate_response_builders(&LambdaEventType::SqsEvent)
916 .unwrap();
917 assert!(builder.contains("SqsResponseBuilder"));
918 assert!(builder.contains("add_failure"));
919 }
920
921 #[test]
922 fn test_generate_response_builders_s3() {
923 let mapper = LambdaTypeMapper::new();
924 let builder = mapper
925 .generate_response_builders(&LambdaEventType::S3Event)
926 .unwrap();
927 assert!(builder.is_empty()); }
929
930 #[test]
931 fn test_generate_response_builders_sns() {
932 let mapper = LambdaTypeMapper::new();
933 let builder = mapper
934 .generate_response_builders(&LambdaEventType::SnsEvent)
935 .unwrap();
936 assert!(builder.is_empty());
937 }
938
939 #[test]
940 fn test_generate_response_builders_dynamodb() {
941 let mapper = LambdaTypeMapper::new();
942 let builder = mapper
943 .generate_response_builders(&LambdaEventType::DynamodbEvent)
944 .unwrap();
945 assert!(builder.is_empty());
946 }
947
948 #[test]
949 fn test_generate_response_builders_eventbridge() {
950 let mapper = LambdaTypeMapper::new();
951 let builder = mapper
952 .generate_response_builders(&LambdaEventType::EventBridgeEvent(None))
953 .unwrap();
954 assert!(builder.is_empty());
955 }
956
957 #[test]
958 fn test_add_custom_response_mapping() {
959 let mut mapper = LambdaTypeMapper::new();
960 let custom_event = LambdaEventType::Custom("MyEvent".to_string());
961 let custom_mapping = ResponseTypeMapping {
962 rust_type: "MyResponse".to_string(),
963 conversion_impl: Some("impl From ...".to_string()),
964 imports: vec!["use my_crate::MyResponse;".to_string()],
965 };
966
967 mapper.add_custom_response_mapping(custom_event.clone(), custom_mapping);
968
969 let retrieved = mapper.get_response_mapping(&custom_event).unwrap();
970 assert_eq!(retrieved.rust_type, "MyResponse");
971 assert!(retrieved.conversion_impl.is_some());
972 }
973
974 #[test]
975 fn test_required_imports_deduplication() {
976 let mapper = LambdaTypeMapper::new();
977 let imports = mapper.get_required_imports(&LambdaEventType::ApiGatewayProxyRequest);
978 let mut sorted = imports.clone();
980 sorted.sort();
981 sorted.dedup();
982 assert_eq!(imports.len(), sorted.len());
983 }
984
985 #[test]
986 fn test_required_imports_s3() {
987 let mapper = LambdaTypeMapper::new();
988 let imports = mapper.get_required_imports(&LambdaEventType::S3Event);
989 assert!(imports.iter().any(|i| i.contains("S3Event")));
990 assert!(imports.iter().any(|i| i.contains("serde_json")));
991 }
992
993 #[test]
994 fn test_required_imports_sqs() {
995 let mapper = LambdaTypeMapper::new();
996 let imports = mapper.get_required_imports(&LambdaEventType::SqsEvent);
997 assert!(imports.iter().any(|i| i.contains("SqsEvent")));
998 assert!(imports.iter().any(|i| i.contains("SqsBatchResponse")));
999 }
1000
1001 #[test]
1002 fn test_error_conversions_content() {
1003 let mapper = LambdaTypeMapper::new();
1004 let errors = mapper.generate_error_conversions();
1005 assert!(errors.contains("Serialization"));
1006 assert!(errors.contains("Handler"));
1007 assert!(errors.contains("Http"));
1008 assert!(errors.contains("KeyError"));
1009 assert!(errors.contains("ValueError"));
1010 }
1011
1012 #[test]
1013 fn test_event_type_mapping_fields() {
1014 let mapping = EventTypeMapping {
1015 rust_type: "Test".to_string(),
1016 aws_events_module: "test".to_string(),
1017 imports: vec!["use test::Test;".to_string()],
1018 serde_attributes: vec!["#[serde(default)]".to_string()],
1019 };
1020 assert_eq!(mapping.rust_type, "Test");
1021 assert_eq!(mapping.aws_events_module, "test");
1022 assert_eq!(mapping.imports.len(), 1);
1023 assert_eq!(mapping.serde_attributes.len(), 1);
1024 }
1025
1026 #[test]
1027 fn test_response_type_mapping_fields() {
1028 let mapping = ResponseTypeMapping {
1029 rust_type: "TestResponse".to_string(),
1030 conversion_impl: None,
1031 imports: vec![],
1032 };
1033 assert_eq!(mapping.rust_type, "TestResponse");
1034 assert!(mapping.conversion_impl.is_none());
1035 assert!(mapping.imports.is_empty());
1036 }
1037
1038 #[test]
1039 fn test_type_conversion_rule_fields() {
1040 let rule = TypeConversionRule {
1041 python_pattern: "str".to_string(),
1042 rust_type: "String".to_string(),
1043 lambda_context: LambdaContext::Body,
1044 serde_attribute: None,
1045 };
1046 assert_eq!(rule.python_pattern, "str");
1047 assert_eq!(rule.rust_type, "String");
1048 assert!(matches!(rule.lambda_context, LambdaContext::Body));
1049 assert!(rule.serde_attribute.is_none());
1050 }
1051
1052 #[test]
1053 fn test_lambda_context_variants() {
1054 let contexts = [
1056 LambdaContext::EventRoot,
1057 LambdaContext::HeaderValue,
1058 LambdaContext::StatusCode,
1059 LambdaContext::Timestamp,
1060 LambdaContext::Records,
1061 LambdaContext::RequestContext,
1062 LambdaContext::PathParameters,
1063 LambdaContext::QueryStringParameters,
1064 LambdaContext::Body,
1065 ];
1066 assert_eq!(contexts.len(), 9);
1067 }
1068
1069 #[test]
1070 fn test_apigw_response_impl_content() {
1071 assert!(APIGW_RESPONSE_IMPL.contains("impl From<HandlerOutput>"));
1072 assert!(APIGW_RESPONSE_IMPL.contains("ApiGatewayProxyResponse"));
1073 assert!(APIGW_RESPONSE_IMPL.contains("status_code"));
1074 }
1075
1076 #[test]
1077 fn test_apigw_v2_response_impl_content() {
1078 assert!(APIGW_V2_RESPONSE_IMPL.contains("impl From<HandlerOutput>"));
1079 assert!(APIGW_V2_RESPONSE_IMPL.contains("ApiGatewayV2httpResponse"));
1080 assert!(APIGW_V2_RESPONSE_IMPL.contains("cookies"));
1081 }
1082
1083 #[test]
1084 fn test_custom_eventbridge_types_struct_definition() {
1085 let mapper = LambdaTypeMapper::new();
1086 let generated = mapper
1087 .generate_custom_eventbridge_types("TestEvent")
1088 .unwrap();
1089 assert!(generated.contains("pub struct TestEvent"));
1090 assert!(generated.contains("#[derive(Debug, Deserialize, Serialize)]"));
1091 }
1092
1093 #[test]
1094 fn test_custom_eventbridge_types_enum_definition() {
1095 let mapper = LambdaTypeMapper::new();
1096 let generated = mapper
1097 .generate_custom_eventbridge_types("MyCustom")
1098 .unwrap();
1099 assert!(generated.contains("pub enum EventType"));
1100 assert!(generated.contains("CustomEvent(MyCustom)"));
1101 }
1102
1103 #[test]
1104 fn test_s3_event_imports() {
1105 let mapper = LambdaTypeMapper::new();
1106 let mapping = mapper.get_event_mapping(&LambdaEventType::S3Event).unwrap();
1107 assert!(mapping
1108 .imports
1109 .iter()
1110 .any(|i| i.contains("aws_lambda_events::s3")));
1111 }
1112
1113 #[test]
1114 fn test_apigw_v1_response_has_conversion() {
1115 let mapper = LambdaTypeMapper::new();
1116 let mapping = mapper
1117 .get_response_mapping(&LambdaEventType::ApiGatewayProxyRequest)
1118 .unwrap();
1119 let impl_str = mapping.conversion_impl.as_ref().unwrap();
1120 assert!(impl_str.contains("From<HandlerOutput>"));
1121 }
1122
1123 #[test]
1124 fn test_eventbridge_imports() {
1125 let mapper = LambdaTypeMapper::new();
1126 let mapping = mapper
1127 .get_event_mapping(&LambdaEventType::EventBridgeEvent(None))
1128 .unwrap();
1129 assert!(mapping
1130 .imports
1131 .iter()
1132 .any(|i| i.contains("EventBridgeEvent")));
1133 assert!(mapping.imports.iter().any(|i| i.contains("serde_json")));
1134 }
1135
1136 #[test]
1137 fn test_empty_serde_attributes() {
1138 let mapper = LambdaTypeMapper::new();
1139 let mapping = mapper.get_event_mapping(&LambdaEventType::S3Event).unwrap();
1140 assert!(mapping.serde_attributes.is_empty());
1141 }
1142
1143 #[test]
1144 fn test_response_builder_contains_build_method() {
1145 let mapper = LambdaTypeMapper::new();
1146 let builder = mapper
1147 .generate_response_builders(&LambdaEventType::ApiGatewayProxyRequest)
1148 .unwrap();
1149 assert!(builder.contains("fn build(self)"));
1150 }
1151
1152 #[test]
1153 fn test_sqs_response_builder_batch_failures() {
1154 let mapper = LambdaTypeMapper::new();
1155 let builder = mapper
1156 .generate_response_builders(&LambdaEventType::SqsEvent)
1157 .unwrap();
1158 assert!(builder.contains("batch_item_failures"));
1159 assert!(builder.contains("SqsBatchItemFailure"));
1160 }
1161
1162 #[test]
1163 fn test_lambda_error_conversions_from_str() {
1164 let errors = LAMBDA_ERROR_CONVERSIONS;
1165 assert!(errors.contains("impl From<&str> for LambdaError"));
1166 }
1167}