tldr_core/patterns/
async_patterns.rs1use super::signals::PatternSignals;
10use crate::types::AsyncPattern;
11
12pub fn signals_to_pattern(signals: &PatternSignals, evidence_limit: usize) -> Option<AsyncPattern> {
14 let async_patterns = &signals.async_patterns;
15
16 if !async_patterns.has_signals() {
17 return None;
18 }
19
20 let concurrency_confidence = async_patterns.calculate_confidence();
21
22 let mut patterns = Vec::new();
24
25 if !async_patterns.async_await.is_empty() {
26 patterns.push("async_await".to_string());
27 }
28
29 if !async_patterns.goroutines.is_empty() {
30 patterns.push("goroutines".to_string());
31 }
32
33 if !async_patterns.tokio_usage.is_empty() {
34 patterns.push("tokio".to_string());
35 }
36
37 if !async_patterns.thread_spawns.is_empty() {
38 patterns.push("thread_spawn".to_string());
39 }
40
41 let mut sync_primitives: Vec<String> = async_patterns
43 .sync_primitives
44 .iter()
45 .map(|(name, _)| name.clone())
46 .collect();
47 sync_primitives.sort();
48 sync_primitives.dedup();
49
50 let mut evidence = Vec::new();
52 evidence.extend(
53 async_patterns
54 .async_await
55 .iter()
56 .take(evidence_limit)
57 .cloned(),
58 );
59 evidence.extend(
60 async_patterns
61 .goroutines
62 .iter()
63 .take(evidence_limit)
64 .cloned(),
65 );
66 evidence.extend(
67 async_patterns
68 .tokio_usage
69 .iter()
70 .take(evidence_limit)
71 .cloned(),
72 );
73 evidence.extend(
74 async_patterns
75 .sync_primitives
76 .iter()
77 .take(evidence_limit)
78 .map(|(_, e)| e.clone()),
79 );
80 evidence.truncate(evidence_limit);
81
82 Some(AsyncPattern {
83 concurrency_confidence,
84 patterns,
85 sync_primitives,
86 evidence,
87 })
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::types::Evidence;
94
95 #[test]
96 fn test_no_signals_returns_none() {
97 let signals = PatternSignals::default();
98 assert!(signals_to_pattern(&signals, 3).is_none());
99 }
100
101 #[test]
102 fn test_async_await_detected() {
103 let mut signals = PatternSignals::default();
104 signals.async_patterns.async_await.push(Evidence::new(
105 "service.py",
106 10,
107 "async def fetch_data():",
108 ));
109
110 let pattern = signals_to_pattern(&signals, 3).unwrap();
111 assert!(pattern.patterns.contains(&"async_await".to_string()));
112 }
113
114 #[test]
115 fn test_goroutines_detected() {
116 let mut signals = PatternSignals::default();
117 signals.async_patterns.goroutines.push(Evidence::new(
118 "main.go",
119 15,
120 "go handleRequest(req)",
121 ));
122
123 let pattern = signals_to_pattern(&signals, 3).unwrap();
124 assert!(pattern.patterns.contains(&"goroutines".to_string()));
125 }
126
127 #[test]
128 fn test_tokio_detected() {
129 let mut signals = PatternSignals::default();
130 signals.async_patterns.tokio_usage.push(Evidence::new(
131 "main.rs",
132 5,
133 "use tokio::runtime::Runtime;",
134 ));
135
136 let pattern = signals_to_pattern(&signals, 3).unwrap();
137 assert!(pattern.patterns.contains(&"tokio".to_string()));
138 }
139
140 #[test]
141 fn test_sync_primitives_detected() {
142 let mut signals = PatternSignals::default();
143 signals.async_patterns.sync_primitives.push((
144 "mutex".to_string(),
145 Evidence::new("lib.rs", 10, "let lock = Mutex::new(0);"),
146 ));
147 signals.async_patterns.sync_primitives.push((
148 "channel".to_string(),
149 Evidence::new("lib.rs", 15, "let (tx, rx) = mpsc::channel();"),
150 ));
151
152 let pattern = signals_to_pattern(&signals, 3).unwrap();
153 assert!(pattern.sync_primitives.contains(&"mutex".to_string()));
154 assert!(pattern.sync_primitives.contains(&"channel".to_string()));
155 }
156}