1#![allow(clippy::missing_errors_doc)] use regex::Regex;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use thiserror::Error;
7
8#[derive(Error, Debug)]
9pub enum AnnotationError {
10 #[error("Invalid annotation syntax: {0}")]
11 InvalidSyntax(String),
12 #[error("Unknown annotation key: {0}")]
13 UnknownKey(String),
14 #[error("Invalid value for key {key}: {value}")]
15 InvalidValue { key: String, value: String },
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19#[allow(clippy::struct_excessive_bools)] pub struct TranspilationAnnotations {
21 pub type_strategy: TypeStrategy,
22 pub ownership_model: OwnershipModel,
23 pub safety_level: SafetyLevel,
24 pub performance_hints: Vec<PerformanceHint>,
25 pub fallback_strategy: FallbackStrategy,
26 pub bounds_checking: BoundsChecking,
27 pub optimization_level: OptimizationLevel,
28 pub thread_safety: ThreadSafety,
29 pub interior_mutability: InteriorMutability,
30 pub string_strategy: StringStrategy,
31 pub hash_strategy: HashStrategy,
32 pub panic_behavior: PanicBehavior,
33 pub error_strategy: ErrorStrategy,
34 pub global_strategy: GlobalStrategy,
35 pub termination: Termination,
36 pub invariants: Vec<String>,
37 pub verify_bounds: bool,
38 pub service_type: Option<ServiceType>,
39 pub migration_strategy: Option<MigrationStrategy>,
40 pub compatibility_layer: Option<CompatibilityLayer>,
41 pub pattern: Option<String>,
42 pub lambda_annotations: Option<LambdaAnnotations>,
44}
45
46impl Default for TranspilationAnnotations {
47 fn default() -> Self {
48 Self {
49 type_strategy: TypeStrategy::Conservative,
50 ownership_model: OwnershipModel::Owned,
51 safety_level: SafetyLevel::Safe,
52 performance_hints: Vec::new(),
53 fallback_strategy: FallbackStrategy::Error,
54 bounds_checking: BoundsChecking::Explicit,
55 optimization_level: OptimizationLevel::Standard,
56 thread_safety: ThreadSafety::NotRequired,
57 interior_mutability: InteriorMutability::None,
58 string_strategy: StringStrategy::Conservative,
59 hash_strategy: HashStrategy::Standard,
60 panic_behavior: PanicBehavior::Propagate,
61 error_strategy: ErrorStrategy::Panic,
62 global_strategy: GlobalStrategy::None,
63 termination: Termination::Unknown,
64 invariants: Vec::new(),
65 verify_bounds: false,
66 service_type: None,
67 migration_strategy: None,
68 compatibility_layer: None,
69 pattern: None,
70 lambda_annotations: None,
71 }
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76#[allow(clippy::struct_excessive_bools)] pub struct LambdaAnnotations {
78 pub runtime: LambdaRuntime,
79 pub event_type: Option<LambdaEventType>,
80 pub cold_start_optimize: bool,
81 pub memory_size: u16,
82 pub architecture: Architecture,
83 pub pre_warm_paths: Vec<String>,
84 pub custom_serialization: bool,
85 pub batch_failure_reporting: bool,
86 pub timeout: Option<u16>,
87 pub tracing_enabled: bool,
88 pub environment_variables: Vec<(String, String)>,
89}
90
91impl Default for LambdaAnnotations {
92 fn default() -> Self {
93 Self {
94 runtime: LambdaRuntime::ProvidedAl2,
95 event_type: None,
96 cold_start_optimize: true,
97 memory_size: 128,
98 architecture: Architecture::Arm64,
99 pre_warm_paths: vec![],
100 custom_serialization: false,
101 batch_failure_reporting: false,
102 timeout: None,
103 tracing_enabled: false,
104 environment_variables: vec![],
105 }
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110pub enum LambdaRuntime {
111 ProvidedAl2,
112 ProvidedAl2023,
113 Custom(String),
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
117pub enum LambdaEventType {
118 Auto,
119 S3Event,
120 ApiGatewayProxyRequest,
121 ApiGatewayV2HttpRequest,
122 SqsEvent,
123 SnsEvent,
124 DynamodbEvent,
125 EventBridgeEvent(Option<String>),
126 CloudwatchEvent,
127 KinesisEvent,
128 Custom(String),
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
132pub enum Architecture {
133 X86_64,
134 Arm64,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
138pub enum TypeStrategy {
139 Conservative,
140 Aggressive,
141 ZeroCopy,
142 AlwaysOwned,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146pub enum OwnershipModel {
147 Owned,
148 Borrowed,
149 Shared,
150}
151
152#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
153pub enum SafetyLevel {
154 Safe,
155 UnsafeAllowed,
156}
157
158#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
159pub enum PerformanceHint {
160 Vectorize,
161 UnrollLoops(u32),
162 OptimizeForLatency,
163 OptimizeForThroughput,
164 PerformanceCritical,
165}
166
167#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
168pub enum FallbackStrategy {
169 Mcp,
170 Manual,
171 Error,
172}
173
174#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
175pub enum BoundsChecking {
176 Explicit,
177 Implicit,
178 Disabled,
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
182pub enum OptimizationLevel {
183 Standard,
184 Aggressive,
185 Conservative,
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
189pub enum ThreadSafety {
190 Required,
191 NotRequired,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
195pub enum InteriorMutability {
196 None,
197 ArcMutex,
198 RefCell,
199 Cell,
200}
201
202#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
203pub enum StringStrategy {
204 Conservative,
205 AlwaysOwned,
206 ZeroCopy,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
210pub enum HashStrategy {
211 Standard,
212 Fnv,
213 AHash,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
217pub enum PanicBehavior {
218 Propagate,
219 ReturnError,
220 Abort,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
224pub enum ErrorStrategy {
225 Panic,
226 ResultType,
227 OptionType,
228}
229
230#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
231pub enum GlobalStrategy {
232 None,
233 LazyStatic,
234 OnceCell,
235}
236
237#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
238pub enum Termination {
239 Unknown,
240 Proven,
241 BoundedLoop(u32),
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
245pub enum ServiceType {
246 WebApi,
247 Cli,
248 Library,
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
252pub enum MigrationStrategy {
253 Incremental,
254 BigBang,
255 Hybrid,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
259pub enum CompatibilityLayer {
260 PyO3,
261 CTypes,
262 None,
263}
264
265pub struct AnnotationParser {
266 pattern: Regex,
267}
268
269#[derive(Debug, Clone, Default)]
270pub struct AnnotationValidator;
271
272impl AnnotationValidator {
273 pub fn new() -> Self {
275 Self
276 }
277
278 pub fn validate(&self, annotations: &TranspilationAnnotations) -> Result<(), Vec<String>> {
284 let mut errors = Vec::new();
285
286 if annotations.string_strategy == StringStrategy::ZeroCopy
288 && annotations.ownership_model == OwnershipModel::Owned
289 {
290 errors
291 .push("Zero-copy string strategy conflicts with owned ownership model".to_string());
292 }
293
294 if annotations.thread_safety == ThreadSafety::Required
295 && annotations.interior_mutability == InteriorMutability::RefCell
296 {
297 errors.push("RefCell is not thread-safe, use Arc<Mutex<T>> instead".to_string());
298 }
299
300 if annotations.panic_behavior == PanicBehavior::ReturnError
301 && annotations.error_strategy == ErrorStrategy::Panic
302 {
303 errors.push("Conflicting panic behavior and error strategy".to_string());
304 }
305
306 if annotations.optimization_level == OptimizationLevel::Aggressive
307 && annotations.bounds_checking == BoundsChecking::Explicit
308 {
309 errors.push(
310 "Aggressive optimization may conflict with explicit bounds checking".to_string(),
311 );
312 }
313
314 if errors.is_empty() {
315 Ok(())
316 } else {
317 Err(errors)
318 }
319 }
320
321 pub fn suggest_improvements(&self, annotations: &TranspilationAnnotations) -> Vec<String> {
322 let mut suggestions = Vec::new();
323
324 if annotations
325 .performance_hints
326 .contains(&PerformanceHint::PerformanceCritical)
327 && annotations.optimization_level != OptimizationLevel::Aggressive
328 {
329 suggestions.push(
330 "Consider using optimization_level = \"aggressive\" for performance critical code"
331 .to_string(),
332 );
333 }
334
335 if annotations.thread_safety == ThreadSafety::Required
336 && annotations.ownership_model != OwnershipModel::Shared
337 {
338 suggestions
339 .push("Consider using ownership = \"shared\" for thread-safe code".to_string());
340 }
341
342 if annotations.service_type == Some(ServiceType::WebApi)
343 && !annotations
344 .performance_hints
345 .contains(&PerformanceHint::OptimizeForLatency)
346 {
347 suggestions
348 .push("Consider adding optimization_hint = \"latency\" for web APIs".to_string());
349 }
350
351 suggestions
352 }
353}
354
355#[derive(Debug, Clone)]
356pub struct AnnotationExtractor {
357 function_pattern: Regex,
358 class_pattern: Regex,
359}
360
361impl Default for AnnotationExtractor {
362 fn default() -> Self {
363 Self {
364 function_pattern: Regex::new(r"(?m)^def\s+(\w+)\s*\(").unwrap(),
365 class_pattern: Regex::new(r"(?m)^class\s+(\w+)\s*[\(:]").unwrap(),
366 }
367 }
368}
369
370impl AnnotationExtractor {
371 pub fn new() -> Self {
373 Self::default()
374 }
375
376 pub fn extract_function_annotations(
382 &self,
383 source: &str,
384 function_name: &str,
385 ) -> Option<String> {
386 let lines: Vec<&str> = source.lines().collect();
387
388 for (i, line) in lines.iter().enumerate() {
389 if let Some(captures) = self.function_pattern.captures(line) {
390 if captures.get(1).unwrap().as_str() == function_name {
391 let mut annotations = Vec::new();
393 let mut j = i.saturating_sub(1);
394
395 while j < i && (lines[j].trim().starts_with('#') || lines[j].trim().is_empty())
396 {
397 if lines[j].contains("@depyler:") {
398 annotations.push(lines[j]);
399 }
400 if j == 0 {
401 break;
402 }
403 j = j.saturating_sub(1);
404 }
405
406 if !annotations.is_empty() {
407 annotations.reverse();
408 return Some(annotations.join("\n"));
409 }
410 }
411 }
412 }
413 None
414 }
415
416 pub fn extract_class_annotations(&self, source: &str, class_name: &str) -> Option<String> {
422 let lines: Vec<&str> = source.lines().collect();
423
424 for (i, line) in lines.iter().enumerate() {
425 if let Some(captures) = self.class_pattern.captures(line) {
426 if captures.get(1).unwrap().as_str() == class_name {
427 let mut annotations = Vec::new();
429 let mut j = i.saturating_sub(1);
430
431 while j < i && (lines[j].trim().starts_with('#') || lines[j].trim().is_empty())
432 {
433 if lines[j].contains("@depyler:") {
434 annotations.push(lines[j]);
435 }
436 if j == 0 {
437 break;
438 }
439 j = j.saturating_sub(1);
440 }
441
442 if !annotations.is_empty() {
443 annotations.reverse();
444 return Some(annotations.join("\n"));
445 }
446 }
447 }
448 }
449 None
450 }
451}
452
453impl Default for AnnotationParser {
454 fn default() -> Self {
455 Self::new()
456 }
457}
458
459impl AnnotationParser {
460 pub fn new() -> Self {
466 let pattern =
467 Regex::new(r"#\s*@depyler:\s*(\w+)\s*=\s*(.+)")
469 .unwrap_or_else(|e| panic!("Failed to compile annotation regex: {e}"));
470 Self { pattern }
471 }
472
473 pub fn parse_annotations(
483 &self,
484 source: &str,
485 ) -> Result<TranspilationAnnotations, AnnotationError> {
486 let mut annotations = TranspilationAnnotations::default();
487 let mut parsed_values: HashMap<String, String> = HashMap::new();
488
489 for line in source.lines() {
490 if let Some(captures) = self.pattern.captures(line) {
491 let key = captures.get(1).unwrap().as_str().to_string();
492 let value = captures.get(2).unwrap().as_str().trim_matches('"').trim();
493 parsed_values.insert(key, value.to_string());
494 }
495 }
496
497 self.apply_annotations(&mut annotations, parsed_values)?;
498 Ok(annotations)
499 }
500
501 pub fn parse_function_annotations(
507 &self,
508 function_source: &str,
509 ) -> Result<TranspilationAnnotations, AnnotationError> {
510 self.parse_annotations(function_source)
511 }
512
513 #[allow(clippy::too_many_lines)]
514 fn apply_annotations(
515 &self,
516 annotations: &mut TranspilationAnnotations,
517 values: HashMap<String, String>,
518 ) -> Result<(), AnnotationError> {
519 for (key, value) in values {
520 match key.as_str() {
521 "type_strategy" => {
522 annotations.type_strategy = self.parse_type_strategy(&value)?;
523 }
524 "ownership" => {
525 annotations.ownership_model = self.parse_ownership_model(&value)?;
526 }
527 "safety_level" => {
528 annotations.safety_level = self.parse_safety_level(&value)?;
529 }
530 "fallback" => {
531 annotations.fallback_strategy = self.parse_fallback_strategy(&value)?;
532 }
533 "bounds_checking" => {
534 annotations.bounds_checking = self.parse_bounds_checking(&value)?;
535 }
536 "optimization_level" => {
537 annotations.optimization_level = self.parse_optimization_level(&value)?;
538 }
539 "thread_safety" => {
540 annotations.thread_safety = self.parse_thread_safety(&value)?;
541 }
542 "interior_mutability" => {
543 annotations.interior_mutability = self.parse_interior_mutability(&value)?;
544 }
545 "performance_critical" => {
546 if value == "true" {
547 annotations
548 .performance_hints
549 .push(PerformanceHint::PerformanceCritical);
550 }
551 }
552 "vectorize" => {
553 if value == "true" {
554 annotations
555 .performance_hints
556 .push(PerformanceHint::Vectorize);
557 }
558 }
559 "unroll_loops" => {
560 let count: u32 = value.parse().map_err(|_| AnnotationError::InvalidValue {
561 key: key.clone(),
562 value: value.clone(),
563 })?;
564 annotations
565 .performance_hints
566 .push(PerformanceHint::UnrollLoops(count));
567 }
568 "optimization_hint" => {
569 match value.as_str() {
570 "vectorize" => annotations
571 .performance_hints
572 .push(PerformanceHint::Vectorize),
573 "latency" => annotations
574 .performance_hints
575 .push(PerformanceHint::OptimizeForLatency),
576 "throughput" => annotations
577 .performance_hints
578 .push(PerformanceHint::OptimizeForThroughput),
579 "async_ready" => {
580 eprintln!(
583 "Warning: async_ready is experimental and not yet fully supported"
584 );
585 }
586 _ => return Err(AnnotationError::InvalidValue { key, value }),
587 }
588 }
589 "string_strategy" => {
590 annotations.string_strategy = self.parse_string_strategy(&value)?;
591 }
592 "hash_strategy" => {
593 annotations.hash_strategy = self.parse_hash_strategy(&value)?;
594 }
595 "panic_behavior" => {
596 annotations.panic_behavior = self.parse_panic_behavior(&value)?;
597 }
598 "error_strategy" => {
599 annotations.error_strategy = self.parse_error_strategy(&value)?;
600 }
601 "global_strategy" => {
602 annotations.global_strategy = self.parse_global_strategy(&value)?;
603 }
604 "termination" => {
605 annotations.termination = self.parse_termination(&value)?;
606 }
607 "invariant" => {
608 annotations.invariants.push(value);
609 }
610 "verify_bounds" => {
611 annotations.verify_bounds = value == "true";
612 }
613 "service_type" => {
614 annotations.service_type = Some(self.parse_service_type(&value)?);
615 }
616 "migration_strategy" => {
617 annotations.migration_strategy = Some(self.parse_migration_strategy(&value)?);
618 }
619 "compatibility_layer" => {
620 annotations.compatibility_layer = Some(self.parse_compatibility_layer(&value)?);
621 }
622 "pattern" => {
623 annotations.pattern = Some(value);
624 }
625 "lambda_runtime" => {
627 let lambda_annotations = annotations
628 .lambda_annotations
629 .get_or_insert_with(LambdaAnnotations::default);
630 lambda_annotations.runtime = self.parse_lambda_runtime(&value)?;
631 }
632 "event_type" => {
633 let lambda_annotations = annotations
634 .lambda_annotations
635 .get_or_insert_with(LambdaAnnotations::default);
636 lambda_annotations.event_type = Some(self.parse_lambda_event_type(&value)?);
637 }
638 "cold_start_optimize" => {
639 let lambda_annotations = annotations
640 .lambda_annotations
641 .get_or_insert_with(LambdaAnnotations::default);
642 lambda_annotations.cold_start_optimize = value == "true";
643 }
644 "memory_size" => {
645 let lambda_annotations = annotations
646 .lambda_annotations
647 .get_or_insert_with(LambdaAnnotations::default);
648 lambda_annotations.memory_size =
649 value.parse().map_err(|_| AnnotationError::InvalidValue {
650 key: key.clone(),
651 value: value.clone(),
652 })?;
653 }
654 "architecture" => {
655 let lambda_annotations = annotations
656 .lambda_annotations
657 .get_or_insert_with(LambdaAnnotations::default);
658 lambda_annotations.architecture = self.parse_architecture(&value)?;
659 }
660 "batch_failure_reporting" => {
661 let lambda_annotations = annotations
662 .lambda_annotations
663 .get_or_insert_with(LambdaAnnotations::default);
664 lambda_annotations.batch_failure_reporting = value == "true";
665 }
666 "custom_serialization" => {
667 let lambda_annotations = annotations
668 .lambda_annotations
669 .get_or_insert_with(LambdaAnnotations::default);
670 lambda_annotations.custom_serialization = value == "true";
671 }
672 "timeout" => {
673 let lambda_annotations = annotations
674 .lambda_annotations
675 .get_or_insert_with(LambdaAnnotations::default);
676 lambda_annotations.timeout =
677 Some(value.parse().map_err(|_| AnnotationError::InvalidValue {
678 key: key.clone(),
679 value: value.clone(),
680 })?);
681 }
682 "tracing" => {
683 let lambda_annotations = annotations
684 .lambda_annotations
685 .get_or_insert_with(LambdaAnnotations::default);
686 lambda_annotations.tracing_enabled = value == "true" || value == "Active";
687 }
688 _ => return Err(AnnotationError::UnknownKey(key)),
689 }
690 }
691 Ok(())
692 }
693
694 fn parse_type_strategy(&self, value: &str) -> Result<TypeStrategy, AnnotationError> {
695 match value {
696 "conservative" => Ok(TypeStrategy::Conservative),
697 "aggressive" => Ok(TypeStrategy::Aggressive),
698 "zero_copy" => Ok(TypeStrategy::ZeroCopy),
699 "always_owned" => Ok(TypeStrategy::AlwaysOwned),
700 _ => Err(AnnotationError::InvalidValue {
701 key: "type_strategy".to_string(),
702 value: value.to_string(),
703 }),
704 }
705 }
706
707 fn parse_ownership_model(&self, value: &str) -> Result<OwnershipModel, AnnotationError> {
708 match value {
709 "owned" => Ok(OwnershipModel::Owned),
710 "borrowed" => Ok(OwnershipModel::Borrowed),
711 "shared" => Ok(OwnershipModel::Shared),
712 _ => Err(AnnotationError::InvalidValue {
713 key: "ownership".to_string(),
714 value: value.to_string(),
715 }),
716 }
717 }
718
719 fn parse_safety_level(&self, value: &str) -> Result<SafetyLevel, AnnotationError> {
720 match value {
721 "safe" => Ok(SafetyLevel::Safe),
722 "unsafe_allowed" => Ok(SafetyLevel::UnsafeAllowed),
723 _ => Err(AnnotationError::InvalidValue {
724 key: "safety_level".to_string(),
725 value: value.to_string(),
726 }),
727 }
728 }
729
730 fn parse_fallback_strategy(&self, value: &str) -> Result<FallbackStrategy, AnnotationError> {
731 match value {
732 "mcp" => Ok(FallbackStrategy::Mcp),
733 "manual" => Ok(FallbackStrategy::Manual),
734 "error" => Ok(FallbackStrategy::Error),
735 _ => Err(AnnotationError::InvalidValue {
736 key: "fallback".to_string(),
737 value: value.to_string(),
738 }),
739 }
740 }
741
742 fn parse_bounds_checking(&self, value: &str) -> Result<BoundsChecking, AnnotationError> {
743 match value {
744 "explicit" => Ok(BoundsChecking::Explicit),
745 "implicit" => Ok(BoundsChecking::Implicit),
746 "disabled" => Ok(BoundsChecking::Disabled),
747 _ => Err(AnnotationError::InvalidValue {
748 key: "bounds_checking".to_string(),
749 value: value.to_string(),
750 }),
751 }
752 }
753
754 fn parse_optimization_level(&self, value: &str) -> Result<OptimizationLevel, AnnotationError> {
755 match value {
756 "standard" => Ok(OptimizationLevel::Standard),
757 "aggressive" => Ok(OptimizationLevel::Aggressive),
758 "conservative" => Ok(OptimizationLevel::Conservative),
759 _ => Err(AnnotationError::InvalidValue {
760 key: "optimization_level".to_string(),
761 value: value.to_string(),
762 }),
763 }
764 }
765
766 fn parse_thread_safety(&self, value: &str) -> Result<ThreadSafety, AnnotationError> {
767 match value {
768 "required" => Ok(ThreadSafety::Required),
769 "not_required" => Ok(ThreadSafety::NotRequired),
770 _ => Err(AnnotationError::InvalidValue {
771 key: "thread_safety".to_string(),
772 value: value.to_string(),
773 }),
774 }
775 }
776
777 fn parse_interior_mutability(
778 &self,
779 value: &str,
780 ) -> Result<InteriorMutability, AnnotationError> {
781 match value {
782 "none" => Ok(InteriorMutability::None),
783 "arc_mutex" => Ok(InteriorMutability::ArcMutex),
784 "ref_cell" => Ok(InteriorMutability::RefCell),
785 "cell" => Ok(InteriorMutability::Cell),
786 _ => Err(AnnotationError::InvalidValue {
787 key: "interior_mutability".to_string(),
788 value: value.to_string(),
789 }),
790 }
791 }
792
793 fn parse_string_strategy(&self, value: &str) -> Result<StringStrategy, AnnotationError> {
794 match value {
795 "conservative" => Ok(StringStrategy::Conservative),
796 "always_owned" => Ok(StringStrategy::AlwaysOwned),
797 "zero_copy" => Ok(StringStrategy::ZeroCopy),
798 _ => Err(AnnotationError::InvalidValue {
799 key: "string_strategy".to_string(),
800 value: value.to_string(),
801 }),
802 }
803 }
804
805 fn parse_hash_strategy(&self, value: &str) -> Result<HashStrategy, AnnotationError> {
806 match value {
807 "standard" => Ok(HashStrategy::Standard),
808 "fnv" => Ok(HashStrategy::Fnv),
809 "ahash" => Ok(HashStrategy::AHash),
810 _ => Err(AnnotationError::InvalidValue {
811 key: "hash_strategy".to_string(),
812 value: value.to_string(),
813 }),
814 }
815 }
816
817 fn parse_panic_behavior(&self, value: &str) -> Result<PanicBehavior, AnnotationError> {
818 match value {
819 "propagate" => Ok(PanicBehavior::Propagate),
820 "return_error" => Ok(PanicBehavior::ReturnError),
821 "abort" => Ok(PanicBehavior::Abort),
822 _ => Err(AnnotationError::InvalidValue {
823 key: "panic_behavior".to_string(),
824 value: value.to_string(),
825 }),
826 }
827 }
828
829 fn parse_error_strategy(&self, value: &str) -> Result<ErrorStrategy, AnnotationError> {
830 match value {
831 "panic" => Ok(ErrorStrategy::Panic),
832 "result_type" => Ok(ErrorStrategy::ResultType),
833 "option_type" => Ok(ErrorStrategy::OptionType),
834 _ => Err(AnnotationError::InvalidValue {
835 key: "error_strategy".to_string(),
836 value: value.to_string(),
837 }),
838 }
839 }
840
841 fn parse_global_strategy(&self, value: &str) -> Result<GlobalStrategy, AnnotationError> {
842 match value {
843 "none" => Ok(GlobalStrategy::None),
844 "lazy_static" => Ok(GlobalStrategy::LazyStatic),
845 "once_cell" => Ok(GlobalStrategy::OnceCell),
846 _ => Err(AnnotationError::InvalidValue {
847 key: "global_strategy".to_string(),
848 value: value.to_string(),
849 }),
850 }
851 }
852
853 fn parse_termination(&self, value: &str) -> Result<Termination, AnnotationError> {
854 match value {
855 "unknown" => Ok(Termination::Unknown),
856 "proven" => Ok(Termination::Proven),
857 _ => {
858 if value.starts_with("bounded_") {
859 if let Some(num_str) = value.strip_prefix("bounded_") {
860 if let Ok(bound) = num_str.parse::<u32>() {
861 return Ok(Termination::BoundedLoop(bound));
862 }
863 }
864 }
865 Err(AnnotationError::InvalidValue {
866 key: "termination".to_string(),
867 value: value.to_string(),
868 })
869 }
870 }
871 }
872
873 fn parse_service_type(&self, value: &str) -> Result<ServiceType, AnnotationError> {
874 match value {
875 "web_api" => Ok(ServiceType::WebApi),
876 "cli" => Ok(ServiceType::Cli),
877 "library" => Ok(ServiceType::Library),
878 _ => Err(AnnotationError::InvalidValue {
879 key: "service_type".to_string(),
880 value: value.to_string(),
881 }),
882 }
883 }
884
885 fn parse_migration_strategy(&self, value: &str) -> Result<MigrationStrategy, AnnotationError> {
886 match value {
887 "incremental" => Ok(MigrationStrategy::Incremental),
888 "big_bang" => Ok(MigrationStrategy::BigBang),
889 "hybrid" => Ok(MigrationStrategy::Hybrid),
890 _ => Err(AnnotationError::InvalidValue {
891 key: "migration_strategy".to_string(),
892 value: value.to_string(),
893 }),
894 }
895 }
896
897 fn parse_compatibility_layer(
898 &self,
899 value: &str,
900 ) -> Result<CompatibilityLayer, AnnotationError> {
901 match value {
902 "pyo3" => Ok(CompatibilityLayer::PyO3),
903 "ctypes" => Ok(CompatibilityLayer::CTypes),
904 "none" => Ok(CompatibilityLayer::None),
905 _ => Err(AnnotationError::InvalidValue {
906 key: "compatibility_layer".to_string(),
907 value: value.to_string(),
908 }),
909 }
910 }
911
912 fn parse_lambda_runtime(&self, value: &str) -> Result<LambdaRuntime, AnnotationError> {
913 match value {
914 "provided.al2" => Ok(LambdaRuntime::ProvidedAl2),
915 "provided.al2023" => Ok(LambdaRuntime::ProvidedAl2023),
916 _ => Ok(LambdaRuntime::Custom(value.to_string())),
917 }
918 }
919
920 fn parse_lambda_event_type(&self, value: &str) -> Result<LambdaEventType, AnnotationError> {
921 match value {
922 "auto" => Ok(LambdaEventType::Auto),
923 "S3Event" => Ok(LambdaEventType::S3Event),
924 "APIGatewayProxyRequest" => Ok(LambdaEventType::ApiGatewayProxyRequest),
925 "APIGatewayV2HttpRequest" => Ok(LambdaEventType::ApiGatewayV2HttpRequest),
926 "SqsEvent" => Ok(LambdaEventType::SqsEvent),
927 "SnsEvent" => Ok(LambdaEventType::SnsEvent),
928 "DynamodbEvent" => Ok(LambdaEventType::DynamodbEvent),
929 "CloudwatchEvent" => Ok(LambdaEventType::CloudwatchEvent),
930 "KinesisEvent" => Ok(LambdaEventType::KinesisEvent),
931 _ => {
932 if value.starts_with("EventBridgeEvent<") && value.ends_with('>') {
933 let inner = &value[17..value.len() - 1];
934 Ok(LambdaEventType::EventBridgeEvent(Some(inner.to_string())))
935 } else if value == "EventBridgeEvent" {
936 Ok(LambdaEventType::EventBridgeEvent(None))
937 } else {
938 Ok(LambdaEventType::Custom(value.to_string()))
939 }
940 }
941 }
942 }
943
944 fn parse_architecture(&self, value: &str) -> Result<Architecture, AnnotationError> {
945 match value {
946 "x86_64" | "x64" => Ok(Architecture::X86_64),
947 "arm64" | "aarch64" => Ok(Architecture::Arm64),
948 _ => Err(AnnotationError::InvalidValue {
949 key: "architecture".to_string(),
950 value: value.to_string(),
951 }),
952 }
953 }
954}
955
956#[cfg(test)]
957mod tests {
958 use super::*;
959
960 #[test]
961 fn test_parse_basic_annotations() {
962 let parser = AnnotationParser::new();
963 let source = r#"
964# @depyler: type_strategy = "conservative"
965# @depyler: ownership = "borrowed"
966def test_function():
967 pass
968 "#;
969
970 let annotations = parser.parse_annotations(source).unwrap();
971 assert_eq!(annotations.type_strategy, TypeStrategy::Conservative);
972 assert_eq!(annotations.ownership_model, OwnershipModel::Borrowed);
973 }
974
975 #[test]
976 fn test_parse_performance_annotations() {
977 let parser = AnnotationParser::new();
978 let source = r#"
979# @depyler: performance_critical = "true"
980# @depyler: vectorize = "true"
981# @depyler: unroll_loops = "4"
982def fast_function():
983 pass
984 "#;
985
986 let annotations = parser.parse_annotations(source).unwrap();
987 assert!(annotations
988 .performance_hints
989 .contains(&PerformanceHint::PerformanceCritical));
990 assert!(annotations
991 .performance_hints
992 .contains(&PerformanceHint::Vectorize));
993 assert!(annotations
994 .performance_hints
995 .contains(&PerformanceHint::UnrollLoops(4)));
996 }
997
998 #[test]
999 fn test_parse_safety_annotations() {
1000 let parser = AnnotationParser::new();
1001 let source = r#"
1002# @depyler: safety_level = "unsafe_allowed"
1003# @depyler: bounds_checking = "disabled"
1004def unsafe_function():
1005 pass
1006 "#;
1007
1008 let annotations = parser.parse_annotations(source).unwrap();
1009 assert_eq!(annotations.safety_level, SafetyLevel::UnsafeAllowed);
1010 assert_eq!(annotations.bounds_checking, BoundsChecking::Disabled);
1011 }
1012
1013 #[test]
1014 fn test_parse_fallback_strategy() {
1015 let parser = AnnotationParser::new();
1016 let source = r#"
1017# @depyler: fallback = "mcp"
1018def complex_function():
1019 pass
1020 "#;
1021
1022 let annotations = parser.parse_annotations(source).unwrap();
1023 assert_eq!(annotations.fallback_strategy, FallbackStrategy::Mcp);
1024 }
1025
1026 #[test]
1027 fn test_parse_thread_safety() {
1028 let parser = AnnotationParser::new();
1029 let source = r#"
1030# @depyler: thread_safety = "required"
1031# @depyler: interior_mutability = "arc_mutex"
1032def thread_safe_function():
1033 pass
1034 "#;
1035
1036 let annotations = parser.parse_annotations(source).unwrap();
1037 assert_eq!(annotations.thread_safety, ThreadSafety::Required);
1038 assert_eq!(
1039 annotations.interior_mutability,
1040 InteriorMutability::ArcMutex
1041 );
1042 }
1043
1044 #[test]
1045 fn test_invalid_annotation_key() {
1046 let parser = AnnotationParser::new();
1047 let source = r#"
1048# @depyler: invalid_key = "value"
1049def test_function():
1050 pass
1051 "#;
1052
1053 let result = parser.parse_annotations(source);
1054 assert!(matches!(result, Err(AnnotationError::UnknownKey(_))));
1055 }
1056
1057 #[test]
1058 fn test_invalid_annotation_value() {
1059 let parser = AnnotationParser::new();
1060 let source = r#"
1061# @depyler: type_strategy = "invalid_value"
1062def test_function():
1063 pass
1064 "#;
1065
1066 let result = parser.parse_annotations(source);
1067 assert!(matches!(result, Err(AnnotationError::InvalidValue { .. })));
1068 }
1069
1070 #[test]
1071 fn test_default_annotations() {
1072 let annotations = TranspilationAnnotations::default();
1073 assert_eq!(annotations.type_strategy, TypeStrategy::Conservative);
1074 assert_eq!(annotations.ownership_model, OwnershipModel::Owned);
1075 assert_eq!(annotations.safety_level, SafetyLevel::Safe);
1076 assert_eq!(annotations.fallback_strategy, FallbackStrategy::Error);
1077 }
1078
1079 #[test]
1080 fn test_optimization_hints() {
1081 let parser = AnnotationParser::new();
1082 let source = r#"
1083# @depyler: optimization_hint = "vectorize"
1084# @depyler: optimization_level = "aggressive"
1085def optimized_function():
1086 pass
1087 "#;
1088
1089 let annotations = parser.parse_annotations(source).unwrap();
1090 assert!(annotations
1091 .performance_hints
1092 .contains(&PerformanceHint::Vectorize));
1093 assert_eq!(
1094 annotations.optimization_level,
1095 OptimizationLevel::Aggressive
1096 );
1097 }
1098
1099 #[test]
1100 fn test_string_and_hash_strategies() {
1101 let parser = AnnotationParser::new();
1102 let source = r#"
1103# @depyler: string_strategy = "zero_copy"
1104# @depyler: hash_strategy = "fnv"
1105def string_function():
1106 pass
1107 "#;
1108
1109 let annotations = parser.parse_annotations(source).unwrap();
1110 assert_eq!(annotations.string_strategy, StringStrategy::ZeroCopy);
1111 assert_eq!(annotations.hash_strategy, HashStrategy::Fnv);
1112 }
1113
1114 #[test]
1115 fn test_error_handling_annotations() {
1116 let parser = AnnotationParser::new();
1117 let source = r#"
1118# @depyler: panic_behavior = "return_error"
1119# @depyler: error_strategy = "result_type"
1120def error_function():
1121 pass
1122 "#;
1123
1124 let annotations = parser.parse_annotations(source).unwrap();
1125 assert_eq!(annotations.panic_behavior, PanicBehavior::ReturnError);
1126 assert_eq!(annotations.error_strategy, ErrorStrategy::ResultType);
1127 }
1128
1129 #[test]
1130 fn test_service_and_migration_annotations() {
1131 let parser = AnnotationParser::new();
1132 let source = r#"
1133# @depyler: service_type = "web_api"
1134# @depyler: migration_strategy = "incremental"
1135# @depyler: compatibility_layer = "pyo3"
1136def service_function():
1137 pass
1138 "#;
1139
1140 let annotations = parser.parse_annotations(source).unwrap();
1141 assert_eq!(annotations.service_type, Some(ServiceType::WebApi));
1142 assert_eq!(
1143 annotations.migration_strategy,
1144 Some(MigrationStrategy::Incremental)
1145 );
1146 assert_eq!(
1147 annotations.compatibility_layer,
1148 Some(CompatibilityLayer::PyO3)
1149 );
1150 }
1151
1152 #[test]
1153 fn test_verification_annotations() {
1154 let parser = AnnotationParser::new();
1155 let source = r#"
1156# @depyler: termination = "proven"
1157# @depyler: invariant = "left <= right"
1158# @depyler: verify_bounds = "true"
1159def verified_function():
1160 pass
1161 "#;
1162
1163 let annotations = parser.parse_annotations(source).unwrap();
1164 assert_eq!(annotations.termination, Termination::Proven);
1165 assert!(annotations
1166 .invariants
1167 .contains(&"left <= right".to_string()));
1168 assert!(annotations.verify_bounds);
1169 }
1170
1171 #[test]
1172 fn test_global_strategy() {
1173 let parser = AnnotationParser::new();
1174 let source = r#"
1175# @depyler: global_strategy = "lazy_static"
1176def global_function():
1177 pass
1178 "#;
1179
1180 let annotations = parser.parse_annotations(source).unwrap();
1181 assert_eq!(annotations.global_strategy, GlobalStrategy::LazyStatic);
1182 }
1183
1184 #[test]
1185 fn test_lambda_annotations_basic() {
1186 let parser = AnnotationParser::new();
1187 let source = r#"
1188# @depyler: lambda_runtime = "provided.al2"
1189# @depyler: event_type = "APIGatewayProxyRequest"
1190# @depyler: cold_start_optimize = "true"
1191def handler(event, context):
1192 pass
1193 "#;
1194
1195 let annotations = parser.parse_annotations(source).unwrap();
1196 assert!(annotations.lambda_annotations.is_some());
1197
1198 let lambda_annotations = annotations.lambda_annotations.unwrap();
1199 assert_eq!(lambda_annotations.runtime, LambdaRuntime::ProvidedAl2);
1200 assert_eq!(
1201 lambda_annotations.event_type,
1202 Some(LambdaEventType::ApiGatewayProxyRequest)
1203 );
1204 assert!(lambda_annotations.cold_start_optimize);
1205 }
1206
1207 #[test]
1208 fn test_lambda_annotations_memory_and_architecture() {
1209 let parser = AnnotationParser::new();
1210 let source = r#"
1211# @depyler: memory_size = "256"
1212# @depyler: architecture = "arm64"
1213# @depyler: timeout = "30"
1214def handler(event, context):
1215 pass
1216 "#;
1217
1218 let annotations = parser.parse_annotations(source).unwrap();
1219 let lambda_annotations = annotations.lambda_annotations.unwrap();
1220 assert_eq!(lambda_annotations.memory_size, 256);
1221 assert_eq!(lambda_annotations.architecture, Architecture::Arm64);
1222 assert_eq!(lambda_annotations.timeout, Some(30));
1223 }
1224
1225 #[test]
1226 fn test_lambda_eventbridge_with_custom_type() {
1227 let parser = AnnotationParser::new();
1228 let source = r#"
1229# @depyler: event_type = "EventBridgeEvent<OrderEvent>"
1230# @depyler: custom_serialization = "true"
1231def handler(event, context):
1232 pass
1233 "#;
1234
1235 let annotations = parser.parse_annotations(source).unwrap();
1236 let lambda_annotations = annotations.lambda_annotations.unwrap();
1237 assert_eq!(
1238 lambda_annotations.event_type,
1239 Some(LambdaEventType::EventBridgeEvent(Some(
1240 "OrderEvent".to_string()
1241 )))
1242 );
1243 assert!(lambda_annotations.custom_serialization);
1244 }
1245
1246 #[test]
1247 fn test_lambda_sqs_batch_processing() {
1248 let parser = AnnotationParser::new();
1249 let source = r#"
1250# @depyler: event_type = "SqsEvent"
1251# @depyler: batch_failure_reporting = "true"
1252# @depyler: tracing = "Active"
1253def handler(event, context):
1254 pass
1255 "#;
1256
1257 let annotations = parser.parse_annotations(source).unwrap();
1258 let lambda_annotations = annotations.lambda_annotations.unwrap();
1259 assert_eq!(
1260 lambda_annotations.event_type,
1261 Some(LambdaEventType::SqsEvent)
1262 );
1263 assert!(lambda_annotations.batch_failure_reporting);
1264 assert!(lambda_annotations.tracing_enabled);
1265 }
1266
1267 #[test]
1268 fn test_lambda_auto_event_type() {
1269 let parser = AnnotationParser::new();
1270 let source = r#"
1271# @depyler: event_type = "auto"
1272# @depyler: cold_start_optimize = "true"
1273def handler(event, context):
1274 pass
1275 "#;
1276
1277 let annotations = parser.parse_annotations(source).unwrap();
1278 let lambda_annotations = annotations.lambda_annotations.unwrap();
1279 assert_eq!(lambda_annotations.event_type, Some(LambdaEventType::Auto));
1280 assert!(lambda_annotations.cold_start_optimize);
1281 }
1282
1283 #[test]
1284 fn test_lambda_custom_runtime() {
1285 let parser = AnnotationParser::new();
1286 let source = r#"
1287# @depyler: lambda_runtime = "rust-runtime-1.0"
1288def handler(event, context):
1289 pass
1290 "#;
1291
1292 let annotations = parser.parse_annotations(source).unwrap();
1293 let lambda_annotations = annotations.lambda_annotations.unwrap();
1294 assert_eq!(
1295 lambda_annotations.runtime,
1296 LambdaRuntime::Custom("rust-runtime-1.0".to_string())
1297 );
1298 }
1299}