Convert curl to Rust with reqwest
This curl to Rust converter translates a curl command into code for reqwest, the most widely used HTTP client crate in Rust. It targets the reqwest::blocking API so the result is a plain fn main() with no async runtime to set up, and it returns Result<(), Box<dyn std::error::Error>> so every call can use ?.
Example output
For a JSON POST with a bearer token and --max-time 30, the converter produces:
let client = reqwest::blocking::Client::builder().timeout(std::time::Duration::from_secs(30)).redirect(reqwest::redirect::Policy::none()).build()?;let res = client.post("https://api.example.com/v1/orders?notify=true").header("Authorization", "Bearer YOUR_API_TOKEN").json(&serde_json::json!({"customer_id": 42,"gift": false})).send()?;The first line of the output is a comment with the exact dependency to add, for example reqwest = { version = "0.12", features = ["blocking", "json"] } plus serde_json = "1". The multipart feature is added only when the command uses -F.
How curl options are mapped
- JSON bodies: when the Content-Type is JSON and the body parses, it becomes a
serde_json::json!macro passed to.json(). reqwest then sets the Content-Type header itself, so the duplicate header is dropped. - Other bodies: non-JSON data is sent with
.body(). A bare-dalso gets an explicitapplication/x-www-form-urlencodedheader. - Methods: GET, POST, PUT, PATCH, DELETE and HEAD use the matching shortcut such as
client.post(). Anything else usesreqwest::Method::from_bytes. - Auth:
-u user:passbecomes.basic_auth(user, Some(pass)). - Uploads:
-Ffields build amultipart::Formwith.text()and.file(). - Client settings:
--max-timesets.timeout()and-kadds.danger_accept_invalid_certs(true).
Redirects and other differences
curl does not follow redirects unless you pass -L, but reqwest does by default. To keep the behavior identical, the converter adds .redirect(reqwest::redirect::Policy::none()) when -L is missing. Remove that line if you want reqwest's default of up to 10 hops.
The generated program prints res.status() and the body text. If you want non-2xx responses to become errors, chain .error_for_status()? after .send()?.
Proxy settings, client certificates and retries are not converted. Options with no mapping are listed as comments at the top of the output. Data read from a file with -d @file becomes a placeholder you need to replace.
Running the output in a new project
cargo new curl_test && cd curl_test
cargo add reqwest --features blocking,json
cargo add serde_json
# paste the converted code into src/main.rs
cargo runThe blocking client is meant for scripts and command-line tools. Do not call it from inside an async runtime such as a Tokio #[tokio::main] function: reqwest's blocking client starts its own runtime internally and panics when it is used inside another one. In async code, drop the blocking feature and add .await after .send() and .text() instead, as described in the FAQ below.
Privacy
Everything runs locally in your browser, so tokens and passwords in the command stay on your machine. For other languages, see curl to Go and curl to Python, or generate response structs with JSON to Rust.