1use std::collections::HashMap;
12
13pub struct HttpMapper;
15
16impl HttpMapper {
17 pub fn translate_requests_get(url: &str, params: Option<HashMap<String, String>>) -> String {
31 let url_with_params = if let Some(params) = params {
32 format!("{{
33 let mut query = QueryParams::new();
34 {}
35 query.append_to_url(\"{}\")
36}}",
37 params.iter()
38 .map(|(k, v)| format!(" query.add(\"{}\", \"{}\");", k, v))
39 .collect::<Vec<_>>()
40 .join("\n"),
41 url
42 )
43 } else {
44 format!("\"{}\"", url)
45 };
46
47 format!("let response = WasiFetch::get({}).await?;", url_with_params)
48 }
49
50 pub fn translate_requests_post_json(url: &str, json_var: &str) -> String {
63 format!("let response = WasiFetch::post_json(\"{}\", &{}).await?;", url, json_var)
64 }
65
66 pub fn translate_requests_post_form(url: &str, data: HashMap<String, String>) -> String {
81 let mut code = String::new();
82 code.push_str("let mut form = HashMap::new();\n");
83
84 for (key, value) in data {
85 code.push_str(&format!("form.insert(\"{}\".to_string(), \"{}\".to_string());\n", key, value));
86 }
87
88 code.push_str(&format!("let response = WasiFetch::post_form(\"{}\", form).await?;", url));
89 code
90 }
91
92 pub fn translate_requests_with_headers(
107 method: &str,
108 url: &str,
109 headers: HashMap<String, String>
110 ) -> String {
111 let rust_method = method.to_uppercase();
112 let method_enum = match rust_method.as_str() {
113 "GET" => "Method::Get",
114 "POST" => "Method::Post",
115 "PUT" => "Method::Put",
116 "DELETE" => "Method::Delete",
117 "PATCH" => "Method::Patch",
118 _ => "Method::Get",
119 };
120
121 let mut code = format!("let mut request = Request::new({}, \"{}\");\n", method_enum, url);
122
123 for (key, value) in headers {
124 code.push_str(&format!("request.header(\"{}\", \"{}\");\n", key, value));
125 }
126
127 code.push_str("let response = WasiFetch::fetch(request).await?;");
128 code
129 }
130
131 pub fn translate_urllib_urlopen(url: &str) -> String {
146 format!(
147 "let response = WasiFetch::get(\"{}\").await?;\nlet data = response.bytes();",
148 url
149 )
150 }
151
152 pub fn translate_httpx_async_get(url: &str) -> String {
167 format!("let response = WasiFetch::get(\"{}\").await?;", url)
168 }
169
170 pub fn generate_async_http_function(
189 func_name: &str,
190 params: Vec<(&str, &str)>,
191 return_type: &str,
192 http_call: &str,
193 ) -> String {
194 let param_str = params.iter()
195 .map(|(name, typ)| format!("{}: {}", name, typ))
196 .collect::<Vec<_>>()
197 .join(", ");
198
199 format!(
200 "async fn {}({}) -> Result<{}> {{\n {}\n Ok(data)\n}}",
201 func_name, param_str, return_type, http_call
202 )
203 }
204
205 pub fn translate_response_method(method: &str) -> Option<&'static str> {
207 match method {
208 "json()" => Some("response.json()?"),
210 ".json()" => Some("response.json()?"),
211 "text" => Some("response.text()?"),
212 ".text" => Some("response.text()?"),
213 "content" => Some("response.bytes()"),
214 ".content" => Some("response.bytes()"),
215 "status_code" => Some("response.status()"),
216 ".status_code" => Some("response.status()"),
217 "headers" => Some("response.headers()"),
218 ".headers" => Some("response.headers()"),
219
220 "read()" => Some("response.bytes()"),
222 ".read()" => Some("response.bytes()"),
223 "getcode()" => Some("response.status()"),
224 ".getcode()" => Some("response.status()"),
225
226 _ => None,
227 }
228 }
229
230 pub fn get_http_imports() -> Vec<&'static str> {
232 vec![
233 "use crate::wasi_fetch::{WasiFetch, Request, Response, Method, QueryParams};",
234 "use std::collections::HashMap;",
235 "use anyhow::Result;",
236 ]
237 }
238
239 pub fn get_http_dependencies() -> Vec<(&'static str, &'static str)> {
241 vec![
242 ("reqwest", "0.11"),
243 ("tokio", "1.35"),
244 ("serde_json", "1.0"),
245 ]
246 }
247}
248
249pub struct HttpPatternDetector;
251
252impl HttpPatternDetector {
253 pub fn uses_requests(python_code: &str) -> bool {
255 python_code.contains("import requests")
256 || python_code.contains("from requests import")
257 || python_code.contains("requests.get")
258 || python_code.contains("requests.post")
259 }
260
261 pub fn uses_urllib(python_code: &str) -> bool {
263 python_code.contains("import urllib")
264 || python_code.contains("from urllib")
265 || python_code.contains("urlopen")
266 }
267
268 pub fn uses_httpx(python_code: &str) -> bool {
270 python_code.contains("import httpx")
271 || python_code.contains("from httpx import")
272 || python_code.contains("httpx.get")
273 || python_code.contains("httpx.AsyncClient")
274 }
275
276 pub fn uses_aiohttp(python_code: &str) -> bool {
278 python_code.contains("import aiohttp")
279 || python_code.contains("from aiohttp import")
280 || python_code.contains("aiohttp.ClientSession")
281 }
282
283 pub fn uses_async_http(python_code: &str) -> bool {
285 (Self::uses_httpx(python_code) && python_code.contains("async"))
286 || Self::uses_aiohttp(python_code)
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn test_translate_requests_get() {
296 let result = HttpMapper::translate_requests_get("https://api.example.com/data", None);
297 assert!(result.contains("WasiFetch::get"));
298 assert!(result.contains("https://api.example.com/data"));
299 }
300
301 #[test]
302 fn test_translate_requests_get_with_params() {
303 let mut params = HashMap::new();
304 params.insert("page".to_string(), "1".to_string());
305 params.insert("limit".to_string(), "10".to_string());
306
307 let result = HttpMapper::translate_requests_get("https://api.example.com/data", Some(params));
308 assert!(result.contains("QueryParams"));
309 assert!(result.contains("query.add"));
310 }
311
312 #[test]
313 fn test_translate_requests_post_json() {
314 let result = HttpMapper::translate_requests_post_json("https://api.example.com/users", "user_data");
315 assert!(result.contains("WasiFetch::post_json"));
316 assert!(result.contains("user_data"));
317 }
318
319 #[test]
320 fn test_translate_requests_with_headers() {
321 let mut headers = HashMap::new();
322 headers.insert("Authorization".to_string(), "Bearer token123".to_string());
323
324 let result = HttpMapper::translate_requests_with_headers("GET", "https://api.example.com", headers);
325 assert!(result.contains("Request::new"));
326 assert!(result.contains("Method::Get"));
327 assert!(result.contains("Authorization"));
328 }
329
330 #[test]
331 fn test_response_method_translation() {
332 assert_eq!(HttpMapper::translate_response_method("json()"), Some("response.json()?"));
333 assert_eq!(HttpMapper::translate_response_method("text"), Some("response.text()?"));
334 assert_eq!(HttpMapper::translate_response_method("status_code"), Some("response.status()"));
335 }
336
337 #[test]
338 fn test_pattern_detection() {
339 assert!(HttpPatternDetector::uses_requests("import requests\nrequests.get('url')"));
340 assert!(HttpPatternDetector::uses_urllib("from urllib.request import urlopen"));
341 assert!(HttpPatternDetector::uses_httpx("import httpx\nawait httpx.get('url')"));
342 assert!(HttpPatternDetector::uses_async_http("import httpx\nasync def foo():\n await httpx.get('url')"));
343 }
344
345 #[test]
346 fn test_get_imports() {
347 let imports = HttpMapper::get_http_imports();
348 assert!(!imports.is_empty());
349 assert!(imports[0].contains("wasi_fetch"));
350 }
351
352 #[test]
353 fn test_generate_async_function() {
354 let result = HttpMapper::generate_async_http_function(
355 "fetch_user",
356 vec![("user_id", "i32")],
357 "serde_json::Value",
358 "let response = WasiFetch::get(&url).await?;\n let data = response.json()?;"
359 );
360
361 assert!(result.contains("async fn fetch_user"));
362 assert!(result.contains("user_id: i32"));
363 assert!(result.contains("Result<serde_json::Value>"));
364 }
365}