From 4160ffa2d70c39f282fa8ed9ef532f97ff6d9aaa Mon Sep 17 00:00:00 2001 From: Darren Yong Date: Thu, 4 Jun 2026 16:01:29 -0700 Subject: [PATCH 1/5] feat: add rust support --- src/helpers/code-builder.ts | 9 + src/targets/index.ts | 3 + src/targets/rust/helpers.ts | 84 ++++++ src/targets/rust/reqwest/client.ts | 240 ++++++++++++++++++ .../fixtures/application-form-encoded.rs | 29 +++ .../rust/reqwest/fixtures/application-json.rs | 33 +++ src/targets/rust/reqwest/fixtures/cookies.rs | 22 ++ .../rust/reqwest/fixtures/custom-method.rs | 19 ++ src/targets/rust/reqwest/fixtures/full.rs | 35 +++ src/targets/rust/reqwest/fixtures/headers.rs | 24 ++ src/targets/rust/reqwest/fixtures/https.rs | 18 ++ .../reqwest/fixtures/jsonObj-multiline.rs | 26 ++ .../reqwest/fixtures/jsonObj-null-value.rs | 26 ++ .../rust/reqwest/fixtures/multipart-data.rs | 34 +++ .../rust/reqwest/fixtures/multipart-file.rs | 33 +++ .../fixtures/multipart-form-data-no-params.rs | 24 ++ .../reqwest/fixtures/multipart-form-data.rs | 24 ++ src/targets/rust/reqwest/fixtures/nested.rs | 25 ++ src/targets/rust/reqwest/fixtures/query.rs | 25 ++ src/targets/rust/reqwest/fixtures/short.rs | 18 ++ .../rust/reqwest/fixtures/text-plain.rs | 26 ++ src/targets/rust/target.ts | 13 + 22 files changed, 790 insertions(+) create mode 100644 src/targets/rust/helpers.ts create mode 100644 src/targets/rust/reqwest/client.ts create mode 100644 src/targets/rust/reqwest/fixtures/application-form-encoded.rs create mode 100644 src/targets/rust/reqwest/fixtures/application-json.rs create mode 100644 src/targets/rust/reqwest/fixtures/cookies.rs create mode 100644 src/targets/rust/reqwest/fixtures/custom-method.rs create mode 100644 src/targets/rust/reqwest/fixtures/full.rs create mode 100644 src/targets/rust/reqwest/fixtures/headers.rs create mode 100644 src/targets/rust/reqwest/fixtures/https.rs create mode 100644 src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs create mode 100644 src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs create mode 100644 src/targets/rust/reqwest/fixtures/multipart-data.rs create mode 100644 src/targets/rust/reqwest/fixtures/multipart-file.rs create mode 100644 src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs create mode 100644 src/targets/rust/reqwest/fixtures/multipart-form-data.rs create mode 100644 src/targets/rust/reqwest/fixtures/nested.rs create mode 100644 src/targets/rust/reqwest/fixtures/query.rs create mode 100644 src/targets/rust/reqwest/fixtures/short.rs create mode 100644 src/targets/rust/reqwest/fixtures/text-plain.rs create mode 100644 src/targets/rust/target.ts diff --git a/src/helpers/code-builder.ts b/src/helpers/code-builder.ts index 80fbf4245..ccdcc3427 100644 --- a/src/helpers/code-builder.ts +++ b/src/helpers/code-builder.ts @@ -59,6 +59,15 @@ export class CodeBuilder { this.code.push(newLine); }; + pushToLast = (line: string): void => { + if (!this.code.length) { + this.push(line); + } + const updatedLine = `${this.code[this.code.length - 1]}${line}`; + this.code[this.code.length - 1] = updatedLine; + }; + + /** * Add an empty line at the end of current lines */ diff --git a/src/targets/index.ts b/src/targets/index.ts index 5a60b7833..90605c9ac 100644 --- a/src/targets/index.ts +++ b/src/targets/index.ts @@ -19,6 +19,7 @@ import { powershell } from './powershell/target.js'; import { python } from './python/target.js'; import { r } from './r/target.js'; import { ruby } from './ruby/target.js'; +import { rust } from './rust/target.js'; import { shell } from './shell/target.js'; import { swift } from './swift/target.js'; @@ -118,6 +119,7 @@ type supportedTargets = | 'python' | 'r' | 'ruby' + | 'rust' | 'shell' | 'swift'; @@ -139,6 +141,7 @@ export const targets: Record = { python, r, ruby, + rust, shell, swift, }; diff --git a/src/targets/rust/helpers.ts b/src/targets/rust/helpers.ts new file mode 100644 index 000000000..39844ed88 --- /dev/null +++ b/src/targets/rust/helpers.ts @@ -0,0 +1,84 @@ +function concatValues( + concatType: 'array' | 'object', + values: any, + pretty: boolean, + indentation: string, + indentLevel: number, +): string { + const currentIndent = indentation.repeat(indentLevel); + const closingBraceIndent = indentation.repeat(indentLevel - 1); + const join = pretty ? `,\n${currentIndent}` : ', '; + const openingBrace = concatType === 'object' ? 'json!({' : '('; + const closingBrace = concatType === 'object' ? '})' : ')'; + + if (pretty) { + return `${openingBrace}\n${currentIndent}${values.join( + join, + )}\n${closingBraceIndent}${closingBrace}`; + } + + return `${openingBrace}${values.join(join)}${closingBrace}`; +} + +/** + * Create a valid Rust string of a literal value using serde_json according to its type. + * + * @param {*} value Any Javascript literal + * @param {Object} opts Target options + * @return {string} + */ +export const literalRepresentation = ( + value: any, + opts: Record, + indentLevel?: number, +): any => { + /* + * Note: this version is almost entirely borrowed from the Python client helper. The + * only real modification involves the braces and the types. The helper + * could potentially be parameterised for reuse. + */ + indentLevel = indentLevel === undefined ? 1 : indentLevel + 1; + + switch (Object.prototype.toString.call(value)) { + case '[object Number]': + return value; + + case '[object Array]': { + let pretty = false; + const valuesRep: any = (value as any[]).map(v => { + // Switch to prettify if the value is a dict with more than one key. + if (Object.prototype.toString.call(v) === '[object Object]') { + pretty = Object.keys(v).length > 1; + } + return literalRepresentation(v, opts, indentLevel); + }); + return concatValues('array', valuesRep, pretty, opts.indent, indentLevel); + } + + case '[object Object]': { + const keyValuePairs = []; + for (const key in value) { + keyValuePairs.push(`"${key}": ${literalRepresentation(value[key], opts, indentLevel)}`); + } + return concatValues( + 'object', + keyValuePairs, + opts.pretty && keyValuePairs.length > 1, + opts.indent, + indentLevel, + ); + } + + case '[object Null]': + return 'json!(null)'; + + case '[object Boolean]': + return value ? 'true' : 'false'; + + default: + if (value === null || value === undefined) { + return ''; + } + return `"${value.toString().replace(/"/g, '\\"')}"`; + } +}; diff --git a/src/targets/rust/reqwest/client.ts b/src/targets/rust/reqwest/client.ts new file mode 100644 index 000000000..4c2def087 --- /dev/null +++ b/src/targets/rust/reqwest/client.ts @@ -0,0 +1,240 @@ +/** + * @description + * HTTP code snippet generator for Rust using reqwest + * + * @author + * @Benjscho + * + * for any questions or issues regarding the generated code snippet, please open an issue mentioning the author. + */ + +import { CodeBuilder } from '../../../helpers/code-builder'; +import type { Client } from '../../index.js'; +import { literalRepresentation } from '../helpers'; + +export const reqwest: Client = { + info: { + key: 'reqwest', + title: 'reqwest', + link: 'https://docs.rs/reqwest/latest/reqwest/', + description: 'reqwest HTTP library', + extname: '.rs', + }, + convert: ({ queryObj, url, postData, allHeaders, method }, options) => { + const opts = { + indent: ' ', + pretty: true, + ...options, + }; + + let indentLevel = 0; + + // start snippet + const { push, blank, join, pushToLast, unshift } = new CodeBuilder({ indent: opts.indent }); + + // import reqwest + push('use reqwest;', indentLevel); + blank(); + + // start async main for tokio + push('#[tokio::main]', indentLevel); + push('pub async fn main() {', indentLevel); + indentLevel += 1; + + // add url + push(`let url = "${url}";`, indentLevel); + blank(); + + let hasQuery = false; + // construct query string + if (Object.keys(queryObj).length) { + hasQuery = true; + push('let querystring = [', indentLevel); + indentLevel += 1; + for (const [key, value] of Object.entries(queryObj)) { + push(`("${key}", "${value}"),`, indentLevel); + } + indentLevel -= 1; + push('];', indentLevel); + blank(); + } + + // construct payload + let payload: Record = {}; + const files: Record = {}; + + let hasFiles = false; + let hasForm = false; + let hasBody = false; + let jsonPayload = false; + let isMultipart = false; + switch (postData.mimeType) { + case 'application/json': + if (postData.jsonObj) { + push( + `let payload = ${literalRepresentation(postData.jsonObj, opts, indentLevel)};`, + indentLevel, + ); + } + jsonPayload = true; + break; + + case 'multipart/form-data': + isMultipart = true; + + if (!postData.params) { + push(`let form = reqwest::multipart::Form::new()`, indentLevel); + push(`.text("", "");`, indentLevel + 1); + break; + } + + payload = {}; + postData.params.forEach(p => { + if (p.fileName) { + files[p.name] = p.fileName; + hasFiles = true; + } else { + payload[p.name] = p.value; + } + }); + + if (hasFiles) { + for (const line of fileToPartString) { + push(line, indentLevel); + } + blank(); + } + push(`let form = reqwest::multipart::Form::new()`, indentLevel); + + for (const [name, fileName] of Object.entries(files)) { + push(`.part("${name}", file_to_part("${fileName}").await)`, indentLevel + 1); + } + for (const [name, value] of Object.entries(payload)) { + push(`.text("${name}", "${value}")`, indentLevel + 1); + } + pushToLast(';'); + + break; + + default: { + if (postData.mimeType === 'application/x-www-form-urlencoded' && postData.paramsObj) { + push( + `let payload = ${literalRepresentation(postData.paramsObj, opts, indentLevel)};`, + indentLevel, + ); + hasForm = true; + break; + } + + if (postData.text) { + push( + `let payload = ${literalRepresentation(postData.text, opts, indentLevel)};`, + indentLevel, + ); + hasBody = true; + break; + } + } + } + + if (hasForm || jsonPayload || hasBody) { + unshift(`use serde_json::json;`); + blank(); + } + + let hasHeaders = false; + // construct headers + if (Object.keys(allHeaders).length) { + hasHeaders = true; + push('let mut headers = reqwest::header::HeaderMap::new();', indentLevel); + for (const [key, value] of Object.entries(allHeaders)) { + // Skip setting content-type if there is a file, as this header will + // cause the request to hang, and reqwest will set it for us. + if (key.toLowerCase() === 'content-type' && isMultipart) { + continue; + } + push( + `headers.insert("${key}", ${literalRepresentation(value, opts)}.parse().unwrap());`, + indentLevel, + ); + } + blank(); + } + + // construct client + push('let client = reqwest::Client::new();', indentLevel); + + // construct query + switch (method) { + case 'POST': + push(`let response = client.post(url)`, indentLevel); + break; + + case 'GET': + push(`let response = client.get(url)`, indentLevel); + break; + + default: { + push( + `let response = client.request(reqwest::Method::from_str("${method}").unwrap(), url)`, + indentLevel, + ); + unshift(`use std::str::FromStr;`); + break; + } + } + + if (hasQuery) { + push(`.query(&querystring)`, indentLevel + 1); + } + + if (isMultipart) { + push(`.multipart(form)`, indentLevel + 1); + } + + if (hasHeaders) { + push(`.headers(headers)`, indentLevel + 1); + } + + if (jsonPayload) { + push(`.json(&payload)`, indentLevel + 1); + } + + if (hasForm) { + push(`.form(&payload)`, indentLevel + 1); + } + + if (hasBody) { + push(`.body(payload)`, indentLevel + 1); + } + + // send query + push('.send()', indentLevel + 1); + push('.await;', indentLevel + 1); + blank(); + + // Print response + push('let results = response.unwrap()', indentLevel); + push('.json::()', indentLevel + 1); + push('.await', indentLevel + 1); + push('.unwrap();', indentLevel + 1); + blank(); + + push('dbg!(results);', indentLevel); + + push('}\n'); + + return join(); + }, +}; + +const fileToPartString = [ + `async fn file_to_part(file_name: &'static str) -> reqwest::multipart::Part {`, + ` let file = tokio::fs::File::open(file_name).await.unwrap();`, + ` let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new());`, + ` let body = reqwest::Body::wrap_stream(stream);`, + ` reqwest::multipart::Part::stream(body)`, + ` .file_name(file_name)`, + ` .mime_str("text/plain").unwrap()`, + `}`, +]; diff --git a/src/targets/rust/reqwest/fixtures/application-form-encoded.rs b/src/targets/rust/reqwest/fixtures/application-form-encoded.rs new file mode 100644 index 000000000..cfd4e7058 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/application-form-encoded.rs @@ -0,0 +1,29 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = json!({ + "foo": "bar", + "hello": "world" + }); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "application/x-www-form-urlencoded".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .form(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/application-json.rs b/src/targets/rust/reqwest/fixtures/application-json.rs new file mode 100644 index 000000000..9c2f155d3 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/application-json.rs @@ -0,0 +1,33 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = json!({ + "number": 1, + "string": "f\"oo", + "arr": (1, 2, 3), + "nested": json!({"a": "b"}), + "arr_mix": (1, "a", json!({"arr_mix_nested": json!({})})), + "boolean": false + }); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "application/json".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .json(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/cookies.rs b/src/targets/rust/reqwest/fixtures/cookies.rs new file mode 100644 index 000000000..fd84c788d --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/cookies.rs @@ -0,0 +1,22 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("cookie", "foo=bar; bar=baz".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/custom-method.rs b/src/targets/rust/reqwest/fixtures/custom-method.rs new file mode 100644 index 000000000..be726a016 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/custom-method.rs @@ -0,0 +1,19 @@ +use std::str::FromStr; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let client = reqwest::Client::new(); + let response = client.request(reqwest::Method::from_str("PROPFIND").unwrap(), url) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/full.rs b/src/targets/rust/reqwest/fixtures/full.rs new file mode 100644 index 000000000..6f5d0eb9d --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/full.rs @@ -0,0 +1,35 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let querystring = [ + ("foo", "bar,baz"), + ("baz", "abc"), + ("key", "value"), + ]; + + let payload = json!({"foo": "bar"}); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("cookie", "foo=bar; bar=baz".parse().unwrap()); + headers.insert("accept", "application/json".parse().unwrap()); + headers.insert("content-type", "application/x-www-form-urlencoded".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .query(&querystring) + .headers(headers) + .form(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/headers.rs b/src/targets/rust/reqwest/fixtures/headers.rs new file mode 100644 index 000000000..9552aac2f --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/headers.rs @@ -0,0 +1,24 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("accept", "application/json".parse().unwrap()); + headers.insert("x-foo", "Bar".parse().unwrap()); + headers.insert("quoted-value", "\"quoted\" 'string'".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.get(url) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/https.rs b/src/targets/rust/reqwest/fixtures/https.rs new file mode 100644 index 000000000..ce06b998b --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/https.rs @@ -0,0 +1,18 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "https://mockbin.com/har"; + + let client = reqwest::Client::new(); + let response = client.get(url) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs b/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs new file mode 100644 index 000000000..2d43904dd --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs @@ -0,0 +1,26 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = json!({"foo": "bar"}); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "application/json".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .json(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs b/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs new file mode 100644 index 000000000..4c82a8567 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs @@ -0,0 +1,26 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = json!({"foo": json!(null)}); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "application/json".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .json(&payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/multipart-data.rs b/src/targets/rust/reqwest/fixtures/multipart-data.rs new file mode 100644 index 000000000..72644efce --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/multipart-data.rs @@ -0,0 +1,34 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + async fn file_to_part(file_name: &'static str) -> reqwest::multipart::Part { + let file = tokio::fs::File::open(file_name).await.unwrap(); + let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new()); + let body = reqwest::Body::wrap_stream(stream); + reqwest::multipart::Part::stream(body) + .file_name(file_name) + .mime_str("text/plain").unwrap() + } + + let form = reqwest::multipart::Form::new() + .part("foo", file_to_part("hello.txt").await) + .text("bar", "Bonjour le monde"); + let mut headers = reqwest::header::HeaderMap::new(); + + let client = reqwest::Client::new(); + let response = client.post(url) + .multipart(form) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/multipart-file.rs b/src/targets/rust/reqwest/fixtures/multipart-file.rs new file mode 100644 index 000000000..fa1260c6e --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/multipart-file.rs @@ -0,0 +1,33 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + async fn file_to_part(file_name: &'static str) -> reqwest::multipart::Part { + let file = tokio::fs::File::open(file_name).await.unwrap(); + let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new()); + let body = reqwest::Body::wrap_stream(stream); + reqwest::multipart::Part::stream(body) + .file_name(file_name) + .mime_str("text/plain").unwrap() + } + + let form = reqwest::multipart::Form::new() + .part("foo", file_to_part("test/fixtures/files/hello.txt").await); + let mut headers = reqwest::header::HeaderMap::new(); + + let client = reqwest::Client::new(); + let response = client.post(url) + .multipart(form) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs b/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs new file mode 100644 index 000000000..8cc2a5f0d --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs @@ -0,0 +1,24 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let form = reqwest::multipart::Form::new() + .text("", ""); + let mut headers = reqwest::header::HeaderMap::new(); + + let client = reqwest::Client::new(); + let response = client.post(url) + .multipart(form) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/multipart-form-data.rs b/src/targets/rust/reqwest/fixtures/multipart-form-data.rs new file mode 100644 index 000000000..f17bcb008 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/multipart-form-data.rs @@ -0,0 +1,24 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let form = reqwest::multipart::Form::new() + .text("foo", "bar"); + let mut headers = reqwest::header::HeaderMap::new(); + + let client = reqwest::Client::new(); + let response = client.post(url) + .multipart(form) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/nested.rs b/src/targets/rust/reqwest/fixtures/nested.rs new file mode 100644 index 000000000..d0eec9b8f --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/nested.rs @@ -0,0 +1,25 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let querystring = [ + ("foo[bar]", "baz,zap"), + ("fiz", "buz"), + ("key", "value"), + ]; + + let client = reqwest::Client::new(); + let response = client.get(url) + .query(&querystring) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/query.rs b/src/targets/rust/reqwest/fixtures/query.rs new file mode 100644 index 000000000..3b715402d --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/query.rs @@ -0,0 +1,25 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let querystring = [ + ("foo", "bar,baz"), + ("baz", "abc"), + ("key", "value"), + ]; + + let client = reqwest::Client::new(); + let response = client.get(url) + .query(&querystring) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/short.rs b/src/targets/rust/reqwest/fixtures/short.rs new file mode 100644 index 000000000..886d61d45 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/short.rs @@ -0,0 +1,18 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let client = reqwest::Client::new(); + let response = client.get(url) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/text-plain.rs b/src/targets/rust/reqwest/fixtures/text-plain.rs new file mode 100644 index 000000000..d23f14879 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/text-plain.rs @@ -0,0 +1,26 @@ +use serde_json::json; +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://mockbin.com/har"; + + let payload = "Hello World"; + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "text/plain".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .body(payload) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/target.ts b/src/targets/rust/target.ts new file mode 100644 index 000000000..0155e084d --- /dev/null +++ b/src/targets/rust/target.ts @@ -0,0 +1,13 @@ +import type { Target } from '../index.js'; +import { reqwest } from './reqwest/client'; + +export const rust: Target = { + info: { + key: 'rust', + title: 'Rust', + default: 'reqwest', + }, + clientsById: { + reqwest, + }, +}; From a650387064a708ca1881168969764e011c1f7a76 Mon Sep 17 00:00:00 2001 From: Darren Yong Date: Thu, 4 Jun 2026 16:37:21 -0700 Subject: [PATCH 2/5] test: update fixtures and snapshots --- src/helpers/__snapshots__/utils.test.ts.snap | 14 +++++++++++ .../fixtures/application-form-encoded.rs | 2 +- .../rust/reqwest/fixtures/application-json.rs | 4 ++-- src/targets/rust/reqwest/fixtures/cookies.rs | 4 ++-- .../rust/reqwest/fixtures/custom-method.rs | 2 +- src/targets/rust/reqwest/fixtures/full.rs | 2 +- src/targets/rust/reqwest/fixtures/headers.rs | 3 ++- .../rust/reqwest/fixtures/http-insecure.rs | 18 ++++++++++++++ .../reqwest/fixtures/jsonObj-multiline.rs | 2 +- .../reqwest/fixtures/jsonObj-null-value.rs | 2 +- .../rust/reqwest/fixtures/multipart-data.rs | 4 ++-- .../rust/reqwest/fixtures/multipart-file.rs | 4 ++-- .../fixtures/multipart-form-data-no-params.rs | 2 +- .../reqwest/fixtures/multipart-form-data.rs | 2 +- src/targets/rust/reqwest/fixtures/nested.rs | 2 +- .../reqwest/fixtures/postdata-malformed.rs | 22 +++++++++++++++++ .../rust/reqwest/fixtures/query-encoded.rs | 24 +++++++++++++++++++ src/targets/rust/reqwest/fixtures/query.rs | 2 +- src/targets/rust/reqwest/fixtures/short.rs | 2 +- .../rust/reqwest/fixtures/text-plain.rs | 2 +- 20 files changed, 99 insertions(+), 20 deletions(-) create mode 100644 src/targets/rust/reqwest/fixtures/http-insecure.rs create mode 100644 src/targets/rust/reqwest/fixtures/postdata-malformed.rs create mode 100644 src/targets/rust/reqwest/fixtures/query-encoded.rs diff --git a/src/helpers/__snapshots__/utils.test.ts.snap b/src/helpers/__snapshots__/utils.test.ts.snap index cc6b060bc..b40c71bf8 100644 --- a/src/helpers/__snapshots__/utils.test.ts.snap +++ b/src/helpers/__snapshots__/utils.test.ts.snap @@ -343,6 +343,20 @@ exports[`availableTargets > returns all available targets 1`] = ` "key": "ruby", "title": "Ruby", }, + { + "clients": [ + { + "description": "reqwest HTTP library", + "extname": ".rs", + "key": "reqwest", + "link": "https://docs.rs/reqwest/latest/reqwest/", + "title": "reqwest", + }, + ], + "default": "reqwest", + "key": "rust", + "title": "Rust", + }, { "cli": "%s", "clients": [ diff --git a/src/targets/rust/reqwest/fixtures/application-form-encoded.rs b/src/targets/rust/reqwest/fixtures/application-form-encoded.rs index cfd4e7058..af37cad17 100644 --- a/src/targets/rust/reqwest/fixtures/application-form-encoded.rs +++ b/src/targets/rust/reqwest/fixtures/application-form-encoded.rs @@ -3,7 +3,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let payload = json!({ "foo": "bar", diff --git a/src/targets/rust/reqwest/fixtures/application-json.rs b/src/targets/rust/reqwest/fixtures/application-json.rs index 9c2f155d3..795df61de 100644 --- a/src/targets/rust/reqwest/fixtures/application-json.rs +++ b/src/targets/rust/reqwest/fixtures/application-json.rs @@ -3,14 +3,14 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let payload = json!({ "number": 1, "string": "f\"oo", "arr": (1, 2, 3), "nested": json!({"a": "b"}), - "arr_mix": (1, "a", json!({"arr_mix_nested": json!({})})), + "arr_mix": (1, "a", json!({"arr_mix_nested": ()})), "boolean": false }); diff --git a/src/targets/rust/reqwest/fixtures/cookies.rs b/src/targets/rust/reqwest/fixtures/cookies.rs index fd84c788d..5c27d3988 100644 --- a/src/targets/rust/reqwest/fixtures/cookies.rs +++ b/src/targets/rust/reqwest/fixtures/cookies.rs @@ -2,13 +2,13 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/cookies"; let mut headers = reqwest::header::HeaderMap::new(); headers.insert("cookie", "foo=bar; bar=baz".parse().unwrap()); let client = reqwest::Client::new(); - let response = client.post(url) + let response = client.get(url) .headers(headers) .send() .await; diff --git a/src/targets/rust/reqwest/fixtures/custom-method.rs b/src/targets/rust/reqwest/fixtures/custom-method.rs index be726a016..e7b200a66 100644 --- a/src/targets/rust/reqwest/fixtures/custom-method.rs +++ b/src/targets/rust/reqwest/fixtures/custom-method.rs @@ -3,7 +3,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let client = reqwest::Client::new(); let response = client.request(reqwest::Method::from_str("PROPFIND").unwrap(), url) diff --git a/src/targets/rust/reqwest/fixtures/full.rs b/src/targets/rust/reqwest/fixtures/full.rs index 6f5d0eb9d..250e9418f 100644 --- a/src/targets/rust/reqwest/fixtures/full.rs +++ b/src/targets/rust/reqwest/fixtures/full.rs @@ -3,7 +3,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let querystring = [ ("foo", "bar,baz"), diff --git a/src/targets/rust/reqwest/fixtures/headers.rs b/src/targets/rust/reqwest/fixtures/headers.rs index 9552aac2f..4a7b2ab2e 100644 --- a/src/targets/rust/reqwest/fixtures/headers.rs +++ b/src/targets/rust/reqwest/fixtures/headers.rs @@ -2,11 +2,12 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/headers"; let mut headers = reqwest::header::HeaderMap::new(); headers.insert("accept", "application/json".parse().unwrap()); headers.insert("x-foo", "Bar".parse().unwrap()); + headers.insert("x-bar", "Foo".parse().unwrap()); headers.insert("quoted-value", "\"quoted\" 'string'".parse().unwrap()); let client = reqwest::Client::new(); diff --git a/src/targets/rust/reqwest/fixtures/http-insecure.rs b/src/targets/rust/reqwest/fixtures/http-insecure.rs new file mode 100644 index 000000000..008733250 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/http-insecure.rs @@ -0,0 +1,18 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "http://httpbin.org/anything"; + + let client = reqwest::Client::new(); + let response = client.get(url) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs b/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs index 2d43904dd..aab0d1069 100644 --- a/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs +++ b/src/targets/rust/reqwest/fixtures/jsonObj-multiline.rs @@ -3,7 +3,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let payload = json!({"foo": "bar"}); diff --git a/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs b/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs index 4c82a8567..7250e76fc 100644 --- a/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs +++ b/src/targets/rust/reqwest/fixtures/jsonObj-null-value.rs @@ -3,7 +3,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let payload = json!({"foo": json!(null)}); diff --git a/src/targets/rust/reqwest/fixtures/multipart-data.rs b/src/targets/rust/reqwest/fixtures/multipart-data.rs index 72644efce..8efafcdff 100644 --- a/src/targets/rust/reqwest/fixtures/multipart-data.rs +++ b/src/targets/rust/reqwest/fixtures/multipart-data.rs @@ -2,7 +2,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; async fn file_to_part(file_name: &'static str) -> reqwest::multipart::Part { let file = tokio::fs::File::open(file_name).await.unwrap(); @@ -14,7 +14,7 @@ pub async fn main() { } let form = reqwest::multipart::Form::new() - .part("foo", file_to_part("hello.txt").await) + .part("foo", file_to_part("src/fixtures/files/hello.txt").await) .text("bar", "Bonjour le monde"); let mut headers = reqwest::header::HeaderMap::new(); diff --git a/src/targets/rust/reqwest/fixtures/multipart-file.rs b/src/targets/rust/reqwest/fixtures/multipart-file.rs index fa1260c6e..045c39734 100644 --- a/src/targets/rust/reqwest/fixtures/multipart-file.rs +++ b/src/targets/rust/reqwest/fixtures/multipart-file.rs @@ -2,7 +2,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; async fn file_to_part(file_name: &'static str) -> reqwest::multipart::Part { let file = tokio::fs::File::open(file_name).await.unwrap(); @@ -14,7 +14,7 @@ pub async fn main() { } let form = reqwest::multipart::Form::new() - .part("foo", file_to_part("test/fixtures/files/hello.txt").await); + .part("foo", file_to_part("src/fixtures/files/hello.txt").await); let mut headers = reqwest::header::HeaderMap::new(); let client = reqwest::Client::new(); diff --git a/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs b/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs index 8cc2a5f0d..f253ed357 100644 --- a/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs +++ b/src/targets/rust/reqwest/fixtures/multipart-form-data-no-params.rs @@ -2,7 +2,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let form = reqwest::multipart::Form::new() .text("", ""); diff --git a/src/targets/rust/reqwest/fixtures/multipart-form-data.rs b/src/targets/rust/reqwest/fixtures/multipart-form-data.rs index f17bcb008..7496e4107 100644 --- a/src/targets/rust/reqwest/fixtures/multipart-form-data.rs +++ b/src/targets/rust/reqwest/fixtures/multipart-form-data.rs @@ -2,7 +2,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let form = reqwest::multipart::Form::new() .text("foo", "bar"); diff --git a/src/targets/rust/reqwest/fixtures/nested.rs b/src/targets/rust/reqwest/fixtures/nested.rs index d0eec9b8f..58f0cf2a9 100644 --- a/src/targets/rust/reqwest/fixtures/nested.rs +++ b/src/targets/rust/reqwest/fixtures/nested.rs @@ -2,7 +2,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let querystring = [ ("foo[bar]", "baz,zap"), diff --git a/src/targets/rust/reqwest/fixtures/postdata-malformed.rs b/src/targets/rust/reqwest/fixtures/postdata-malformed.rs new file mode 100644 index 000000000..6ef285bc4 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/postdata-malformed.rs @@ -0,0 +1,22 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "https://httpbin.org/anything"; + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("content-type", "application/json".parse().unwrap()); + + let client = reqwest::Client::new(); + let response = client.post(url) + .headers(headers) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/query-encoded.rs b/src/targets/rust/reqwest/fixtures/query-encoded.rs new file mode 100644 index 000000000..4d3938579 --- /dev/null +++ b/src/targets/rust/reqwest/fixtures/query-encoded.rs @@ -0,0 +1,24 @@ +use reqwest; + +#[tokio::main] +pub async fn main() { + let url = "https://httpbin.org/anything"; + + let querystring = [ + ("startTime", "2019-06-13T19%3A08%3A25.455Z"), + ("endTime", "2015-09-15T14%3A00%3A12-04%3A00"), + ]; + + let client = reqwest::Client::new(); + let response = client.get(url) + .query(&querystring) + .send() + .await; + + let results = response.unwrap() + .json::() + .await + .unwrap(); + + dbg!(results); +} diff --git a/src/targets/rust/reqwest/fixtures/query.rs b/src/targets/rust/reqwest/fixtures/query.rs index 3b715402d..915dfc983 100644 --- a/src/targets/rust/reqwest/fixtures/query.rs +++ b/src/targets/rust/reqwest/fixtures/query.rs @@ -2,7 +2,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let querystring = [ ("foo", "bar,baz"), diff --git a/src/targets/rust/reqwest/fixtures/short.rs b/src/targets/rust/reqwest/fixtures/short.rs index 886d61d45..fe9a06bdc 100644 --- a/src/targets/rust/reqwest/fixtures/short.rs +++ b/src/targets/rust/reqwest/fixtures/short.rs @@ -2,7 +2,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let client = reqwest::Client::new(); let response = client.get(url) diff --git a/src/targets/rust/reqwest/fixtures/text-plain.rs b/src/targets/rust/reqwest/fixtures/text-plain.rs index d23f14879..fbb39c9c4 100644 --- a/src/targets/rust/reqwest/fixtures/text-plain.rs +++ b/src/targets/rust/reqwest/fixtures/text-plain.rs @@ -3,7 +3,7 @@ use reqwest; #[tokio::main] pub async fn main() { - let url = "http://mockbin.com/har"; + let url = "https://httpbin.org/anything"; let payload = "Hello World"; From abb9d3a35f57926ba8700d711d1d3d25ca263657 Mon Sep 17 00:00:00 2001 From: Darren Yong Date: Thu, 4 Jun 2026 16:46:45 -0700 Subject: [PATCH 3/5] chore: fix lint issues --- src/targets/rust/helpers.ts | 4 ++-- src/targets/rust/reqwest/client.ts | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/targets/rust/helpers.ts b/src/targets/rust/helpers.ts index 39844ed88..50c85dfd8 100644 --- a/src/targets/rust/helpers.ts +++ b/src/targets/rust/helpers.ts @@ -57,8 +57,8 @@ export const literalRepresentation = ( case '[object Object]': { const keyValuePairs = []; - for (const key in value) { - keyValuePairs.push(`"${key}": ${literalRepresentation(value[key], opts, indentLevel)}`); + for (const [key, val] of Object.entries(value)) { + keyValuePairs.push(`"${key}": ${literalRepresentation(val, opts, indentLevel)}`); } return concatValues( 'object', diff --git a/src/targets/rust/reqwest/client.ts b/src/targets/rust/reqwest/client.ts index 4c2def087..bf0070a87 100644 --- a/src/targets/rust/reqwest/client.ts +++ b/src/targets/rust/reqwest/client.ts @@ -150,13 +150,12 @@ export const reqwest: Client = { for (const [key, value] of Object.entries(allHeaders)) { // Skip setting content-type if there is a file, as this header will // cause the request to hang, and reqwest will set it for us. - if (key.toLowerCase() === 'content-type' && isMultipart) { - continue; + if (key.toLowerCase() !== 'content-type' || !isMultipart) { + push( + `headers.insert("${key}", ${literalRepresentation(value, opts)}.parse().unwrap());`, + indentLevel, + ); } - push( - `headers.insert("${key}", ${literalRepresentation(value, opts)}.parse().unwrap());`, - indentLevel, - ); } blank(); } From e274af63d5b90c7896325062abf842a6648a034d Mon Sep 17 00:00:00 2001 From: Darren Yong Date: Thu, 4 Jun 2026 16:47:46 -0700 Subject: [PATCH 4/5] chore: more lint --- src/helpers/code-builder.ts | 1 - src/targets/rust/helpers.ts | 18 +++--------------- src/targets/rust/reqwest/client.ts | 28 +++++++--------------------- src/targets/rust/target.ts | 1 + 4 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/helpers/code-builder.ts b/src/helpers/code-builder.ts index ccdcc3427..2bdcf9d71 100644 --- a/src/helpers/code-builder.ts +++ b/src/helpers/code-builder.ts @@ -67,7 +67,6 @@ export class CodeBuilder { this.code[this.code.length - 1] = updatedLine; }; - /** * Add an empty line at the end of current lines */ diff --git a/src/targets/rust/helpers.ts b/src/targets/rust/helpers.ts index 50c85dfd8..603503598 100644 --- a/src/targets/rust/helpers.ts +++ b/src/targets/rust/helpers.ts @@ -12,9 +12,7 @@ function concatValues( const closingBrace = concatType === 'object' ? '})' : ')'; if (pretty) { - return `${openingBrace}\n${currentIndent}${values.join( - join, - )}\n${closingBraceIndent}${closingBrace}`; + return `${openingBrace}\n${currentIndent}${values.join(join)}\n${closingBraceIndent}${closingBrace}`; } return `${openingBrace}${values.join(join)}${closingBrace}`; @@ -27,11 +25,7 @@ function concatValues( * @param {Object} opts Target options * @return {string} */ -export const literalRepresentation = ( - value: any, - opts: Record, - indentLevel?: number, -): any => { +export const literalRepresentation = (value: any, opts: Record, indentLevel?: number): any => { /* * Note: this version is almost entirely borrowed from the Python client helper. The * only real modification involves the braces and the types. The helper @@ -60,13 +54,7 @@ export const literalRepresentation = ( for (const [key, val] of Object.entries(value)) { keyValuePairs.push(`"${key}": ${literalRepresentation(val, opts, indentLevel)}`); } - return concatValues( - 'object', - keyValuePairs, - opts.pretty && keyValuePairs.length > 1, - opts.indent, - indentLevel, - ); + return concatValues('object', keyValuePairs, opts.pretty && keyValuePairs.length > 1, opts.indent, indentLevel); } case '[object Null]': diff --git a/src/targets/rust/reqwest/client.ts b/src/targets/rust/reqwest/client.ts index bf0070a87..6f25c16f6 100644 --- a/src/targets/rust/reqwest/client.ts +++ b/src/targets/rust/reqwest/client.ts @@ -8,8 +8,9 @@ * for any questions or issues regarding the generated code snippet, please open an issue mentioning the author. */ -import { CodeBuilder } from '../../../helpers/code-builder'; import type { Client } from '../../index.js'; + +import { CodeBuilder } from '../../../helpers/code-builder'; import { literalRepresentation } from '../helpers'; export const reqwest: Client = { @@ -71,10 +72,7 @@ export const reqwest: Client = { switch (postData.mimeType) { case 'application/json': if (postData.jsonObj) { - push( - `let payload = ${literalRepresentation(postData.jsonObj, opts, indentLevel)};`, - indentLevel, - ); + push(`let payload = ${literalRepresentation(postData.jsonObj, opts, indentLevel)};`, indentLevel); } jsonPayload = true; break; @@ -118,19 +116,13 @@ export const reqwest: Client = { default: { if (postData.mimeType === 'application/x-www-form-urlencoded' && postData.paramsObj) { - push( - `let payload = ${literalRepresentation(postData.paramsObj, opts, indentLevel)};`, - indentLevel, - ); + push(`let payload = ${literalRepresentation(postData.paramsObj, opts, indentLevel)};`, indentLevel); hasForm = true; break; } if (postData.text) { - push( - `let payload = ${literalRepresentation(postData.text, opts, indentLevel)};`, - indentLevel, - ); + push(`let payload = ${literalRepresentation(postData.text, opts, indentLevel)};`, indentLevel); hasBody = true; break; } @@ -151,10 +143,7 @@ export const reqwest: Client = { // Skip setting content-type if there is a file, as this header will // cause the request to hang, and reqwest will set it for us. if (key.toLowerCase() !== 'content-type' || !isMultipart) { - push( - `headers.insert("${key}", ${literalRepresentation(value, opts)}.parse().unwrap());`, - indentLevel, - ); + push(`headers.insert("${key}", ${literalRepresentation(value, opts)}.parse().unwrap());`, indentLevel); } } blank(); @@ -174,10 +163,7 @@ export const reqwest: Client = { break; default: { - push( - `let response = client.request(reqwest::Method::from_str("${method}").unwrap(), url)`, - indentLevel, - ); + push(`let response = client.request(reqwest::Method::from_str("${method}").unwrap(), url)`, indentLevel); unshift(`use std::str::FromStr;`); break; } diff --git a/src/targets/rust/target.ts b/src/targets/rust/target.ts index 0155e084d..b78eda028 100644 --- a/src/targets/rust/target.ts +++ b/src/targets/rust/target.ts @@ -1,4 +1,5 @@ import type { Target } from '../index.js'; + import { reqwest } from './reqwest/client'; export const rust: Target = { From c26cbf9247fc354eb2fee1b4c11ac7b40b014745 Mon Sep 17 00:00:00 2001 From: Darren Yong <41130056+darrenyong@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:10:54 -0700 Subject: [PATCH 5/5] refactor: escape backslash characters Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/targets/rust/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/targets/rust/helpers.ts b/src/targets/rust/helpers.ts index 603503598..db6b90df2 100644 --- a/src/targets/rust/helpers.ts +++ b/src/targets/rust/helpers.ts @@ -67,6 +67,6 @@ export const literalRepresentation = (value: any, opts: Record, ind if (value === null || value === undefined) { return ''; } - return `"${value.toString().replace(/"/g, '\\"')}"`; + return `"${value.toString().replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`; } };