From cdda439f0f4ab5a81d5dcaea75474f1ea86d28ca Mon Sep 17 00:00:00 2001 From: Tanish2002 Date: Sun, 25 Feb 2024 04:51:56 +0530 Subject: [PATCH] feat: add codegen for rust(ureq) --- lib/codegen/codegen.dart | 3 + lib/codegen/rust/ureq.dart | 214 +++++++++++++++++++++++++++++++++++++ lib/consts.dart | 3 +- 3 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 lib/codegen/rust/ureq.dart diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index 1aec7144..2fb2f7ba 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -1,3 +1,4 @@ +import 'package:apidash/codegen/rust/ureq.dart'; import 'package:apidash/models/models.dart' show RequestModel; import 'package:apidash/consts.dart'; import 'dart/http.dart'; @@ -42,6 +43,8 @@ class Codegen { .getCode(requestModel, defaultUriScheme); case CodegenLanguage.pythonRequests: return PythonRequestsCodeGen().getCode(requestModel, defaultUriScheme); + case CodegenLanguage.rustUreq: + return RustUreqCodeGen().getCode(requestModel, defaultUriScheme); } } } diff --git a/lib/codegen/rust/ureq.dart b/lib/codegen/rust/ureq.dart new file mode 100644 index 00000000..3be8971c --- /dev/null +++ b/lib/codegen/rust/ureq.dart @@ -0,0 +1,214 @@ +import 'dart:io'; +import 'dart:convert'; +import 'package:jinja/jinja.dart' as jj; +import 'package:apidash/consts.dart'; +import 'package:apidash/utils/utils.dart' + show getNewUuid, getValidRequestUri, stripUriParams; +import 'package:apidash/models/models.dart' show RequestModel; + +class RustUreqCodeGen { + final String kTemplateStart = """ +{%- if isFormDataRequest -%} +use std::io::Read; +{% endif -%} +fn main() -> Result<(), ureq::Error> { + let url = "{{url}}"; +"""; + + // String kTemplateParams = """\n .query_pairs({{ params }})"""; + String kTemplateParams = + """\n {% for key, val in params -%}.query("{{key}}", "{{val}}"){% if not loop.last %}{{ '\n ' }}{% endif %}{%- endfor -%}"""; + + String kTemplateBody = """ + + let payload = r#"{{body}}"#; + +"""; + + String kTemplateJson = """ + + let payload = ureq::json!({{body}}); + +"""; + + String kTemplateHeaders = + """\n {% for key, val in headers -%}.set("{{key}}", "{{val}}"){% if not loop.last %}{{ '\n ' }}{% endif %}{%- endfor -%}"""; + + String kTemplateFormHeaderContentType = ''' +multipart/form-data; boundary={{boundary}}'''; + + String kTemplateRequest = """ + + let response = ureq::{{method}}(url) +"""; + + final String kStringFormDataBody = r""" + + struct FormDataItem { + name: String, + value: String, + field_type: String, + } + + let form_data_items: Vec = vec![ + {%- for formitem in fields_list %} + FormDataItem { + {%- for key, val in formitem %} + {% if key == "type" %}field_type: "{{ val }}".to_string(),{% else %}{{ key }}: "{{ val }}".to_string(),{% endif %} + {%- endfor %} + }, + {%- endfor %} + ]; + + fn build_data_list(fields: Vec) -> Vec { + let mut data_list = Vec::new(); + + for field in fields { + data_list.extend_from_slice(b"--{{boundary}}\r\n"); + + if field.field_type == "text" { + data_list.extend_from_slice(format!("Content-Disposition: form-data; name=\"{}\"\r\n", field.name).as_bytes()); + data_list.extend_from_slice(b"Content-Type: text/plain\r\n\r\n"); + data_list.extend_from_slice(field.value.as_bytes()); + data_list.extend_from_slice(b"\r\n"); + } else if field.field_type == "file" { + data_list.extend_from_slice(format!("Content-Disposition: form-data; name=\"{}\"; filename=\"{}\"\r\n", field.name, field.value).as_bytes()); + + let mime_type = mime_guess::from_path(&field.value).first_or(mime_guess::mime::APPLICATION_OCTET_STREAM); + data_list.extend_from_slice(format!("Content-Type: {}\r\n\r\n", mime_type).as_bytes()); + + let mut file = std::fs::File::open(&field.value).unwrap(); + let mut file_contents = Vec::new(); + file.read_to_end(&mut file_contents).unwrap(); + data_list.extend_from_slice(&file_contents); + data_list.extend_from_slice(b"\r\n"); + } + } + + data_list.extend_from_slice(b"--{{boundary}}--\r\n"); + data_list + } + + let payload = build_data_list(form_data_items); +"""; + + String kStringRequestBody = """\n .send_string(payload)?;"""; + + String kStringRequestForm = """\n .send_bytes(&payload)?;"""; + + String kStringRequestJson = """\n .send_json(payload)?;"""; + + String kStringRequestNormal = """\n .call()?;"""; + + final String kStringRequestEnd = """\n + println!("Response Status: {}", response.status()); + println!("Response: {}", response.into_string()?); + + Ok(()) +} +"""; + + String? getCode( + RequestModel requestModel, + String defaultUriScheme, + ) { + try { + String result = ""; + bool hasBody = false; + bool hasJsonBody = false; + String uuid = getNewUuid(); + + String url = requestModel.url; + if (!url.contains("://") && url.isNotEmpty) { + url = "$defaultUriScheme://$url"; + } + + var rec = getValidRequestUri( + url, + requestModel.enabledRequestParams, + ); + Uri? uri = rec.$1; + if (uri != null) { + var templateStartUrl = jj.Template(kTemplateStart); + result += templateStartUrl.render({ + "url": stripUriParams(uri), + 'isFormDataRequest': requestModel.isFormDataRequest, + "method": requestModel.method.name.toLowerCase() + }); + + var method = requestModel.method; + var requestBody = requestModel.requestBody; + if (kMethodsWithBody.contains(method) && requestBody != null) { + var contentLength = utf8.encode(requestBody).length; + if (contentLength > 0) { + if (requestModel.requestBodyContentType == ContentType.json) { + hasJsonBody = true; + var templateBody = jj.Template(kTemplateJson); + result += templateBody.render({"body": requestBody}); + } else if (!requestModel.isFormDataRequest) { + hasBody = true; + var templateBody = jj.Template(kTemplateBody); + result += templateBody.render({"body": requestBody}); + } + } + } + + if (requestModel.isFormDataRequest) { + var formDataBodyData = jj.Template(kStringFormDataBody); + result += formDataBodyData.render( + { + "fields_list": requestModel.formDataMapList, + "boundary": uuid, + }, + ); + } + var templateRequest = jj.Template(kTemplateRequest); + result += templateRequest.render({ + "method": method.name.toLowerCase(), + }); + + if (uri.hasQuery) { + var params = uri.queryParameters; + if (params.isNotEmpty) { + var templateParms = jj.Template(kTemplateParams); + result += templateParms.render({"params": params}); + } + } + + var headersList = requestModel.enabledRequestHeaders; + if (headersList != null || hasBody || requestModel.isFormDataRequest) { + var headers = requestModel.enabledHeadersMap; + if (requestModel.isFormDataRequest) { + var formHeaderTemplate = + jj.Template(kTemplateFormHeaderContentType); + headers[HttpHeaders.contentTypeHeader] = formHeaderTemplate.render({ + "boundary": uuid, + }); + } else if (hasBody) { + headers[HttpHeaders.contentTypeHeader] = + requestModel.requestBodyContentType.header; + } + + if (headers.isNotEmpty) { + var templateHeaders = jj.Template(kTemplateHeaders); + result += templateHeaders.render({"headers": headers}); + } + } + if (requestModel.isFormDataRequest) { + result += kStringRequestForm; + } else if (hasBody) { + result += kStringRequestBody; + } else if (hasJsonBody) { + result += kStringRequestJson; + } else { + result += kStringRequestNormal; + } + + result += kStringRequestEnd; + } + return result; + } catch (e) { + return null; + } + } +} diff --git a/lib/consts.dart b/lib/consts.dart index f8f9f97c..58e482ab 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -268,7 +268,8 @@ enum CodegenLanguage { nodejsFetch("node.js (fetch)", "javascript", "js"), kotlinOkHttp("Kotlin (okhttp3)", "java", "kt"), pythonHttpClient("Python (http.client)", "python", "py"), - pythonRequests("Python (requests)", "python", "py"); + pythonRequests("Python (requests)", "python", "py"), + rustUreq("Rust (ureq)", "rust", "rs"); const CodegenLanguage(this.label, this.codeHighlightLang, this.ext); final String label;